@lostgradient/weft 0.10.0 → 0.11.0

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 (34) hide show
  1. package/README.md +10 -2
  2. package/dist/cli-main.js +62 -62
  3. package/dist/core/checkpoint/serialization.js +2 -2
  4. package/dist/core/engine/disposal.js +4 -0
  5. package/dist/core/engine/engine-leak-warnings.d.ts +2 -0
  6. package/dist/core/engine/engine-leak-warnings.js +7 -1
  7. package/dist/core/engine/index.d.ts +6 -3
  8. package/dist/core/engine/index.js +19 -3
  9. package/dist/core/engine/inline-parking.js +14 -1
  10. package/dist/core/engine/internals.d.ts +21 -0
  11. package/dist/core/engine/operations-time.js +8 -28
  12. package/dist/core/engine/sleep-timer-acknowledgements.d.ts +15 -0
  13. package/dist/core/engine/sleep-timer-acknowledgements.js +144 -0
  14. package/dist/core/engine/termination/cleanup.js +3 -0
  15. package/dist/mcp/cli.js +17 -17
  16. package/dist/server/handler.js +12 -12
  17. package/dist/server/index.js +17 -17
  18. package/dist/service-worker/index.js +12 -12
  19. package/dist/service-worker/scheduler.js +1 -0
  20. package/dist/storage/lazy-postgres-pool.d.ts +38 -0
  21. package/dist/storage/lazy-postgres-pool.js +38 -0
  22. package/dist/storage/neon-batch.d.ts +8 -8
  23. package/dist/storage/neon.d.ts +26 -79
  24. package/dist/storage/neon.js +3 -3
  25. package/dist/storage/postgres-key-value-queries.d.ts +1 -1
  26. package/dist/storage/postgres-key-value-queries.js +1 -1
  27. package/dist/storage/postgres-key-value-storage.d.ts +110 -0
  28. package/dist/storage/postgres-key-value-storage.js +204 -0
  29. package/dist/storage/postgres.d.ts +59 -0
  30. package/dist/storage/postgres.js +15 -0
  31. package/dist/testing/index.js +26 -26
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/package.json +16 -3
@@ -0,0 +1,204 @@
1
+ import { normalizeDeleteRangeOptions } from "./delete-range.js";
2
+ import {
3
+ assertStorageBatchOperationCount
4
+ } from "./interface.js";
5
+ import { conditionsHold, writeBatchNetEffect } from "./neon-batch.js";
6
+ import {
7
+ affectedRowCount,
8
+ resolveBatchNetEffect,
9
+ toBytea,
10
+ toStorageValue
11
+ } from "./neon-value-mapping.js";
12
+ import {
13
+ buildPostgresCreateSchema,
14
+ buildPostgresKeyValueQueries,
15
+ buildPostgresPrefixRangeParameters,
16
+ PG_BEGIN_READ_COMMITTED,
17
+ PG_BEGIN_READ_ONLY,
18
+ PG_BEGIN_SERIALIZABLE,
19
+ PG_COMMIT,
20
+ PG_ROLLBACK,
21
+ resolvePostgresTableReference
22
+ } from "./postgres-key-value-queries.js";
23
+ import { assertReadOnlyQuery } from "./read-only-query.js";
24
+ import { scopedStorage } from "./scoped-storage.js";
25
+ const RETRYABLE_TRANSACTION_FAILURES = new Set(["40001", "40P01"]), MAX_SERIALIZATION_RETRIES = 5;
26
+ function isRetryableTransactionFailure(error) {
27
+ if (typeof error !== "object" || error === null || !("code" in error))
28
+ return !1;
29
+ const code = error.code;
30
+ return typeof code === "string" && RETRYABLE_TRANSACTION_FAILURES.has(code);
31
+ }
32
+
33
+ export class PostgresKeyValueStorage {
34
+ #pool;
35
+ #queries;
36
+ #schema;
37
+ #initializationPromise;
38
+ #ownsPool;
39
+ #poolShutdown;
40
+ constructor(options, poolFactory) {
41
+ this.#ownsPool = options.pool === void 0;
42
+ if (options.pool === void 0) {
43
+ if (options.url === void 0)
44
+ throw Error("PostgresKeyValueStorage requires either a `url` to build a pool from, or a pre-built `pool`; neither was provided.");
45
+ this.#pool = poolFactory(options.url);
46
+ } else
47
+ this.#pool = options.pool;
48
+ this.#schema = options.schema;
49
+ const tableReference = resolvePostgresTableReference({
50
+ schema: options.schema,
51
+ table: options.table
52
+ });
53
+ this.#queries = buildPostgresKeyValueQueries(tableReference);
54
+ }
55
+ capabilities() {
56
+ return {
57
+ persistence: "remote",
58
+ readAfterWrite: "linearizable",
59
+ scanConsistency: "snapshot",
60
+ atomicBatch: !0,
61
+ conditionalBatch: !0,
62
+ boundedRangeDelete: !0
63
+ };
64
+ }
65
+ #ensureTable() {
66
+ this.#initializationPromise ??= this.#initialize().catch((error) => {
67
+ this.#initializationPromise = void 0;
68
+ throw error;
69
+ });
70
+ return this.#initializationPromise;
71
+ }
72
+ async#initialize() {
73
+ if (this.#schema !== void 0)
74
+ await this.#pool.query(buildPostgresCreateSchema(this.#schema));
75
+ await this.#pool.query(this.#queries.createTable);
76
+ await this.#assertKeyCollation();
77
+ }
78
+ async#assertKeyCollation() {
79
+ const collation = (await this.#pool.query(this.#queries.selectKeyCollation, [
80
+ this.#queries.tableReference
81
+ ])).rows[0]?.collation;
82
+ if (collation === "C")
83
+ return;
84
+ const found = collation === void 0 ? "no such table" : typeof collation === "string" ? `"${collation}"` : "an unexpected collation value";
85
+ throw Error(`Postgres storage requires the ${this.#queries.tableReference} table's "key" column to use COLLATE "C" (found ${found}). A table created without COLLATE "C" sorts keys by the database locale, which breaks Weft's lexicographic prefix scans. Use a fresh table, or recreate it as: CREATE TABLE ${this.#queries.tableReference} (key TEXT COLLATE "C" PRIMARY KEY, value BYTEA NOT NULL).`);
86
+ }
87
+ async get(key) {
88
+ await this.#ensureTable();
89
+ const row = (await this.#pool.query(this.#queries.selectValueByKey, [key])).rows[0];
90
+ if (row === void 0)
91
+ return null;
92
+ const raw = row.value;
93
+ if (raw === null || raw === void 0)
94
+ return null;
95
+ return toStorageValue(raw);
96
+ }
97
+ async put(key, value) {
98
+ await this.#ensureTable();
99
+ await this.#pool.query(this.#queries.upsertValueByKey, [key, toBytea(value)]);
100
+ }
101
+ async delete(key) {
102
+ await this.#ensureTable();
103
+ await this.#pool.query(this.#queries.deleteValueByKey, [key]);
104
+ }
105
+ async has(key) {
106
+ await this.#ensureTable();
107
+ return (await this.#pool.query(this.#queries.selectKeyPresence, [key])).rows.length > 0;
108
+ }
109
+ async deletePrefix(prefix) {
110
+ await this.#ensureTable();
111
+ const [rangeStart, rangeEnd] = buildPostgresPrefixRangeParameters(prefix), result = await this.#pool.query(this.#queries.deleteKeysByPrefix, [rangeStart, rangeEnd]);
112
+ return affectedRowCount(result);
113
+ }
114
+ async deleteRange(prefix, options) {
115
+ await this.#ensureTable();
116
+ const normalized = normalizeDeleteRangeOptions(options), { parameters, sql } = this.#queries.keyRangeDelete(prefix, normalized), result = await this.#pool.query(sql, parameters);
117
+ return affectedRowCount(result);
118
+ }
119
+ async* scan(prefix, options = {}) {
120
+ await this.#ensureTable();
121
+ const { parameters, sql } = this.#queries.keyValueRangeSelect(prefix, options), result = await this.#pool.query(sql, parameters);
122
+ for (const row of result.rows)
123
+ yield [row.key, toStorageValue(row.value)];
124
+ }
125
+ async* keys(prefix, options = {}) {
126
+ await this.#ensureTable();
127
+ const { parameters, sql } = this.#queries.keyRangeSelect(prefix, options), result = await this.#pool.query(sql, parameters);
128
+ for (const row of result.rows)
129
+ yield row.key;
130
+ }
131
+ async count(prefix) {
132
+ await this.#ensureTable();
133
+ const [rangeStart, rangeEnd] = buildPostgresPrefixRangeParameters(prefix), result = await this.#pool.query(this.#queries.countKeysByPrefix, [rangeStart, rangeEnd]);
134
+ return Number(result.rows[0]?.count ?? 0);
135
+ }
136
+ scoped(prefix) {
137
+ return scopedStorage(this, prefix);
138
+ }
139
+ async#withTransaction(beginStatement, runner, shouldCommit = () => !0) {
140
+ const client = await this.#pool.connect();
141
+ try {
142
+ await client.query(beginStatement);
143
+ try {
144
+ const result = await runner(client);
145
+ await client.query(shouldCommit(result) ? PG_COMMIT : PG_ROLLBACK);
146
+ return result;
147
+ } catch (error) {
148
+ await client.query(PG_ROLLBACK).catch(() => {});
149
+ throw error;
150
+ }
151
+ } finally {
152
+ client.release();
153
+ }
154
+ }
155
+ async batch(operations) {
156
+ assertStorageBatchOperationCount("batch operations", operations.length);
157
+ if (operations.length === 0)
158
+ return;
159
+ await this.#ensureTable();
160
+ await this.#withTransaction(PG_BEGIN_READ_COMMITTED, async (client) => {
161
+ await writeBatchNetEffect(client, this.#queries, resolveBatchNetEffect(operations));
162
+ });
163
+ }
164
+ async conditionalBatch(conditions, operations) {
165
+ assertStorageBatchOperationCount("conditionalBatch conditions", conditions.length);
166
+ assertStorageBatchOperationCount("conditionalBatch operations", operations.length);
167
+ await this.#ensureTable();
168
+ const netEffect = resolveBatchNetEffect(operations);
169
+ let lastError;
170
+ for (let attempt = 0;attempt < MAX_SERIALIZATION_RETRIES; attempt += 1)
171
+ try {
172
+ return await this.#withTransaction(PG_BEGIN_SERIALIZABLE, async (client) => {
173
+ if (!await conditionsHold(client, this.#queries, conditions))
174
+ return !1;
175
+ await writeBatchNetEffect(client, this.#queries, netEffect);
176
+ return !0;
177
+ }, (committed) => committed);
178
+ } catch (error) {
179
+ if (!isRetryableTransactionFailure(error))
180
+ throw error;
181
+ lastError = error;
182
+ }
183
+ throw Error(`conditionalBatch exhausted ${MAX_SERIALIZATION_RETRIES} attempts after retryable transaction failures`, { cause: lastError });
184
+ }
185
+ async query(sql, parameters) {
186
+ await this.#ensureTable();
187
+ assertReadOnlyQuery(sql);
188
+ return this.#withTransaction(PG_BEGIN_READ_ONLY, async (client) => {
189
+ return (await client.query(sql, parameters ?? [])).rows;
190
+ });
191
+ }
192
+ #endPoolOnce() {
193
+ if (!this.#ownsPool)
194
+ return Promise.resolve();
195
+ this.#poolShutdown ??= this.#pool.end();
196
+ return this.#poolShutdown;
197
+ }
198
+ [Symbol.dispose]() {
199
+ this.#endPoolOnce().catch(() => {});
200
+ }
201
+ async[Symbol.asyncDispose]() {
202
+ await this.#endPoolOnce();
203
+ }
204
+ }
@@ -0,0 +1,59 @@
1
+ import { PostgresKeyValueStorage, type PostgresKeyValueStorageOptions, type PostgresPool, type PostgresPoolClient } from './postgres-key-value-storage.ts';
2
+ export type { PostgresPool, PostgresPoolClient };
3
+ /**
4
+ * Configuration for connecting to a standard Postgres database over the ordinary
5
+ * wire protocol via the `pg` (node-postgres) driver. Alias of the shared
6
+ * {@link PostgresKeyValueStorageOptions}; `url` is optional and required only when
7
+ * no `pool` is supplied.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { PostgresStorage, type PostgresStorageOptions } from '@lostgradient/weft/storage/postgres';
12
+ *
13
+ * const options: PostgresStorageOptions = {
14
+ * url: 'postgresql://user:password@localhost:5432/weft',
15
+ * };
16
+ * await using storage = new PostgresStorage(options);
17
+ * ```
18
+ */
19
+ export type PostgresStorageOptions = PostgresKeyValueStorageOptions;
20
+ /**
21
+ * Storage adapter backed by a standard Postgres server over the ordinary wire
22
+ * protocol via the `pg` (node-postgres) driver. A thin
23
+ * {@link PostgresKeyValueStorage} subclass whose only job is to supply the `pg`
24
+ * default pool factory; all storage behavior (SQL, value mapping,
25
+ * transactions/retry, schema/table qualification) lives in the driver-agnostic
26
+ * base. Implements the same `Storage` interface as the SQLite adapters over a
27
+ * single `kv(key TEXT COLLATE "C", value BYTEA)` table.
28
+ *
29
+ * Use this instead of {@link NeonStorage} when connecting to a self-hosted or
30
+ * managed Postgres over TCP — including sharing the same database as your
31
+ * application's tables by pointing `schema` at a dedicated Weft schema. The Neon
32
+ * serverless (WebSocket) driver is not required and never imported.
33
+ *
34
+ * **Endpoint assumption.** `capabilities()` reports `readAfterWrite:
35
+ * 'linearizable'`, which holds for the **primary** endpoint. A read-replica
36
+ * connection string would violate that guarantee — point this adapter at the
37
+ * primary.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { PostgresStorage } from '@lostgradient/weft/storage/postgres';
42
+ * import { Engine } from '@lostgradient/weft';
43
+ *
44
+ * await using storage = new PostgresStorage({
45
+ * url: process.env['DATABASE_URL']!,
46
+ * });
47
+ * await using engine = new Engine({ storage });
48
+ * ```
49
+ */
50
+ export declare class PostgresStorage extends PostgresKeyValueStorage {
51
+ /**
52
+ * @param options Connection configuration ({@link PostgresStorageOptions}).
53
+ * @param poolFactory Internal seam for constructing the owned pool from `url`.
54
+ * Defaults to lazily importing the real `pg` `Pool`; tests inject a fake to
55
+ * exercise owned-pool teardown without a network. Used only when no `pool` is
56
+ * supplied; an injected `pool` stays caller-owned.
57
+ */
58
+ constructor(options: PostgresStorageOptions, poolFactory?: (url: string) => PostgresPool);
59
+ }
@@ -0,0 +1,15 @@
1
+ // @bun
2
+ var $_=Object.defineProperty;var M_=(_)=>_;function H_(_,E){this[_]=M_.bind(null,E)}var b_=(_,E)=>{for(var W in E)$_(_,W,{get:E[W],enumerable:!0,configurable:!0,set:H_.bind(E,W)})};var M=(_,E)=>()=>(_&&(E=_(_=0)),E);var U_=import.meta.require,x_=(_,E,W)=>{if(E!=null){if(typeof E!=="object"&&typeof E!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let X;if(W)X=E[Symbol.asyncDispose];if(X===void 0)X=E[Symbol.dispose];if(typeof X!=="function")throw TypeError("Object not disposable");_.push([W,X,E])}else if(W)_.push([W]);return E},I_=(_,E,W)=>{let X=(F)=>E=W?new SuppressedError(F,E,"An error was suppressed during disposal"):(W=!0,F),J=(F)=>{while(F=_.pop())try{var Q=F[1]&&F[1].call(F[2]);if(F[0])return Promise.resolve(Q).then(J,(Z)=>(X(Z),J()))}catch(Z){X(Z)}if(W)throw E};return J()};function N(_,E,W){if(!_.capabilities()[E])throw Error(`Feature "${W}" requires storage capability "${E}", but this storage backend does not provide it.`)}function V_(_){let E=_.capabilities(),W=[];if(E.persistence!=="local"&&E.persistence!=="remote")W.push(`persistence must be "local" or "remote" (got "${E.persistence}")`);if(E.readAfterWrite!=="linearizable")W.push(`readAfterWrite must be "linearizable" (got "${E.readAfterWrite}")`);if(E.scanConsistency!=="snapshot")W.push(`scanConsistency must be "snapshot" (got "${E.scanConsistency}")`);if(!E.atomicBatch)W.push("atomicBatch must be true");if(!E.conditionalBatch)W.push("conditionalBatch must be true");if(W.length>0)throw Error(`Storage is not durable enough for recovery: ${W.join("; ")}.`)}var C="default";async function h(_,E){return await _.get(E)!==null}async function*U(_,E,W){for await(let[X]of _.scan(E,W))yield X}async function b(_,E){let W=0;for await(let X of U(_,E))W++;return W}async function x(_,E){let W=[];for await(let X of U(_,E))W.push({type:"delete",key:X});if(W.length===0)return 0;return await _.batch(W),W.length}async function I(_,E,W){let X=[];for await(let J of U(_,E,W))X.push({type:"delete",key:J});if(X.length===0)return 0;return await _.batch(X),X.length}var j_;var y=M(()=>{j_=["actrec:","archive:","async-act:","attr:","audit:bulk:","blob:","budget:","budget-charged:","ev:","fleet-event-by-workflow:","fleet-event:","fleet-event-tail","idx:","lease:","liveness:","offload:","op:","review:","schedule:","schedule-due:","schedule-run:","sig:","sigres:","sigseq:","start-idem:","state:","tag:","tool-effect:","upd:","upk:","upr:","wf:","wf-cleanup:","wf-cleanup-needed:","wf-concurrency:","wf-concurrency-holder:","wf-deadline:","wf-delayed:","wf-finalizer-state:","wf-has-services:","wf-headers:","wf-idx-","wf-teardown:","wf-teardown-deadletter:","wf-teardown-needed:","wf-terminal:"]});function $(_,E){if(E>A)throw new g(_,E)}function u(_){return _.length>0?_.slice(0,-1)+String.fromCharCode(_.charCodeAt(_.length-1)+1):"\xFF"}function l_(_,E={}){if(E.gt!==void 0&&_<=E.gt)return!1;if(E.gte!==void 0&&_<E.gte)return!1;if(E.lt!==void 0&&_>=E.lt)return!1;if(E.lte!==void 0&&_>E.lte)return!1;return!0}function k(_,E){if(_===null||E===null)return _===E;if(_.byteLength!==E.byteLength)return!1;for(let W=0;W<_.byteLength;W++)if(_[W]!==E[W])return!1;return!0}async function c(_,E){if(_.has)return _.has(E);return h(_,E)}function w(_,E,W){if(_.keys)return _.keys(E,W);return U(_,E,W)}async function f(_,E){if(_.count)return _.count(E);return b(_,E)}async function d(_,E){if(_.deletePrefix)return _.deletePrefix(E);return x(_,E)}async function p_(_,E){$("batch operations",E.length),await _.batch(E)}async function l(_,E,W){if($("conditionalBatch conditions",E.length),$("conditionalBatch operations",W.length),N(_,"conditionalBatch","storageConditionalBatch"),!_.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return _.conditionalBatch(E,W)}function Y(_){return encodeURIComponent(_)}function a_(_){return decodeURIComponent(_)}function s_(_){try{return decodeURIComponent(_)}catch{return null}}var A=1e4,d_=1e4,g,G=(_)=>String(_).padStart(16,"0"),B_="0",L_="1",m=(_,E,W,X)=>`sig:${Y(_)}:${Y(E)}:${X}:${Y(W)}`,n_;var V=M(()=>{y();g=class g extends Error{code="StorageBatchOperationLimitExceededError";cap=A;count;target;constructor(_,E){super(`${_} count ${E} exceeds MAX_BATCH_OPERATIONS (${A}).`);this.name="StorageBatchOperationLimitExceededError",this.target=_,this.count=E}};n_={workflow:(_)=>`wf:${Y(_)}`,checkpoint:(_)=>`wf:${Y(_)}:ckpt`,checkpointHistory:(_,E)=>`wf:${Y(_)}:ckpt:${String(E).padStart(10,"0")}`,timelinePrefix:(_)=>`wf:${Y(_)}:timeline:`,timeline:(_,E)=>`wf:${Y(_)}:timeline:${String(E).padStart(10,"0")}`,schedule:(_)=>`schedule:${Y(_)}`,scheduleTick:(_,E)=>`schedule-due:${String(_).padStart(16,"0")}:${Y(E)}`,scheduleRun:(_)=>`schedule-run:${Y(_)}`,operation:(_,E,W)=>`op:${_}:${G(E)}:${W}`,operationInflight:(_)=>`op:inflight:${_}`,operationQueued:(_)=>`op:queued:${_}`,operationResolved:(_)=>`op:resolved:${_}`,operationDeadLetterPrefix:()=>"op:dead-letter:",operationDeadLetter:(_)=>`op:dead-letter:${_}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(_,E,W)=>`audit:bulk:${G(_)}:${Y(E)}:${Y(W)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(_,E)=>`op:resolved-by-time:${G(_)}:${Y(E)}`,asyncActivity:(_,E)=>`async-act:v1:${Y(_)}:${Y(E)}`,asyncActivityResolution:(_,E)=>`async-act:v1:${Y(_)}:${Y(E)}:resolution`,activityReconciliationPrefix:(_)=>`actrec:v1:${Y(_)}:`,activityReconciliation:(_,E,W)=>`actrec:v1:${Y(_)}:${Y(E)}:${W}`,eventPrefix:(_)=>`ev:${Y(_)}:`,event:(_,E)=>`ev:${Y(_)}:${String(E).padStart(10,"0")}`,eventHead:(_)=>`ev:${Y(_)}:head`,eventWatermark:(_)=>`ev:${Y(_)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(_)=>`fleet-event:${String(_).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(_)=>`fleet-event-by-workflow:${Y(_)}:`,fleetEventByWorkflow:(_,E)=>`fleet-event-by-workflow:${Y(_)}:${String(E).padStart(10,"0")}`,signal:(_,E,W)=>m(_,E,W,L_),startSignal:(_,E,W)=>m(_,E,W,B_),signalSequence:(_)=>`sigseq:v1:${Y(_)}`,signalAcceptedResponsePrefix:(_)=>`sigres:v1:${Y(_)}:`,signalAcceptedResponse:(_,E,W)=>`sigres:v1:${Y(_)}:${Y(E)}:${Y(W)}`,deadline:(_,E)=>`wf-deadline:${G(_)}:${Y(E)}`,terminalCleanup:(_,E)=>`wf-cleanup:${G(_)}:${Y(E)}`,teardownTimer:(_,E)=>`wf-teardown:${G(_)}:${Y(E)}`,delayedStart:(_,E)=>`wf-delayed:${G(_)}:${Y(E)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(_,E)=>`wf-terminal:${G(_)}:${Y(E)}`,attribute:(_)=>`attr:${Y(_)}`,attributeIndex:(_,E,W)=>`idx:${_}:${E}:${Y(W)}`,tagIndex:(_,E)=>`tag:${Y(_)}:${Y(E)}`,updatePrefix:(_)=>`upd:${Y(_)}:`,update:(_,E)=>`upd:${Y(_)}:${E}`,updateResponse:(_)=>`upr:${_}`,updateIdempotency:(_,E)=>`upk:${Y(_)}:${E}`,startIdempotency:(_)=>`start-idem:${Y(_)}`,startIdempotencySignalId:(_)=>`start-idem:${_}`,livenessPrefix:()=>"liveness:",liveness:(_)=>`liveness:${Y(_)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",budget:(_,E,W)=>`budget:${_}:${E}:${W}`,review:(_,E)=>`review:${Y(_)}:${E}`,workflowHeaders:(_)=>`wf-headers:${Y(_)}`,childCancellationPrefix:(_)=>`child-cancel:${Y(_)}:`,childCancellation:(_,E)=>`child-cancel:${Y(_)}:${Y(E)}`,terminalCleanupNeeded:(_)=>`wf-cleanup-needed:${Y(_)}`,workflowConcurrency:(_,E)=>`wf-concurrency:${Y(_)}:${Y(E)}`,workflowConcurrencyHolder:(_)=>`wf-concurrency-holder:${Y(_)}`,workflowHasServices:(_)=>`wf-has-services:${Y(_)}`,finalizerState:(_)=>`wf-finalizer-state:${Y(_)}`,teardownOwed:(_)=>`wf-teardown-needed:${Y(_)}`,teardownDeadLetter:(_)=>`wf-teardown-deadletter:${Y(_)}`,offload:(_,E)=>`offload:${Y(_)}:${E}`,archive:(_,E)=>`archive:${Y(_)}:${E}`,stateExecution:(_,E)=>`state:execution:${Y(_)}:${Y(E)}`,stateWorkflow:(_,E)=>`state:workflow-scope:${C}:${Y(_)}:${Y(E)}`,streamChunkPrefix:(_,E)=>`blob:${Y(_)}:${E}:chunk:`,streamChunk:(_,E,W)=>`blob:${Y(_)}:${E}:chunk:${String(W).padStart(10,"0")}`,streamTail:(_,E)=>`blob:${Y(_)}:${E}:tail`,streamMetadata:(_,E)=>`blob:${Y(_)}:${E}:meta`,budgetCharged:(_)=>`budget-charged:${_}`,toolEffect:(_,E,W)=>`tool-effect:${Y(_)}:${E}:${W}`,workflowVisibilityStatus:(_,E)=>`wf-idx-status:${Y(_)}:${Y(E)}`,workflowVisibilityType:(_,E)=>`wf-idx-type:${Y(_)}:${Y(E)}`,workflowVisibilityCreated:(_,E)=>`wf-idx-created:${G(_)}:${Y(E)}`,workflowVisibilityUpdated:(_,E)=>`wf-idx-updated:${G(_)}:${Y(E)}`,workflowVisibilityDeadline:(_,E)=>`wf-idx-deadline:${G(_)}:${Y(E)}`,workflowVisibilityManifest:(_)=>`wf-idx-manifest:${Y(_)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"}});function D_(_){if(_===void 0)return;if(typeof _!=="number"||!Number.isInteger(_)||_<0)throw Error("deleteRange limit must be a finite non-negative integer");return _===0?0:_}function j(_){let E={},W=!1;for(let J of["gt","gte","lt","lte"]){let F=_[J];if(F===void 0)continue;if(typeof F!=="string")throw Error("deleteRange bounds must be strings");E[J]=F,W=!0}if(!W)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let X=D_(_.limit);if(X!==void 0)E.limit=X;return E}async function p(_,E,W){let X=j(W);if(_.deleteRange)return _.deleteRange(E,X);return I(_,E,X)}var S=()=>{};function T(_){return _.replaceAll(/:+$/g,"")}function N_(_,E){let W=T(_),X=T(E);if(W.length===0)return X;if(X.length===0)return W;return`${W}:${X}`}function a(_,E){return new K(_,E)}var K;var s=M(()=>{S();V();K=class K{#_;#E;constructor(_,E){this.#_=_,this.#E=T(E)}#W(_){if(this.#E.length===0)return _;return _.length===0?`${this.#E}:`:`${this.#E}:${_}`}#Y(_){if(this.#E.length===0)return _;return _.slice(this.#E.length+1)}#F(_={}){let E={};if(_.limit!==void 0)E.limit=_.limit;if(_.reverse!==void 0)E.reverse=_.reverse;if(_.gt!==void 0)E.gt=this.#W(_.gt);if(_.gte!==void 0)E.gte=this.#W(_.gte);if(_.lt!==void 0)E.lt=this.#W(_.lt);if(_.lte!==void 0)E.lte=this.#W(_.lte);return E}#J(_){let E={};if(_.limit!==void 0)E.limit=_.limit;if(_.gt!==void 0)E.gt=this.#W(_.gt);if(_.gte!==void 0)E.gte=this.#W(_.gte);if(_.lt!==void 0)E.lt=this.#W(_.lt);if(_.lte!==void 0)E.lte=this.#W(_.lte);return E}capabilities(){return this.#_.capabilities()}scoped(_){return new K(this.#_,N_(this.#E,_))}async get(_){return this.#_.get(this.#W(_))}async put(_,E){await this.#_.put(this.#W(_),E)}async delete(_){await this.#_.delete(this.#W(_))}async*scan(_,E){for await(let[W,X]of this.#_.scan(this.#W(_),this.#F(E)))yield[this.#Y(W),X]}async batch(_){$("batch operations",_.length),await this.#_.batch(_.map((E)=>{if(E.type==="put")return{type:"put",key:this.#W(E.key),value:E.value};return{type:"delete",key:this.#W(E.key)}}))}async conditionalBatch(_,E){return l(this.#_,_.map((W)=>({key:this.#W(W.key),expectedValue:W.expectedValue})),E.map((W)=>{if(W.type==="put")return{type:"put",key:this.#W(W.key),value:W.value};return{type:"delete",key:this.#W(W.key)}}))}async has(_){return c(this.#_,this.#W(_))}async deletePrefix(_){return d(this.#_,this.#W(_))}async deleteRange(_,E){let W=this.#J(j(E));return p(this.#_,this.#W(_),W)}async*keys(_,E){for await(let W of w(this.#_,this.#W(_),this.#F(E)))yield this.#Y(W)}async count(_){return f(this.#_,this.#W(_))}[Symbol.dispose](){this.#_[Symbol.dispose]()}}});function z_(_){return _.trim().replace(/;+\s*$/u,"").trim()}function O_(_){return T_.test(_)}function Q_(_){let E=z_(_);if(E.length===0)throw Error("Storage query must not be empty.");if(E.includes(";"))throw Error("Storage query must contain exactly one read-only statement.");if(K_.test(E))return;if(S_.test(E)&&O_(E))return;throw Error("Storage query only supports read-only SELECT and PRAGMA statements.")}var S_,T_,K_;var Z_=M(()=>{S_=/^PRAGMA\b/iu,T_=/^PRAGMA\s+(?:[A-Z_][A-Z0-9_]*\.)?[A-Z_][A-Z0-9_]*\s*$/iu,K_=/^SELECT\b/iu});function n(_,E){let W,X,J=!1,F=()=>{if(J)throw Error(`${E.storageName} pool has been disposed and cannot be reused. Construct a new adapter.`);return W??=E.loadPool(_).catch((Q)=>{throw W=void 0,A_(Q,E)}),W};return{query:async(Q,Z)=>{return(await F()).query(Q,Z)},connect:async()=>{return(await F()).connect()},end:async()=>{return J=!0,X??=(async()=>{if(W===void 0)return;await(await W.catch(()=>{return}))?.end()})(),X}}}var C_=/cannot find module|module_not_found|failed to resolve|could not resolve/i;function A_(_,E){let W=_ instanceof Error?_.message:String(_);if(C_.test(W))return Error(`${E.storageName} requires the optional peer dependency "${E.driverName}". Install it in your application with: bun add ${E.driverName} (or npm install ${E.driverName}).`,{cause:_});return _ instanceof Error?_:Error(W)}S();V();V();function z(_){return _.rowCount??_.affectedRows??_.rows.length}function B(_){if(_ instanceof Uint8Array)return new Uint8Array(_);return new Uint8Array(_)}function L(_){return Buffer.from(_)}function O(_){let E=new Map,W=new Set;for(let X of _)if(X.type==="put")W.delete(X.key),E.set(X.key,X.value);else E.delete(X.key),W.add(X.key);return{puts:E,deletes:W}}async function i(_,E,W){if(W.length===0)return!0;let X=W.map((Q)=>Q.key),J=await _.query(E.selectValuesByKeys,[X]),F=new Map;for(let Q of J.rows){let Z=Q.value;if(Z!==null&&Z!==void 0)F.set(Q.key,B(Z))}for(let Q of W){let Z=F.get(Q.key)??null;if(!k(Z,Q.expectedValue))return!1}return!0}async function v(_,E,W){if(W.puts.size>0){let X=[],J=[];for(let[F,Q]of W.puts)X.push(F),J.push(L(Q));await _.query(E.upsertValuesByKeys,[X,J])}if(W.deletes.size>0)await _.query(E.deleteValuesByKeys,[[...W.deletes]])}V();var r="kv",t=/^[a-z_][a-z0-9_]*$/i;function R(_,E){if(!t.test(_))throw Error(`Postgres storage ${E} name "${_}" is not a valid Postgres identifier. Use only letters, digits, and underscores, starting with a letter or underscore (matching ${t.source}).`)}function e(_){let{schema:E,table:W}=_;if(E===void 0&&W===void 0)return r;let X=W??r;if(R(X,"table"),E===void 0)return`"${X}"`;return R(E,"schema"),`"${E}"."${X}"`}function D(_){return[_,u(_)]}function __(_,E){let{gt:W,gte:X,lt:J,lte:F}=E,Q=D(_),Z=["key >= $1 AND key < $2"];if(W!==void 0)Q.push(W),Z.push(`key > $${Q.length}`);if(X!==void 0)Q.push(X),Z.push(`key >= $${Q.length}`);if(J!==void 0)Q.push(J),Z.push(`key < $${Q.length}`);if(F!==void 0)Q.push(F),Z.push(`key <= $${Q.length}`);return{conditions:Z,parameters:Q}}function o(_,E={}){let{limit:W,reverse:X}=E,{conditions:J,parameters:F}=__(_,E),Q=X?"DESC":"ASC",Z="";if(W!==void 0)F.push(W),Z=` LIMIT $${F.length}`;return{parameters:F,whereOrderLimit:`WHERE ${J.join(" AND ")} ORDER BY key ${Q}${Z}`}}function E_(_){let E=`CREATE TABLE IF NOT EXISTS ${_} (
3
+ key TEXT COLLATE "C" PRIMARY KEY,
4
+ value BYTEA NOT NULL
5
+ )`,W=`
6
+ SELECT COALESCE(co.collname, 'default') AS collation
7
+ FROM pg_attribute a
8
+ LEFT JOIN pg_collation co ON co.oid = a.attcollation
9
+ WHERE a.attrelid = to_regclass($1) AND a.attname = 'key' AND a.attnum > 0
10
+ `;return{tableReference:_,createTable:E,selectKeyCollation:`
11
+ SELECT COALESCE(co.collname, 'default') AS collation
12
+ FROM pg_attribute a
13
+ LEFT JOIN pg_collation co ON co.oid = a.attcollation
14
+ WHERE a.attrelid = to_regclass($1) AND a.attname = 'key' AND a.attnum > 0
15
+ `,selectValueByKey:`SELECT value FROM ${_} WHERE key = $1`,upsertValueByKey:`INSERT INTO ${_} (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`,deleteValueByKey:`DELETE FROM ${_} WHERE key = $1`,selectKeyPresence:`SELECT 1 AS present FROM ${_} WHERE key = $1 LIMIT 1`,countKeysByPrefix:`SELECT COUNT(*) AS count FROM ${_} WHERE key >= $1 AND key < $2`,deleteKeysByPrefix:`DELETE FROM ${_} WHERE key >= $1 AND key < $2`,selectValuesByKeys:`SELECT key, value FROM ${_} WHERE key = ANY($1)`,upsertValuesByKeys:`INSERT INTO ${_} (key, value) SELECT * FROM unnest($1::text[], $2::bytea[]) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value`,deleteValuesByKeys:`DELETE FROM ${_} WHERE key = ANY($1)`,keyValueRangeSelect(X,J={}){let{parameters:F,whereOrderLimit:Q}=o(X,J);return{parameters:F,sql:`SELECT key, value FROM ${_} ${Q}`}},keyRangeSelect(X,J={}){let{parameters:F,whereOrderLimit:Q}=o(X,J);return{parameters:F,sql:`SELECT key FROM ${_} ${Q}`}},keyRangeDelete(X,J){let{conditions:F,parameters:Q}=__(X,J),Z=F.join(" AND ");if(J.limit===void 0)return{parameters:Q,sql:`DELETE FROM ${_} WHERE ${Z}`};return Q.push(J.limit),{parameters:Q,sql:`DELETE FROM ${_} WHERE key IN (SELECT key FROM ${_} WHERE ${Z} ORDER BY key ASC LIMIT $${Q.length})`}}}}var W_="BEGIN ISOLATION LEVEL SERIALIZABLE",X_="BEGIN ISOLATION LEVEL READ COMMITTED",Y_="BEGIN READ ONLY",F_="COMMIT",q="ROLLBACK";function J_(_){return R(_,"schema"),`CREATE SCHEMA IF NOT EXISTS "${_}"`}Z_();s();var v_=new Set(["40001","40P01"]),G_=5;function R_(_){if(typeof _!=="object"||_===null||!("code"in _))return!1;let E=_.code;return typeof E==="string"&&v_.has(E)}class P{#_;#E;#W;#Y;#F;#J;constructor(_,E){if(this.#F=_.pool===void 0,_.pool===void 0){if(_.url===void 0)throw Error("PostgresKeyValueStorage requires either a `url` to build a pool from, or a pre-built `pool`; neither was provided.");this.#_=E(_.url)}else this.#_=_.pool;this.#W=_.schema;let W=e({schema:_.schema,table:_.table});this.#E=E_(W)}capabilities(){return{persistence:"remote",readAfterWrite:"linearizable",scanConsistency:"snapshot",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#X(){return this.#Y??=this.#G().catch((_)=>{throw this.#Y=void 0,_}),this.#Y}async#G(){if(this.#W!==void 0)await this.#_.query(J_(this.#W));await this.#_.query(this.#E.createTable),await this.#$()}async#$(){let E=(await this.#_.query(this.#E.selectKeyCollation,[this.#E.tableReference])).rows[0]?.collation;if(E==="C")return;let W=E===void 0?"no such table":typeof E==="string"?`"${E}"`:"an unexpected collation value";throw Error(`Postgres storage requires the ${this.#E.tableReference} table's "key" column to use COLLATE "C" (found ${W}). A table created without COLLATE "C" sorts keys by the database locale, which breaks Weft's lexicographic prefix scans. Use a fresh table, or recreate it as: CREATE TABLE ${this.#E.tableReference} (key TEXT COLLATE "C" PRIMARY KEY, value BYTEA NOT NULL).`)}async get(_){await this.#X();let W=(await this.#_.query(this.#E.selectValueByKey,[_])).rows[0];if(W===void 0)return null;let X=W.value;if(X===null||X===void 0)return null;return B(X)}async put(_,E){await this.#X(),await this.#_.query(this.#E.upsertValueByKey,[_,L(E)])}async delete(_){await this.#X(),await this.#_.query(this.#E.deleteValueByKey,[_])}async has(_){return await this.#X(),(await this.#_.query(this.#E.selectKeyPresence,[_])).rows.length>0}async deletePrefix(_){await this.#X();let[E,W]=D(_),X=await this.#_.query(this.#E.deleteKeysByPrefix,[E,W]);return z(X)}async deleteRange(_,E){await this.#X();let W=j(E),{parameters:X,sql:J}=this.#E.keyRangeDelete(_,W),F=await this.#_.query(J,X);return z(F)}async*scan(_,E={}){await this.#X();let{parameters:W,sql:X}=this.#E.keyValueRangeSelect(_,E),J=await this.#_.query(X,W);for(let F of J.rows)yield[F.key,B(F.value)]}async*keys(_,E={}){await this.#X();let{parameters:W,sql:X}=this.#E.keyRangeSelect(_,E),J=await this.#_.query(X,W);for(let F of J.rows)yield F.key}async count(_){await this.#X();let[E,W]=D(_),X=await this.#_.query(this.#E.countKeysByPrefix,[E,W]);return Number(X.rows[0]?.count??0)}scoped(_){return a(this,_)}async#Q(_,E,W=()=>!0){let X=await this.#_.connect();try{await X.query(_);try{let J=await E(X);return await X.query(W(J)?F_:q),J}catch(J){throw await X.query(q).catch(()=>{}),J}}finally{X.release()}}async batch(_){if($("batch operations",_.length),_.length===0)return;await this.#X(),await this.#Q(X_,async(E)=>{await v(E,this.#E,O(_))})}async conditionalBatch(_,E){$("conditionalBatch conditions",_.length),$("conditionalBatch operations",E.length),await this.#X();let W=O(E),X;for(let J=0;J<G_;J+=1)try{return await this.#Q(W_,async(F)=>{if(!await i(F,this.#E,_))return!1;return await v(F,this.#E,W),!0},(F)=>F)}catch(F){if(!R_(F))throw F;X=F}throw Error(`conditionalBatch exhausted ${G_} attempts after retryable transaction failures`,{cause:X})}async query(_,E){return await this.#X(),Q_(_),this.#Q(Y_,async(W)=>{return(await W.query(_,E??[])).rows})}#Z(){if(!this.#F)return Promise.resolve();return this.#J??=this.#_.end(),this.#J}[Symbol.dispose](){this.#Z().catch(()=>{})}async[Symbol.asyncDispose](){await this.#Z()}}function q_(_){return n(_,{driverName:"pg",storageName:"PostgresStorage",loadPool:(E)=>import("pg").then(({default:W})=>new W.Pool({connectionString:E}))})}class P_ extends P{constructor(_,E=q_){super(_,E)}}export{P_ as PostgresStorage};