@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,169 @@
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 { createIdempotencyStorageKey, normalizeIdempotencyScope, } from "@beignet/core/idempotency";
14
+ import { sql } from "drizzle-orm";
15
+ import { encodeTimestamp, idempotencyReservationFromRow, } from "./rows.js";
16
+ /**
17
+ * Build the mutation error message. Shared so the wording is identical
18
+ * across every dialect's mutation error class.
19
+ */
20
+ export function formatIdempotencyMutationErrorMessage(args) {
21
+ return `Idempotency ${args.action} for key "${args.key}" in namespace "${args.namespace}" matched no in-progress reservation with this fingerprint. The reservation may be missing, expired, already completed or released, or reserved with a different fingerprint.`;
22
+ }
23
+ function nowFrom(options) {
24
+ return options.now?.() ?? new Date();
25
+ }
26
+ function assertNonEmptyString(name, value) {
27
+ if (typeof value !== "string" || value.trim().length === 0) {
28
+ throw new Error(`${name} must be a non-empty string`);
29
+ }
30
+ }
31
+ function assertIdempotencyTtl(ttlSec) {
32
+ if (ttlSec === undefined)
33
+ return;
34
+ if (!Number.isInteger(ttlSec) || ttlSec <= 0) {
35
+ throw new Error("ttlSec must be a positive integer when provided");
36
+ }
37
+ }
38
+ function resolveIdempotencyExpiresAt(ttlSec, now) {
39
+ assertIdempotencyTtl(ttlSec);
40
+ return ttlSec === undefined ? null : new Date(now.getTime() + ttlSec * 1000);
41
+ }
42
+ function validateReserveInput(input) {
43
+ assertNonEmptyString("namespace", input.namespace);
44
+ assertNonEmptyString("key", input.key);
45
+ assertNonEmptyString("fingerprint", input.fingerprint);
46
+ assertIdempotencyTtl(input.ttlSec);
47
+ }
48
+ function validateMutationInput(input) {
49
+ assertNonEmptyString("namespace", input.namespace);
50
+ assertNonEmptyString("key", input.key);
51
+ assertNonEmptyString("fingerprint", input.fingerprint);
52
+ }
53
+ /**
54
+ * Create the dialect-independent idempotency port orchestration on top of a
55
+ * dialect's `IdempotencySqlExecutor`.
56
+ *
57
+ * The port accepts both root databases and transaction clients (via the
58
+ * executor), so applications can expose root idempotency for ordinary
59
+ * retry-safe commands and transaction-scoped idempotency from Unit of Work.
60
+ */
61
+ export function createIdempotencyPortCore(executor, options, MutationErrorClass) {
62
+ /**
63
+ * Verify that an idempotency mutation matched an in-progress reservation.
64
+ *
65
+ * Like `reserve(...)`, a missing `rowsAffected` from the driver is treated
66
+ * as success; the assertion only fires when the driver positively reports
67
+ * that no row matched.
68
+ */
69
+ function assertIdempotencyMutationSucceeded(action, input, rowsAffected) {
70
+ if (rowsAffected === undefined || rowsAffected > 0) {
71
+ return;
72
+ }
73
+ throw new MutationErrorClass({
74
+ action,
75
+ namespace: input.namespace,
76
+ key: input.key,
77
+ scopeKey: normalizeIdempotencyScope(input.scope),
78
+ });
79
+ }
80
+ async function reserveWith(tx, input) {
81
+ validateReserveInput(input);
82
+ const now = nowFrom(options);
83
+ const nowIso = encodeTimestamp(now);
84
+ const expiresAt = resolveIdempotencyExpiresAt(input.ttlSec, now);
85
+ const storageKey = createIdempotencyStorageKey(input);
86
+ const scopeKey = normalizeIdempotencyScope(input.scope);
87
+ await tx.run(sql `delete from ${tx.table}
88
+ where storage_key = ${storageKey}
89
+ and expires_at is not null
90
+ and expires_at <= ${nowIso}`);
91
+ const rowsAffected = await tx.run(sql `${tx.insertPrefix} ${tx.table} (
92
+ storage_key,
93
+ namespace,
94
+ idempotency_key,
95
+ scope_key,
96
+ fingerprint,
97
+ status,
98
+ result_json,
99
+ reserved_at,
100
+ completed_at,
101
+ expires_at,
102
+ updated_at
103
+ ) values (
104
+ ${storageKey},
105
+ ${input.namespace},
106
+ ${input.key},
107
+ ${scopeKey},
108
+ ${input.fingerprint},
109
+ 'in-progress',
110
+ null,
111
+ ${nowIso},
112
+ null,
113
+ ${expiresAt ? encodeTimestamp(expiresAt) : null},
114
+ ${nowIso}
115
+ )${tx.insertConflictSuffix}`);
116
+ if (rowsAffected === undefined || rowsAffected > 0) {
117
+ return {
118
+ status: "reserved",
119
+ namespace: input.namespace,
120
+ key: input.key,
121
+ scopeKey,
122
+ fingerprint: input.fingerprint,
123
+ reservedAt: now,
124
+ expiresAt,
125
+ };
126
+ }
127
+ const row = (await tx.row(sql `select * from ${tx.table} where storage_key = ${storageKey}`));
128
+ if (!row) {
129
+ throw new Error(`Idempotency reservation "${input.key}" could not be read after insert conflict.`);
130
+ }
131
+ return idempotencyReservationFromRow(row, input.fingerprint);
132
+ }
133
+ return {
134
+ reserve(input) {
135
+ const transactional = executor.withTransaction((tx) => reserveWith(tx, input));
136
+ if (transactional !== undefined) {
137
+ return transactional;
138
+ }
139
+ return reserveWith(executor, input);
140
+ },
141
+ async complete(input) {
142
+ validateMutationInput(input);
143
+ const nowIso = encodeTimestamp(nowFrom(options));
144
+ const storageKey = createIdempotencyStorageKey(input);
145
+ const serializedResult = JSON.stringify(input.result);
146
+ const resultJson = serializedResult === undefined ? null : serializedResult;
147
+ const rowsAffected = await executor.run(sql `update ${executor.table}
148
+ set
149
+ status = 'completed',
150
+ result_json = ${resultJson},
151
+ completed_at = ${nowIso},
152
+ updated_at = ${nowIso}
153
+ where storage_key = ${storageKey}
154
+ and fingerprint = ${input.fingerprint}
155
+ and status = 'in-progress'`);
156
+ assertIdempotencyMutationSucceeded("complete", input, rowsAffected);
157
+ },
158
+ async fail(input) {
159
+ validateMutationInput(input);
160
+ const storageKey = createIdempotencyStorageKey(input);
161
+ const rowsAffected = await executor.run(sql `delete from ${executor.table}
162
+ where storage_key = ${storageKey}
163
+ and fingerprint = ${input.fingerprint}
164
+ and status = 'in-progress'`);
165
+ assertIdempotencyMutationSucceeded("fail", input, rowsAffected);
166
+ },
167
+ };
168
+ }
169
+ //# sourceMappingURL=idempotency-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency-core.js","sourceRoot":"","sources":["../../src/shared/idempotency-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Shared SQL identifier helpers used by every Drizzle dialect in this
3
+ * package. Identifiers are validated before quoting so table names from
4
+ * options can never inject SQL.
5
+ */
6
+ /**
7
+ * Assert that a SQL identifier only contains safe characters.
8
+ *
9
+ * @throws Error when the identifier contains anything beyond
10
+ * `[A-Za-z0-9_]` or starts with a digit.
11
+ */
12
+ export declare function assertSafeIdentifier(name: string): void;
13
+ /**
14
+ * Quote a validated SQL identifier with the dialect's identifier quote.
15
+ *
16
+ * - SQLite and Postgres use `"`.
17
+ * - MySQL uses `` ` ``.
18
+ */
19
+ export declare function quoteIdentifier(name: string, quote: '"' | "`"): string;
20
+ //# sourceMappingURL=identifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../../src/shared/identifiers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIvD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,GAAG,MAAM,CAGtE"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared SQL identifier helpers used by every Drizzle dialect in this
3
+ * package. Identifiers are validated before quoting so table names from
4
+ * options can never inject SQL.
5
+ */
6
+ /**
7
+ * Assert that a SQL identifier only contains safe characters.
8
+ *
9
+ * @throws Error when the identifier contains anything beyond
10
+ * `[A-Za-z0-9_]` or starts with a digit.
11
+ */
12
+ export function assertSafeIdentifier(name) {
13
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
14
+ throw new Error(`Unsafe SQL identifier "${name}".`);
15
+ }
16
+ }
17
+ /**
18
+ * Quote a validated SQL identifier with the dialect's identifier quote.
19
+ *
20
+ * - SQLite and Postgres use `"`.
21
+ * - MySQL uses `` ` ``.
22
+ */
23
+ export function quoteIdentifier(name, quote) {
24
+ assertSafeIdentifier(name);
25
+ return `${quote}${name.replaceAll(quote, `${quote}${quote}`)}${quote}`;
26
+ }
27
+ //# sourceMappingURL=identifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../../src/shared/identifiers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,IAAI,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,KAAgB;IAC5D,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,GAAG,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,KAAK,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC;AACzE,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared devtools instrumentation helpers for Drizzle database providers.
3
+ */
4
+ import type { ProviderInstrumentation } from "@beignet/core/providers";
5
+ import type { Logger } from "drizzle-orm";
6
+ /**
7
+ * Summarize a SQL query for devtools event summaries: collapse whitespace
8
+ * and truncate long statements.
9
+ */
10
+ export declare function summarizeQuery(query: string): string;
11
+ /**
12
+ * Create a Drizzle logger that records `db.query` custom instrumentation
13
+ * events for every executed query.
14
+ *
15
+ * Returns `undefined` when instrumentation is disabled so providers can skip
16
+ * installing a logger entirely.
17
+ */
18
+ export declare function createDbQueryLogger(instrumentation: ProviderInstrumentation, portName: string): Logger | undefined;
19
+ //# sourceMappingURL=instrumentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.d.ts","sourceRoot":"","sources":["../../src/shared/instrumentation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKpD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,uBAAuB,EACxC,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAoBpB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared devtools instrumentation helpers for Drizzle database providers.
3
+ */
4
+ /**
5
+ * Summarize a SQL query for devtools event summaries: collapse whitespace
6
+ * and truncate long statements.
7
+ */
8
+ export function summarizeQuery(query) {
9
+ const normalized = query.replace(/\s+/g, " ").trim();
10
+ return normalized.length > 120
11
+ ? `${normalized.slice(0, 117)}...`
12
+ : normalized;
13
+ }
14
+ /**
15
+ * Create a Drizzle logger that records `db.query` custom instrumentation
16
+ * events for every executed query.
17
+ *
18
+ * Returns `undefined` when instrumentation is disabled so providers can skip
19
+ * installing a logger entirely.
20
+ */
21
+ export function createDbQueryLogger(instrumentation, portName) {
22
+ if (!instrumentation.isEnabled()) {
23
+ return undefined;
24
+ }
25
+ return {
26
+ logQuery(query, params) {
27
+ instrumentation.custom({
28
+ name: "db.query",
29
+ label: "Database query",
30
+ summary: summarizeQuery(query),
31
+ details: {
32
+ query,
33
+ params: params.length > 0 ? "[redacted]" : [],
34
+ paramsCount: params.length,
35
+ portName,
36
+ },
37
+ });
38
+ },
39
+ };
40
+ }
41
+ //# sourceMappingURL=instrumentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instrumentation.js","sourceRoot":"","sources":["../../src/shared/instrumentation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACrD,OAAO,UAAU,CAAC,MAAM,GAAG,GAAG;QAC5B,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK;QAClC,CAAC,CAAC,UAAU,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,eAAwC,EACxC,QAAgB;IAEhB,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,EAAE,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,QAAQ,CAAC,KAAa,EAAE,MAAiB;YACvC,eAAe,CAAC,MAAM,CAAC;gBACrB,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,gBAAgB;gBACvB,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC;gBAC9B,OAAO,EAAE;oBACP,KAAK;oBACL,MAAM,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;oBAC7C,WAAW,EAAE,MAAM,CAAC,MAAM;oBAC1B,QAAQ;iBACT;aACF,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Dialect-independent outbox port orchestration.
3
+ *
4
+ * Every Drizzle dialect (SQLite, Postgres, MySQL) implements the small
5
+ * `OutboxSqlExecutor` seam below; the complete enqueue/claim/deliver/fail
6
+ * workflow lives here so the semantics stay byte-identical across dialects.
7
+ *
8
+ * This module must never import dialect database types — only `SQL` from
9
+ * drizzle-orm and core ports from `@beignet/core`.
10
+ */
11
+ import { type OutboxPort } from "@beignet/core/outbox";
12
+ import { type SQL } from "drizzle-orm";
13
+ /**
14
+ * Minimal SQL execution seam a dialect implements to back the shared
15
+ * database executor cores.
16
+ *
17
+ * @template TSelf - The concrete executor interface, so `withTransaction`
18
+ * hands the callback a transaction-scoped executor of the same shape.
19
+ */
20
+ export interface SqlExecutorBase<TSelf> {
21
+ /**
22
+ * The quoted table name as a raw SQL fragment, e.g.
23
+ * `sql.raw(quoteIdentifier(tableName, '"'))`.
24
+ */
25
+ table: SQL;
26
+ /**
27
+ * Execute a query and return all rows as plain column-name keyed records.
28
+ */
29
+ rows(query: SQL): Promise<Record<string, unknown>[]>;
30
+ /**
31
+ * Execute a query and return the first row, or `undefined` when no row
32
+ * matched.
33
+ */
34
+ row(query: SQL): Promise<Record<string, unknown> | undefined>;
35
+ /**
36
+ * Execute a mutation and return the number of affected rows, or
37
+ * `undefined` when the driver does not report it.
38
+ */
39
+ run(query: SQL): Promise<number | undefined>;
40
+ /**
41
+ * Run `fn` inside a database transaction with a transaction-scoped
42
+ * executor. Return `undefined` (synchronously) when the executor is
43
+ * already transaction-scoped or the client cannot open a transaction; the
44
+ * core then runs the work directly on this executor.
45
+ */
46
+ withTransaction<R>(fn: (tx: TSelf) => Promise<R>): Promise<R> | undefined;
47
+ }
48
+ /**
49
+ * SQL execution seam for the shared outbox core.
50
+ */
51
+ export interface OutboxSqlExecutor extends SqlExecutorBase<OutboxSqlExecutor> {
52
+ /**
53
+ * Row-locking suffix appended to the eligible-message select.
54
+ *
55
+ * SQLite: empty. Postgres/MySQL: ` for update skip locked`.
56
+ */
57
+ claimLockSuffix: SQL;
58
+ }
59
+ /**
60
+ * Options for the shared outbox core.
61
+ */
62
+ export interface OutboxPortCoreOptions {
63
+ /**
64
+ * Clock used by tests and deterministic environments.
65
+ */
66
+ now?: () => Date;
67
+ }
68
+ /**
69
+ * Create the dialect-independent outbox port orchestration on top of a
70
+ * dialect's `OutboxSqlExecutor`.
71
+ *
72
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
73
+ * for eligible messages.
74
+ */
75
+ export declare function createOutboxPortCore(executor: OutboxSqlExecutor, options?: OutboxPortCoreOptions): OutboxPort;
76
+ //# sourceMappingURL=outbox-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-core.d.ts","sourceRoot":"","sources":["../../src/shared/outbox-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAQL,KAAK,UAAU,EAEhB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,GAAG,EAAO,MAAM,aAAa,CAAC;AAO5C;;;;;;GAMG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK;IACpC;;;OAGG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAErD;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC;IAE9D;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE7C;;;;;OAKG;IACH,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CAC3E;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,eAAe,CAAC,iBAAiB,CAAC;IAC3E;;;;OAIG;IACH,eAAe,EAAE,GAAG,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AA2DD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE,qBAA0B,GAClC,UAAU,CAmKZ"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Dialect-independent outbox port orchestration.
3
+ *
4
+ * Every Drizzle dialect (SQLite, Postgres, MySQL) implements the small
5
+ * `OutboxSqlExecutor` seam below; the complete enqueue/claim/deliver/fail
6
+ * workflow lives here so the semantics stay byte-identical across dialects.
7
+ *
8
+ * This module must never import dialect database types — only `SQL` from
9
+ * drizzle-orm and core ports from `@beignet/core`.
10
+ */
11
+ import { createOutboxMessage, DEFAULT_OUTBOX_LEASE_MS, OutboxClaimError, serializeOutboxError, } from "@beignet/core/outbox";
12
+ import { sql } from "drizzle-orm";
13
+ import { encodeTimestamp, rowToClaimedMessage, } from "./rows.js";
14
+ function nowFrom(options) {
15
+ return options.now?.() ?? new Date();
16
+ }
17
+ function createClaimToken() {
18
+ return crypto.randomUUID();
19
+ }
20
+ async function getOutboxRow(executor, id) {
21
+ const row = await executor.row(sql `select * from ${executor.table} where id = ${id}`);
22
+ return row;
23
+ }
24
+ function assertClaimedRowSucceeded(row, input) {
25
+ if (!row || row.status !== input.expectedStatus || row.claim_token !== null) {
26
+ throw new OutboxClaimError({
27
+ id: input.id,
28
+ message: `Outbox message "${input.id}" is not claimed by this worker.`,
29
+ });
30
+ }
31
+ }
32
+ /**
33
+ * Verify that a claim-token-guarded outbox update matched a row.
34
+ *
35
+ * When the driver reports `rowsAffected`, trust it: a verification re-read
36
+ * would race with legitimate re-claims (for example, a concurrent worker can
37
+ * re-claim a message immediately after `markFailed` returns it to `pending`)
38
+ * and report a spurious claim failure. The re-read only runs as a fallback for
39
+ * drivers that do not report `rowsAffected`.
40
+ */
41
+ async function assertOutboxMutationSucceeded(executor, input, rowsAffected) {
42
+ if (rowsAffected !== undefined) {
43
+ if (rowsAffected <= 0) {
44
+ throw new OutboxClaimError({
45
+ id: input.id,
46
+ message: `Outbox message "${input.id}" is not claimed by this worker.`,
47
+ });
48
+ }
49
+ return;
50
+ }
51
+ assertClaimedRowSucceeded(await getOutboxRow(executor, input.id), input);
52
+ }
53
+ /**
54
+ * Create the dialect-independent outbox port orchestration on top of a
55
+ * dialect's `OutboxSqlExecutor`.
56
+ *
57
+ * Claiming uses a claim-token lease so concurrent workers can safely compete
58
+ * for eligible messages.
59
+ */
60
+ export function createOutboxPortCore(executor, options = {}) {
61
+ async function claimWith(tx, input) {
62
+ const nowIso = encodeTimestamp(input.now);
63
+ const lockedUntilIso = encodeTimestamp(new Date(input.now.getTime() + input.leaseMs));
64
+ const rows = (await tx.rows(sql `select * from ${tx.table}
65
+ where (
66
+ (status = 'pending' and available_at <= ${nowIso})
67
+ or (status = 'claimed' and locked_until is not null and locked_until <= ${nowIso})
68
+ )
69
+ order by available_at asc, created_at asc
70
+ limit ${input.limit}${tx.claimLockSuffix}`));
71
+ const claimed = [];
72
+ for (const row of rows) {
73
+ const claimToken = createClaimToken();
74
+ const rowsAffected = await tx.run(sql `update ${tx.table}
75
+ set
76
+ status = 'claimed',
77
+ attempts = attempts + 1,
78
+ claimed_at = ${nowIso},
79
+ locked_until = ${lockedUntilIso},
80
+ claim_token = ${claimToken},
81
+ updated_at = ${nowIso}
82
+ where id = ${row.id}
83
+ and (
84
+ (status = 'pending' and available_at <= ${nowIso})
85
+ or (status = 'claimed' and locked_until is not null and locked_until <= ${nowIso})
86
+ )`);
87
+ if (rowsAffected !== undefined && rowsAffected <= 0) {
88
+ continue;
89
+ }
90
+ const claimedRow = (await tx.row(sql `select * from ${tx.table} where id = ${row.id} and claim_token = ${claimToken}`));
91
+ if (claimedRow) {
92
+ claimed.push(rowToClaimedMessage(claimedRow));
93
+ }
94
+ }
95
+ return claimed;
96
+ }
97
+ return {
98
+ async enqueue(input) {
99
+ const message = createOutboxMessage(input, {
100
+ now: nowFrom(options),
101
+ });
102
+ const nowIso = encodeTimestamp(message.createdAt);
103
+ await executor.run(sql `insert into ${executor.table} (
104
+ id,
105
+ kind,
106
+ name,
107
+ payload_json,
108
+ status,
109
+ attempts,
110
+ max_attempts,
111
+ available_at,
112
+ claimed_at,
113
+ locked_until,
114
+ claim_token,
115
+ delivered_at,
116
+ last_error_json,
117
+ created_at,
118
+ updated_at
119
+ ) values (
120
+ ${message.id},
121
+ ${message.kind},
122
+ ${message.name},
123
+ ${JSON.stringify(message.payload)},
124
+ ${message.status},
125
+ ${message.attempts},
126
+ ${message.maxAttempts},
127
+ ${encodeTimestamp(message.availableAt)},
128
+ null,
129
+ null,
130
+ null,
131
+ null,
132
+ null,
133
+ ${nowIso},
134
+ ${nowIso}
135
+ )`);
136
+ return message;
137
+ },
138
+ async claimBatch(input) {
139
+ const limit = input.limit;
140
+ if (!Number.isInteger(limit) || limit <= 0) {
141
+ throw new Error("limit must be a positive integer");
142
+ }
143
+ const now = input.now ?? nowFrom(options);
144
+ const leaseMs = input.leaseMs ?? DEFAULT_OUTBOX_LEASE_MS;
145
+ if (!Number.isInteger(leaseMs) || leaseMs <= 0) {
146
+ throw new Error("leaseMs must be a positive integer");
147
+ }
148
+ const claimInput = { limit, now, leaseMs };
149
+ const transactional = executor.withTransaction((tx) => claimWith(tx, claimInput));
150
+ if (transactional !== undefined) {
151
+ return transactional;
152
+ }
153
+ return claimWith(executor, claimInput);
154
+ },
155
+ async markDelivered(input) {
156
+ const nowIso = encodeTimestamp(input.now ?? nowFrom(options));
157
+ const rowsAffected = await executor.run(sql `update ${executor.table}
158
+ set
159
+ status = 'delivered',
160
+ delivered_at = ${nowIso},
161
+ claimed_at = null,
162
+ locked_until = null,
163
+ claim_token = null,
164
+ updated_at = ${nowIso}
165
+ where id = ${input.id}
166
+ and status = 'claimed'
167
+ and claim_token = ${input.claimToken}`);
168
+ await assertOutboxMutationSucceeded(executor, { id: input.id, expectedStatus: "delivered" }, rowsAffected);
169
+ },
170
+ async markFailed(input) {
171
+ const now = input.now ?? nowFrom(options);
172
+ const nowIso = encodeTimestamp(now);
173
+ const status = input.deadLetter
174
+ ? "deadLettered"
175
+ : "pending";
176
+ const availableAt = input.retryAt ?? now;
177
+ const rowsAffected = await executor.run(sql `update ${executor.table}
178
+ set
179
+ status = ${status},
180
+ available_at = ${encodeTimestamp(availableAt)},
181
+ claimed_at = null,
182
+ locked_until = null,
183
+ claim_token = null,
184
+ last_error_json = ${JSON.stringify(serializeOutboxError(input.error))},
185
+ updated_at = ${nowIso}
186
+ where id = ${input.id}
187
+ and status = 'claimed'
188
+ and claim_token = ${input.claimToken}`);
189
+ await assertOutboxMutationSucceeded(executor, { id: input.id, expectedStatus: status }, rowsAffected);
190
+ },
191
+ };
192
+ }
193
+ //# sourceMappingURL=outbox-core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-core.js","sourceRoot":"","sources":["../../src/shared/outbox-core.ts"],"names":[],"mappings":"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"}