@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.
- package/CHANGELOG.md +82 -0
- package/README.md +1046 -0
- package/dist/mysql/index.d.ts +254 -0
- package/dist/mysql/index.d.ts.map +1 -0
- package/dist/mysql/index.js +348 -0
- package/dist/mysql/index.js.map +1 -0
- package/dist/postgres/index.d.ts +240 -0
- package/dist/postgres/index.d.ts.map +1 -0
- package/dist/postgres/index.js +296 -0
- package/dist/postgres/index.js.map +1 -0
- package/dist/shared/idempotency-core.d.ts +71 -0
- package/dist/shared/idempotency-core.d.ts.map +1 -0
- package/dist/shared/idempotency-core.js +169 -0
- package/dist/shared/idempotency-core.js.map +1 -0
- package/dist/shared/identifiers.d.ts +20 -0
- package/dist/shared/identifiers.d.ts.map +1 -0
- package/dist/shared/identifiers.js +27 -0
- package/dist/shared/identifiers.js.map +1 -0
- package/dist/shared/instrumentation.d.ts +19 -0
- package/dist/shared/instrumentation.d.ts.map +1 -0
- package/dist/shared/instrumentation.js +41 -0
- package/dist/shared/instrumentation.js.map +1 -0
- package/dist/shared/outbox-core.d.ts +76 -0
- package/dist/shared/outbox-core.d.ts.map +1 -0
- package/dist/shared/outbox-core.js +193 -0
- package/dist/shared/outbox-core.js.map +1 -0
- package/dist/shared/rows.d.ts +84 -0
- package/dist/shared/rows.d.ts.map +1 -0
- package/dist/shared/rows.js +128 -0
- package/dist/shared/rows.js.map +1 -0
- package/dist/sqlite/index.d.ts +235 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +293 -0
- package/dist/sqlite/index.js.map +1 -0
- package/package.json +173 -0
- package/src/mysql/index.ts +627 -0
- package/src/postgres/index.ts +572 -0
- package/src/shared/idempotency-core.ts +280 -0
- package/src/shared/identifiers.ts +28 -0
- package/src/shared/instrumentation.ts +49 -0
- package/src/shared/outbox-core.ts +322 -0
- package/src/shared/rows.ts +197 -0
- package/src/sqlite/index.ts +547 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared row encode/decode helpers for Drizzle database providers.
|
|
3
|
+
*
|
|
4
|
+
* Every dialect stores timestamps as ISO-8601 UTC strings in `text` columns.
|
|
5
|
+
* This is deliberate: lexicographic order equals chronological order, the
|
|
6
|
+
* storage semantics stay byte-identical across SQLite, Postgres, and MySQL,
|
|
7
|
+
* and all values route through this single encode/decode point so a dialect
|
|
8
|
+
* could later move to native timestamp types without touching the shared
|
|
9
|
+
* orchestration in `outbox-core.ts` and `idempotency-core.ts`.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { IdempotencyReservation } from "@beignet/core/idempotency";
|
|
13
|
+
import {
|
|
14
|
+
type ClaimedOutboxMessage,
|
|
15
|
+
OutboxClaimError,
|
|
16
|
+
type OutboxErrorInfo,
|
|
17
|
+
type OutboxJsonValue,
|
|
18
|
+
type OutboxMessage,
|
|
19
|
+
type OutboxMessageKind,
|
|
20
|
+
type OutboxMessageStatus,
|
|
21
|
+
} from "@beignet/core/outbox";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Encode a timestamp for storage. All dialects store ISO-8601 UTC strings.
|
|
25
|
+
*/
|
|
26
|
+
export function encodeTimestamp(value: Date): string {
|
|
27
|
+
return value.toISOString();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Decode a stored timestamp string back into a `Date`.
|
|
32
|
+
*/
|
|
33
|
+
export function decodeTimestamp(value: string): Date {
|
|
34
|
+
return new Date(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Decode a nullable stored timestamp string.
|
|
39
|
+
*/
|
|
40
|
+
export function parseNullableDate(value: string | null): Date | null {
|
|
41
|
+
return value ? decodeTimestamp(value) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Decode a stored outbox error JSON column.
|
|
46
|
+
*/
|
|
47
|
+
export function parseLastError(value: string | null): OutboxErrorInfo | null {
|
|
48
|
+
return value ? (JSON.parse(value) as OutboxErrorInfo) : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Raw outbox table row shape shared by every dialect.
|
|
53
|
+
*/
|
|
54
|
+
export interface OutboxRow {
|
|
55
|
+
id: string;
|
|
56
|
+
kind: OutboxMessageKind;
|
|
57
|
+
name: string;
|
|
58
|
+
payload_json: string;
|
|
59
|
+
status: OutboxMessageStatus;
|
|
60
|
+
attempts: number;
|
|
61
|
+
max_attempts: number;
|
|
62
|
+
available_at: string;
|
|
63
|
+
claimed_at: string | null;
|
|
64
|
+
locked_until: string | null;
|
|
65
|
+
claim_token: string | null;
|
|
66
|
+
delivered_at: string | null;
|
|
67
|
+
last_error_json: string | null;
|
|
68
|
+
created_at: string;
|
|
69
|
+
updated_at: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Decode a raw outbox row into an `OutboxMessage`.
|
|
74
|
+
*/
|
|
75
|
+
export function rowToMessage(row: OutboxRow): OutboxMessage {
|
|
76
|
+
return {
|
|
77
|
+
id: row.id,
|
|
78
|
+
kind: row.kind,
|
|
79
|
+
name: row.name,
|
|
80
|
+
payload: JSON.parse(row.payload_json) as OutboxJsonValue,
|
|
81
|
+
status: row.status,
|
|
82
|
+
attempts: row.attempts,
|
|
83
|
+
maxAttempts: row.max_attempts,
|
|
84
|
+
availableAt: decodeTimestamp(row.available_at),
|
|
85
|
+
claimedAt: parseNullableDate(row.claimed_at),
|
|
86
|
+
lockedUntil: parseNullableDate(row.locked_until),
|
|
87
|
+
claimToken: row.claim_token,
|
|
88
|
+
deliveredAt: parseNullableDate(row.delivered_at),
|
|
89
|
+
lastError: parseLastError(row.last_error_json),
|
|
90
|
+
createdAt: decodeTimestamp(row.created_at),
|
|
91
|
+
updatedAt: decodeTimestamp(row.updated_at),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Decode a raw outbox row into a `ClaimedOutboxMessage`.
|
|
97
|
+
*
|
|
98
|
+
* @throws OutboxClaimError when the row is not in a fully claimed state.
|
|
99
|
+
*/
|
|
100
|
+
export function rowToClaimedMessage(row: OutboxRow): ClaimedOutboxMessage {
|
|
101
|
+
const message = rowToMessage(row);
|
|
102
|
+
if (
|
|
103
|
+
message.status !== "claimed" ||
|
|
104
|
+
!message.claimToken ||
|
|
105
|
+
!message.claimedAt ||
|
|
106
|
+
!message.lockedUntil
|
|
107
|
+
) {
|
|
108
|
+
throw new OutboxClaimError({
|
|
109
|
+
id: message.id,
|
|
110
|
+
message: `Outbox message "${message.id}" is not claimed.`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
...message,
|
|
116
|
+
status: "claimed",
|
|
117
|
+
claimToken: message.claimToken,
|
|
118
|
+
claimedAt: message.claimedAt,
|
|
119
|
+
lockedUntil: message.lockedUntil,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Stored idempotency record status values shared by every dialect.
|
|
125
|
+
*/
|
|
126
|
+
export type IdempotencyRowStatus = "in-progress" | "completed";
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Raw idempotency table row shape shared by every dialect.
|
|
130
|
+
*/
|
|
131
|
+
export interface IdempotencyRow {
|
|
132
|
+
storage_key: string;
|
|
133
|
+
namespace: string;
|
|
134
|
+
idempotency_key: string;
|
|
135
|
+
scope_key: string;
|
|
136
|
+
fingerprint: string;
|
|
137
|
+
status: IdempotencyRowStatus;
|
|
138
|
+
result_json: string | null;
|
|
139
|
+
reserved_at: string;
|
|
140
|
+
completed_at: string | null;
|
|
141
|
+
expires_at: string | null;
|
|
142
|
+
updated_at: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Decode a raw idempotency row into the reservation outcome for a caller
|
|
147
|
+
* holding `receivedFingerprint`.
|
|
148
|
+
*/
|
|
149
|
+
export function idempotencyReservationFromRow(
|
|
150
|
+
row: IdempotencyRow,
|
|
151
|
+
receivedFingerprint: string,
|
|
152
|
+
): IdempotencyReservation {
|
|
153
|
+
const reservedAt = decodeTimestamp(row.reserved_at);
|
|
154
|
+
const completedAt = parseNullableDate(row.completed_at);
|
|
155
|
+
const expiresAt = parseNullableDate(row.expires_at);
|
|
156
|
+
|
|
157
|
+
if (row.fingerprint !== receivedFingerprint) {
|
|
158
|
+
return {
|
|
159
|
+
status: "conflict",
|
|
160
|
+
namespace: row.namespace,
|
|
161
|
+
key: row.idempotency_key,
|
|
162
|
+
scopeKey: row.scope_key,
|
|
163
|
+
storedFingerprint: row.fingerprint,
|
|
164
|
+
receivedFingerprint,
|
|
165
|
+
reservedAt,
|
|
166
|
+
completedAt: completedAt ?? undefined,
|
|
167
|
+
expiresAt,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (row.status === "completed" && completedAt) {
|
|
172
|
+
return {
|
|
173
|
+
status: "replay",
|
|
174
|
+
namespace: row.namespace,
|
|
175
|
+
key: row.idempotency_key,
|
|
176
|
+
scopeKey: row.scope_key,
|
|
177
|
+
fingerprint: row.fingerprint,
|
|
178
|
+
result:
|
|
179
|
+
row.result_json === null
|
|
180
|
+
? undefined
|
|
181
|
+
: (JSON.parse(row.result_json) as unknown),
|
|
182
|
+
reservedAt,
|
|
183
|
+
completedAt,
|
|
184
|
+
expiresAt,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
status: "inProgress",
|
|
190
|
+
namespace: row.namespace,
|
|
191
|
+
key: row.idempotency_key,
|
|
192
|
+
scopeKey: row.scope_key,
|
|
193
|
+
fingerprint: row.fingerprint,
|
|
194
|
+
reservedAt,
|
|
195
|
+
expiresAt,
|
|
196
|
+
};
|
|
197
|
+
}
|