@venturekit/data 0.0.32 → 0.0.33
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/dist/files/index.d.ts +9 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +8 -0
- package/dist/files/index.js.map +1 -0
- package/dist/files/postgres.d.ts +150 -0
- package/dist/files/postgres.d.ts.map +1 -0
- package/dist/files/postgres.js +194 -0
- package/dist/files/postgres.js.map +1 -0
- package/dist/idempotency/index.d.ts +9 -0
- package/dist/idempotency/index.d.ts.map +1 -0
- package/dist/idempotency/index.js +8 -0
- package/dist/idempotency/index.js.map +1 -0
- package/dist/idempotency/postgres.d.ts +107 -0
- package/dist/idempotency/postgres.d.ts.map +1 -0
- package/dist/idempotency/postgres.js +145 -0
- package/dist/idempotency/postgres.js.map +1 -0
- package/dist/internal/identifier.d.ts +16 -0
- package/dist/internal/identifier.d.ts.map +1 -0
- package/dist/internal/identifier.js +23 -0
- package/dist/internal/identifier.js.map +1 -0
- package/dist/jobs/index.d.ts +9 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +8 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/jobs/postgres.d.ts +197 -0
- package/dist/jobs/postgres.d.ts.map +1 -0
- package/dist/jobs/postgres.js +270 -0
- package/dist/jobs/postgres.js.map +1 -0
- package/dist/outbox/index.d.ts +9 -0
- package/dist/outbox/index.d.ts.map +1 -0
- package/dist/outbox/index.js +8 -0
- package/dist/outbox/index.js.map +1 -0
- package/dist/outbox/postgres.d.ts +124 -0
- package/dist/outbox/postgres.d.ts.map +1 -0
- package/dist/outbox/postgres.js +177 -0
- package/dist/outbox/postgres.js.map +1 -0
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js.map +1 -1
- package/dist/query/secret.d.ts.map +1 -1
- package/dist/query/secret.js +1 -1
- package/dist/query/secret.js.map +1 -1
- package/package.json +18 -2
- package/src/sql/{vk_data_001_tenancy_foundation.sql → 0000_vk_data_foundation.sql} +305 -278
- package/src/sql/vk_data_001_idempotency.sql +48 -0
- package/src/sql/vk_data_002_outbox.sql +99 -0
- package/src/sql/vk_data_003_jobs.sql +114 -0
- package/src/sql/vk_data_004_file_object.sql +112 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @venturekit/data — durable background jobs in Postgres.
|
|
3
|
+
*
|
|
4
|
+
* The `queues` intent (SQS) stays the default for opaque, high-throughput
|
|
5
|
+
* fan-out. This is for the other kind of work: the kind the product talks about.
|
|
6
|
+
* "Which imports are still geocoding for this tenant" is a screen, and a queue
|
|
7
|
+
* has no query surface — messages are invisible until received and then
|
|
8
|
+
* invisible to everyone else. Rebuilding that visibility over SQS means a second
|
|
9
|
+
* table tracking what you enqueued, at which point the queue is the redundant
|
|
10
|
+
* half, because the table can be claimed from directly.
|
|
11
|
+
*
|
|
12
|
+
* Unlike the outbox, there is no port and no adapter here. The whole value is
|
|
13
|
+
* that the work lives in queryable rows, and the claim — `FOR UPDATE SKIP
|
|
14
|
+
* LOCKED` — is SQL rather than portable logic. An abstraction over it would have
|
|
15
|
+
* exactly one implementation and would hide the one feature worth having.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* // enqueue in the transaction that made the work necessary
|
|
19
|
+
* await withTransaction(async (tx) => {
|
|
20
|
+
* const batch = await stageImport(tx, rows);
|
|
21
|
+
* await enqueueJob(tx.query, { kind: 'geocode-import', tenantId, payload: { batch: batch.id } });
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // drain in a `schedules` cron
|
|
25
|
+
* await runJobsOnce({
|
|
26
|
+
* 'geocode-import': async (job) => { await geocode(job.payload); },
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
import { query } from '../query/index.js';
|
|
31
|
+
import { assertSqlIdentifier } from '../internal/identifier.js';
|
|
32
|
+
/** Default table name, created by `vk_data_003_jobs.sql`. */
|
|
33
|
+
export const DEFAULT_JOB_TABLE = 'vk_job';
|
|
34
|
+
const iso = (v) => v instanceof Date ? v.toISOString() : v;
|
|
35
|
+
const isoOrNull = (v) => v === null ? null : iso(v);
|
|
36
|
+
function mapRow(r) {
|
|
37
|
+
return {
|
|
38
|
+
id: r.id,
|
|
39
|
+
tenantId: r.tenant_id,
|
|
40
|
+
kind: r.kind,
|
|
41
|
+
payload: r.payload,
|
|
42
|
+
status: r.status,
|
|
43
|
+
runAfter: iso(r.run_after),
|
|
44
|
+
attempts: r.attempts,
|
|
45
|
+
maxAttempts: r.max_attempts,
|
|
46
|
+
lockedAt: isoOrNull(r.locked_at),
|
|
47
|
+
lockedBy: r.locked_by,
|
|
48
|
+
lastError: r.last_error,
|
|
49
|
+
createdAt: iso(r.created_at),
|
|
50
|
+
finishedAt: isoOrNull(r.finished_at),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const COLUMNS = `id, tenant_id, kind, payload, status, run_after, attempts,
|
|
54
|
+
max_attempts, locked_at, locked_by, last_error, created_at,
|
|
55
|
+
finished_at`;
|
|
56
|
+
function tableOf(options, context) {
|
|
57
|
+
const table = options.tableName ?? DEFAULT_JOB_TABLE;
|
|
58
|
+
assertSqlIdentifier(table, context);
|
|
59
|
+
return table;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Enqueue a job **on the caller's transaction**.
|
|
63
|
+
*
|
|
64
|
+
* `querier` is required and not defaulted, for the same reason as
|
|
65
|
+
* `appendToOutbox`: a job enqueued on a pooled connection can commit while the
|
|
66
|
+
* transaction that needed it rolls back, leaving a worker to act on a state that
|
|
67
|
+
* does not exist. Pass the transaction's `query`.
|
|
68
|
+
*/
|
|
69
|
+
export async function enqueueJob(querier, input, options = {}) {
|
|
70
|
+
const table = tableOf(options, 'enqueueJob.tableName');
|
|
71
|
+
const rows = await querier(`INSERT INTO ${table} (tenant_id, kind, payload, run_after, max_attempts)
|
|
72
|
+
VALUES ($1, $2, $3::jsonb, coalesce($4, now()), coalesce($5, 5))
|
|
73
|
+
RETURNING id`, [
|
|
74
|
+
input.tenantId ?? null,
|
|
75
|
+
input.kind,
|
|
76
|
+
JSON.stringify(input.payload ?? {}),
|
|
77
|
+
input.runAfter ? iso(input.runAfter instanceof Date ? input.runAfter : new Date(input.runAfter)) : null,
|
|
78
|
+
input.maxAttempts ?? null,
|
|
79
|
+
]);
|
|
80
|
+
const id = rows[0]?.id;
|
|
81
|
+
if (!id)
|
|
82
|
+
throw new Error('[venturekit/data] enqueueJob: insert returned no id');
|
|
83
|
+
return id;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Claim due jobs, marking them `running`.
|
|
87
|
+
*
|
|
88
|
+
* `FOR UPDATE SKIP LOCKED` inside the UPDATE's subquery is what makes several
|
|
89
|
+
* workers safe on one table: each takes rows nobody else holds and skips the
|
|
90
|
+
* rest rather than queueing behind them. Without SKIP LOCKED, a second worker
|
|
91
|
+
* blocks on the first's rows and the pool drains.
|
|
92
|
+
*/
|
|
93
|
+
export async function claimJobs(options = {}) {
|
|
94
|
+
const table = tableOf(options, 'claimJobs.tableName');
|
|
95
|
+
const run = options.querier ?? query;
|
|
96
|
+
const limit = options.limit ?? 10;
|
|
97
|
+
const workerId = options.workerId ?? process.env.AWS_LAMBDA_LOG_STREAM_NAME ?? 'worker';
|
|
98
|
+
const rows = await run(`UPDATE ${table} AS j
|
|
99
|
+
SET status = 'running',
|
|
100
|
+
locked_at = now(),
|
|
101
|
+
locked_by = $2,
|
|
102
|
+
attempts = j.attempts + 1,
|
|
103
|
+
updated_at = now()
|
|
104
|
+
WHERE j.id IN (
|
|
105
|
+
SELECT c.id FROM ${table} c
|
|
106
|
+
WHERE c.status = 'queued'
|
|
107
|
+
AND c.run_after <= now()
|
|
108
|
+
AND ($3::text[] IS NULL OR c.kind = ANY($3::text[]))
|
|
109
|
+
ORDER BY c.run_after
|
|
110
|
+
LIMIT $1
|
|
111
|
+
FOR UPDATE SKIP LOCKED
|
|
112
|
+
)
|
|
113
|
+
RETURNING ${COLUMNS}`, [limit, workerId, options.kinds ?? null]);
|
|
114
|
+
return rows.map((r) => mapRow(r));
|
|
115
|
+
}
|
|
116
|
+
/** Mark a claimed job done. */
|
|
117
|
+
export async function completeJob(id, options = {}) {
|
|
118
|
+
const table = tableOf(options, 'completeJob.tableName');
|
|
119
|
+
const run = options.querier ?? query;
|
|
120
|
+
await run(`UPDATE ${table}
|
|
121
|
+
SET status = 'succeeded', finished_at = now(), locked_at = NULL,
|
|
122
|
+
locked_by = NULL, last_error = NULL, updated_at = now()
|
|
123
|
+
WHERE id = $1`, [id]);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Record a failed attempt: re-queue it with a later `run_after`, or leave it
|
|
127
|
+
* `failed` once the attempts are spent.
|
|
128
|
+
*
|
|
129
|
+
* `attempts` was already incremented by the claim, so a crash between claim and
|
|
130
|
+
* this call still counts — otherwise a job that kills its worker every time
|
|
131
|
+
* would retry forever.
|
|
132
|
+
*/
|
|
133
|
+
export async function failJob(id, error, options = {}) {
|
|
134
|
+
const table = tableOf(options, 'failJob.tableName');
|
|
135
|
+
const run = options.querier ?? query;
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
await run(`UPDATE ${table}
|
|
138
|
+
SET status = CASE WHEN attempts >= max_attempts THEN 'failed' ELSE 'queued' END,
|
|
139
|
+
run_after = now() + make_interval(
|
|
140
|
+
secs => coalesce(
|
|
141
|
+
$3::double precision,
|
|
142
|
+
least(power(2, attempts)::double precision, 3600)
|
|
143
|
+
)
|
|
144
|
+
),
|
|
145
|
+
locked_at = NULL,
|
|
146
|
+
locked_by = NULL,
|
|
147
|
+
last_error = $2,
|
|
148
|
+
finished_at = CASE WHEN attempts >= max_attempts THEN now() ELSE NULL END,
|
|
149
|
+
updated_at = now()
|
|
150
|
+
WHERE id = $1`, [id, message.slice(0, 2000), options.retryInSeconds ?? null]);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Hand a claimed job back **without counting the attempt**.
|
|
154
|
+
*
|
|
155
|
+
* For "I cannot run this right now", as distinct from "I ran it and it failed":
|
|
156
|
+
* no handler is registered yet, a dependency is briefly unavailable, the worker
|
|
157
|
+
* is shutting down. The claim already incremented `attempts`, so this undoes
|
|
158
|
+
* that — otherwise a job nobody can run yet would exhaust its attempts while
|
|
159
|
+
* never actually being attempted, which is how a mid-deploy job ends up `failed`
|
|
160
|
+
* for no reason.
|
|
161
|
+
*/
|
|
162
|
+
export async function releaseJob(id, reason, options = {}) {
|
|
163
|
+
const table = tableOf(options, 'releaseJob.tableName');
|
|
164
|
+
const run = options.querier ?? query;
|
|
165
|
+
await run(`UPDATE ${table}
|
|
166
|
+
SET status = 'queued',
|
|
167
|
+
attempts = greatest(attempts - 1, 0),
|
|
168
|
+
run_after = now() + make_interval(secs => coalesce($3::double precision, 60)),
|
|
169
|
+
locked_at = NULL,
|
|
170
|
+
locked_by = NULL,
|
|
171
|
+
last_error = $2,
|
|
172
|
+
updated_at = now()
|
|
173
|
+
WHERE id = $1`, [id, reason.slice(0, 2000), options.retryInSeconds ?? null]);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Re-queue jobs left `running` by a worker that died, and return how many.
|
|
177
|
+
*
|
|
178
|
+
* A crashed worker cannot clean up after itself, so nothing else will ever move
|
|
179
|
+
* these rows: `running` plus an old `locked_at` is indistinguishable from a dead
|
|
180
|
+
* process, and treating it as one is the only way the work resumes. Worth a
|
|
181
|
+
* schedule of its own, or a call at the top of each worker pass.
|
|
182
|
+
*
|
|
183
|
+
* Jobs whose attempts are spent go to `failed` rather than back to `queued` —
|
|
184
|
+
* a job that reliably kills its worker must not do so forever.
|
|
185
|
+
*/
|
|
186
|
+
export async function reclaimStuckJobs(options = {}) {
|
|
187
|
+
const table = tableOf(options, 'reclaimStuckJobs.tableName');
|
|
188
|
+
const run = options.querier ?? query;
|
|
189
|
+
const stale = options.staleAfterSeconds ?? 900;
|
|
190
|
+
const rows = await run(`UPDATE ${table}
|
|
191
|
+
SET status = CASE WHEN attempts >= max_attempts THEN 'failed' ELSE 'queued' END,
|
|
192
|
+
locked_at = NULL,
|
|
193
|
+
locked_by = NULL,
|
|
194
|
+
last_error = coalesce(last_error, 'worker lock expired'),
|
|
195
|
+
finished_at = CASE WHEN attempts >= max_attempts THEN now() ELSE NULL END,
|
|
196
|
+
updated_at = now()
|
|
197
|
+
WHERE status = 'running'
|
|
198
|
+
AND locked_at < now() - make_interval(secs => $1::double precision)
|
|
199
|
+
RETURNING id`, [stale]);
|
|
200
|
+
return rows.length;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The query this table exists to make possible: what work is outstanding.
|
|
204
|
+
*
|
|
205
|
+
* With `tenantScoped` (the default) the predicate is
|
|
206
|
+
* `tenant_id = ANY (vk_tenant_scope())` — the same predicate an RLS policy would
|
|
207
|
+
* apply, so a caller who goes through this helper cannot see another tenant's
|
|
208
|
+
* jobs. Platform rows (`tenant_id IS NULL`) fall out naturally, since
|
|
209
|
+
* `NULL = ANY (...)` is never true.
|
|
210
|
+
*/
|
|
211
|
+
export async function listJobs(options = {}) {
|
|
212
|
+
const table = tableOf(options, 'listJobs.tableName');
|
|
213
|
+
const run = options.querier ?? query;
|
|
214
|
+
const scoped = options.tenantScoped !== false;
|
|
215
|
+
const rows = await run(`SELECT ${COLUMNS}
|
|
216
|
+
FROM ${table}
|
|
217
|
+
WHERE ($1::text IS NULL OR kind = $1)
|
|
218
|
+
AND ($2::text IS NULL OR status = $2)
|
|
219
|
+
AND (NOT $4::boolean OR tenant_id = ANY (vk_tenant_scope()))
|
|
220
|
+
ORDER BY created_at DESC
|
|
221
|
+
LIMIT $3`, [options.kind ?? null, options.status ?? null, options.limit ?? 50, scoped]);
|
|
222
|
+
return rows.map((r) => mapRow(r));
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* One worker pass: claim, dispatch, settle. The job-queue analogue of the
|
|
226
|
+
* outbox's `relayOnce`.
|
|
227
|
+
*
|
|
228
|
+
* Returns counts rather than throwing on a handler failure — a failing job is
|
|
229
|
+
* an expected operating condition and the caller's job is to schedule the next
|
|
230
|
+
* pass. A failing *store* propagates, for the same reason as in the relay: a
|
|
231
|
+
* worker that cannot record outcomes must stop rather than keep claiming.
|
|
232
|
+
*
|
|
233
|
+
* A `kind` with no handler is re-queued with a short delay rather than failed.
|
|
234
|
+
* A deploy in progress is the usual cause — one version enqueues a kind the
|
|
235
|
+
* still-running version does not know — and burning an attempt on that would
|
|
236
|
+
* exhaust a perfectly good job during a rollout.
|
|
237
|
+
*/
|
|
238
|
+
export async function runJobsOnce(handlers, options = {}) {
|
|
239
|
+
const kinds = options.kinds ?? Object.keys(handlers);
|
|
240
|
+
const jobs = await claimJobs({ ...options, kinds });
|
|
241
|
+
const result = {
|
|
242
|
+
claimed: jobs.length,
|
|
243
|
+
succeeded: 0,
|
|
244
|
+
failed: 0,
|
|
245
|
+
unhandled: 0,
|
|
246
|
+
};
|
|
247
|
+
for (const job of jobs) {
|
|
248
|
+
const handler = handlers[job.kind];
|
|
249
|
+
if (!handler) {
|
|
250
|
+
result.unhandled++;
|
|
251
|
+
/* `releaseJob`, not `failJob`: the job was never attempted, so it must not
|
|
252
|
+
be charged for the claim. A deploy in progress is the usual cause — one
|
|
253
|
+
version enqueues a kind the still-running version does not know — and
|
|
254
|
+
charging that would exhaust a perfectly good job during a rollout. */
|
|
255
|
+
await releaseJob(job.id, `no handler registered for kind '${job.kind}'`, { ...options, retryInSeconds: 60 });
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
await handler(job);
|
|
260
|
+
await completeJob(job.id, options);
|
|
261
|
+
result.succeeded++;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
await failJob(job.id, error, options);
|
|
265
|
+
result.failed++;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/jobs/postgres.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @venturekit/data — Postgres outbox store.
|
|
3
|
+
*
|
|
4
|
+
* The storage half of `@venturekit/runtime/patterns`' transactional outbox. See
|
|
5
|
+
* `./postgres.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export { createPostgresOutboxStore, appendToOutbox, discardOutboxEvent, DEFAULT_OUTBOX_TABLE, } from './postgres.js';
|
|
8
|
+
export type { EventActor, OutboxRow, OutboxFailure, AppendOutboxInput, PostgresOutboxStore, PostgresOutboxStoreOptions, } from './postgres.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAEvB,YAAY,EACV,UAAU,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @venturekit/data — Postgres outbox store.
|
|
3
|
+
*
|
|
4
|
+
* The storage half of `@venturekit/runtime/patterns`' transactional outbox. See
|
|
5
|
+
* `./postgres.ts`.
|
|
6
|
+
*/
|
|
7
|
+
export { createPostgresOutboxStore, appendToOutbox, discardOutboxEvent, DEFAULT_OUTBOX_TABLE, } from './postgres.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/outbox/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @venturekit/data — Postgres outbox store.
|
|
3
|
+
*
|
|
4
|
+
* The storage half of `@venturekit/runtime/patterns`' transactional outbox. The
|
|
5
|
+
* relay's rules live there and are pure; this is the adapter that satisfies the
|
|
6
|
+
* `OutboxStore` port against `vk_outbox`.
|
|
7
|
+
*
|
|
8
|
+
* Almost all of the difficulty is in one query. `claim()` carries three
|
|
9
|
+
* exclusions that the relay explicitly cannot make for itself, because each is a
|
|
10
|
+
* fact about rows *outside* the batch it is handed:
|
|
11
|
+
*
|
|
12
|
+
* 1. quarantined rows (`attempts >= maxAttempts`);
|
|
13
|
+
* 2. anything queued behind a quarantined row **for the same aggregate** —
|
|
14
|
+
* without this, giving up on one event silently promotes its successors and
|
|
15
|
+
* the per-aggregate ordering guarantee is lost between passes;
|
|
16
|
+
* 3. discarded rows, and the successors held behind them must be *released*,
|
|
17
|
+
* since a discard is an operator deciding the event will never go.
|
|
18
|
+
*
|
|
19
|
+
* (2) and (3) pull in opposite directions and that is the point: a row is held
|
|
20
|
+
* behind an abandoned predecessor only while that predecessor is *still*
|
|
21
|
+
* abandoned. Get it wrong in one direction and a quarantine reorders the stream;
|
|
22
|
+
* wrong in the other and a discard freezes an aggregate permanently.
|
|
23
|
+
*/
|
|
24
|
+
import type { Querier } from '../query/index.js';
|
|
25
|
+
/** Default table name, created by `vk_data_002_outbox.sql`. */
|
|
26
|
+
export declare const DEFAULT_OUTBOX_TABLE = "vk_outbox";
|
|
27
|
+
/** Mirrors `@venturekit/runtime/patterns`' `EventActor`. */
|
|
28
|
+
export interface EventActor {
|
|
29
|
+
type: 'user' | 'system';
|
|
30
|
+
id: string;
|
|
31
|
+
}
|
|
32
|
+
/** Mirrors `@venturekit/runtime/patterns`' `OutboxRow`. */
|
|
33
|
+
export interface OutboxRow {
|
|
34
|
+
id: string;
|
|
35
|
+
seq: number;
|
|
36
|
+
type: string;
|
|
37
|
+
aggregateType: string;
|
|
38
|
+
aggregateId: string;
|
|
39
|
+
tenantId: string | null;
|
|
40
|
+
payload: unknown;
|
|
41
|
+
eventVersion: number;
|
|
42
|
+
occurredAt: string;
|
|
43
|
+
actor: EventActor;
|
|
44
|
+
attempts: number;
|
|
45
|
+
}
|
|
46
|
+
export interface OutboxFailure {
|
|
47
|
+
id: string;
|
|
48
|
+
attempts: number;
|
|
49
|
+
error: string;
|
|
50
|
+
}
|
|
51
|
+
export interface PostgresOutboxStoreOptions {
|
|
52
|
+
/**
|
|
53
|
+
* Runs the statements. Defaults to the pooled `query`.
|
|
54
|
+
*
|
|
55
|
+
* The relay reads and marks outside any request, so the pool is right for it.
|
|
56
|
+
* The *append* side is the opposite — see {@link appendToOutbox}, which must
|
|
57
|
+
* run on the transaction that makes the state change.
|
|
58
|
+
*/
|
|
59
|
+
querier?: Querier;
|
|
60
|
+
/** Table name. Validated as an identifier, since it is interpolated. */
|
|
61
|
+
tableName?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface PostgresOutboxStore {
|
|
64
|
+
claim(limit: number, maxAttempts: number): Promise<OutboxRow[]>;
|
|
65
|
+
markPublished(ids: string[]): Promise<void>;
|
|
66
|
+
recordFailures(failures: OutboxFailure[]): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a Postgres-backed outbox store.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { relayOnce, createEventBridgeEventBus } from '@venturekit/runtime/patterns';
|
|
74
|
+
* import { createPostgresOutboxStore } from '@venturekit/data/outbox';
|
|
75
|
+
*
|
|
76
|
+
* // in a `schedules` cron
|
|
77
|
+
* await relayOnce(createPostgresOutboxStore(), createEventBridgeEventBus());
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function createPostgresOutboxStore(options?: PostgresOutboxStoreOptions): PostgresOutboxStore;
|
|
81
|
+
/** What {@link appendToOutbox} needs; the envelope plus nothing. */
|
|
82
|
+
export interface AppendOutboxInput {
|
|
83
|
+
id: string;
|
|
84
|
+
type: string;
|
|
85
|
+
aggregateType: string;
|
|
86
|
+
aggregateId: string;
|
|
87
|
+
tenantId?: string | null;
|
|
88
|
+
payload: unknown;
|
|
89
|
+
/** Payload schema version. Defaults to 1. */
|
|
90
|
+
version?: number;
|
|
91
|
+
occurredAt?: string;
|
|
92
|
+
actor: EventActor;
|
|
93
|
+
correlationId?: string | null;
|
|
94
|
+
causationId?: string | null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Append an event to the outbox **on the caller's transaction**.
|
|
98
|
+
*
|
|
99
|
+
* `querier` is required and not defaulted, unlike everywhere else in this
|
|
100
|
+
* module. Defaulting it to the pool would make the easiest call the wrong one:
|
|
101
|
+
* an event written on a pooled connection does not commit with the state change
|
|
102
|
+
* it describes, which is the single guarantee the outbox exists to provide. Pass
|
|
103
|
+
* the transaction's `query`.
|
|
104
|
+
*
|
|
105
|
+
* ```ts
|
|
106
|
+
* await withTransaction(async (tx) => {
|
|
107
|
+
* await tx.query('UPDATE orders SET status = $2 WHERE id = $1', [id, 'placed']);
|
|
108
|
+
* await appendToOutbox(tx.query, createEvent({ ... }));
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare function appendToOutbox(querier: Querier, event: AppendOutboxInput, options?: {
|
|
113
|
+
tableName?: string;
|
|
114
|
+
}): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Retire an event an operator has decided will never publish.
|
|
117
|
+
*
|
|
118
|
+
* Deliberately does **not** touch `attempts`: the quarantine bound and the
|
|
119
|
+
* discard are different decisions, and collapsing them means raising the bound
|
|
120
|
+
* later drags retired events back onto the bus. Discarding also releases any
|
|
121
|
+
* successors of this aggregate that were held behind it.
|
|
122
|
+
*/
|
|
123
|
+
export declare function discardOutboxEvent(id: string, reason: string, options?: PostgresOutboxStoreOptions): Promise<boolean>;
|
|
124
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/outbox/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD,+DAA+D;AAC/D,eAAO,MAAM,oBAAoB,cAAc,CAAC;AAEhD,4DAA4D;AAC5D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,2DAA2D;AAC3D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAChE,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAuCD;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,0BAA+B,GACvC,mBAAmB,CAwErB;AAED,oEAAoE;AACpE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,iBAAiB,EACxB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACnC,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,OAAO,CAAC,CAYlB"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @venturekit/data — Postgres outbox store.
|
|
3
|
+
*
|
|
4
|
+
* The storage half of `@venturekit/runtime/patterns`' transactional outbox. The
|
|
5
|
+
* relay's rules live there and are pure; this is the adapter that satisfies the
|
|
6
|
+
* `OutboxStore` port against `vk_outbox`.
|
|
7
|
+
*
|
|
8
|
+
* Almost all of the difficulty is in one query. `claim()` carries three
|
|
9
|
+
* exclusions that the relay explicitly cannot make for itself, because each is a
|
|
10
|
+
* fact about rows *outside* the batch it is handed:
|
|
11
|
+
*
|
|
12
|
+
* 1. quarantined rows (`attempts >= maxAttempts`);
|
|
13
|
+
* 2. anything queued behind a quarantined row **for the same aggregate** —
|
|
14
|
+
* without this, giving up on one event silently promotes its successors and
|
|
15
|
+
* the per-aggregate ordering guarantee is lost between passes;
|
|
16
|
+
* 3. discarded rows, and the successors held behind them must be *released*,
|
|
17
|
+
* since a discard is an operator deciding the event will never go.
|
|
18
|
+
*
|
|
19
|
+
* (2) and (3) pull in opposite directions and that is the point: a row is held
|
|
20
|
+
* behind an abandoned predecessor only while that predecessor is *still*
|
|
21
|
+
* abandoned. Get it wrong in one direction and a quarantine reorders the stream;
|
|
22
|
+
* wrong in the other and a discard freezes an aggregate permanently.
|
|
23
|
+
*/
|
|
24
|
+
import { query } from '../query/index.js';
|
|
25
|
+
import { assertSqlIdentifier } from '../internal/identifier.js';
|
|
26
|
+
/** Default table name, created by `vk_data_002_outbox.sql`. */
|
|
27
|
+
export const DEFAULT_OUTBOX_TABLE = 'vk_outbox';
|
|
28
|
+
const toIso = (v) => v instanceof Date ? v.toISOString() : v;
|
|
29
|
+
function mapRow(r) {
|
|
30
|
+
return {
|
|
31
|
+
id: r.id,
|
|
32
|
+
/* `bigint` comes back from node-postgres as a STRING to avoid silent
|
|
33
|
+
precision loss. Left unconverted the relay would sort `'10' < '9'` and be
|
|
34
|
+
wrong from the tenth event onward — so the conversion is the adapter's
|
|
35
|
+
job, exactly as the port says. */
|
|
36
|
+
seq: typeof r.seq === 'string' ? Number(r.seq) : r.seq,
|
|
37
|
+
type: r.type,
|
|
38
|
+
aggregateType: r.aggregate_type,
|
|
39
|
+
aggregateId: r.aggregate_id,
|
|
40
|
+
tenantId: r.tenant_id,
|
|
41
|
+
payload: r.payload,
|
|
42
|
+
eventVersion: r.event_version,
|
|
43
|
+
occurredAt: toIso(r.occurred_at),
|
|
44
|
+
actor: r.actor,
|
|
45
|
+
attempts: r.attempts,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a Postgres-backed outbox store.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* import { relayOnce, createEventBridgeEventBus } from '@venturekit/runtime/patterns';
|
|
54
|
+
* import { createPostgresOutboxStore } from '@venturekit/data/outbox';
|
|
55
|
+
*
|
|
56
|
+
* // in a `schedules` cron
|
|
57
|
+
* await relayOnce(createPostgresOutboxStore(), createEventBridgeEventBus());
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createPostgresOutboxStore(options = {}) {
|
|
61
|
+
const table = options.tableName ?? DEFAULT_OUTBOX_TABLE;
|
|
62
|
+
assertSqlIdentifier(table, 'PostgresOutboxStoreOptions.tableName');
|
|
63
|
+
const run = options.querier ?? query;
|
|
64
|
+
return {
|
|
65
|
+
async claim(limit, maxAttempts) {
|
|
66
|
+
const rows = await run(`WITH
|
|
67
|
+
/* Aggregates with an abandoned event: quarantined (out of attempts) or
|
|
68
|
+
discarded. Holds only those STILL abandoned, so a discard releases
|
|
69
|
+
the successors it was taken to release. */
|
|
70
|
+
blocked AS (
|
|
71
|
+
SELECT DISTINCT aggregate_type, aggregate_id
|
|
72
|
+
FROM ${table}
|
|
73
|
+
WHERE published_at IS NULL
|
|
74
|
+
AND discarded_at IS NULL
|
|
75
|
+
AND attempts >= $2
|
|
76
|
+
)
|
|
77
|
+
SELECT o.id, o.seq, o.type, o.aggregate_type, o.aggregate_id,
|
|
78
|
+
o.tenant_id, o.payload, o.event_version, o.occurred_at,
|
|
79
|
+
o.actor, o.attempts
|
|
80
|
+
FROM ${table} o
|
|
81
|
+
LEFT JOIN blocked b
|
|
82
|
+
ON b.aggregate_type = o.aggregate_type
|
|
83
|
+
AND b.aggregate_id = o.aggregate_id
|
|
84
|
+
WHERE o.published_at IS NULL
|
|
85
|
+
AND o.discarded_at IS NULL
|
|
86
|
+
AND o.attempts < $2
|
|
87
|
+
AND b.aggregate_id IS NULL
|
|
88
|
+
ORDER BY o.seq
|
|
89
|
+
LIMIT $1
|
|
90
|
+
/* Two relay instances reading the same rows is safe but wasteful.
|
|
91
|
+
SKIP LOCKED lets a second pass take different work instead of
|
|
92
|
+
waiting on the first. */
|
|
93
|
+
FOR UPDATE SKIP LOCKED`, [limit, maxAttempts]);
|
|
94
|
+
return rows.map(mapRow);
|
|
95
|
+
},
|
|
96
|
+
async markPublished(ids) {
|
|
97
|
+
if (ids.length === 0)
|
|
98
|
+
return;
|
|
99
|
+
await run(`UPDATE ${table} SET published_at = now() WHERE id = ANY($1::uuid[])`, [ids]);
|
|
100
|
+
},
|
|
101
|
+
async recordFailures(failures) {
|
|
102
|
+
if (failures.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
/* One statement via UNNEST rather than a loop: the relay calls this once
|
|
105
|
+
per pass with whatever failed, and N round trips for N bad rows is the
|
|
106
|
+
slowest part of a bad pass. */
|
|
107
|
+
await run(`UPDATE ${table} AS o
|
|
108
|
+
SET attempts = f.attempts,
|
|
109
|
+
last_error = f.error,
|
|
110
|
+
failed_at = now()
|
|
111
|
+
FROM (
|
|
112
|
+
SELECT * FROM unnest($1::uuid[], $2::int[], $3::text[])
|
|
113
|
+
AS t(id, attempts, error)
|
|
114
|
+
) AS f
|
|
115
|
+
WHERE o.id = f.id`, [
|
|
116
|
+
failures.map((f) => f.id),
|
|
117
|
+
failures.map((f) => f.attempts),
|
|
118
|
+
failures.map((f) => f.error.slice(0, 2000)),
|
|
119
|
+
]);
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Append an event to the outbox **on the caller's transaction**.
|
|
125
|
+
*
|
|
126
|
+
* `querier` is required and not defaulted, unlike everywhere else in this
|
|
127
|
+
* module. Defaulting it to the pool would make the easiest call the wrong one:
|
|
128
|
+
* an event written on a pooled connection does not commit with the state change
|
|
129
|
+
* it describes, which is the single guarantee the outbox exists to provide. Pass
|
|
130
|
+
* the transaction's `query`.
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* await withTransaction(async (tx) => {
|
|
134
|
+
* await tx.query('UPDATE orders SET status = $2 WHERE id = $1', [id, 'placed']);
|
|
135
|
+
* await appendToOutbox(tx.query, createEvent({ ... }));
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export async function appendToOutbox(querier, event, options = {}) {
|
|
140
|
+
const table = options.tableName ?? DEFAULT_OUTBOX_TABLE;
|
|
141
|
+
assertSqlIdentifier(table, 'appendToOutbox.tableName');
|
|
142
|
+
await querier(`INSERT INTO ${table}
|
|
143
|
+
(id, type, aggregate_type, aggregate_id, tenant_id, payload,
|
|
144
|
+
event_version, occurred_at, actor, correlation_id, causation_id)
|
|
145
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7, $8, $9::jsonb, $10, $11)`, [
|
|
146
|
+
event.id,
|
|
147
|
+
event.type,
|
|
148
|
+
event.aggregateType,
|
|
149
|
+
event.aggregateId,
|
|
150
|
+
event.tenantId ?? null,
|
|
151
|
+
JSON.stringify(event.payload),
|
|
152
|
+
event.version ?? 1,
|
|
153
|
+
event.occurredAt ?? new Date().toISOString(),
|
|
154
|
+
JSON.stringify(event.actor),
|
|
155
|
+
event.correlationId ?? null,
|
|
156
|
+
event.causationId ?? null,
|
|
157
|
+
]);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Retire an event an operator has decided will never publish.
|
|
161
|
+
*
|
|
162
|
+
* Deliberately does **not** touch `attempts`: the quarantine bound and the
|
|
163
|
+
* discard are different decisions, and collapsing them means raising the bound
|
|
164
|
+
* later drags retired events back onto the bus. Discarding also releases any
|
|
165
|
+
* successors of this aggregate that were held behind it.
|
|
166
|
+
*/
|
|
167
|
+
export async function discardOutboxEvent(id, reason, options = {}) {
|
|
168
|
+
const table = options.tableName ?? DEFAULT_OUTBOX_TABLE;
|
|
169
|
+
assertSqlIdentifier(table, 'discardOutboxEvent.tableName');
|
|
170
|
+
const run = options.querier ?? query;
|
|
171
|
+
const rows = await run(`UPDATE ${table}
|
|
172
|
+
SET discarded_at = now(), discard_reason = $2
|
|
173
|
+
WHERE id = $1 AND published_at IS NULL AND discarded_at IS NULL
|
|
174
|
+
RETURNING id`, [id, reason]);
|
|
175
|
+
return rows.length > 0;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/outbox/postgres.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,OAAQ,CAAC;AAqBrD,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,UAAU,EACV,MAAM,EACN,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AA+B5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACvD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,OAAO,EAAE,EACvB,aAAa,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,EAChC,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA6CZ"}
|
package/dist/query/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/query/secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AAsCtD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBxD;
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/query/secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAKH;;;;;;;;GAQG;AACH,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AAsCtD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBxD;AAqHD;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|