@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,254 @@
1
+ /**
2
+ * @beignet/provider-db-drizzle/mysql
3
+ *
4
+ * Drizzle ORM MySQL provider, backed by mysql2, that creates a typed
5
+ * database port. Works with self-hosted MySQL 8.0+ and hosted MySQL services.
6
+ *
7
+ * Configuration:
8
+ * - MYSQL_DB_URL: MySQL connection URL (required), e.g.
9
+ * "mysql://user:password@localhost:3306/app"
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import * as schema from "@/db/schema";
14
+ * import { createDrizzleMysqlProvider } from "@beignet/provider-db-drizzle/mysql";
15
+ *
16
+ * export const drizzleMysqlProvider = createDrizzleMysqlProvider({ schema });
17
+ *
18
+ * // In createNextServer:
19
+ * const server = await createNextServer({
20
+ * ports: basePorts,
21
+ * providers: [drizzleMysqlProvider],
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 { MySqlDatabase, MySqlQueryResultHKT, MySqlTransaction, MySqlTransactionConfig, PreparedQueryHKTBase } from "drizzle-orm/mysql-core";
34
+ import { type MySql2Database } from "drizzle-orm/mysql2";
35
+ import type { Pool, PoolOptions } from "mysql2/promise";
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: MySql2Database<TSchema>;
49
+ /**
50
+ * The underlying mysql2 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
+ * `MySqlTransaction` extends `MySqlDatabase`, so both the root database and
59
+ * transaction clients satisfy this seam.
60
+ */
61
+ export type DrizzleMysqlDatabase<TSchema extends Record<string, unknown> = Record<string, unknown>> = MySqlDatabase<MySqlQueryResultHKT, PreparedQueryHKTBase, TSchema>;
62
+ /**
63
+ * Drizzle transaction client passed to Unit of Work repository factories.
64
+ */
65
+ export type DrizzleMysqlTransaction<TSchema extends Record<string, unknown> = Record<string, unknown>> = MySqlTransaction<MySqlQueryResultHKT, PreparedQueryHKTBase, TSchema, ExtractTablesWithRelations<TSchema>>;
66
+ /**
67
+ * Options for creating a Drizzle MySQL-backed Unit of Work port.
68
+ */
69
+ export interface DrizzleMysqlUnitOfWorkOptions<TSchema extends Record<string, unknown>, TxPorts> {
70
+ /**
71
+ * The root Drizzle database instance from `ctx.ports.db.db`.
72
+ */
73
+ db: DrizzleMysqlDatabase<TSchema>;
74
+ /**
75
+ * Create transaction-scoped ports from the Drizzle transaction client.
76
+ *
77
+ * The event recorder is transaction-local. Record domain events inside the
78
+ * callback and pass `eventBus` to publish them after the database commit.
79
+ */
80
+ createTransactionPorts: (db: DrizzleMysqlTransaction<TSchema>, events: BufferedDomainEventRecorder) => TxPorts;
81
+ /**
82
+ * Optional event bus used to flush recorded domain events after commit.
83
+ */
84
+ eventBus?: EventBusPort;
85
+ /**
86
+ * Optional Drizzle transaction configuration.
87
+ */
88
+ transactionConfig?: MySqlTransactionConfig;
89
+ }
90
+ /**
91
+ * Create a Unit of Work port backed by Drizzle's MySQL transaction API.
92
+ *
93
+ * Beignet does not own your repository interfaces. This helper only
94
+ * provides the transaction boundary and post-commit event flushing; your app
95
+ * decides which transaction-scoped ports to create.
96
+ */
97
+ export declare function createDrizzleMysqlUnitOfWork<TSchema extends Record<string, unknown>, TxPorts>(options: DrizzleMysqlUnitOfWorkOptions<TSchema, TxPorts>): UnitOfWorkPort<TxPorts>;
98
+ /**
99
+ * Options for a Drizzle MySQL-backed outbox port.
100
+ */
101
+ export interface DrizzleMysqlOutboxOptions {
102
+ /**
103
+ * Table that stores outbox messages.
104
+ *
105
+ * Default: "outbox_messages".
106
+ */
107
+ tableName?: string;
108
+ /**
109
+ * Clock used by tests and deterministic environments.
110
+ */
111
+ now?: () => Date;
112
+ }
113
+ /**
114
+ * Create idempotent SQL statements for the Drizzle MySQL outbox table.
115
+ *
116
+ * Run these during migrations or local setup before using
117
+ * `createDrizzleMysqlOutboxPort(...)`.
118
+ *
119
+ * Indexes are declared inline because MySQL has no
120
+ * `CREATE INDEX IF NOT EXISTS`; the whole shape stays idempotent under
121
+ * `CREATE TABLE IF NOT EXISTS`. The binary no-pad collation
122
+ * `utf8mb4_0900_bin` (MySQL 8.0+) keeps string comparisons case-sensitive
123
+ * and free of PAD SPACE semantics, which claim tokens and fingerprints
124
+ * require.
125
+ */
126
+ export declare function createDrizzleMysqlOutboxSetupStatements(options?: DrizzleMysqlOutboxOptions): readonly string[];
127
+ /**
128
+ * Create a Beignet outbox port backed by a Drizzle MySQL database.
129
+ *
130
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
131
+ * for eligible messages, combined with `for update skip locked` row locking.
132
+ */
133
+ export declare function createDrizzleMysqlOutboxPort<TSchema extends Record<string, unknown>>(db: DrizzleMysqlDatabase<TSchema>, adapterOptions?: DrizzleMysqlOutboxOptions): OutboxPort;
134
+ /**
135
+ * Options for a Drizzle MySQL-backed idempotency port.
136
+ */
137
+ export interface DrizzleMysqlIdempotencyOptions {
138
+ /**
139
+ * Table that stores idempotency records.
140
+ *
141
+ * Default: "idempotency_records".
142
+ */
143
+ tableName?: string;
144
+ /**
145
+ * Clock used by tests and deterministic environments.
146
+ */
147
+ now?: () => Date;
148
+ }
149
+ /**
150
+ * Error thrown when `complete(...)` or `fail(...)` does not match an
151
+ * in-progress idempotency reservation with the provided fingerprint.
152
+ *
153
+ * This usually signals a workflow bug: the reservation was never made, was
154
+ * already completed or released, expired and was cleaned up, or the caller
155
+ * passed a different fingerprint than the one used to reserve.
156
+ */
157
+ export declare class DrizzleMysqlIdempotencyMutationError extends Error {
158
+ readonly action: "complete" | "fail";
159
+ readonly namespace: string;
160
+ readonly key: string;
161
+ readonly scopeKey: string;
162
+ constructor(args: {
163
+ action: "complete" | "fail";
164
+ namespace: string;
165
+ key: string;
166
+ scopeKey: string;
167
+ });
168
+ }
169
+ /**
170
+ * Create idempotent SQL statements for the Drizzle MySQL idempotency table.
171
+ *
172
+ * Run these during migrations or local setup before using
173
+ * `createDrizzleMysqlIdempotencyPort(...)`.
174
+ *
175
+ * Indexes are declared inline because MySQL has no
176
+ * `CREATE INDEX IF NOT EXISTS`; the whole shape stays idempotent under
177
+ * `CREATE TABLE IF NOT EXISTS`. The binary no-pad collation
178
+ * `utf8mb4_0900_bin` (MySQL 8.0+) keeps fingerprint comparisons
179
+ * case-sensitive — "ABC" and "abc" must classify as conflict, never replay.
180
+ */
181
+ export declare function createDrizzleMysqlIdempotencySetupStatements(options?: DrizzleMysqlIdempotencyOptions): readonly string[];
182
+ /**
183
+ * Create a Beignet idempotency port backed by a Drizzle MySQL database.
184
+ *
185
+ * The port accepts both the root Drizzle database and transaction clients, so
186
+ * applications can expose root idempotency for ordinary retry-safe commands and
187
+ * transaction-scoped idempotency from Unit of Work.
188
+ */
189
+ export declare function createDrizzleMysqlIdempotencyPort<TSchema extends Record<string, unknown>>(db: DrizzleMysqlDatabase<TSchema>, adapterOptions?: DrizzleMysqlIdempotencyOptions): IdempotencyPort;
190
+ /**
191
+ * Configuration schema for the Drizzle MySQL provider.
192
+ * Validates environment variables with MYSQL_ prefix.
193
+ */
194
+ export declare const MysqlConfigSchema: z.ZodObject<{
195
+ DB_URL: z.ZodString;
196
+ }, z.core.$strip>;
197
+ /**
198
+ * MySQL provider config loaded from `MYSQL_*` env vars.
199
+ */
200
+ export type MysqlConfig = z.infer<typeof MysqlConfigSchema>;
201
+ /**
202
+ * Options for creating a Drizzle MySQL provider.
203
+ *
204
+ * @template TSchema - The Drizzle schema type
205
+ */
206
+ export interface DrizzleMysqlProviderOptions<TSchema extends Record<string, unknown>, PortName extends string = "db"> {
207
+ /**
208
+ * The Drizzle schema object (e.g. `import * as schema from '@/db/schema'`).
209
+ * This schema is used to create a typed Drizzle database instance.
210
+ */
211
+ schema: TSchema;
212
+ /**
213
+ * Optional port name. Defaults to "db".
214
+ * Renames the contributed port. The provider always reads its connection
215
+ * from `MYSQL_DB_URL`; for a second database, wire an app-owned provider
216
+ * with its own env prefix instead.
217
+ */
218
+ portName?: PortName;
219
+ /**
220
+ * Optional mysql2 pool options merged into the pool created from
221
+ * `MYSQL_DB_URL` (e.g. `connectionLimit`, `timezone`, TLS settings).
222
+ */
223
+ pool?: Omit<PoolOptions, "uri">;
224
+ /**
225
+ * Drizzle relational-query mode. Use "planetscale" when connecting to
226
+ * PlanetScale; defaults to "default".
227
+ */
228
+ mode?: "default" | "planetscale";
229
+ }
230
+ /**
231
+ * Create a Drizzle MySQL provider factory.
232
+ *
233
+ * This factory creates a service provider that:
234
+ * - Uses a mysql2/promise connection pool
235
+ * - Uses Drizzle ORM via drizzle-orm/mysql2
236
+ * - Exposes a typed DbPort<TSchema> on ports
237
+ * - Uses MYSQL_-prefixed env vars for connection config
238
+ *
239
+ * @template TSchema - The Drizzle schema type (inferred from the schema parameter)
240
+ * @param options - Configuration options including the schema
241
+ * @returns A service provider that can be used with createNextServer
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * import * as schema from "@/db/schema";
246
+ * import { createDrizzleMysqlProvider } from "@beignet/provider-db-drizzle/mysql";
247
+ *
248
+ * export const drizzleMysqlProvider = createDrizzleMysqlProvider({ schema });
249
+ * ```
250
+ */
251
+ export declare function createDrizzleMysqlProvider<TSchema extends Record<string, unknown>, PortName extends string = "db">(options: DrizzleMysqlProviderOptions<TSchema, PortName>): import("@beignet/core/providers").ServiceProvider<unknown, z.ZodObject<{
252
+ DB_URL: z.ZodString;
253
+ }, z.core.$strip>, Record<PortName, DbPort<TSchema>>, unknown, void>;
254
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mysql/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,KAAK,2BAA2B,EAEhC,KAAK,YAAY,EAEjB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,KAAK,0BAA0B,EAAiB,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAW,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAcxB;;;;;GAKG;AACH,MAAM,WAAW,MAAM,CACrB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE;;;OAGG;IACH,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAE5B;;;OAGG;IACH,IAAI,EAAE,IAAI,CAAC;CACZ;AAED;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,CAC9B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,aAAa,CAAC,mBAAmB,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,uBAAuB,CACjC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAC/D,gBAAgB,CAClB,mBAAmB,EACnB,oBAAoB,EACpB,OAAO,EACP,0BAA0B,CAAC,OAAO,CAAC,CACpC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,6BAA6B,CAC5C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO;IAEP;;OAEG;IACH,EAAE,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAElC;;;;;OAKG;IACH,sBAAsB,EAAE,CACtB,EAAE,EAAE,uBAAuB,CAAC,OAAO,CAAC,EACpC,MAAM,EAAE,2BAA2B,KAChC,OAAO,CAAC;IAEb;;OAEG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IAExB;;OAEG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO,EAEP,OAAO,EAAE,6BAA6B,CAAC,OAAO,EAAE,OAAO,CAAC,GACvD,cAAc,CAAC,OAAO,CAAC,CAsBzB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAsHD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uCAAuC,CACrD,OAAO,GAAE,yBAA8B,GACtC,SAAS,MAAM,EAAE,CA0BnB;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEvC,EAAE,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACjC,cAAc,GAAE,yBAA8B,GAC7C,UAAU,CAOZ;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBAAa,oCAAqC,SAAQ,KAAK;IAC7D,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE;QAChB,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;KAClB;CAQF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4CAA4C,CAC1D,OAAO,GAAE,8BAAmC,GAC3C,SAAS,MAAM,EAAE,CAsBnB;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAEvC,EAAE,EAAE,oBAAoB,CAAC,OAAO,CAAC,EACjC,cAAc,GAAE,8BAAmC,GAClD,eAAe,CAWjB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;iBAY5B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,WAAW,2BAA2B,CAC1C,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,QAAQ,SAAS,MAAM,GAAG,IAAI;IAE9B;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAEhC;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,QAAQ,SAAS,MAAM,GAAG,IAAI,EAC9B,OAAO,EAAE,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC;;qEAyDxD"}
@@ -0,0 +1,348 @@
1
+ /**
2
+ * @beignet/provider-db-drizzle/mysql
3
+ *
4
+ * Drizzle ORM MySQL provider, backed by mysql2, that creates a typed
5
+ * database port. Works with self-hosted MySQL 8.0+ and hosted MySQL services.
6
+ *
7
+ * Configuration:
8
+ * - MYSQL_DB_URL: MySQL connection URL (required), e.g.
9
+ * "mysql://user:password@localhost:3306/app"
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import * as schema from "@/db/schema";
14
+ * import { createDrizzleMysqlProvider } from "@beignet/provider-db-drizzle/mysql";
15
+ *
16
+ * export const drizzleMysqlProvider = createDrizzleMysqlProvider({ schema });
17
+ *
18
+ * // In createNextServer:
19
+ * const server = await createNextServer({
20
+ * ports: basePorts,
21
+ * providers: [drizzleMysqlProvider],
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/mysql2";
33
+ import * as mysql from "mysql2/promise";
34
+ import { z } from "zod";
35
+ import { createIdempotencyPortCore, formatIdempotencyMutationErrorMessage, } from "../shared/idempotency-core.js";
36
+ import { quoteIdentifier } from "../shared/identifiers.js";
37
+ import { createDbQueryLogger } from "../shared/instrumentation.js";
38
+ import { createOutboxPortCore, } from "../shared/outbox-core.js";
39
+ /**
40
+ * Create a Unit of Work port backed by Drizzle's MySQL transaction API.
41
+ *
42
+ * Beignet does not own your repository interfaces. This helper only
43
+ * provides the transaction boundary and post-commit event flushing; your app
44
+ * decides which transaction-scoped ports to create.
45
+ */
46
+ export function createDrizzleMysqlUnitOfWork(options) {
47
+ return {
48
+ async transaction(work) {
49
+ const events = createDomainEventRecorder();
50
+ const result = await options.db.transaction(async (tx) => {
51
+ const txPorts = options.createTransactionPorts(tx, events);
52
+ return work(txPorts);
53
+ }, options.transactionConfig);
54
+ if (options.eventBus) {
55
+ await events.flush(options.eventBus);
56
+ }
57
+ return result;
58
+ },
59
+ };
60
+ }
61
+ /**
62
+ * Read the first element of a drizzle mysql2 `db.execute(...)` result tuple.
63
+ *
64
+ * drizzle-orm/mysql2 returns `[rows | ResultSetHeader, fields]`: for selects
65
+ * element 0 is the row array; for mutations element 0 is a ResultSetHeader.
66
+ */
67
+ function readResultHead(result) {
68
+ return Array.isArray(result) ? result[0] : undefined;
69
+ }
70
+ function readResultRows(result) {
71
+ const head = readResultHead(result);
72
+ return Array.isArray(head) ? head : [];
73
+ }
74
+ function readAffectedRows(result) {
75
+ const head = readResultHead(result);
76
+ if (head === null || typeof head !== "object" || Array.isArray(head)) {
77
+ return undefined;
78
+ }
79
+ const affectedRows = head.affectedRows;
80
+ return typeof affectedRows === "number" ? affectedRows : undefined;
81
+ }
82
+ /**
83
+ * Detect a MySQL duplicate-key violation (ER_DUP_ENTRY, errno 1062) anywhere
84
+ * in an error's cause chain — drizzle wraps driver errors in
85
+ * DrizzleQueryError with the mysql2 error as `cause`.
86
+ */
87
+ function isDuplicateKeyError(error) {
88
+ for (let current = error; current !== null && typeof current === "object"; current = current.cause) {
89
+ const candidate = current;
90
+ if (candidate.errno === 1062 || candidate.code === "ER_DUP_ENTRY") {
91
+ return true;
92
+ }
93
+ }
94
+ return false;
95
+ }
96
+ function createMysqlExecutor(db, table) {
97
+ return {
98
+ table,
99
+ // MySQL 8.0+ row-locking clause so concurrent claimers skip locked rows.
100
+ claimLockSuffix: sql.raw(" for update skip locked"),
101
+ // Duplicate keys surface as ER_DUP_ENTRY caught in run() below — a plain
102
+ // insert keeps the conflict signal independent of connection flags.
103
+ // mysql2 enables CLIENT_FOUND_ROWS by default, which makes
104
+ // `on duplicate key update x = x` report affectedRows 1 instead of 0,
105
+ // so counting-based duplicate detection silently misreads duplicates as
106
+ // inserts. Do NOT switch to INSERT IGNORE either: strict mode downgrades
107
+ // truncation errors to warnings, silently corrupting stored values.
108
+ insertPrefix: sql.raw("insert into"),
109
+ insertConflictSuffix: sql.raw(""),
110
+ async rows(query) {
111
+ return readResultRows(await db.execute(query));
112
+ },
113
+ async row(query) {
114
+ return readResultRows(await db.execute(query))[0];
115
+ },
116
+ async run(query) {
117
+ // With mysql2's default CLIENT_FOUND_ROWS flag, affectedRows for
118
+ // UPDATE counts MATCHED rows (without it, CHANGED rows). Every guarded
119
+ // update issued by the shared cores deterministically changes at least
120
+ // one column (fresh claim token or status transition), so the count is
121
+ // identical either way. Keep it that way: a no-op update would make
122
+ // the two flag modes disagree.
123
+ //
124
+ // A duplicate-key violation reports 0 instead of throwing: the only
125
+ // unique-key inserts the shared cores issue are the idempotency
126
+ // reserve (where "0 rows" is exactly the conflict fall-through signal)
127
+ // and outbox enqueues keyed by fresh UUIDs. MySQL does not abort the
128
+ // surrounding transaction on a statement error, so the reserve's
129
+ // read-and-classify step still runs inside the same transaction.
130
+ try {
131
+ return readAffectedRows(await db.execute(query));
132
+ }
133
+ catch (error) {
134
+ if (isDuplicateKeyError(error)) {
135
+ return 0;
136
+ }
137
+ throw error;
138
+ }
139
+ },
140
+ withTransaction(fn) {
141
+ if ("transaction" in db && typeof db.transaction === "function") {
142
+ return db.transaction((tx) => fn(createMysqlExecutor(tx, table)));
143
+ }
144
+ return undefined;
145
+ },
146
+ };
147
+ }
148
+ /**
149
+ * Create idempotent SQL statements for the Drizzle MySQL outbox table.
150
+ *
151
+ * Run these during migrations or local setup before using
152
+ * `createDrizzleMysqlOutboxPort(...)`.
153
+ *
154
+ * Indexes are declared inline because MySQL has no
155
+ * `CREATE INDEX IF NOT EXISTS`; the whole shape stays idempotent under
156
+ * `CREATE TABLE IF NOT EXISTS`. The binary no-pad collation
157
+ * `utf8mb4_0900_bin` (MySQL 8.0+) keeps string comparisons case-sensitive
158
+ * and free of PAD SPACE semantics, which claim tokens and fingerprints
159
+ * require.
160
+ */
161
+ export function createDrizzleMysqlOutboxSetupStatements(options = {}) {
162
+ const tableName = options.tableName ?? "outbox_messages";
163
+ const table = quoteIdentifier(tableName, "`");
164
+ const indexPrefix = tableName.replaceAll(/[^A-Za-z0-9_]/g, "_");
165
+ return [
166
+ `CREATE TABLE IF NOT EXISTS ${table} (
167
+ id varchar(255) NOT NULL PRIMARY KEY,
168
+ kind varchar(32) NOT NULL,
169
+ name varchar(255) NOT NULL,
170
+ payload_json longtext NOT NULL,
171
+ status varchar(32) NOT NULL,
172
+ attempts int NOT NULL DEFAULT 0,
173
+ max_attempts int NOT NULL DEFAULT 3,
174
+ available_at varchar(32) NOT NULL,
175
+ claimed_at varchar(32),
176
+ locked_until varchar(32),
177
+ claim_token varchar(64),
178
+ delivered_at varchar(32),
179
+ last_error_json longtext,
180
+ created_at varchar(32) NOT NULL,
181
+ updated_at varchar(32) NOT NULL,
182
+ INDEX ${quoteIdentifier(`${indexPrefix}_available_idx`, "`")} (status, available_at),
183
+ INDEX ${quoteIdentifier(`${indexPrefix}_locked_idx`, "`")} (status, locked_until)
184
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin`,
185
+ ];
186
+ }
187
+ /**
188
+ * Create a Beignet outbox port backed by a Drizzle MySQL database.
189
+ *
190
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
191
+ * for eligible messages, combined with `for update skip locked` row locking.
192
+ */
193
+ export function createDrizzleMysqlOutboxPort(db, adapterOptions = {}) {
194
+ const table = sql.raw(quoteIdentifier(adapterOptions.tableName ?? "outbox_messages", "`"));
195
+ const executor = createMysqlExecutor(db, table);
196
+ return createOutboxPortCore(executor, { now: adapterOptions.now });
197
+ }
198
+ /**
199
+ * Error thrown when `complete(...)` or `fail(...)` does not match an
200
+ * in-progress idempotency reservation with the provided fingerprint.
201
+ *
202
+ * This usually signals a workflow bug: the reservation was never made, was
203
+ * already completed or released, expired and was cleaned up, or the caller
204
+ * passed a different fingerprint than the one used to reserve.
205
+ */
206
+ export class DrizzleMysqlIdempotencyMutationError extends Error {
207
+ action;
208
+ namespace;
209
+ key;
210
+ scopeKey;
211
+ constructor(args) {
212
+ super(formatIdempotencyMutationErrorMessage(args));
213
+ this.name = "DrizzleMysqlIdempotencyMutationError";
214
+ this.action = args.action;
215
+ this.namespace = args.namespace;
216
+ this.key = args.key;
217
+ this.scopeKey = args.scopeKey;
218
+ }
219
+ }
220
+ /**
221
+ * Create idempotent SQL statements for the Drizzle MySQL idempotency table.
222
+ *
223
+ * Run these during migrations or local setup before using
224
+ * `createDrizzleMysqlIdempotencyPort(...)`.
225
+ *
226
+ * Indexes are declared inline because MySQL has no
227
+ * `CREATE INDEX IF NOT EXISTS`; the whole shape stays idempotent under
228
+ * `CREATE TABLE IF NOT EXISTS`. The binary no-pad collation
229
+ * `utf8mb4_0900_bin` (MySQL 8.0+) keeps fingerprint comparisons
230
+ * case-sensitive — "ABC" and "abc" must classify as conflict, never replay.
231
+ */
232
+ export function createDrizzleMysqlIdempotencySetupStatements(options = {}) {
233
+ const tableName = options.tableName ?? "idempotency_records";
234
+ const table = quoteIdentifier(tableName, "`");
235
+ const indexPrefix = tableName.replaceAll(/[^A-Za-z0-9_]/g, "_");
236
+ return [
237
+ `CREATE TABLE IF NOT EXISTS ${table} (
238
+ storage_key varchar(512) NOT NULL PRIMARY KEY,
239
+ namespace varchar(255) NOT NULL,
240
+ idempotency_key varchar(255) NOT NULL,
241
+ scope_key varchar(255) NOT NULL,
242
+ fingerprint varchar(255) NOT NULL,
243
+ status varchar(16) NOT NULL,
244
+ result_json longtext,
245
+ reserved_at varchar(32) NOT NULL,
246
+ completed_at varchar(32),
247
+ expires_at varchar(32),
248
+ updated_at varchar(32) NOT NULL,
249
+ INDEX ${quoteIdentifier(`${indexPrefix}_lookup_idx`, "`")} (namespace, scope_key, idempotency_key),
250
+ INDEX ${quoteIdentifier(`${indexPrefix}_expires_idx`, "`")} (expires_at)
251
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin`,
252
+ ];
253
+ }
254
+ /**
255
+ * Create a Beignet idempotency port backed by a Drizzle MySQL database.
256
+ *
257
+ * The port accepts both the root Drizzle database and transaction clients, so
258
+ * applications can expose root idempotency for ordinary retry-safe commands and
259
+ * transaction-scoped idempotency from Unit of Work.
260
+ */
261
+ export function createDrizzleMysqlIdempotencyPort(db, adapterOptions = {}) {
262
+ const table = sql.raw(quoteIdentifier(adapterOptions.tableName ?? "idempotency_records", "`"));
263
+ const executor = createMysqlExecutor(db, table);
264
+ return createIdempotencyPortCore(executor, { now: adapterOptions.now }, DrizzleMysqlIdempotencyMutationError);
265
+ }
266
+ /**
267
+ * Configuration schema for the Drizzle MySQL provider.
268
+ * Validates environment variables with MYSQL_ prefix.
269
+ */
270
+ export const MysqlConfigSchema = z.object({
271
+ /**
272
+ * MySQL connection URL.
273
+ * Example: "mysql://user:password@localhost:3306/app"
274
+ */
275
+ DB_URL: z
276
+ .string()
277
+ .min(1)
278
+ .refine((val) => val.startsWith("mysql://"), {
279
+ message: 'DB_URL must start with "mysql://" (e.g., "mysql://user:password@localhost:3306/app")',
280
+ }),
281
+ });
282
+ /**
283
+ * Create a Drizzle MySQL provider factory.
284
+ *
285
+ * This factory creates a service provider that:
286
+ * - Uses a mysql2/promise connection pool
287
+ * - Uses Drizzle ORM via drizzle-orm/mysql2
288
+ * - Exposes a typed DbPort<TSchema> on ports
289
+ * - Uses MYSQL_-prefixed env vars for connection config
290
+ *
291
+ * @template TSchema - The Drizzle schema type (inferred from the schema parameter)
292
+ * @param options - Configuration options including the schema
293
+ * @returns A service provider that can be used with createNextServer
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * import * as schema from "@/db/schema";
298
+ * import { createDrizzleMysqlProvider } from "@beignet/provider-db-drizzle/mysql";
299
+ *
300
+ * export const drizzleMysqlProvider = createDrizzleMysqlProvider({ schema });
301
+ * ```
302
+ */
303
+ export function createDrizzleMysqlProvider(options) {
304
+ const { schema, portName = "db", mode = "default" } = options;
305
+ return createProvider({
306
+ name: "drizzle-mysql",
307
+ config: {
308
+ schema: MysqlConfigSchema,
309
+ envPrefix: "MYSQL_",
310
+ },
311
+ async setup({ ports, config }) {
312
+ if (!config) {
313
+ throw new Error("[drizzleMysqlProvider] Missing config. Set MYSQL_DB_URL.");
314
+ }
315
+ // 1. Create mysql2 connection pool
316
+ const pool = mysql.createPool({
317
+ uri: config.DB_URL,
318
+ ...options.pool,
319
+ });
320
+ const instrumentation = createProviderInstrumentation(ports, {
321
+ providerName: "drizzle-mysql",
322
+ watcher: "db",
323
+ });
324
+ const logger = createDbQueryLogger(instrumentation, portName);
325
+ // 2. Create typed Drizzle instance (mode is required when a schema is
326
+ // provided to drizzle-orm/mysql2)
327
+ const db = logger
328
+ ? drizzle(pool, { schema, logger, mode })
329
+ : drizzle(pool, { schema, mode });
330
+ // 3. Build a typed DbPort
331
+ const dbPort = { db, pool };
332
+ return {
333
+ ports: { [portName]: dbPort },
334
+ async stop() {
335
+ // Gracefully close the database connection pool
336
+ try {
337
+ await dbPort.pool.end();
338
+ }
339
+ catch (error) {
340
+ // Log error but don't throw - we're shutting down anyway
341
+ console.error("[drizzleMysqlProvider] Error closing database connection:", error);
342
+ }
343
+ },
344
+ };
345
+ },
346
+ });
347
+ }
348
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mysql/index.ts"],"names":[],"mappings":"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"}