Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/db/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,3 +888,11 @@ export async function resetPostgresDatabase(
}

export type Db = ReturnType<typeof createDb>;

/**
* The open transaction handle drizzle hands to a `db.transaction(...)` callback.
* Split across two aliases so the one-line nested-`Parameters` incantation this
* type replaces appears nowhere in the tree (BLO-34656).
*/
type DbTransactionCallback = Parameters<Db["transaction"]>[0];
export type DbTransaction = Parameters<DbTransactionCallback>[0];
1 change: 1 addition & 0 deletions packages/db/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
migratePostgresIfEmpty,
type MigrationBootstrapResult,
type Db,
type DbTransaction,
} from "./client.js";
export {
checkPendingMigrationPreflight,
Expand Down
4 changes: 2 additions & 2 deletions server/src/routes/cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Router, type Request, type Response } from "express";
import multer from "multer";
import { z } from "zod";
import { and, asc, desc, eq, ilike, inArray, isNull, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
agents,
assets,
Expand Down Expand Up @@ -33,7 +33,7 @@ import { documentAnnotationService, logActivity } from "../services/index.js";
import type { StorageService } from "../storage/types.js";
import { assertCompanyAccess, getActorInfo, hasCompanyAccess } from "./authz.js";

type CaseRouteDb = Db | Parameters<Parameters<Db["transaction"]>[0]>[0];
type CaseRouteDb = Db | DbTransaction;
type CaseActor = ReturnType<typeof getActorInfo>;

const CASE_STATUSES = ["draft", "in_progress", "in_review", "approved", "done", "cancelled"] as const;
Expand Down
2 changes: 1 addition & 1 deletion server/src/routes/github-webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { Router } from "express";
import crypto from "node:crypto";
import {
type Db,
type DbTransaction,
POSTGRES_POOL_MAX,
agents,
agentWakeupRequests,
Expand Down Expand Up @@ -114,7 +115,6 @@ import {
type GithubReviewGateAuthorityConfig,
} from "../services/github-review-gate-authority.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type PrReviewerSelectionDb = Pick<Db | DbTransaction, "select">;

// Keep lock contention well below GitHub's webhook timeout. If this bounded
Expand Down
4 changes: 2 additions & 2 deletions server/src/routes/pipelines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Router, type Request } from "express";
import { z } from "zod";
import { and, asc, desc, eq, ilike, inArray, isNotNull, isNull, ne, or, sql } from "drizzle-orm";
import { alias } from "drizzle-orm/pg-core";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
agents,
documents,
Expand Down Expand Up @@ -84,7 +84,7 @@ import {

/** Per-stage instructions document keys look like `stage-instructions:{stageId}`. */
const STAGE_INSTRUCTIONS_PREFIX = "stage-instructions:";
type PipelineRouteDb = Db | Parameters<Parameters<Db["transaction"]>[0]>[0];
type PipelineRouteDb = Db | DbTransaction;

const stageKindSchema = z.enum(["open", "working", "review", "done", "cancelled"]);
const jsonObjectSchema = z.record(z.string(), z.unknown());
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/agent-invokability.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import { agents } from "@paperclipai/db";
import { getAgentWorkEligibility, type AgentEligibilityAgent, type AgentOrgChainHealth } from "@paperclipai/shared";
import { eq } from "drizzle-orm";

type AgentStatus = (typeof agents.$inferSelect)["status"];
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];

export type AgentOrgRow = Pick<
typeof agents.$inferSelect,
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/branch-run-claims.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { and, eq, isNull } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import { branchRunClaims, externalRuntimeReservations, heartbeatRuns } from "@paperclipai/db";
import { TERMINAL_HEARTBEAT_RUN_STATUSES } from "./issues.js";
import { logger } from "../middleware/logger.js";
Expand All @@ -17,7 +17,6 @@ export type BranchRunClaim = typeof branchRunClaims.$inferSelect;

const ACTIVE_BRANCH_CONSTRAINT = "branch_run_claims_active_branch_idx";
const DEFAULT_BRANCH_CLAIM_LEASE_MS = 30 * 60 * 1000;
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type BranchClaimReadDb = Pick<Db | DbTransaction, "select">;

export class BranchClaimConflictError extends Error {
Expand Down
2 changes: 1 addition & 1 deletion server/src/services/github-status-delivery-outbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
heartbeatRunEvents,
heartbeatRuns,
type Db,
type DbTransaction,
} from "@paperclipai/db";
import { logger } from "../middleware/logger.js";
import {
Expand All @@ -15,7 +16,6 @@ import {
} from "./github-app-auth.js";

type DeliveryRow = typeof githubCommitStatusDeliveries.$inferSelect;
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
// Either handle works for the delivery bookkeeping below. Code reached from
// inside withGithubStatusDeliveryLock must use the transaction handle so the
// critical section does not take a second pool connection.
Expand Down
6 changes: 2 additions & 4 deletions server/src/services/heartbeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import { execFile as execFileCallback } from "node:child_process";
import { promisify } from "node:util";
import { createHash, randomUUID } from "node:crypto";
import { and, asc, desc, eq, exists, getTableColumns, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, not, notInArray, or, sql, type SQL } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";

/**
* Either the pool or an open transaction. BLO-19722 needs a handful of writes
* to run on the same transaction that took a per-run advisory lock, so the
* helpers they go through accept an executor rather than always closing over
* the pool.
*/
type HeartbeatTx = Parameters<Parameters<Db["transaction"]>[0]>[0];
type HeartbeatDbExecutor = Db | HeartbeatTx;
type HeartbeatDbExecutor = Db | DbTransaction;

/**
* Outcome of `enqueueProcessLossRetry` (BLO-19722).
Expand Down Expand Up @@ -584,7 +583,6 @@ import type { PluginWorkerManager } from "./plugin-worker-manager.js";
import { createServerGbrainClient } from "./gbrain-client-factory.js";
import { runSweepWakePreflight } from "./sweep-wake-preflight.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type WakeCoalescingDb = Pick<Db | DbTransaction, "select" | "update" | "insert">;

// Run statuses considered terminal. Used to gate the agent-image-bump
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/issue-checkout-status.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { and, eq, inArray, isNotNull, isNull, or, sql } from "drizzle-orm";
import { heartbeatRuns, issueRelations, issues, type Db } from "@paperclipai/db";
import { heartbeatRuns, issueRelations, issues, type Db, type DbTransaction } from "@paperclipai/db";
import { TERMINAL_HEARTBEAT_RUN_STATUS_VALUES } from "./issue-execution-lock.js";
import { buildIssueMonitorEligibilityPatch } from "./issue-execution-policy.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type DbOrTransaction = Db | DbTransaction;

/**
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/issue-recovery-actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { and, asc, desc, eq, inArray, isNotNull, isNull, lte, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import { issueRecoveryActions, issues } from "@paperclipai/db";
import type {
IssueRecoveryAction,
Expand Down Expand Up @@ -82,7 +82,6 @@ export const RESOLVED_ISSUE_STATUS_EVIDENCE_KEY = "resolvedIssueStatus";
export const RECOVERY_HANDOFF_COMMENT_GRANT_TTL_MS = 24 * 60 * 60 * 1000;

type IssueRecoveryActionRow = typeof issueRecoveryActions.$inferSelect;
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type DbOrTransaction = Db | DbTransaction;

export type UpsertIssueRecoveryActionInput = {
Expand Down
5 changes: 2 additions & 3 deletions server/src/services/issues.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Buffer } from "node:buffer";
import { createHash } from "node:crypto";
import { and, asc, desc, eq, exists, gt, gte, inArray, isNotNull, isNull, like, lt, lte, ne, notInArray, or, sql, type SQL } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
activityLog,
agentWakeupRequests,
Expand Down Expand Up @@ -1007,7 +1007,6 @@ type IssueUserContextInput = {
};
type ProjectGoalReader = Pick<Db, "select">;
type DbReader = Pick<Db, "select">;
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];

/**
* Serialize mutations to the issue parent/blocker graph for one company.
Expand Down Expand Up @@ -5735,7 +5734,7 @@ export function issueService(db: Db) {
agentId: string,
now: Date,
operation: (
tx: Parameters<Parameters<Db["transaction"]>[0]>[0],
tx: DbTransaction,
checkoutExecutionPatch: NonNullable<Awaited<ReturnType<typeof runningCheckoutExecutionPatch>>>["patch"],
) => Promise<T>,
) {
Expand Down
4 changes: 2 additions & 2 deletions server/src/services/pipelines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
import { isDeepStrictEqual } from "node:util";
import { and, asc, desc, eq, inArray, isNotNull, isNull, ne, or, sql } from "drizzle-orm";
import { alias } from "drizzle-orm/pg-core";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
agentWakeupRequests,
agents,
Expand Down Expand Up @@ -178,7 +178,7 @@ export type PipelineAutomationExecutionResult =
| { status: "succeeded"; execution: typeof pipelineAutomationExecutions.$inferSelect }
| { status: "failed"; execution: typeof pipelineAutomationExecutions.$inferSelect };

type PipelineDb = Db | Parameters<Parameters<Db["transaction"]>[0]>[0];
type PipelineDb = Db | DbTransaction;

type PipelineRetryPlanInternal = PipelineAutomationRetryPlan & {
targetStageRow: typeof pipelineStages.$inferSelect | null;
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/pr-review-dispatch-lock.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { and, eq, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import { heartbeatRuns } from "@paperclipai/db";
import {
matchesTaskKey,
prReviewTaskLockSpellings,
} from "./pr-review-duplicate-issue-guard.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];

const PR_REVIEW_DISPATCH_LOCK_PREFIX = "heartbeat:pr-review-dispatch:";

Expand Down
3 changes: 1 addition & 2 deletions server/src/services/pr-review-duplicate-issue-guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,12 @@
* — one duplicate that slips through while the webhook's wake is still
* uncommitted — remains tracked as BLO-21790.
*/
import { type Db, heartbeatRuns } from "@paperclipai/db";
import { type Db, type DbTransaction, heartbeatRuns } from "@paperclipai/db";
import { type Column, type SQL, and, desc, eq, inArray, or, sql } from "drizzle-orm";
import { readGithubPrReviewerAgentIds } from "../config.js";
import { conflict } from "../errors.js";
import { logger } from "../middleware/logger.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type DuplicatePrReviewIssueGuardDb = Pick<DbTransaction, "select" | "transaction">;

/**
Expand Down
4 changes: 2 additions & 2 deletions server/src/services/productivity-review.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { randomUUID } from "node:crypto";
import { and, asc, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, notInArray, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import { clampIssueRequestDepth } from "@paperclipai/shared";
import {
activityLog,
Expand Down Expand Up @@ -1962,7 +1962,7 @@ function dominantErrorCode(
* the BLO-3737 refresh-throttle critical section accept this so the read and the
* write land on the same connection (and therefore inside the same advisory lock).
*/
type DbOrTx = Db | Parameters<Parameters<Db["transaction"]>[0]>[0];
type DbOrTx = Db | DbTransaction;

export function productivityReviewService(db: Db, deps?: ProductivityReviewServiceDeps) {
const issuesSvc = issueService(db);
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/recovery/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { and, asc, desc, eq, gt, gte, inArray, isNull, lt, notInArray, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
DEFAULT_ISSUE_GRAPH_LIVENESS_AUTO_RECOVERY_LOOKBACK_HOURS,
MAX_ISSUE_GRAPH_LIVENESS_AUTO_RECOVERY_LOOKBACK_HOURS,
Expand Down Expand Up @@ -146,7 +146,6 @@ import {
ZERO_TOKEN_SESSION_RESET_RETRY_REASON,
} from "./zero-token-startup-failure.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];

/**
* The recovery transaction has already upserted its source-scoped action when
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/secrets.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { randomUUID } from "node:crypto";
import { and, desc, eq, inArray, like, ne, notInArray, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
agents,
companySecretBindings,
Expand Down Expand Up @@ -75,7 +75,6 @@ const FALLBACK_ADAPTER_SCHEMA_SECRET_FIELDS: Readonly<Record<string, readonly st
};
const USER_SECRET_DEFINITION_KEY_UNIQUE_CONSTRAINT = "user_secret_definitions_company_key_uq";
const USER_SECRET_VALUE_UNIQUE_CONSTRAINT = "company_secrets_user_definition_owner_uq";
type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type SecretBindingDb = Pick<Db | DbTransaction, "select" | "delete" | "insert">;

function isUniqueConstraintViolation(error: unknown, constraintName: string) {
Expand Down
3 changes: 1 addition & 2 deletions server/src/services/tool-access.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createHash, randomBytes, randomUUID } from "node:crypto";
import { readFileSync } from "node:fs";
import { and, asc, desc, eq, gte, inArray, isNull, lt, max, ne, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
activityLog,
agents,
Expand Down Expand Up @@ -153,7 +153,6 @@ type ToolAccessServiceOptions = {
now?: () => Date;
};

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];
type ToolAccessMutationDb = Pick<Db | DbTransaction, "select" | "insert" | "update" | "delete">;

export type McpToolDescriptor = {
Expand Down
4 changes: 1 addition & 3 deletions server/src/services/tool-oauth-legacy-backfill.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { and, eq, ne, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { Db, DbTransaction } from "@paperclipai/db";
import {
companySecretBindings,
companySecretProviderConfigs,
Expand All @@ -11,8 +11,6 @@ import type { McpConnectionCredentialRef, SecretProvider, ToolCredentialSecretRe
import { getSecretProvider } from "../secrets/provider-registry.js";
import type { SecretProviderVaultRuntimeConfig } from "../secrets/types.js";

type DbTransaction = Parameters<Parameters<Db["transaction"]>[0]>[0];

type OAuthTokenKind = "access_token" | "refresh_token";

type LegacyOAuthToken = {
Expand Down