@lostgradient/weft 0.11.0 → 0.12.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.
- package/README.md +1 -1
- package/dist/cli/generated/operation-catalog.snapshot.json +4671 -0
- package/dist/cli/generated/operation-client.generated.d.ts +1 -0
- package/dist/cli/index.js +1 -0
- package/dist/cli-main.js +227 -401
- package/dist/client/http-operations.js +7 -1
- package/dist/client/http-request.d.ts +23 -1
- package/dist/client/http-request.js +12 -3
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +7 -0
- package/dist/connection.d.ts +8 -0
- package/dist/connection.js +41 -17
- package/dist/core/engine/callback-creators-core.js +2 -1
- package/dist/core/engine/index.d.ts +43 -0
- package/dist/core/engine/lifecycle/shared.d.ts +25 -0
- package/dist/core/engine/lifecycle/transition.js +19 -9
- package/dist/core/types/definition-schema-to-json.d.ts +11 -0
- package/dist/core/types/definition-schema-to-json.js +11 -2
- package/dist/http.js +2 -0
- package/dist/indexeddb.js +1 -0
- package/dist/json-schema.js +3 -3
- package/dist/mcp/cli.js +63 -61
- package/dist/mcp/http.d.ts +7 -2
- package/dist/mcp/session.d.ts +3 -3
- package/dist/mcp/session.js +3 -2
- package/dist/mcp/stdio.d.ts +7 -2
- package/dist/observability/index.js +2 -2
- package/dist/runtime/portable.d.ts +33 -0
- package/dist/runtime/portable.js +17 -6
- package/dist/server/engine-event-feed-backend.d.ts +22 -3
- package/dist/server/fleet-event-feed.d.ts +65 -0
- package/dist/server/handler/index.d.ts +11 -2
- package/dist/server/handler/index.js +7 -0
- package/dist/server/handler.js +1 -56
- package/dist/server/index.d.ts +12 -1
- package/dist/server/index.js +87 -64
- package/dist/server/operations/submit-review-decision.d.ts +2 -0
- package/dist/server/operations/submit-review-decision.js +19 -2
- package/dist/server/replay-live-feed-internals.d.ts +19 -0
- package/dist/server/replay-live-feed-internals.js +76 -0
- package/dist/server/runtime/authentication-bridge.d.ts +2 -2
- package/dist/server/serve-internals.js +3 -3
- package/dist/server/workflow-event-feed.d.ts +100 -11
- package/dist/server/workflow-event-feed.js +1 -140
- package/dist/service-worker/index.d.ts +8 -2
- package/dist/service-worker/index.js +37 -56
- package/dist/service-worker/setup.d.ts +6 -2
- package/dist/storage/auto.js +1 -1
- package/dist/storage/bun-sql.js +3 -3
- package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
- package/dist/storage/cloudflare-durable-object-sql.js +0 -0
- package/dist/storage/cloudflare-value-codec.d.ts +65 -0
- package/dist/storage/cloudflare-value-codec.js +46 -0
- package/dist/storage/cloudflare.d.ts +140 -0
- package/dist/storage/cloudflare.js +127 -0
- package/dist/storage/compressed-storage.js +1 -1
- package/dist/storage/http.js +200 -2
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/indexeddb.js +311 -1
- package/dist/storage/interface.js +1 -1
- package/dist/storage/lmdb.js +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/neon.js +4 -4
- package/dist/storage/node-sqlite.js +3 -3
- package/dist/storage/postgres-key-value-queries.d.ts +6 -0
- package/dist/storage/postgres-key-value-queries.js +2 -3
- package/dist/storage/postgres.js +4 -4
- package/dist/storage/resolve.js +1 -1
- package/dist/storage/scoped-storage.js +1 -1
- package/dist/storage/sql-identifier.d.ts +35 -0
- package/dist/storage/sql-identifier.js +5 -0
- package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
- package/dist/storage/sqlite-key-value-queries.js +3 -3
- package/dist/storage/testing.js +1 -1
- package/dist/storage/turso.js +2 -2
- package/dist/storage/typed-storage.js +138 -2
- package/dist/storage/web-extension.js +298 -1
- package/dist/testing/index.js +21 -60
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web-extension.js +1 -0
- package/dist/worker/protocol.js +1 -1
- package/package.json +8 -3
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @module service-worker/setup
|
|
12
12
|
*/
|
|
13
|
-
import { Engine } from '../core/engine';
|
|
13
|
+
import { Engine, type RegistryAgnosticEngine } from '../core/engine';
|
|
14
14
|
import type { Storage as WeftStorage } from '../storage/interface';
|
|
15
15
|
import { ServiceWorkerScheduler } from './scheduler';
|
|
16
16
|
/**
|
|
@@ -47,8 +47,12 @@ export interface SetupServiceWorkerOptions {
|
|
|
47
47
|
/**
|
|
48
48
|
* Pre-built engine. If provided, must use the same storage instance as
|
|
49
49
|
* `options.storage` (or its own storage if `storage` is omitted).
|
|
50
|
+
*
|
|
51
|
+
* Typed as {@link RegistryAgnosticEngine} (see its JSDoc) rather than the
|
|
52
|
+
* plain default `Engine`, so both `new Engine({ storage })` and
|
|
53
|
+
* `Engine.create({ workflows })` type-check here directly.
|
|
50
54
|
*/
|
|
51
|
-
engine?:
|
|
55
|
+
engine?: RegistryAgnosticEngine;
|
|
52
56
|
/**
|
|
53
57
|
* Pre-built storage instance. Must be the same `===` reference as the
|
|
54
58
|
* engine's storage when both are provided.
|
package/dist/storage/auto.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var
|
|
2
|
+
var Z=import.meta.require,I=(z,C,J)=>{if(C!=null){if(typeof C!=="object"&&typeof C!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let K;if(J)K=C[Symbol.asyncDispose];if(K===void 0)K=C[Symbol.dispose];if(typeof K!=="function")throw TypeError("Object not disposable");z.push([J,K,C])}else if(J)z.push([J]);return C},L=(z,C,J)=>{let K=(V)=>C=J?new SuppressedError(V,C,"An error was suppressed during disposal"):(J=!0,V),Y=(V)=>{while(V=z.pop())try{var A=V[1]&&V[1].call(V[2]);if(V[0])return Promise.resolve(A).then(Y,(q)=>(K(q),Y()))}catch(q){K(q)}if(J)throw C};return Y()};function F(z){return{hasBun:typeof z.Bun<"u",hasIndexedDB:typeof z.indexedDB<"u",hasNode:z.process!==void 0&&typeof z.process.versions?.node==="string",hasWebExtensionStorage:z.browser?.storage!==void 0||z.chrome?.storage!==void 0}}async function P(z){let{createHash:C}=await import("crypto"),J=z.process?.cwd?.()??"weft-default";return C("sha256").update(J).digest("hex").slice(0,16)}async function X(z){let[{mkdirSync:C},{tmpdir:J},K]=await Promise.all([import("fs"),import("os"),import("path")]),Y=z.process?.env?.WEFT_DEFAULT_STORAGE_PATH,V=Y!==void 0&&Y.length>0?Y:K.join(J(),"weft-default",`${await P(z)}.db`);return C(K.dirname(V),{recursive:!0}),V}function H(z,C){if(C==="Bun")return typeof z.Bun;if(C==="browser.storage")return typeof(z.browser?.storage??z.chrome?.storage);return typeof z.process}function j(z,C){return import.meta.url.endsWith(".ts")?z:C}async function $(z){return await import(z)}function k(z){if(z.browser?.storage!==void 0)return z.browser;if(z.chrome?.storage!==void 0)return z.chrome;return{}}function Q(z){let{indexedDB:C,IDBKeyRange:J}=z;if(C===void 0||J===void 0)throw Error("resolveDefaultStorage: IndexedDB resolution requires both indexedDB and IDBKeyRange.");return{indexedDB:C,IDBKeyRange:J}}function U(z){let C=typeof z.indexedDB,J=typeof z.IDBKeyRange;return`typeof indexedDB=${C}, typeof IDBKeyRange=${J}`}var W=j("./bun-sql.ts","./bun-sql.js"),B=j("./node-sqlite.ts","./node-sqlite.js"),N=j("./indexeddb.ts","./indexeddb.js"),w=j("./web-extension.ts","./web-extension.js");async function T(z=globalThis){let C=F(z);if(C.hasBun){let{BunSQLiteStorage:J}=await $(W);return new J(await X(z))}if(C.hasNode){let{NodeSQLiteStorage:J}=await $(B);return new J(await X(z))}if(C.hasWebExtensionStorage){let{WebExtensionStorage:J}=await $(w);return new J({},k(z))}if(C.hasIndexedDB){let{IndexedDBStorage:J}=await $(N);return new J("weft",Q(z))}throw Error(`resolveDefaultStorage: requires Bun, Node, WebExtension storage, or IndexedDB. Detected: typeof Bun=${H(z,"Bun")}, typeof process=${H(z,"process")}, typeof browser.storage=${H(z,"browser.storage")}, ${U(z)}.`)}export{T as resolveDefaultStorage};
|
package/dist/storage/bun-sql.js
CHANGED
|
@@ -391,19 +391,19 @@ function buildSqliteKeyRangeQuery(prefix, options = {}) {
|
|
|
391
391
|
sqlSuffix: `WHERE ${conditions.join(" AND ")} ORDER BY key ${direction}${limitClause}`
|
|
392
392
|
};
|
|
393
393
|
}
|
|
394
|
-
function buildSqliteKeyRangeDelete(prefix, options) {
|
|
394
|
+
function buildSqliteKeyRangeDelete(prefix, options, table = "kv") {
|
|
395
395
|
const { conditions, parameters } = buildSqliteKeyRangeConditions(prefix, options);
|
|
396
396
|
const whereClause = conditions.join(" AND ");
|
|
397
397
|
if (options.limit === undefined) {
|
|
398
398
|
return {
|
|
399
399
|
parameters,
|
|
400
|
-
sql: `DELETE FROM
|
|
400
|
+
sql: `DELETE FROM ${table} WHERE ${whereClause}`
|
|
401
401
|
};
|
|
402
402
|
}
|
|
403
403
|
parameters.push(options.limit);
|
|
404
404
|
return {
|
|
405
405
|
parameters,
|
|
406
|
-
sql: `DELETE FROM
|
|
406
|
+
sql: `DELETE FROM ${table} WHERE key IN (SELECT key FROM ${table} WHERE ${whereClause} ORDER BY key ASC LIMIT ?)`
|
|
407
407
|
};
|
|
408
408
|
}
|
|
409
409
|
function buildSqliteKeyValueRangeSelect(prefix, options = {}) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural view of a Cloudflare Durable Object's `ctx.storage.sql`
|
|
3
|
+
* binding.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately hand-written instead of importing `@cloudflare/workers-types`:
|
|
6
|
+
* the real binding's `.exec()` method is synchronous (it runs against
|
|
7
|
+
* transactional local storage with no I/O wait), and this structural
|
|
8
|
+
* interface only needs the slice of that surface the adapter actually calls.
|
|
9
|
+
* Any object satisfying this shape — the real Durable Object binding, or a
|
|
10
|
+
* test double backed by `bun:sqlite` — works as `sql`.
|
|
11
|
+
*
|
|
12
|
+
* @module storage/cloudflare-durable-object-sql
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The value types a Durable Object SQL column can hold.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import type { SqlStorageValue } from '@lostgradient/weft/storage/cloudflare';
|
|
20
|
+
*
|
|
21
|
+
* const bound: SqlStorageValue = 'a bound query parameter';
|
|
22
|
+
* void bound;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export type SqlStorageValue = ArrayBuffer | string | number | null;
|
|
26
|
+
/**
|
|
27
|
+
* The lazily-iterated result of one `Sql.exec()` call. Matches the subset of
|
|
28
|
+
* Cloudflare's `SqlStorageCursor` this adapter reads: row iteration.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately omits `rowsWritten`: on the real Durable Object binding that
|
|
31
|
+
* field is a billing counter that also counts index writes, not a logical
|
|
32
|
+
* rows-affected count — this `kv` table's implicit primary-key index means a
|
|
33
|
+
* single-row `DELETE` can report more than one. `deletePrefix()` and
|
|
34
|
+
* `deleteRange()` instead read the logical count with a same-connection
|
|
35
|
+
* `SELECT changes()` query run immediately after the write, with no `await`
|
|
36
|
+
* in between (see `cloudflare.ts`).
|
|
37
|
+
*
|
|
38
|
+
* On the real binding, `exec()` runs the statement immediately — iterating
|
|
39
|
+
* only consumes the already-computed result, it does not defer execution.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import type { Sql, SqlStorageCursor } from '@lostgradient/weft/storage/cloudflare';
|
|
44
|
+
*
|
|
45
|
+
* declare const sql: Sql;
|
|
46
|
+
* const cursor: SqlStorageCursor<{ key: string }> = sql.exec('SELECT key FROM kv');
|
|
47
|
+
* const rows = [...cursor];
|
|
48
|
+
* void rows;
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export type SqlStorageCursor<T = Record<string, SqlStorageValue>> = Iterable<T>;
|
|
52
|
+
/**
|
|
53
|
+
* The structural shape of a Cloudflare Durable Object's `ctx.storage.sql`
|
|
54
|
+
* binding that this adapter depends on.
|
|
55
|
+
*
|
|
56
|
+
* `exec()` is synchronous by contract: Durable Object storage operations are
|
|
57
|
+
* transactional only up to the next `await` in the calling code, so every
|
|
58
|
+
* code path in {@link CloudflareDurableObjectSQLiteStorage} that needs that
|
|
59
|
+
* guarantee (`conditionalBatch`'s read-compare-write, `batch`'s multi-write,
|
|
60
|
+
* `scan`'s snapshot read) calls `exec()` with no `await` in between.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { CloudflareDurableObjectSQLiteStorage, type Sql } from '@lostgradient/weft/storage/cloudflare';
|
|
65
|
+
*
|
|
66
|
+
* // Injected by the Durable Object runtime: `ctx.storage.sql` inside a
|
|
67
|
+
* // `DurableObject` subclass.
|
|
68
|
+
* declare const sql: Sql;
|
|
69
|
+
* const storage = new CloudflareDurableObjectSQLiteStorage({ sql });
|
|
70
|
+
* void storage;
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export interface Sql {
|
|
74
|
+
exec<T = Record<string, SqlStorageValue>>(query: string, ...bindings: SqlStorageValue[]): SqlStorageCursor<T>;
|
|
75
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value encoding for {@link CloudflareDurableObjectSQLiteStorage}'s `value`
|
|
3
|
+
* column: the default base64-`TEXT` encoding, and the opt-in raw
|
|
4
|
+
* `ArrayBuffer`/`BLOB` encoding.
|
|
5
|
+
*
|
|
6
|
+
* @module storage/cloudflare-value-codec
|
|
7
|
+
*/
|
|
8
|
+
import type { SqlStorageValue } from './cloudflare-durable-object-sql.ts';
|
|
9
|
+
/**
|
|
10
|
+
* How {@link CloudflareDurableObjectSQLiteStorage} stores values in its
|
|
11
|
+
* `value` column.
|
|
12
|
+
*
|
|
13
|
+
* - `'base64'` (the default): values are base64-encoded and stored as
|
|
14
|
+
* `TEXT`. Keeps the adapter's SQL binding contract to the TEXT/number/null
|
|
15
|
+
* value types the Durable Object SQL binding guarantees, at the cost of
|
|
16
|
+
* ~4/3 size expansion per value.
|
|
17
|
+
* - `'blob'`: values are bound and stored as raw `ArrayBuffer`/`BLOB`,
|
|
18
|
+
* avoiding the base64 expansion. Requires the wider binding contract the
|
|
19
|
+
* real Durable Object SQL binding also supports (`ArrayBuffer` bind
|
|
20
|
+
* parameters and `BLOB` columns).
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* import type { CloudflareValueEncoding } from '@lostgradient/weft/storage/cloudflare';
|
|
25
|
+
*
|
|
26
|
+
* const encoding: CloudflareValueEncoding = 'blob';
|
|
27
|
+
* void encoding;
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export type CloudflareValueEncoding = 'base64' | 'blob';
|
|
31
|
+
/**
|
|
32
|
+
* A `value`-column codec: how to bind a `Uint8Array` as a SQL parameter, and
|
|
33
|
+
* how to decode a column value read back out. `sqlColumnType` is the
|
|
34
|
+
* declared type used only when `CREATE TABLE IF NOT EXISTS` creates a fresh
|
|
35
|
+
* table — SQLite's manifest typing means an already-existing table keeps
|
|
36
|
+
* whichever storage class (`TEXT` or `BLOB`) each row was actually written
|
|
37
|
+
* with, regardless of the column's declared type. That is exactly what lets
|
|
38
|
+
* {@link decode} on both codecs detect and reject a value written under the
|
|
39
|
+
* *other* encoding: reading a `TEXT` value with the `'blob'` codec (or a
|
|
40
|
+
* `BLOB` value with the `'base64'` codec) fails the type guard below and
|
|
41
|
+
* throws immediately, instead of silently misinterpreting the bytes.
|
|
42
|
+
*
|
|
43
|
+
* This is the cross-mode contract for
|
|
44
|
+
* {@link CloudflareDurableObjectSQLiteStorageOptions.valueEncoding}: a table
|
|
45
|
+
* must use a single `valueEncoding` for its lifetime. There is no dual-read
|
|
46
|
+
* fallback and no migration path between encodings — pick one per table.
|
|
47
|
+
*/
|
|
48
|
+
type CloudflareValueCodec = {
|
|
49
|
+
readonly sqlColumnType: 'TEXT' | 'BLOB';
|
|
50
|
+
encode(value: Uint8Array): SqlStorageValue;
|
|
51
|
+
decode(value: SqlStorageValue, key: string): Uint8Array;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a {@link CloudflareValueEncoding} to its {@link CloudflareValueCodec}.
|
|
55
|
+
*
|
|
56
|
+
* `valueEncoding` is a durable, per-table format choice (see
|
|
57
|
+
* {@link CloudflareValueCodec}'s docs on the cross-mode contract), so an
|
|
58
|
+
* unrecognized value is rejected outright rather than silently falling back
|
|
59
|
+
* to `'base64'` — the TypeScript type only guards callers who type-check;
|
|
60
|
+
* a caller passing an unvalidated string (a config value, a typo'd literal
|
|
61
|
+
* from plain JavaScript) must not have that typo silently pick the wrong
|
|
62
|
+
* on-disk format for a table.
|
|
63
|
+
*/
|
|
64
|
+
export declare function resolveCloudflareValueCodec(encoding: CloudflareValueEncoding): CloudflareValueCodec;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const BASE64_CHUNK_SIZE = 32768;
|
|
2
|
+
function bytesToBase64(bytes) {
|
|
3
|
+
let binary = "";
|
|
4
|
+
for (let offset = 0;offset < bytes.length; offset += BASE64_CHUNK_SIZE) {
|
|
5
|
+
const chunk = bytes.subarray(offset, offset + BASE64_CHUNK_SIZE);
|
|
6
|
+
binary += String.fromCharCode(...chunk);
|
|
7
|
+
}
|
|
8
|
+
return btoa(binary);
|
|
9
|
+
}
|
|
10
|
+
function base64ToBytes(base64) {
|
|
11
|
+
const binary = atob(base64), bytes = new Uint8Array(binary.length);
|
|
12
|
+
for (let index = 0;index < binary.length; index += 1)
|
|
13
|
+
bytes[index] = binary.charCodeAt(index);
|
|
14
|
+
return bytes;
|
|
15
|
+
}
|
|
16
|
+
function unexpectedValueTypeError(key, expectedEncoding, actualType) {
|
|
17
|
+
return Error(`Cloudflare Durable Object SQLite storage: key "${key}" holds a value of type ${actualType}, but this instance is configured for valueEncoding: '${expectedEncoding}'. A table must use a single, ` + "consistent valueEncoding for its lifetime \u2014 this row was written by an instance configured " + "with a different valueEncoding. Pick one encoding per table (or per-instance configured table name) and do not change it; there is no automatic migration between encodings.");
|
|
18
|
+
}
|
|
19
|
+
const base64Codec = {
|
|
20
|
+
sqlColumnType: "TEXT",
|
|
21
|
+
encode(value) {
|
|
22
|
+
return bytesToBase64(value);
|
|
23
|
+
},
|
|
24
|
+
decode(value, key) {
|
|
25
|
+
if (typeof value !== "string")
|
|
26
|
+
throw unexpectedValueTypeError(key, "base64", value === null ? "null" : typeof value);
|
|
27
|
+
return base64ToBytes(value);
|
|
28
|
+
}
|
|
29
|
+
}, blobCodec = {
|
|
30
|
+
sqlColumnType: "BLOB",
|
|
31
|
+
encode(value) {
|
|
32
|
+
return new Uint8Array(value).buffer;
|
|
33
|
+
},
|
|
34
|
+
decode(value, key) {
|
|
35
|
+
if (!(value instanceof ArrayBuffer))
|
|
36
|
+
throw unexpectedValueTypeError(key, "blob", value === null ? "null" : typeof value);
|
|
37
|
+
return new Uint8Array(value);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
export function resolveCloudflareValueCodec(encoding) {
|
|
41
|
+
if (encoding === "base64")
|
|
42
|
+
return base64Codec;
|
|
43
|
+
if (encoding === "blob")
|
|
44
|
+
return blobCodec;
|
|
45
|
+
throw Error(`Cloudflare Durable Object SQLite storage: valueEncoding must be 'base64' or 'blob', received ${JSON.stringify(encoding)}.`);
|
|
46
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Durable Object SQLite storage adapter.
|
|
3
|
+
*
|
|
4
|
+
* Wraps a Durable Object's `ctx.storage.sql` binding (structurally typed as
|
|
5
|
+
* {@link Sql} — see `cloudflare-durable-object-sql.ts`) in the same `Storage`
|
|
6
|
+
* interface every other adapter implements, over a single key/value table.
|
|
7
|
+
* `@cloudflare/workers-types` is never imported: the structural `Sql`/
|
|
8
|
+
* `SqlStorageCursor` shape is enough to both type this adapter and drive it
|
|
9
|
+
* in tests with a `bun:sqlite`-backed double.
|
|
10
|
+
*
|
|
11
|
+
* This module has zero runtime-specific imports of its own — the `sql`
|
|
12
|
+
* binding is injected by the caller — so it is bundleable for Bun, Node, and
|
|
13
|
+
* the Cloudflare Workers (`workerd`) runtime alike. Import from
|
|
14
|
+
* `@lostgradient/weft/storage/cloudflare`.
|
|
15
|
+
*
|
|
16
|
+
* @module storage/cloudflare
|
|
17
|
+
*/
|
|
18
|
+
import type { Sql } from './cloudflare-durable-object-sql.ts';
|
|
19
|
+
import { type CloudflareValueEncoding } from './cloudflare-value-codec.ts';
|
|
20
|
+
import { type DeleteRangeOptions } from './delete-range.ts';
|
|
21
|
+
import { type BatchOperation, type ConditionalBatchCondition, type ScanOptions, type Storage, type StorageCapabilities } from './interface.ts';
|
|
22
|
+
export type { Sql, SqlStorageCursor, SqlStorageValue } from './cloudflare-durable-object-sql.ts';
|
|
23
|
+
export type { CloudflareValueEncoding } from './cloudflare-value-codec.ts';
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for {@link CloudflareDurableObjectSQLiteStorage}.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import type { CloudflareDurableObjectSQLiteStorageOptions } from '@lostgradient/weft/storage/cloudflare';
|
|
30
|
+
* import type { Sql } from '@lostgradient/weft/storage/cloudflare';
|
|
31
|
+
*
|
|
32
|
+
* declare const sql: Sql;
|
|
33
|
+
* const options: CloudflareDurableObjectSQLiteStorageOptions = { sql, table: 'weft_kv' };
|
|
34
|
+
* void options;
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export type CloudflareDurableObjectSQLiteStorageOptions = {
|
|
38
|
+
/** The Durable Object's `ctx.storage.sql` binding, or a structurally compatible double. */
|
|
39
|
+
sql: Sql;
|
|
40
|
+
/**
|
|
41
|
+
* Table name for the single key/value table this adapter reads and writes.
|
|
42
|
+
* Validated as a strict SQL identifier at construction; defaults to `kv`.
|
|
43
|
+
*/
|
|
44
|
+
table?: string;
|
|
45
|
+
/**
|
|
46
|
+
* How values are stored in the `value` column. Defaults to `'base64'`
|
|
47
|
+
* (base64-encoded `TEXT`), which keeps this adapter's SQL binding contract
|
|
48
|
+
* to the TEXT/number/null value types the Durable Object SQL binding
|
|
49
|
+
* guarantees. Opt into `'blob'` to bind and store raw `ArrayBuffer`/`BLOB`
|
|
50
|
+
* values instead, avoiding base64's ~4/3 size expansion for large values —
|
|
51
|
+
* this requires the wider binding contract the real Durable Object SQL
|
|
52
|
+
* binding also supports.
|
|
53
|
+
*
|
|
54
|
+
* A table's `value` column holds whichever encoding wrote each row: pick
|
|
55
|
+
* one `valueEncoding` for a table's lifetime and do not change it. Reading
|
|
56
|
+
* a row written under the other encoding fails fast with a descriptive
|
|
57
|
+
* error rather than silently misinterpreting the bytes — see the
|
|
58
|
+
* `documentation/guides/storage.md` Cloudflare section for the full
|
|
59
|
+
* cross-mode contract.
|
|
60
|
+
*/
|
|
61
|
+
valueEncoding?: CloudflareValueEncoding;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Storage adapter over a Cloudflare Durable Object's `ctx.storage.sql`
|
|
65
|
+
* binding.
|
|
66
|
+
*
|
|
67
|
+
* A **non-owning** view: the `sql` binding is injected, the Durable Object
|
|
68
|
+
* owns its storage connection, and `[Symbol.dispose]` is a no-op — there is
|
|
69
|
+
* nothing here to close.
|
|
70
|
+
*
|
|
71
|
+
* Schema is one `kv(key TEXT PRIMARY KEY, value ...)` table (name
|
|
72
|
+
* configurable via `table`). By default (`valueEncoding: 'base64'`) values
|
|
73
|
+
* are stored as base64-encoded text, keeping this adapter's SQL binding
|
|
74
|
+
* contract to the TEXT/number/null value types the Durable Object SQL
|
|
75
|
+
* binding guarantees. Set `valueEncoding: 'blob'` to bind and store raw
|
|
76
|
+
* `ArrayBuffer`/`BLOB` values instead — see
|
|
77
|
+
* {@link CloudflareDurableObjectSQLiteStorageOptions.valueEncoding}.
|
|
78
|
+
*
|
|
79
|
+
* `ctx.storage.sql.exec()` is synchronous — Durable Object storage is
|
|
80
|
+
* transactional only up to the next `await`/yield point in the calling code.
|
|
81
|
+
* Every method here that needs that guarantee (`batch`, `conditionalBatch`,
|
|
82
|
+
* `scan`) runs its `exec()` calls with no `await` in between, so a single
|
|
83
|
+
* `Storage` call from this adapter is one atomic unit of Durable Object
|
|
84
|
+
* storage work.
|
|
85
|
+
*
|
|
86
|
+
* `deletePrefix()` and `deleteRange()` are native single-statement `DELETE`s
|
|
87
|
+
* (not a scan-then-batch fallback), so `capabilities().boundedRangeDelete`
|
|
88
|
+
* is honestly `true`.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* import { CloudflareDurableObjectSQLiteStorage, type Sql } from '@lostgradient/weft/storage/cloudflare';
|
|
93
|
+
* import { Engine, workflow, type WorkflowContext } from '@lostgradient/weft';
|
|
94
|
+
*
|
|
95
|
+
* // Injected by the Durable Object runtime: `ctx.storage.sql` inside a
|
|
96
|
+
* // `DurableObject` subclass.
|
|
97
|
+
* declare const sql: Sql;
|
|
98
|
+
*
|
|
99
|
+
* const storage = new CloudflareDurableObjectSQLiteStorage({ sql });
|
|
100
|
+
*
|
|
101
|
+
* // Durable Objects drive their own event loop; there is no host process to
|
|
102
|
+
* // own background intervals, so the engine runs in manual maintenance mode
|
|
103
|
+
* // and the Durable Object alarm (or a Worker Cron Trigger) drives
|
|
104
|
+
* // `engine.runMaintenance()` explicitly.
|
|
105
|
+
* const engine = await Engine.create({
|
|
106
|
+
* storage,
|
|
107
|
+
* backgroundTasks: 'manual',
|
|
108
|
+
* startScheduler: false,
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* engine.register(
|
|
112
|
+
* workflow({ name: 'echo' }).execute(async function* (ctx: WorkflowContext, input: unknown) {
|
|
113
|
+
* return input;
|
|
114
|
+
* }),
|
|
115
|
+
* );
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export declare class CloudflareDurableObjectSQLiteStorage implements Storage {
|
|
119
|
+
#private;
|
|
120
|
+
constructor(options: CloudflareDurableObjectSQLiteStorageOptions);
|
|
121
|
+
capabilities(): StorageCapabilities;
|
|
122
|
+
get(key: string): Promise<Uint8Array | null>;
|
|
123
|
+
put(key: string, value: Uint8Array): Promise<void>;
|
|
124
|
+
delete(key: string): Promise<void>;
|
|
125
|
+
has(key: string): Promise<boolean>;
|
|
126
|
+
count(prefix: string): Promise<number>;
|
|
127
|
+
deletePrefix(prefix: string): Promise<number>;
|
|
128
|
+
deleteRange(prefix: string, options: DeleteRangeOptions): Promise<number>;
|
|
129
|
+
scan(prefix: string, options?: ScanOptions): AsyncIterable<[string, Uint8Array]>;
|
|
130
|
+
keys(prefix: string, options?: ScanOptions): AsyncIterable<string>;
|
|
131
|
+
batch(operations: BatchOperation[]): Promise<void>;
|
|
132
|
+
conditionalBatch(conditions: ConditionalBatchCondition[], operations: BatchOperation[]): Promise<boolean>;
|
|
133
|
+
/**
|
|
134
|
+
* No-op. This adapter is a non-owning view over an injected `sql` binding
|
|
135
|
+
* — the Durable Object owns its storage connection, so there is nothing
|
|
136
|
+
* here to close. Disposing this adapter never touches `sql` beyond the
|
|
137
|
+
* ordinary reads/writes issued by the methods above.
|
|
138
|
+
*/
|
|
139
|
+
[Symbol.dispose](): void;
|
|
140
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveCloudflareValueCodec
|
|
3
|
+
} from "./cloudflare-value-codec.js";
|
|
4
|
+
import { normalizeDeleteRangeOptions } from "./delete-range.js";
|
|
5
|
+
import {
|
|
6
|
+
assertStorageBatchOperationCount,
|
|
7
|
+
storageValuesEqual
|
|
8
|
+
} from "./interface.js";
|
|
9
|
+
import { assertSqlIdentifier } from "./sql-identifier.js";
|
|
10
|
+
import {
|
|
11
|
+
buildSqliteKeyRangeDelete,
|
|
12
|
+
buildSqliteKeyRangeQuery,
|
|
13
|
+
buildSqlitePrefixRangeParameters
|
|
14
|
+
} from "./sqlite-key-value-queries.js";
|
|
15
|
+
const DEFAULT_TABLE = "kv", DEFAULT_VALUE_ENCODING = "base64";
|
|
16
|
+
|
|
17
|
+
export class CloudflareDurableObjectSQLiteStorage {
|
|
18
|
+
#sql;
|
|
19
|
+
#table;
|
|
20
|
+
#codec;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
const { sql, table = DEFAULT_TABLE, valueEncoding = DEFAULT_VALUE_ENCODING } = options;
|
|
23
|
+
assertSqlIdentifier(table, "table", "Cloudflare Durable Object SQLite");
|
|
24
|
+
this.#sql = sql;
|
|
25
|
+
this.#table = table;
|
|
26
|
+
this.#codec = resolveCloudflareValueCodec(valueEncoding);
|
|
27
|
+
this.#sql.exec(`CREATE TABLE IF NOT EXISTS ${this.#table} (
|
|
28
|
+
key TEXT PRIMARY KEY,
|
|
29
|
+
value ${this.#codec.sqlColumnType} NOT NULL
|
|
30
|
+
)`);
|
|
31
|
+
}
|
|
32
|
+
capabilities() {
|
|
33
|
+
return {
|
|
34
|
+
persistence: "local",
|
|
35
|
+
readAfterWrite: "linearizable",
|
|
36
|
+
scanConsistency: "snapshot",
|
|
37
|
+
atomicBatch: !0,
|
|
38
|
+
conditionalBatch: !0,
|
|
39
|
+
boundedRangeDelete: !0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async get(key) {
|
|
43
|
+
return this.#getSync(key);
|
|
44
|
+
}
|
|
45
|
+
async put(key, value) {
|
|
46
|
+
this.#putSync(key, value);
|
|
47
|
+
}
|
|
48
|
+
async delete(key) {
|
|
49
|
+
this.#deleteSync(key);
|
|
50
|
+
}
|
|
51
|
+
async has(key) {
|
|
52
|
+
return [
|
|
53
|
+
...this.#sql.exec(`SELECT 1 AS present FROM ${this.#table} WHERE key = ? LIMIT 1`, key)
|
|
54
|
+
].length > 0;
|
|
55
|
+
}
|
|
56
|
+
async count(prefix) {
|
|
57
|
+
const [rangeStart, rangeEnd] = buildSqlitePrefixRangeParameters(prefix);
|
|
58
|
+
return [
|
|
59
|
+
...this.#sql.exec(`SELECT COUNT(*) AS count FROM ${this.#table} WHERE key >= ? AND key < ?`, rangeStart, rangeEnd)
|
|
60
|
+
][0]?.count ?? 0;
|
|
61
|
+
}
|
|
62
|
+
async deletePrefix(prefix) {
|
|
63
|
+
const [rangeStart, rangeEnd] = buildSqlitePrefixRangeParameters(prefix);
|
|
64
|
+
this.#sql.exec(`DELETE FROM ${this.#table} WHERE key >= ? AND key < ?`, rangeStart, rangeEnd);
|
|
65
|
+
return this.#logicalChangeCount();
|
|
66
|
+
}
|
|
67
|
+
async deleteRange(prefix, options) {
|
|
68
|
+
const normalized = normalizeDeleteRangeOptions(options), { parameters, sql } = buildSqliteKeyRangeDelete(prefix, normalized, this.#table);
|
|
69
|
+
this.#sql.exec(sql, ...parameters);
|
|
70
|
+
return this.#logicalChangeCount();
|
|
71
|
+
}
|
|
72
|
+
#logicalChangeCount() {
|
|
73
|
+
return [...this.#sql.exec("SELECT changes() AS value")][0]?.value ?? 0;
|
|
74
|
+
}
|
|
75
|
+
async* scan(prefix, options = {}) {
|
|
76
|
+
const { parameters, sqlSuffix } = buildSqliteKeyRangeQuery(prefix, options), rows = [
|
|
77
|
+
...this.#sql.exec(`SELECT key, value FROM ${this.#table} ${sqlSuffix}`, ...parameters)
|
|
78
|
+
];
|
|
79
|
+
for (const row of rows)
|
|
80
|
+
yield [row.key, this.#codec.decode(row.value, row.key)];
|
|
81
|
+
}
|
|
82
|
+
async* keys(prefix, options = {}) {
|
|
83
|
+
const { parameters, sqlSuffix } = buildSqliteKeyRangeQuery(prefix, options), rows = [
|
|
84
|
+
...this.#sql.exec(`SELECT key FROM ${this.#table} ${sqlSuffix}`, ...parameters)
|
|
85
|
+
];
|
|
86
|
+
for (const row of rows)
|
|
87
|
+
yield row.key;
|
|
88
|
+
}
|
|
89
|
+
async batch(operations) {
|
|
90
|
+
assertStorageBatchOperationCount("batch operations", operations.length);
|
|
91
|
+
if (operations.length === 0)
|
|
92
|
+
return;
|
|
93
|
+
for (const operation of operations)
|
|
94
|
+
this.#applyOperation(operation);
|
|
95
|
+
}
|
|
96
|
+
async conditionalBatch(conditions, operations) {
|
|
97
|
+
assertStorageBatchOperationCount("conditionalBatch conditions", conditions.length);
|
|
98
|
+
assertStorageBatchOperationCount("conditionalBatch operations", operations.length);
|
|
99
|
+
for (const condition of conditions) {
|
|
100
|
+
const currentValue = this.#getSync(condition.key);
|
|
101
|
+
if (!storageValuesEqual(currentValue, condition.expectedValue))
|
|
102
|
+
return !1;
|
|
103
|
+
}
|
|
104
|
+
for (const operation of operations)
|
|
105
|
+
this.#applyOperation(operation);
|
|
106
|
+
return !0;
|
|
107
|
+
}
|
|
108
|
+
#getSync(key) {
|
|
109
|
+
const row = [
|
|
110
|
+
...this.#sql.exec(`SELECT value FROM ${this.#table} WHERE key = ?`, key)
|
|
111
|
+
][0];
|
|
112
|
+
return row ? this.#codec.decode(row.value, key) : null;
|
|
113
|
+
}
|
|
114
|
+
#putSync(key, value) {
|
|
115
|
+
this.#sql.exec(`INSERT INTO ${this.#table} (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`, key, this.#codec.encode(value));
|
|
116
|
+
}
|
|
117
|
+
#deleteSync(key) {
|
|
118
|
+
this.#sql.exec(`DELETE FROM ${this.#table} WHERE key = ?`, key);
|
|
119
|
+
}
|
|
120
|
+
#applyOperation(operation) {
|
|
121
|
+
if (operation.type === "put")
|
|
122
|
+
this.#putSync(operation.key, operation.value);
|
|
123
|
+
else
|
|
124
|
+
this.#deleteSync(operation.key);
|
|
125
|
+
}
|
|
126
|
+
[Symbol.dispose]() {}
|
|
127
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var n=Object.defineProperty;var l=(W)=>W;function s(W,F){this[W]=l.bind(null,F)}var UW=(W,F)=>{for(var X in F)n(W,X,{get:F[X],enumerable:!0,configurable:!0,set:s.bind(F,X)})};var P=(W,F)=>()=>(W&&(F=W(W=0)),F);var VW=import.meta.require,PW=(W,F,X)=>{if(F!=null){if(typeof F!=="object"&&typeof F!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let Y;if(X)Y=F[Symbol.asyncDispose];if(Y===void 0)Y=F[Symbol.dispose];if(typeof Y!=="function")throw TypeError("Object not disposable");W.push([X,Y,F])}else if(X)W.push([X]);return F},qW=(W,F,X)=>{let Y=($)=>F=X?new SuppressedError($,F,"An error was suppressed during disposal"):(X=!0,$),U=($)=>{while($=W.pop())try{var j=$[1]&&$[1].call($[2]);if($[0])return Promise.resolve(j).then(U,(D)=>(Y(D),U()))}catch(D){Y(D)}if(X)throw F};return U()};function S(W,F,X){if(!W.capabilities()[F])throw Error(`Feature "${X}" requires storage capability "${F}", but this storage backend does not provide it.`)}function d(W){let F=W.capabilities(),X=[];if(F.persistence!=="local"&&F.persistence!=="remote")X.push(`persistence must be "local" or "remote" (got "${F.persistence}")`);if(F.readAfterWrite!=="linearizable")X.push(`readAfterWrite must be "linearizable" (got "${F.readAfterWrite}")`);if(F.scanConsistency!=="snapshot")X.push(`scanConsistency must be "snapshot" (got "${F.scanConsistency}")`);if(!F.atomicBatch)X.push("atomicBatch must be true");if(!F.conditionalBatch)X.push("conditionalBatch must be true");if(X.length>0)throw Error(`Storage is not durable enough for recovery: ${X.join("; ")}.`)}var Z="default";async function B(W,F){return await W.get(F)!==null}async function*z(W,F,X){for await(let[Y]of W.scan(F,X))yield Y}async function x(W,F){let X=0;for await(let Y of z(W,F))X++;return X}async function v(W,F){let X=[];for await(let Y of z(W,F))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await W.batch(X),X.length}async function CW(W,F,X){let Y=[];for await(let U of z(W,F,X))Y.push({type:"delete",key:U});if(Y.length===0)return 0;return await W.batch(Y),Y.length}var a;var O=P(()=>{a=["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 G(W,F){if(F>_)throw new h(W,F)}function HW(W){return W.length>0?W.slice(0,-1)+String.fromCharCode(W.charCodeAt(W.length-1)+1):"\xFF"}function EW(W,F={}){if(F.gt!==void 0&&W<=F.gt)return!1;if(F.gte!==void 0&&W<F.gte)return!1;if(F.lt!==void 0&&W>=F.lt)return!1;if(F.lte!==void 0&&W>F.lte)return!1;return!0}function BW(W,F){if(W===null||F===null)return W===F;if(W.byteLength!==F.byteLength)return!1;for(let X=0;X<W.byteLength;X++)if(W[X]!==F[X])return!1;return!0}async function xW(W,F){if(W.has)return W.has(F);return B(W,F)}function vW(W,F,X){if(W.keys)return W.keys(F,X);return z(W,F,X)}async function OW(W,F){if(W.count)return W.count(F);return x(W,F)}async function TW(W,F){if(W.deletePrefix)return W.deletePrefix(F);return v(W,F)}async function hW(W,F){G("batch operations",F.length),await W.batch(F)}async function AW(W,F,X){if(G("conditionalBatch conditions",F.length),G("conditionalBatch operations",X.length),S(W,"conditionalBatch","storageConditionalBatch"),!W.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return W.conditionalBatch(F,X)}function J(W){return encodeURIComponent(W)}function RW(W){return decodeURIComponent(W)}function bW(W){try{return decodeURIComponent(W)}catch{return null}}var _=1e4,NW=1e4,h,K=(W)=>String(W).padStart(16,"0"),i="0",t="1",T=(W,F,X,Y)=>`sig:${J(W)}:${J(F)}:${Y}:${J(X)}`,cW;var A=P(()=>{O();h=class h extends Error{code="StorageBatchOperationLimitExceededError";cap=_;count;target;constructor(W,F){super(`${W} count ${F} exceeds MAX_BATCH_OPERATIONS (${_}).`);this.name="StorageBatchOperationLimitExceededError",this.target=W,this.count=F}};cW={workflow:(W)=>`wf:${J(W)}`,checkpoint:(W)=>`wf:${J(W)}:ckpt`,checkpointHistory:(W,F)=>`wf:${J(W)}:ckpt:${String(F).padStart(10,"0")}`,timelinePrefix:(W)=>`wf:${J(W)}:timeline:`,timeline:(W,F)=>`wf:${J(W)}:timeline:${String(F).padStart(10,"0")}`,schedule:(W)=>`schedule:${J(W)}`,scheduleTick:(W,F)=>`schedule-due:${String(W).padStart(16,"0")}:${J(F)}`,scheduleRun:(W)=>`schedule-run:${J(W)}`,operation:(W,F,X)=>`op:${W}:${K(F)}:${X}`,operationInflight:(W)=>`op:inflight:${W}`,operationQueued:(W)=>`op:queued:${W}`,operationResolved:(W)=>`op:resolved:${W}`,operationDeadLetterPrefix:()=>"op:dead-letter:",operationDeadLetter:(W)=>`op:dead-letter:${W}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(W,F,X)=>`audit:bulk:${K(W)}:${J(F)}:${J(X)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(W,F)=>`op:resolved-by-time:${K(W)}:${J(F)}`,asyncActivity:(W,F)=>`async-act:v1:${J(W)}:${J(F)}`,asyncActivityResolution:(W,F)=>`async-act:v1:${J(W)}:${J(F)}:resolution`,activityReconciliationPrefix:(W)=>`actrec:v1:${J(W)}:`,activityReconciliation:(W,F,X)=>`actrec:v1:${J(W)}:${J(F)}:${X}`,eventPrefix:(W)=>`ev:${J(W)}:`,event:(W,F)=>`ev:${J(W)}:${String(F).padStart(10,"0")}`,eventHead:(W)=>`ev:${J(W)}:head`,eventWatermark:(W)=>`ev:${J(W)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(W)=>`fleet-event:${String(W).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(W)=>`fleet-event-by-workflow:${J(W)}:`,fleetEventByWorkflow:(W,F)=>`fleet-event-by-workflow:${J(W)}:${String(F).padStart(10,"0")}`,signal:(W,F,X)=>T(W,F,X,t),startSignal:(W,F,X)=>T(W,F,X,i),signalSequence:(W)=>`sigseq:v1:${J(W)}`,signalAcceptedResponsePrefix:(W)=>`sigres:v1:${J(W)}:`,signalAcceptedResponse:(W,F,X)=>`sigres:v1:${J(W)}:${J(F)}:${J(X)}`,deadline:(W,F)=>`wf-deadline:${K(W)}:${J(F)}`,terminalCleanup:(W,F)=>`wf-cleanup:${K(W)}:${J(F)}`,teardownTimer:(W,F)=>`wf-teardown:${K(W)}:${J(F)}`,delayedStart:(W,F)=>`wf-delayed:${K(W)}:${J(F)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(W,F)=>`wf-terminal:${K(W)}:${J(F)}`,attribute:(W)=>`attr:${J(W)}`,attributeIndex:(W,F,X)=>`idx:${W}:${F}:${J(X)}`,tagIndex:(W,F)=>`tag:${J(W)}:${J(F)}`,updatePrefix:(W)=>`upd:${J(W)}:`,update:(W,F)=>`upd:${J(W)}:${F}`,updateResponse:(W)=>`upr:${W}`,updateIdempotency:(W,F)=>`upk:${J(W)}:${F}`,startIdempotency:(W)=>`start-idem:${J(W)}`,startIdempotencySignalId:(W)=>`start-idem:${W}`,livenessPrefix:()=>"liveness:",liveness:(W)=>`liveness:${J(W)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",budget:(W,F,X)=>`budget:${W}:${F}:${X}`,review:(W,F)=>`review:${J(W)}:${F}`,workflowHeaders:(W)=>`wf-headers:${J(W)}`,childCancellationPrefix:(W)=>`child-cancel:${J(W)}:`,childCancellation:(W,F)=>`child-cancel:${J(W)}:${J(F)}`,terminalCleanupNeeded:(W)=>`wf-cleanup-needed:${J(W)}`,workflowConcurrency:(W,F)=>`wf-concurrency:${J(W)}:${J(F)}`,workflowConcurrencyHolder:(W)=>`wf-concurrency-holder:${J(W)}`,workflowHasServices:(W)=>`wf-has-services:${J(W)}`,finalizerState:(W)=>`wf-finalizer-state:${J(W)}`,teardownOwed:(W)=>`wf-teardown-needed:${J(W)}`,teardownDeadLetter:(W)=>`wf-teardown-deadletter:${J(W)}`,offload:(W,F)=>`offload:${J(W)}:${F}`,archive:(W,F)=>`archive:${J(W)}:${F}`,stateExecution:(W,F)=>`state:execution:${J(W)}:${J(F)}`,stateWorkflow:(W,F)=>`state:workflow-scope:${Z}:${J(W)}:${J(F)}`,streamChunkPrefix:(W,F)=>`blob:${J(W)}:${F}:chunk:`,streamChunk:(W,F,X)=>`blob:${J(W)}:${F}:chunk:${String(X).padStart(10,"0")}`,streamTail:(W,F)=>`blob:${J(W)}:${F}:tail`,streamMetadata:(W,F)=>`blob:${J(W)}:${F}:meta`,budgetCharged:(W)=>`budget-charged:${W}`,toolEffect:(W,F,X)=>`tool-effect:${J(W)}:${F}:${X}`,workflowVisibilityStatus:(W,F)=>`wf-idx-status:${J(W)}:${J(F)}`,workflowVisibilityType:(W,F)=>`wf-idx-type:${J(W)}:${J(F)}`,workflowVisibilityCreated:(W,F)=>`wf-idx-created:${K(W)}:${J(F)}`,workflowVisibilityUpdated:(W,F)=>`wf-idx-updated:${K(W)}:${J(F)}`,workflowVisibilityDeadline:(W,F)=>`wf-idx-deadline:${K(W)}:${J(F)}`,workflowVisibilityManifest:(W)=>`wf-idx-manifest:${J(W)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"}});function L(){return typeof(V&&"bun"in V?V.bun:globalThis.Bun)<"u"}function r(){if(V&&"process"in V)return V.process;return globalThis.process}function fW(W){if(L())return Bun.sleep(W);return new Promise((F)=>setTimeout(F,W))}function R(W){let F=e;for(let X=0;X<W.length;X++)F^=BigInt(W[X]),F=F*o&WW;return F.toString(16).padStart(16,"0")}function IW(W){return R(W)}function pW(W){return R(FW.encode(W))}function M(W){let X=r()?.getBuiltinModule;if(typeof X!=="function")return;return X(W)}function kW(W){if(L())return Bun.file(W).size;let F=M("node:fs");if(!F)throw Error("fileSize() requires Bun or Node 22.5+ (process.getBuiltinModule). Not available in browser or edge runtimes.");try{return F.statSync(W).size}catch(X){if(typeof X==="object"&&X!==null&&"code"in X&&X.code==="ENOENT")return 0;throw X}}function b(){let W=M("node:zlib");if(!W)throw Error("gzip/gunzip require Bun or Node 22.5+ (process.getBuiltinModule). "+"Not available in browser or edge runtimes \u2014 use CompressionStream directly.");return W}function c(W){if(L())return new Uint8Array(Bun.gzipSync(new Uint8Array(W)));return new Uint8Array(b().gzipSync(W))}function y(W){if(L())return new Uint8Array(Bun.gunzipSync(new Uint8Array(W)));return new Uint8Array(b().gunzipSync(W))}function m(){return M("node:zlib")}var V,e=0xcbf29ce484222325n,o=0x00000100000001b3n,WW=0xffffffffffffffffn,FW;var g=P(()=>{FW=new TextEncoder});function u(){let W=m();if(!W)throw Error("Brotli compression requires Bun or Node 22.5+ with process.getBuiltinModule support. Use gzip compression for browser/edge runtimes.");return W}function XW(W){return new Uint8Array(u().brotliCompressSync(W))}function JW(W){return new Uint8Array(u().brotliDecompressSync(W))}function p(W){return YW(W)}function YW(W){switch(W){case"gzip":return{algorithm:"gzip",compress(F){return c(F)}};case"brotli":return{algorithm:"brotli",compress(F){return XW(F)}};case"none":return{algorithm:"none",compress(F){return F}}}}async function H(W,F,X){if(W.length<X||F.algorithm==="none"){let j=new Uint8Array(W.length+Q);return j[0]=C,j[1]=N,j.set(W,Q),j}let Y=await F.compress(W);if(Y.length+Q>=W.length+Q){let j=new Uint8Array(W.length+Q);return j[0]=C,j[1]=N,j.set(W,Q),j}let U=F.algorithm==="gzip"?f:I,$=new Uint8Array(Y.length+Q);return $[0]=C,$[1]=U,$.set(Y,Q),$}async function E(W){if(W.length<Q)throw Error("Compression payload missing 2-byte header.");if(W[0]!==C)throw Error("Compression payload missing magic byte 0xC1.");let F=W[1],X=W.slice(Q);switch(F){case N:return X;case f:return y(X);case I:return JW(X);default:throw Error(`Compression payload uses unsupported algorithm byte 0x${F.toString(16).padStart(2,"0")}.`)}}function k(W){return{threshold:W?.threshold??$W,algorithm:W?.algorithm??jW}}var C=193,N=0,f=1,I=2,Q=2,$W=4096,jW="gzip";var w=P(()=>{g()});var KW;var QW=P(()=>{w();A();KW=class KW{#W;#F;#X;constructor(W,F){this.#W=W;let X=k(F);if(this.#F=p(X.algorithm),this.#X=X.threshold,W.query){let Y=W.query.bind(W);Object.defineProperty(this,"query",{value:Y,enumerable:!0,configurable:!0})}}capabilities(){let W=this.#W.capabilities();return{...W.persistence===void 0?{}:{persistence:W.persistence},readAfterWrite:W.readAfterWrite,scanConsistency:W.scanConsistency,atomicBatch:W.atomicBatch,conditionalBatch:!1,boundedRangeDelete:!1}}async get(W){let F=await this.#W.get(W);if(!F)return null;return E(F)}async put(W,F){let X=await H(F,this.#F,this.#X);return this.#W.put(W,X)}async delete(W){return this.#W.delete(W)}async*scan(W,F){for await(let[X,Y]of this.#W.scan(W,F))yield[X,await E(Y)]}async batch(W){G("batch operations",W.length);let F=await Promise.all(W.map(async(X)=>{if(X.type==="put")return{type:"put",key:X.key,value:await H(X.value,this.#F,this.#X)};return X}));return this.#W.batch(F)}[Symbol.dispose](){this.#W[Symbol.dispose]()}}});QW();export{KW as CompressedStorage};
|
|
2
|
+
var i=import.meta.require,t=(W,F,X)=>{if(F!=null){if(typeof F!=="object"&&typeof F!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let Y;if(X)Y=F[Symbol.asyncDispose];if(Y===void 0)Y=F[Symbol.dispose];if(typeof Y!=="function")throw TypeError("Object not disposable");W.push([X,Y,F])}else if(X)W.push([X]);return F},r=(W,F,X)=>{let Y=($)=>F=X?new SuppressedError($,F,"An error was suppressed during disposal"):(X=!0,$),U=($)=>{while($=W.pop())try{var j=$[1]&&$[1].call($[2]);if($[0])return Promise.resolve(j).then(U,(C)=>(Y(C),U()))}catch(C){Y(C)}if(X)throw F};return U()};function G(W,F,X){if(!W.capabilities()[F])throw Error(`Feature "${X}" requires storage capability "${F}", but this storage backend does not provide it.`)}function g(W){let F=W.capabilities(),X=[];if(F.persistence!=="local"&&F.persistence!=="remote")X.push(`persistence must be "local" or "remote" (got "${F.persistence}")`);if(F.readAfterWrite!=="linearizable")X.push(`readAfterWrite must be "linearizable" (got "${F.readAfterWrite}")`);if(F.scanConsistency!=="snapshot")X.push(`scanConsistency must be "snapshot" (got "${F.scanConsistency}")`);if(!F.atomicBatch)X.push("atomicBatch must be true");if(!F.conditionalBatch)X.push("conditionalBatch must be true");if(X.length>0)throw Error(`Storage is not durable enough for recovery: ${X.join("; ")}.`)}var D="default";async function M(W,F){return await W.get(F)!==null}async function*P(W,F,X){for await(let[Y]of W.scan(F,X))yield Y}async function N(W,F){let X=0;for await(let Y of P(W,F))X++;return X}async function H(W,F){let X=[];for await(let Y of P(W,F))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await W.batch(X),X.length}async function FW(W,F,X){let Y=[];for await(let U of P(W,F,X))Y.push({type:"delete",key:U});if(Y.length===0)return 0;return await W.batch(Y),Y.length}var u=["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:"];var L=1e4,KW=1e4;class B extends Error{code="StorageBatchOperationLimitExceededError";cap=L;count;target;constructor(W,F){super(`${W} count ${F} exceeds MAX_BATCH_OPERATIONS (${L}).`);this.name="StorageBatchOperationLimitExceededError",this.target=W,this.count=F}}function q(W,F){if(F>L)throw new B(W,F)}function QW(W){return W.length>0?W.slice(0,-1)+String.fromCharCode(W.charCodeAt(W.length-1)+1):"\xFF"}function UW(W,F={}){if(F.gt!==void 0&&W<=F.gt)return!1;if(F.gte!==void 0&&W<F.gte)return!1;if(F.lt!==void 0&&W>=F.lt)return!1;if(F.lte!==void 0&&W>F.lte)return!1;return!0}function VW(W,F){if(W===null||F===null)return W===F;if(W.byteLength!==F.byteLength)return!1;for(let X=0;X<W.byteLength;X++)if(W[X]!==F[X])return!1;return!0}async function PW(W,F){if(W.has)return W.has(F);return M(W,F)}function qW(W,F,X){if(W.keys)return W.keys(F,X);return P(W,F,X)}async function zW(W,F){if(W.count)return W.count(F);return N(W,F)}async function CW(W,F){if(W.deletePrefix)return W.deletePrefix(F);return H(W,F)}async function GW(W,F){q("batch operations",F.length),await W.batch(F)}async function DW(W,F,X){if(q("conditionalBatch conditions",F.length),q("conditionalBatch operations",X.length),G(W,"conditionalBatch","storageConditionalBatch"),!W.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return W.conditionalBatch(F,X)}function J(W){return encodeURIComponent(W)}function LW(W){return decodeURIComponent(W)}function SW(W){try{return decodeURIComponent(W)}catch{return null}}var K=(W)=>String(W).padStart(16,"0"),f="0",I="1",E=(W,F,X,Y)=>`sig:${J(W)}:${J(F)}:${Y}:${J(X)}`,ZW={workflow:(W)=>`wf:${J(W)}`,checkpoint:(W)=>`wf:${J(W)}:ckpt`,checkpointHistory:(W,F)=>`wf:${J(W)}:ckpt:${String(F).padStart(10,"0")}`,timelinePrefix:(W)=>`wf:${J(W)}:timeline:`,timeline:(W,F)=>`wf:${J(W)}:timeline:${String(F).padStart(10,"0")}`,schedule:(W)=>`schedule:${J(W)}`,scheduleTick:(W,F)=>`schedule-due:${String(W).padStart(16,"0")}:${J(F)}`,scheduleRun:(W)=>`schedule-run:${J(W)}`,operation:(W,F,X)=>`op:${W}:${K(F)}:${X}`,operationInflight:(W)=>`op:inflight:${W}`,operationQueued:(W)=>`op:queued:${W}`,operationResolved:(W)=>`op:resolved:${W}`,operationDeadLetterPrefix:()=>"op:dead-letter:",operationDeadLetter:(W)=>`op:dead-letter:${W}`,bulkOperationAuditPrefix:()=>"audit:bulk:",bulkOperationAudit:(W,F,X)=>`audit:bulk:${K(W)}:${J(F)}:${J(X)}`,operationResolvedByTimePrefix:()=>"op:resolved-by-time:",operationResolvedByTime:(W,F)=>`op:resolved-by-time:${K(W)}:${J(F)}`,asyncActivity:(W,F)=>`async-act:v1:${J(W)}:${J(F)}`,asyncActivityResolution:(W,F)=>`async-act:v1:${J(W)}:${J(F)}:resolution`,activityReconciliationPrefix:(W)=>`actrec:v1:${J(W)}:`,activityReconciliation:(W,F,X)=>`actrec:v1:${J(W)}:${J(F)}:${X}`,eventPrefix:(W)=>`ev:${J(W)}:`,event:(W,F)=>`ev:${J(W)}:${String(F).padStart(10,"0")}`,eventHead:(W)=>`ev:${J(W)}:head`,eventWatermark:(W)=>`ev:${J(W)}:watermark`,fleetEventPrefix:()=>"fleet-event:",fleetEvent:(W)=>`fleet-event:${String(W).padStart(10,"0")}`,fleetEventTail:()=>"fleet-event-tail",fleetEventByWorkflowPrefix:(W)=>`fleet-event-by-workflow:${J(W)}:`,fleetEventByWorkflow:(W,F)=>`fleet-event-by-workflow:${J(W)}:${String(F).padStart(10,"0")}`,signal:(W,F,X)=>E(W,F,X,I),startSignal:(W,F,X)=>E(W,F,X,f),signalSequence:(W)=>`sigseq:v1:${J(W)}`,signalAcceptedResponsePrefix:(W)=>`sigres:v1:${J(W)}:`,signalAcceptedResponse:(W,F,X)=>`sigres:v1:${J(W)}:${J(F)}:${J(X)}`,deadline:(W,F)=>`wf-deadline:${K(W)}:${J(F)}`,terminalCleanup:(W,F)=>`wf-cleanup:${K(W)}:${J(F)}`,teardownTimer:(W,F)=>`wf-teardown:${K(W)}:${J(F)}`,delayedStart:(W,F)=>`wf-delayed:${K(W)}:${J(F)}`,terminalWorkflowPrefix:()=>"wf-terminal:",terminalWorkflow:(W,F)=>`wf-terminal:${K(W)}:${J(F)}`,attribute:(W)=>`attr:${J(W)}`,attributeIndex:(W,F,X)=>`idx:${W}:${F}:${J(X)}`,tagIndex:(W,F)=>`tag:${J(W)}:${J(F)}`,updatePrefix:(W)=>`upd:${J(W)}:`,update:(W,F)=>`upd:${J(W)}:${F}`,updateResponse:(W)=>`upr:${W}`,updateIdempotency:(W,F)=>`upk:${J(W)}:${F}`,startIdempotency:(W)=>`start-idem:${J(W)}`,startIdempotencySignalId:(W)=>`start-idem:${W}`,livenessPrefix:()=>"liveness:",liveness:(W)=>`liveness:${J(W)}`,leasePrefix:()=>"lease:",leaseEpoch:()=>"lease:epoch",leaseHolder:()=>"lease:holder",budget:(W,F,X)=>`budget:${W}:${F}:${X}`,review:(W,F)=>`review:${J(W)}:${F}`,workflowHeaders:(W)=>`wf-headers:${J(W)}`,childCancellationPrefix:(W)=>`child-cancel:${J(W)}:`,childCancellation:(W,F)=>`child-cancel:${J(W)}:${J(F)}`,terminalCleanupNeeded:(W)=>`wf-cleanup-needed:${J(W)}`,workflowConcurrency:(W,F)=>`wf-concurrency:${J(W)}:${J(F)}`,workflowConcurrencyHolder:(W)=>`wf-concurrency-holder:${J(W)}`,workflowHasServices:(W)=>`wf-has-services:${J(W)}`,finalizerState:(W)=>`wf-finalizer-state:${J(W)}`,teardownOwed:(W)=>`wf-teardown-needed:${J(W)}`,teardownDeadLetter:(W)=>`wf-teardown-deadletter:${J(W)}`,offload:(W,F)=>`offload:${J(W)}:${F}`,archive:(W,F)=>`archive:${J(W)}:${F}`,stateExecution:(W,F)=>`state:execution:${J(W)}:${J(F)}`,stateWorkflow:(W,F)=>`state:workflow-scope:${D}:${J(W)}:${J(F)}`,streamChunkPrefix:(W,F)=>`blob:${J(W)}:${F}:chunk:`,streamChunk:(W,F,X)=>`blob:${J(W)}:${F}:chunk:${String(X).padStart(10,"0")}`,streamTail:(W,F)=>`blob:${J(W)}:${F}:tail`,streamMetadata:(W,F)=>`blob:${J(W)}:${F}:meta`,budgetCharged:(W)=>`budget-charged:${W}`,toolEffect:(W,F,X)=>`tool-effect:${J(W)}:${F}:${X}`,workflowVisibilityStatus:(W,F)=>`wf-idx-status:${J(W)}:${J(F)}`,workflowVisibilityType:(W,F)=>`wf-idx-type:${J(W)}:${J(F)}`,workflowVisibilityCreated:(W,F)=>`wf-idx-created:${K(W)}:${J(F)}`,workflowVisibilityUpdated:(W,F)=>`wf-idx-updated:${K(W)}:${J(F)}`,workflowVisibilityDeadline:(W,F)=>`wf-idx-deadline:${K(W)}:${J(F)}`,workflowVisibilityManifest:(W)=>`wf-idx-manifest:${J(W)}`,workflowVisibilityMetaVersion:()=>"wf-idx-meta:version",workflowVisibilityMetaBuiltAt:()=>"wf-idx-meta:built-at",workflowVisibilityMetaCursor:()=>"wf-idx-meta:cursor"};var V;function p(){if(V&&"bun"in V)return V.bun;return globalThis.Bun}function x(){return typeof p()<"u"}function k(){if(V&&"process"in V)return V.process;return globalThis.process}function v(W){let X=k()?.getBuiltinModule;if(typeof X!=="function")return;return X(W)}function O(){let W=v("node:zlib");if(!W)throw Error("gzip/gunzip require Bun or Node 22.5+ (process.getBuiltinModule). "+"Not available in browser or edge runtimes \u2014 use CompressionStream directly.");return W}function T(W){if(x())return new Uint8Array(Bun.gzipSync(new Uint8Array(W)));return new Uint8Array(O().gzipSync(W))}function h(W){if(x())return new Uint8Array(Bun.gunzipSync(new Uint8Array(W)));return new Uint8Array(O().gunzipSync(W))}function A(){return v("node:zlib")}function R(){let W=A();if(!W)throw Error("Brotli compression requires Bun or Node 22.5+ with process.getBuiltinModule support. Use gzip compression for browser/edge runtimes.");return W}function w(W){return new Uint8Array(R().brotliCompressSync(W))}function n(W){return new Uint8Array(R().brotliDecompressSync(W))}var z=193,S=0,b=1,c=2,Q=2;function y(W){return l(W)}function l(W){switch(W){case"gzip":return{algorithm:"gzip",compress(F){return T(F)}};case"brotli":return{algorithm:"brotli",compress(F){return w(F)}};case"none":return{algorithm:"none",compress(F){return F}}}}async function Z(W,F,X){if(W.length<X||F.algorithm==="none"){let j=new Uint8Array(W.length+Q);return j[0]=z,j[1]=S,j.set(W,Q),j}let Y=await F.compress(W);if(Y.length+Q>=W.length+Q){let j=new Uint8Array(W.length+Q);return j[0]=z,j[1]=S,j.set(W,Q),j}let U=F.algorithm==="gzip"?b:c,$=new Uint8Array(Y.length+Q);return $[0]=z,$[1]=U,$.set(Y,Q),$}async function _(W){if(W.length<Q)throw Error("Compression payload missing 2-byte header.");if(W[0]!==z)throw Error("Compression payload missing magic byte 0xC1.");let F=W[1],X=W.slice(Q);switch(F){case S:return X;case b:return h(X);case c:return n(X);default:throw Error(`Compression payload uses unsupported algorithm byte 0x${F.toString(16).padStart(2,"0")}.`)}}var s=4096,d="gzip";function m(W){return{threshold:W?.threshold??s,algorithm:W?.algorithm??d}}class a{#W;#F;#X;constructor(W,F){this.#W=W;let X=m(F);if(this.#F=y(X.algorithm),this.#X=X.threshold,W.query){let Y=W.query.bind(W);Object.defineProperty(this,"query",{value:Y,enumerable:!0,configurable:!0})}}capabilities(){let W=this.#W.capabilities();return{...W.persistence===void 0?{}:{persistence:W.persistence},readAfterWrite:W.readAfterWrite,scanConsistency:W.scanConsistency,atomicBatch:W.atomicBatch,conditionalBatch:!1,boundedRangeDelete:!1}}async get(W){let F=await this.#W.get(W);if(!F)return null;return _(F)}async put(W,F){let X=await Z(F,this.#F,this.#X);return this.#W.put(W,X)}async delete(W){return this.#W.delete(W)}async*scan(W,F){for await(let[X,Y]of this.#W.scan(W,F))yield[X,await _(Y)]}async batch(W){q("batch operations",W.length);let F=await Promise.all(W.map(async(X)=>{if(X.type==="put")return{type:"put",key:X.key,value:await Z(X.value,this.#F,this.#X)};return X}));return this.#W.batch(F)}[Symbol.dispose](){this.#W[Symbol.dispose]()}}export{a as CompressedStorage};
|