@beignet/provider-db-drizzle 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +1046 -0
  3. package/dist/mysql/index.d.ts +254 -0
  4. package/dist/mysql/index.d.ts.map +1 -0
  5. package/dist/mysql/index.js +348 -0
  6. package/dist/mysql/index.js.map +1 -0
  7. package/dist/postgres/index.d.ts +240 -0
  8. package/dist/postgres/index.d.ts.map +1 -0
  9. package/dist/postgres/index.js +296 -0
  10. package/dist/postgres/index.js.map +1 -0
  11. package/dist/shared/idempotency-core.d.ts +71 -0
  12. package/dist/shared/idempotency-core.d.ts.map +1 -0
  13. package/dist/shared/idempotency-core.js +169 -0
  14. package/dist/shared/idempotency-core.js.map +1 -0
  15. package/dist/shared/identifiers.d.ts +20 -0
  16. package/dist/shared/identifiers.d.ts.map +1 -0
  17. package/dist/shared/identifiers.js +27 -0
  18. package/dist/shared/identifiers.js.map +1 -0
  19. package/dist/shared/instrumentation.d.ts +19 -0
  20. package/dist/shared/instrumentation.d.ts.map +1 -0
  21. package/dist/shared/instrumentation.js +41 -0
  22. package/dist/shared/instrumentation.js.map +1 -0
  23. package/dist/shared/outbox-core.d.ts +76 -0
  24. package/dist/shared/outbox-core.d.ts.map +1 -0
  25. package/dist/shared/outbox-core.js +193 -0
  26. package/dist/shared/outbox-core.js.map +1 -0
  27. package/dist/shared/rows.d.ts +84 -0
  28. package/dist/shared/rows.d.ts.map +1 -0
  29. package/dist/shared/rows.js +128 -0
  30. package/dist/shared/rows.js.map +1 -0
  31. package/dist/sqlite/index.d.ts +235 -0
  32. package/dist/sqlite/index.d.ts.map +1 -0
  33. package/dist/sqlite/index.js +293 -0
  34. package/dist/sqlite/index.js.map +1 -0
  35. package/package.json +173 -0
  36. package/src/mysql/index.ts +627 -0
  37. package/src/postgres/index.ts +572 -0
  38. package/src/shared/idempotency-core.ts +280 -0
  39. package/src/shared/identifiers.ts +28 -0
  40. package/src/shared/instrumentation.ts +49 -0
  41. package/src/shared/outbox-core.ts +322 -0
  42. package/src/shared/rows.ts +197 -0
  43. package/src/sqlite/index.ts +547 -0
@@ -0,0 +1,240 @@
1
+ /**
2
+ * @beignet/provider-db-drizzle/postgres
3
+ *
4
+ * Drizzle ORM Postgres provider, backed by node-postgres (pg), that creates
5
+ * a typed database port. Works with any Postgres-compatible server reachable
6
+ * through a standard connection string.
7
+ *
8
+ * Configuration:
9
+ * - POSTGRES_DB_URL: Postgres connection string (required)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import * as schema from "@/db/schema";
14
+ * import { createDrizzlePostgresProvider } from "@beignet/provider-db-drizzle/postgres";
15
+ *
16
+ * export const drizzlePostgresProvider = createDrizzlePostgresProvider({ schema });
17
+ *
18
+ * // In createNextServer:
19
+ * const server = await createNextServer({
20
+ * ports: basePorts,
21
+ * providers: [drizzlePostgresProvider],
22
+ * // ...
23
+ * });
24
+ *
25
+ * // In infra:
26
+ * const todos = createDrizzleTodoRepository(ctx.ports.db.db);
27
+ * ```
28
+ */
29
+ import type { IdempotencyPort } from "@beignet/core/idempotency";
30
+ import type { OutboxPort } from "@beignet/core/outbox";
31
+ import { type BufferedDomainEventRecorder, type EventBusPort, type UnitOfWorkPort } from "@beignet/core/ports";
32
+ import { type ExtractTablesWithRelations } from "drizzle-orm";
33
+ import { type NodePgDatabase } from "drizzle-orm/node-postgres";
34
+ import { type PgDatabase, type PgQueryResultHKT, PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core";
35
+ import { type Pool, type PoolConfig } from "pg";
36
+ import { z } from "zod";
37
+ /**
38
+ * Typed database port interface.
39
+ * Exposes a typed Drizzle database instance for your schema.
40
+ *
41
+ * @template TSchema - The Drizzle schema type (e.g., `typeof schema`)
42
+ */
43
+ export interface DbPort<TSchema extends Record<string, unknown> = Record<string, unknown>> {
44
+ /**
45
+ * The typed Drizzle database instance.
46
+ * Use this to query your database with full type safety.
47
+ */
48
+ db: NodePgDatabase<TSchema>;
49
+ /**
50
+ * The underlying node-postgres connection pool.
51
+ * Use this for advanced operations not covered by Drizzle ORM.
52
+ */
53
+ pool: Pool;
54
+ }
55
+ /**
56
+ * Drizzle database or transaction client accepted by repository factories.
57
+ *
58
+ * `PgTransaction` extends `PgDatabase`, so this single seam covers the root
59
+ * database, transaction clients, and Postgres-protocol drivers such as
60
+ * node-postgres and PGlite.
61
+ */
62
+ export type DrizzlePostgresDatabase<TSchema extends Record<string, unknown> = Record<string, unknown>> = PgDatabase<PgQueryResultHKT, TSchema>;
63
+ /**
64
+ * Drizzle transaction client passed to Unit of Work repository factories.
65
+ */
66
+ export type DrizzlePostgresTransaction<TSchema extends Record<string, unknown> = Record<string, unknown>> = PgTransaction<PgQueryResultHKT, TSchema, ExtractTablesWithRelations<TSchema>>;
67
+ /**
68
+ * Options for creating a Drizzle Postgres-backed Unit of Work port.
69
+ */
70
+ export interface DrizzlePostgresUnitOfWorkOptions<TSchema extends Record<string, unknown>, TxPorts> {
71
+ /**
72
+ * The root Drizzle database instance from `ctx.ports.db.db`.
73
+ */
74
+ db: DrizzlePostgresDatabase<TSchema>;
75
+ /**
76
+ * Create transaction-scoped ports from the Drizzle transaction client.
77
+ *
78
+ * The event recorder is transaction-local. Record domain events inside the
79
+ * callback and pass `eventBus` to publish them after the database commit.
80
+ */
81
+ createTransactionPorts: (db: DrizzlePostgresTransaction<TSchema>, events: BufferedDomainEventRecorder) => TxPorts;
82
+ /**
83
+ * Optional event bus used to flush recorded domain events after commit.
84
+ */
85
+ eventBus?: EventBusPort;
86
+ /**
87
+ * Optional Drizzle transaction configuration.
88
+ */
89
+ transactionConfig?: PgTransactionConfig;
90
+ }
91
+ /**
92
+ * Create a Unit of Work port backed by Drizzle's Postgres transaction API.
93
+ *
94
+ * Beignet does not own your repository interfaces. This helper only
95
+ * provides the transaction boundary and post-commit event flushing; your app
96
+ * decides which transaction-scoped ports to create.
97
+ */
98
+ export declare function createDrizzlePostgresUnitOfWork<TSchema extends Record<string, unknown>, TxPorts>(options: DrizzlePostgresUnitOfWorkOptions<TSchema, TxPorts>): UnitOfWorkPort<TxPorts>;
99
+ /**
100
+ * Options for a Drizzle Postgres-backed outbox port.
101
+ */
102
+ export interface DrizzlePostgresOutboxOptions {
103
+ /**
104
+ * Table that stores outbox messages.
105
+ *
106
+ * Default: "outbox_messages".
107
+ */
108
+ tableName?: string;
109
+ /**
110
+ * Clock used by tests and deterministic environments.
111
+ */
112
+ now?: () => Date;
113
+ }
114
+ /**
115
+ * Create idempotent SQL statements for the Drizzle Postgres outbox table.
116
+ *
117
+ * Run these during migrations or local setup before using
118
+ * `createDrizzlePostgresOutboxPort(...)`.
119
+ */
120
+ export declare function createDrizzlePostgresOutboxSetupStatements(options?: DrizzlePostgresOutboxOptions): readonly string[];
121
+ /**
122
+ * Create a Beignet outbox port backed by a Drizzle Postgres database.
123
+ *
124
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
125
+ * for eligible messages, with `for update skip locked` row locking inside the
126
+ * claim transaction.
127
+ */
128
+ export declare function createDrizzlePostgresOutboxPort<TSchema extends Record<string, unknown>>(db: DrizzlePostgresDatabase<TSchema>, adapterOptions?: DrizzlePostgresOutboxOptions): OutboxPort;
129
+ /**
130
+ * Options for a Drizzle Postgres-backed idempotency port.
131
+ */
132
+ export interface DrizzlePostgresIdempotencyOptions {
133
+ /**
134
+ * Table that stores idempotency records.
135
+ *
136
+ * Default: "idempotency_records".
137
+ */
138
+ tableName?: string;
139
+ /**
140
+ * Clock used by tests and deterministic environments.
141
+ */
142
+ now?: () => Date;
143
+ }
144
+ /**
145
+ * Error thrown when `complete(...)` or `fail(...)` does not match an
146
+ * in-progress idempotency reservation with the provided fingerprint.
147
+ *
148
+ * This usually signals a workflow bug: the reservation was never made, was
149
+ * already completed or released, expired and was cleaned up, or the caller
150
+ * passed a different fingerprint than the one used to reserve.
151
+ */
152
+ export declare class DrizzlePostgresIdempotencyMutationError extends Error {
153
+ readonly action: "complete" | "fail";
154
+ readonly namespace: string;
155
+ readonly key: string;
156
+ readonly scopeKey: string;
157
+ constructor(args: {
158
+ action: "complete" | "fail";
159
+ namespace: string;
160
+ key: string;
161
+ scopeKey: string;
162
+ });
163
+ }
164
+ /**
165
+ * Create idempotent SQL statements for the Drizzle Postgres idempotency table.
166
+ *
167
+ * Run these during migrations or local setup before using
168
+ * `createDrizzlePostgresIdempotencyPort(...)`.
169
+ */
170
+ export declare function createDrizzlePostgresIdempotencySetupStatements(options?: DrizzlePostgresIdempotencyOptions): readonly string[];
171
+ /**
172
+ * Create a Beignet idempotency port backed by a Drizzle Postgres database.
173
+ *
174
+ * The port accepts both the root Drizzle database and transaction clients, so
175
+ * applications can expose root idempotency for ordinary retry-safe commands and
176
+ * transaction-scoped idempotency from Unit of Work.
177
+ */
178
+ export declare function createDrizzlePostgresIdempotencyPort<TSchema extends Record<string, unknown>>(db: DrizzlePostgresDatabase<TSchema>, adapterOptions?: DrizzlePostgresIdempotencyOptions): IdempotencyPort;
179
+ /**
180
+ * Configuration schema for the Drizzle Postgres provider.
181
+ * Validates environment variables with POSTGRES_ prefix.
182
+ */
183
+ export declare const PostgresConfigSchema: z.ZodObject<{
184
+ DB_URL: z.ZodString;
185
+ }, z.core.$strip>;
186
+ /**
187
+ * Postgres provider config loaded from `POSTGRES_*` env vars.
188
+ */
189
+ export type PostgresConfig = z.infer<typeof PostgresConfigSchema>;
190
+ /**
191
+ * Options for creating a Drizzle Postgres provider.
192
+ *
193
+ * @template TSchema - The Drizzle schema type
194
+ */
195
+ export interface DrizzlePostgresProviderOptions<TSchema extends Record<string, unknown>, PortName extends string = "db"> {
196
+ /**
197
+ * The Drizzle schema object (e.g. `import * as schema from '@/db/schema'`).
198
+ * This schema is used to create a typed Drizzle database instance.
199
+ */
200
+ schema: TSchema;
201
+ /**
202
+ * Optional port name. Defaults to "db".
203
+ * Renames the contributed port. The provider always reads its connection
204
+ * from `POSTGRES_DB_URL`; for a second database, wire an app-owned provider
205
+ * with its own env prefix instead.
206
+ */
207
+ portName?: PortName;
208
+ /**
209
+ * Optional node-postgres pool configuration merged into the pool created
210
+ * from `POSTGRES_DB_URL`, e.g. `max`, `ssl`, or timeouts.
211
+ */
212
+ pool?: Omit<PoolConfig, "connectionString">;
213
+ }
214
+ /**
215
+ * Create a Drizzle Postgres provider factory.
216
+ *
217
+ * This factory creates a service provider that:
218
+ * - Uses a node-postgres (pg) connection pool
219
+ * - Uses Drizzle ORM via drizzle-orm/node-postgres
220
+ * - Exposes a typed DbPort<TSchema> on ports
221
+ * - Uses POSTGRES_-prefixed env vars for connection config
222
+ *
223
+ * The pool connects lazily, so provider setup does not open a connection.
224
+ *
225
+ * @template TSchema - The Drizzle schema type (inferred from the schema parameter)
226
+ * @param options - Configuration options including the schema
227
+ * @returns A service provider that can be used with createNextServer
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * import * as schema from "@/db/schema";
232
+ * import { createDrizzlePostgresProvider } from "@beignet/provider-db-drizzle/postgres";
233
+ *
234
+ * export const drizzlePostgresProvider = createDrizzlePostgresProvider({ schema });
235
+ * ```
236
+ */
237
+ export declare function createDrizzlePostgresProvider<TSchema extends Record<string, unknown>, PortName extends string = "db">(options: DrizzlePostgresProviderOptions<TSchema, PortName>): import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
238
+ DB_URL: z.ZodString;
239
+ }, z.core.$strip>, Record<PortName, DbPort<TSchema>>, unknown, void>;
240
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,296 @@
1
+ /**
2
+ * @beignet/provider-db-drizzle/postgres
3
+ *
4
+ * Drizzle ORM Postgres provider, backed by node-postgres (pg), that creates
5
+ * a typed database port. Works with any Postgres-compatible server reachable
6
+ * through a standard connection string.
7
+ *
8
+ * Configuration:
9
+ * - POSTGRES_DB_URL: Postgres connection string (required)
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import * as schema from "@/db/schema";
14
+ * import { createDrizzlePostgresProvider } from "@beignet/provider-db-drizzle/postgres";
15
+ *
16
+ * export const drizzlePostgresProvider = createDrizzlePostgresProvider({ schema });
17
+ *
18
+ * // In createNextServer:
19
+ * const server = await createNextServer({
20
+ * ports: basePorts,
21
+ * providers: [drizzlePostgresProvider],
22
+ * // ...
23
+ * });
24
+ *
25
+ * // In infra:
26
+ * const todos = createDrizzleTodoRepository(ctx.ports.db.db);
27
+ * ```
28
+ */
29
+ import { createDomainEventRecorder, } from "@beignet/core/ports";
30
+ import { createProvider, createProviderInstrumentation, } from "@beignet/core/providers";
31
+ import { sql } from "drizzle-orm";
32
+ import { drizzle } from "drizzle-orm/node-postgres";
33
+ import { PgTransaction, } from "drizzle-orm/pg-core";
34
+ import pg from "pg";
35
+ import { z } from "zod";
36
+ import { createIdempotencyPortCore, formatIdempotencyMutationErrorMessage, } from "../shared/idempotency-core.js";
37
+ import { quoteIdentifier } from "../shared/identifiers.js";
38
+ import { createDbQueryLogger } from "../shared/instrumentation.js";
39
+ import { createOutboxPortCore, } from "../shared/outbox-core.js";
40
+ /**
41
+ * Create a Unit of Work port backed by Drizzle's Postgres transaction API.
42
+ *
43
+ * Beignet does not own your repository interfaces. This helper only
44
+ * provides the transaction boundary and post-commit event flushing; your app
45
+ * decides which transaction-scoped ports to create.
46
+ */
47
+ export function createDrizzlePostgresUnitOfWork(options) {
48
+ return {
49
+ async transaction(work) {
50
+ const events = createDomainEventRecorder();
51
+ const result = await options.db.transaction(async (tx) => {
52
+ const txPorts = options.createTransactionPorts(tx, events);
53
+ return work(txPorts);
54
+ }, options.transactionConfig);
55
+ if (options.eventBus) {
56
+ await events.flush(options.eventBus);
57
+ }
58
+ return result;
59
+ },
60
+ };
61
+ }
62
+ function readRowsAffected(result) {
63
+ const { rowCount, affectedRows } = result;
64
+ if (typeof rowCount === "number") {
65
+ return rowCount;
66
+ }
67
+ if (typeof affectedRows === "number") {
68
+ return affectedRows;
69
+ }
70
+ return undefined;
71
+ }
72
+ function readRows(result) {
73
+ const rows = result.rows;
74
+ return rows ?? [];
75
+ }
76
+ function createPostgresExecutor(db, table) {
77
+ return {
78
+ table,
79
+ // Postgres supports row locking, so competing claim workers skip rows
80
+ // another transaction already locked.
81
+ claimLockSuffix: sql.raw(" for update skip locked"),
82
+ insertPrefix: sql.raw("insert into"),
83
+ insertConflictSuffix: sql.raw(" on conflict (storage_key) do nothing"),
84
+ async rows(query) {
85
+ return readRows(await db.execute(query));
86
+ },
87
+ async row(query) {
88
+ return readRows(await db.execute(query))[0];
89
+ },
90
+ async run(query) {
91
+ return readRowsAffected(await db.execute(query));
92
+ },
93
+ withTransaction(fn) {
94
+ // Already transaction-scoped: the core runs directly on this executor
95
+ // instead of opening a nested savepoint.
96
+ if (db instanceof PgTransaction) {
97
+ return undefined;
98
+ }
99
+ if ("transaction" in db && typeof db.transaction === "function") {
100
+ return db.transaction((tx) => fn(createPostgresExecutor(tx, table)));
101
+ }
102
+ return undefined;
103
+ },
104
+ };
105
+ }
106
+ /**
107
+ * Create idempotent SQL statements for the Drizzle Postgres outbox table.
108
+ *
109
+ * Run these during migrations or local setup before using
110
+ * `createDrizzlePostgresOutboxPort(...)`.
111
+ */
112
+ export function createDrizzlePostgresOutboxSetupStatements(options = {}) {
113
+ const tableName = options.tableName ?? "outbox_messages";
114
+ const table = quoteIdentifier(tableName, '"');
115
+ const indexPrefix = tableName.replaceAll(/[^A-Za-z0-9_]/g, "_");
116
+ return [
117
+ `CREATE TABLE IF NOT EXISTS ${table} (
118
+ id text PRIMARY KEY NOT NULL,
119
+ kind text NOT NULL,
120
+ name text NOT NULL,
121
+ payload_json text NOT NULL,
122
+ status text NOT NULL,
123
+ attempts integer DEFAULT 0 NOT NULL,
124
+ max_attempts integer DEFAULT 3 NOT NULL,
125
+ available_at text NOT NULL,
126
+ claimed_at text,
127
+ locked_until text,
128
+ claim_token text,
129
+ delivered_at text,
130
+ last_error_json text,
131
+ created_at text NOT NULL,
132
+ updated_at text NOT NULL
133
+ )`,
134
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(`${indexPrefix}_available_idx`, '"')} ON ${table} (status, available_at)`,
135
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(`${indexPrefix}_locked_idx`, '"')} ON ${table} (status, locked_until)`,
136
+ ];
137
+ }
138
+ /**
139
+ * Create a Beignet outbox port backed by a Drizzle Postgres database.
140
+ *
141
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
142
+ * for eligible messages, with `for update skip locked` row locking inside the
143
+ * claim transaction.
144
+ */
145
+ export function createDrizzlePostgresOutboxPort(db, adapterOptions = {}) {
146
+ const table = sql.raw(quoteIdentifier(adapterOptions.tableName ?? "outbox_messages", '"'));
147
+ const executor = createPostgresExecutor(db, table);
148
+ return createOutboxPortCore(executor, { now: adapterOptions.now });
149
+ }
150
+ /**
151
+ * Error thrown when `complete(...)` or `fail(...)` does not match an
152
+ * in-progress idempotency reservation with the provided fingerprint.
153
+ *
154
+ * This usually signals a workflow bug: the reservation was never made, was
155
+ * already completed or released, expired and was cleaned up, or the caller
156
+ * passed a different fingerprint than the one used to reserve.
157
+ */
158
+ export class DrizzlePostgresIdempotencyMutationError extends Error {
159
+ action;
160
+ namespace;
161
+ key;
162
+ scopeKey;
163
+ constructor(args) {
164
+ super(formatIdempotencyMutationErrorMessage(args));
165
+ this.name = "DrizzlePostgresIdempotencyMutationError";
166
+ this.action = args.action;
167
+ this.namespace = args.namespace;
168
+ this.key = args.key;
169
+ this.scopeKey = args.scopeKey;
170
+ }
171
+ }
172
+ /**
173
+ * Create idempotent SQL statements for the Drizzle Postgres idempotency table.
174
+ *
175
+ * Run these during migrations or local setup before using
176
+ * `createDrizzlePostgresIdempotencyPort(...)`.
177
+ */
178
+ export function createDrizzlePostgresIdempotencySetupStatements(options = {}) {
179
+ const tableName = options.tableName ?? "idempotency_records";
180
+ const table = quoteIdentifier(tableName, '"');
181
+ const indexPrefix = tableName.replaceAll(/[^A-Za-z0-9_]/g, "_");
182
+ return [
183
+ `CREATE TABLE IF NOT EXISTS ${table} (
184
+ storage_key text PRIMARY KEY NOT NULL,
185
+ namespace text NOT NULL,
186
+ idempotency_key text NOT NULL,
187
+ scope_key text NOT NULL,
188
+ fingerprint text NOT NULL,
189
+ status text NOT NULL,
190
+ result_json text,
191
+ reserved_at text NOT NULL,
192
+ completed_at text,
193
+ expires_at text,
194
+ updated_at text NOT NULL
195
+ )`,
196
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(`${indexPrefix}_lookup_idx`, '"')} ON ${table} (namespace, scope_key, idempotency_key)`,
197
+ `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(`${indexPrefix}_expires_idx`, '"')} ON ${table} (expires_at)`,
198
+ ];
199
+ }
200
+ /**
201
+ * Create a Beignet idempotency port backed by a Drizzle Postgres database.
202
+ *
203
+ * The port accepts both the root Drizzle database and transaction clients, so
204
+ * applications can expose root idempotency for ordinary retry-safe commands and
205
+ * transaction-scoped idempotency from Unit of Work.
206
+ */
207
+ export function createDrizzlePostgresIdempotencyPort(db, adapterOptions = {}) {
208
+ const table = sql.raw(quoteIdentifier(adapterOptions.tableName ?? "idempotency_records", '"'));
209
+ const executor = createPostgresExecutor(db, table);
210
+ return createIdempotencyPortCore(executor, { now: adapterOptions.now }, DrizzlePostgresIdempotencyMutationError);
211
+ }
212
+ const POSTGRES_DB_URL_PREFIXES = ["postgres://", "postgresql://"];
213
+ /**
214
+ * Configuration schema for the Drizzle Postgres provider.
215
+ * Validates environment variables with POSTGRES_ prefix.
216
+ */
217
+ export const PostgresConfigSchema = z.object({
218
+ /**
219
+ * Postgres connection string.
220
+ * Example: "postgres://user:password@127.0.0.1:5432/app"
221
+ */
222
+ DB_URL: z
223
+ .string()
224
+ .min(1)
225
+ .refine((val) => POSTGRES_DB_URL_PREFIXES.some((prefix) => val.startsWith(prefix)), {
226
+ message: 'DB_URL must start with "postgres://" or "postgresql://" (e.g., "postgres://user:password@127.0.0.1:5432/app")',
227
+ }),
228
+ });
229
+ /**
230
+ * Create a Drizzle Postgres provider factory.
231
+ *
232
+ * This factory creates a service provider that:
233
+ * - Uses a node-postgres (pg) connection pool
234
+ * - Uses Drizzle ORM via drizzle-orm/node-postgres
235
+ * - Exposes a typed DbPort<TSchema> on ports
236
+ * - Uses POSTGRES_-prefixed env vars for connection config
237
+ *
238
+ * The pool connects lazily, so provider setup does not open a connection.
239
+ *
240
+ * @template TSchema - The Drizzle schema type (inferred from the schema parameter)
241
+ * @param options - Configuration options including the schema
242
+ * @returns A service provider that can be used with createNextServer
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * import * as schema from "@/db/schema";
247
+ * import { createDrizzlePostgresProvider } from "@beignet/provider-db-drizzle/postgres";
248
+ *
249
+ * export const drizzlePostgresProvider = createDrizzlePostgresProvider({ schema });
250
+ * ```
251
+ */
252
+ export function createDrizzlePostgresProvider(options) {
253
+ const { schema, portName = "db" } = options;
254
+ return createProvider({
255
+ name: "drizzle-postgres",
256
+ config: {
257
+ schema: PostgresConfigSchema,
258
+ envPrefix: "POSTGRES_",
259
+ },
260
+ async setup({ ports, config }) {
261
+ if (!config) {
262
+ throw new Error("[drizzlePostgresProvider] Missing config. Set POSTGRES_DB_URL.");
263
+ }
264
+ // 1. Create the node-postgres pool (lazy: connects on first query)
265
+ const pool = new pg.Pool({
266
+ connectionString: config.DB_URL,
267
+ ...options.pool,
268
+ });
269
+ const instrumentation = createProviderInstrumentation(ports, {
270
+ providerName: "drizzle-postgres",
271
+ watcher: "db",
272
+ });
273
+ const logger = createDbQueryLogger(instrumentation, portName);
274
+ // 2. Create typed Drizzle instance
275
+ const db = logger
276
+ ? drizzle(pool, { schema, logger })
277
+ : drizzle(pool, { schema });
278
+ // 3. Build a typed DbPort
279
+ const dbPort = { db, pool };
280
+ return {
281
+ ports: { [portName]: dbPort },
282
+ async stop() {
283
+ // Gracefully close the connection pool
284
+ try {
285
+ await dbPort.pool.end();
286
+ }
287
+ catch (error) {
288
+ // Log error but don't throw - we're shutting down anyway
289
+ console.error("[drizzlePostgresProvider] Error closing database connection:", error);
290
+ }
291
+ },
292
+ };
293
+ },
294
+ });
295
+ }
296
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/postgres/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Dialect-independent idempotency port orchestration.
3
+ *
4
+ * Every Drizzle dialect (SQLite, Postgres, MySQL) implements the small
5
+ * `IdempotencySqlExecutor` seam below; reservation, replay, conflict, and
6
+ * mutation-guard semantics live here so they stay byte-identical across
7
+ * dialects. The dialect-specific mutation error class is injected so callers
8
+ * keep catching the error type their dialect package exports.
9
+ *
10
+ * This module must never import dialect database types — only `SQL` from
11
+ * drizzle-orm and core ports from `@beignet/core`.
12
+ */
13
+ import { type IdempotencyPort } from "@beignet/core/idempotency";
14
+ import { type SQL } from "drizzle-orm";
15
+ import type { SqlExecutorBase } from "./outbox-core.js";
16
+ /**
17
+ * SQL execution seam for the shared idempotency core.
18
+ */
19
+ export interface IdempotencySqlExecutor extends SqlExecutorBase<IdempotencySqlExecutor> {
20
+ /**
21
+ * Insert statement prefix for conflict-tolerant reservation inserts.
22
+ *
23
+ * SQLite: `insert or ignore into`. Postgres/MySQL: `insert into`.
24
+ */
25
+ insertPrefix: SQL;
26
+ /**
27
+ * Insert statement suffix for conflict-tolerant reservation inserts.
28
+ *
29
+ * SQLite: empty. Postgres: `on conflict (storage_key) do nothing`.
30
+ * MySQL: `on duplicate key update storage_key = storage_key`.
31
+ */
32
+ insertConflictSuffix: SQL;
33
+ }
34
+ /**
35
+ * Options for the shared idempotency core.
36
+ */
37
+ export interface IdempotencyPortCoreOptions {
38
+ /**
39
+ * Clock used by tests and deterministic environments.
40
+ */
41
+ now?: () => Date;
42
+ }
43
+ /**
44
+ * Arguments for a dialect's idempotency mutation error class.
45
+ */
46
+ export interface IdempotencyMutationErrorArgs {
47
+ action: "complete" | "fail";
48
+ namespace: string;
49
+ key: string;
50
+ scopeKey: string;
51
+ }
52
+ /**
53
+ * Constructor shape for the dialect-specific mutation error class injected
54
+ * into `createIdempotencyPortCore(...)`.
55
+ */
56
+ export type IdempotencyMutationErrorConstructor = new (args: IdempotencyMutationErrorArgs) => Error;
57
+ /**
58
+ * Build the mutation error message. Shared so the wording is identical
59
+ * across every dialect's mutation error class.
60
+ */
61
+ export declare function formatIdempotencyMutationErrorMessage(args: IdempotencyMutationErrorArgs): string;
62
+ /**
63
+ * Create the dialect-independent idempotency port orchestration on top of a
64
+ * dialect's `IdempotencySqlExecutor`.
65
+ *
66
+ * The port accepts both root databases and transaction clients (via the
67
+ * executor), so applications can expose root idempotency for ordinary
68
+ * retry-safe commands and transaction-scoped idempotency from Unit of Work.
69
+ */
70
+ export declare function createIdempotencyPortCore(executor: IdempotencySqlExecutor, options: IdempotencyPortCoreOptions, MutationErrorClass: IdempotencyMutationErrorConstructor): IdempotencyPort;
71
+ //# sourceMappingURL=idempotency-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-core.d.ts","sourceRoot":"","sources":["../../src/shared/idempotency-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAIL,KAAK,eAAe,EAIrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAOxD;;GAEG;AACH,MAAM,WAAW,sBACf,SAAQ,eAAe,CAAC,sBAAsB,CAAC;IAC/C;;;;OAIG;IACH,YAAY,EAAE,GAAG,CAAC;IAElB;;;;;OAKG;IACH,oBAAoB,EAAE,GAAG,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,mCAAmC,GAAG,KAChD,IAAI,EAAE,4BAA4B,KAC/B,KAAK,CAAC;AAEX;;;GAGG;AACH,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,4BAA4B,GACjC,MAAM,CAER;AA0CD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,0BAA0B,EACnC,kBAAkB,EAAE,mCAAmC,GACtD,eAAe,CA0IjB"}