@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.
Files changed (83) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/generated/operation-catalog.snapshot.json +4671 -0
  3. package/dist/cli/generated/operation-client.generated.d.ts +1 -0
  4. package/dist/cli/index.js +1 -0
  5. package/dist/cli-main.js +227 -401
  6. package/dist/client/http-operations.js +7 -1
  7. package/dist/client/http-request.d.ts +23 -1
  8. package/dist/client/http-request.js +12 -3
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +7 -0
  11. package/dist/connection.d.ts +8 -0
  12. package/dist/connection.js +41 -17
  13. package/dist/core/engine/callback-creators-core.js +2 -1
  14. package/dist/core/engine/index.d.ts +43 -0
  15. package/dist/core/engine/lifecycle/shared.d.ts +25 -0
  16. package/dist/core/engine/lifecycle/transition.js +19 -9
  17. package/dist/core/types/definition-schema-to-json.d.ts +11 -0
  18. package/dist/core/types/definition-schema-to-json.js +11 -2
  19. package/dist/http.js +2 -0
  20. package/dist/indexeddb.js +1 -0
  21. package/dist/json-schema.js +3 -3
  22. package/dist/mcp/cli.js +63 -61
  23. package/dist/mcp/http.d.ts +7 -2
  24. package/dist/mcp/session.d.ts +3 -3
  25. package/dist/mcp/session.js +3 -2
  26. package/dist/mcp/stdio.d.ts +7 -2
  27. package/dist/observability/index.js +2 -2
  28. package/dist/runtime/portable.d.ts +33 -0
  29. package/dist/runtime/portable.js +17 -6
  30. package/dist/server/engine-event-feed-backend.d.ts +22 -3
  31. package/dist/server/fleet-event-feed.d.ts +65 -0
  32. package/dist/server/handler/index.d.ts +11 -2
  33. package/dist/server/handler/index.js +7 -0
  34. package/dist/server/handler.js +1 -56
  35. package/dist/server/index.d.ts +12 -1
  36. package/dist/server/index.js +87 -64
  37. package/dist/server/operations/submit-review-decision.d.ts +2 -0
  38. package/dist/server/operations/submit-review-decision.js +19 -2
  39. package/dist/server/replay-live-feed-internals.d.ts +19 -0
  40. package/dist/server/replay-live-feed-internals.js +76 -0
  41. package/dist/server/runtime/authentication-bridge.d.ts +2 -2
  42. package/dist/server/serve-internals.js +3 -3
  43. package/dist/server/workflow-event-feed.d.ts +100 -11
  44. package/dist/server/workflow-event-feed.js +1 -140
  45. package/dist/service-worker/index.d.ts +8 -2
  46. package/dist/service-worker/index.js +37 -56
  47. package/dist/service-worker/setup.d.ts +6 -2
  48. package/dist/storage/auto.js +1 -1
  49. package/dist/storage/bun-sql.js +3 -3
  50. package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
  51. package/dist/storage/cloudflare-durable-object-sql.js +0 -0
  52. package/dist/storage/cloudflare-value-codec.d.ts +65 -0
  53. package/dist/storage/cloudflare-value-codec.js +46 -0
  54. package/dist/storage/cloudflare.d.ts +140 -0
  55. package/dist/storage/cloudflare.js +127 -0
  56. package/dist/storage/compressed-storage.js +1 -1
  57. package/dist/storage/http.js +200 -2
  58. package/dist/storage/index.d.ts +1 -0
  59. package/dist/storage/indexeddb.js +311 -1
  60. package/dist/storage/interface.js +1 -1
  61. package/dist/storage/lmdb.js +1 -1
  62. package/dist/storage/memory.js +1 -1
  63. package/dist/storage/neon.js +4 -4
  64. package/dist/storage/node-sqlite.js +3 -3
  65. package/dist/storage/postgres-key-value-queries.d.ts +6 -0
  66. package/dist/storage/postgres-key-value-queries.js +2 -3
  67. package/dist/storage/postgres.js +4 -4
  68. package/dist/storage/resolve.js +1 -1
  69. package/dist/storage/scoped-storage.js +1 -1
  70. package/dist/storage/sql-identifier.d.ts +35 -0
  71. package/dist/storage/sql-identifier.js +5 -0
  72. package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
  73. package/dist/storage/sqlite-key-value-queries.js +3 -3
  74. package/dist/storage/testing.js +1 -1
  75. package/dist/storage/turso.js +2 -2
  76. package/dist/storage/typed-storage.js +138 -2
  77. package/dist/storage/web-extension.js +298 -1
  78. package/dist/testing/index.js +21 -60
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +1 -1
  81. package/dist/web-extension.js +1 -0
  82. package/dist/worker/protocol.js +1 -1
  83. package/package.json +8 -3
@@ -6,6 +6,9 @@ function isJsonRpcFailure(value) {
6
6
  function isJsonRpcSuccess(value) {
7
7
  return typeof value === "object" && value !== null && "result" in value;
8
8
  }
9
+ function isRecord(value) {
10
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11
+ }
9
12
  export function httpClientCatalogTransport(baseUrl, headers) {
10
13
  const endpoint = `${baseUrl}/jsonrpc`;
11
14
  return async (operationName, input) => {
@@ -27,7 +30,10 @@ export function httpClientCatalogTransport(baseUrl, headers) {
27
30
  }
28
31
  if (isJsonRpcFailure(body)) {
29
32
  const { message, data } = body.error, httpStatus = typeof data?.httpStatus === "number" ? data.httpStatus : response.status, faultCode = isFaultCode(data?.weftCode) ? data.weftCode : void 0;
30
- throw new HttpClientError(httpStatus, message, { faultCode });
33
+ throw new HttpClientError(httpStatus, message, {
34
+ faultCode,
35
+ data: isRecord(data) ? data : void 0
36
+ });
31
37
  }
32
38
  if (isJsonRpcSuccess(body))
33
39
  return body.result;
@@ -67,7 +67,11 @@ export declare function resolveHttpClientConnection(options: HttpClientOptions):
67
67
  * the wire fault `code` is surfaced as {@link HttpClientError.faultCode} and a
68
68
  * derived {@link HttpClientError.category} so callers can branch programmatically
69
69
  * instead of string-matching `message`. Both are `undefined` when the body is a
70
- * plain `{ error: string }` or carries no recognized code.
70
+ * plain `{ error: string }` or carries no recognized code. The fault's typed
71
+ * `data` payload (e.g. `InvalidParams`'s `issues`, `NotFound`/`Conflict`'s
72
+ * `resource`/`identifier`) is surfaced verbatim as {@link HttpClientError.data}
73
+ * when the body carries a structured `data` object; `undefined` otherwise —
74
+ * including for a masked `EngineFailure`, whose flat body carries no `data`.
71
75
  *
72
76
  * @example
73
77
  * ```ts
@@ -110,9 +114,27 @@ export declare class HttpClientError extends WeftError<'HttpClientError'> {
110
114
  * carried only the coarse {@link faultCode} (most faults) or no structured body.
111
115
  */
112
116
  readonly weftCode?: WeftErrorCode | undefined;
117
+ /**
118
+ * The fault's wire `data` payload, when the response carried a structured
119
+ * body (`{ error: { data } }` for REST, `error.data` for JSON-RPC). Shape is
120
+ * fault-code-dependent — see {@link FaultCode} and the server's
121
+ * `OperationFault` per-code `data` union for what each code carries (e.g.
122
+ * `InvalidParams.data.issues`, `NotFound.data.resource`). `undefined` for
123
+ * plain-string error bodies, bodies with no `data` field, or a `data` field
124
+ * that is not a JSON object.
125
+ *
126
+ * Over JSON-RPC-over-HTTP this is the raw envelope `error.data` verbatim
127
+ * (see `httpClientCatalogTransport` in `http-operations.ts`), so it also
128
+ * carries the envelope's own `weftCode` (the coarse {@link FaultCode}, not
129
+ * a fine-grained {@link WeftErrorCode}) and `httpStatus` keys alongside the
130
+ * per-code payload — those two are not part of the `OperationFault` data
131
+ * union.
132
+ */
133
+ readonly data?: Readonly<Record<string, unknown>> | undefined;
113
134
  constructor(status: number, message: string, options?: {
114
135
  faultCode?: FaultCode | undefined;
115
136
  weftCode?: WeftErrorCode | undefined;
137
+ data?: Readonly<Record<string, unknown>> | undefined;
116
138
  });
117
139
  }
118
140
  export declare function request<T>(baseUrl: string, path: string, baseHeaders: Record<string, string>, options?: RequestInit): Promise<T>;
@@ -17,12 +17,14 @@ export class HttpClientError extends WeftError {
17
17
  faultCode;
18
18
  category;
19
19
  weftCode;
20
+ data;
20
21
  constructor(status, message, options) {
21
22
  super("HttpClientError", message);
22
23
  this.status = status;
23
24
  this.faultCode = options?.faultCode;
24
25
  this.category = options?.faultCode === void 0 ? void 0 : failureCategoryForFaultCode(options.faultCode);
25
26
  this.weftCode = options?.weftCode;
27
+ this.data = options?.data;
26
28
  }
27
29
  }
28
30
  function buildRequestHeaders(baseHeaders, options) {
@@ -53,11 +55,18 @@ function weftCodeFromData(data) {
53
55
  const candidate = data.weftCode;
54
56
  return isWeftErrorCode(candidate) ? candidate : void 0;
55
57
  }
58
+ function isRecord(value) {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
56
61
  async function parseErrorBody(response) {
57
62
  try {
58
63
  const body = await response.json();
59
64
  if (isStructuredErrorBody(body)) {
60
- const { code, message, data } = body.error, weftCode = weftCodeFromData(data), base = weftCode === void 0 ? { message } : { message, weftCode };
65
+ const { code, message, data } = body.error, weftCode = weftCodeFromData(data), base = { message };
66
+ if (weftCode !== void 0)
67
+ base.weftCode = weftCode;
68
+ if (isRecord(data))
69
+ base.data = data;
61
70
  return isFaultCode(code) ? { ...base, faultCode: code } : base;
62
71
  }
63
72
  if (isFlatErrorBody(body) && body.error) {
@@ -74,8 +83,8 @@ export async function request(baseUrl, path, baseHeaders, options) {
74
83
  if (response.status === 404 && (!options?.method || options.method === "GET"))
75
84
  return null;
76
85
  if (!response.ok) {
77
- const { message, faultCode, weftCode } = await parseErrorBody(response);
78
- throw new HttpClientError(response.status, message, { faultCode, weftCode });
86
+ const { message, faultCode, weftCode, data } = await parseErrorBody(response);
87
+ throw new HttpClientError(response.status, message, { faultCode, weftCode, data });
79
88
  }
80
89
  if (response.status === 204)
81
90
  return;
@@ -7,6 +7,8 @@
7
7
  *
8
8
  * @module client/index
9
9
  */
10
+ export { isWeftError, isWeftErrorCode, isWeftErrorLike, isWeftFault, WeftError, } from '../core/weft-error.ts';
11
+ export type { WeftErrorCode } from '../core/weft-error.ts';
10
12
  export type { WorkflowEventStreamOptions, WorkflowEventTransport } from './event-stream-options.ts';
11
13
  export type { WorkflowEventTail } from './event-tail.ts';
12
14
  export { HttpClient } from './http-client.ts';
@@ -1,2 +1,9 @@
1
+ export {
2
+ isWeftError,
3
+ isWeftErrorCode,
4
+ isWeftErrorLike,
5
+ isWeftFault,
6
+ WeftError
7
+ } from "../core/weft-error.js";
1
8
  export { HttpClient } from "./http-client.js";
2
9
  export { HttpClientError } from "./http-request.js";
@@ -14,6 +14,14 @@
14
14
  * A profile token is only applied when neither an explicit `server` option nor
15
15
  * `WEFT_ADDR` redirected the request to a different destination.
16
16
  *
17
+ * This module is imported from `@lostgradient/weft/client` (browser-reachable),
18
+ * so it must stay statically free of `node:*` and Bun-only imports. Environment
19
+ * variables go through {@link readEnvironmentVariable}; `~/.weft/config` and the
20
+ * run lockfile are read through {@link tryLoadNodeBuiltin}, which resolves
21
+ * `node:fs`/`node:fs/promises` via `process.getBuiltinModule` instead of a
22
+ * static import. Both return `undefined` outside Bun/Node, so a browser caller
23
+ * that supplies explicit `server`/`token` never touches either.
24
+ *
17
25
  * @module connection
18
26
  */
19
27
  /**
@@ -1,5 +1,4 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { mkdir, rm } from "node:fs/promises";
1
+ import { isBunRuntime, readEnvironmentVariable, tryLoadNodeBuiltin } from "./runtime/portable.js";
3
2
  export const DEFAULT_WEFT_ADDRESS = "http://localhost:7233";
4
3
 
5
4
  export class ConnectionConfigurationError extends Error {
@@ -9,7 +8,7 @@ export class ConnectionConfigurationError extends Error {
9
8
  }
10
9
  }
11
10
  export function resolveConnection(options = {}) {
12
- const context = resolveConnectionContext(options), server = resolveServerString(context), fallbackProfile = profileForToken(context, server), token = resolveToken(options.token ?? Bun.env.WEFT_TOKEN, fallbackProfile);
11
+ const context = resolveConnectionContext(options), server = resolveServerString(context), fallbackProfile = profileForToken(context, server), token = resolveToken(options.token ?? readEnvironmentVariable("WEFT_TOKEN"), fallbackProfile);
13
12
  return {
14
13
  server: parseServerUrl(server),
15
14
  ...token === void 0 ? {} : { token }
@@ -26,7 +25,7 @@ function parseServerUrl(server) {
26
25
  function profileForToken(context, resolvedServer) {
27
26
  if (context.profile === void 0)
28
27
  return;
29
- if (!(context.options.server !== void 0 || Bun.env.WEFT_ADDR !== void 0))
28
+ if (!(context.options.server !== void 0 || readEnvironmentVariable("WEFT_ADDR") !== void 0))
30
29
  return context.profile;
31
30
  const profileServer = context.profile.server;
32
31
  if (profileServer === void 0)
@@ -44,7 +43,7 @@ function sameDestination(a, b) {
44
43
  return left.origin === right.origin && left.pathname.replace(/\/+$/, "") === right.pathname.replace(/\/+$/, "");
45
44
  }
46
45
  function resolveConnectionContext(options) {
47
- const configuration = readWeftConfiguration(), profileName = options.profile ?? Bun.env.WEFT_PROFILE ?? configuration.defaultProfile, profile = profileName === void 0 ? void 0 : configuration.profiles?.[profileName], runLockfile = options.includeRunLockfile === !1 ? void 0 : readRunLockfile();
46
+ const configuration = readWeftConfiguration(), profileName = options.profile ?? readEnvironmentVariable("WEFT_PROFILE") ?? configuration.defaultProfile, profile = profileName === void 0 ? void 0 : configuration.profiles?.[profileName], runLockfile = options.includeRunLockfile === !1 ? void 0 : readRunLockfile();
48
47
  return {
49
48
  options,
50
49
  ...profile === void 0 ? {} : { profile },
@@ -52,12 +51,26 @@ function resolveConnectionContext(options) {
52
51
  };
53
52
  }
54
53
  function resolveServerString(context) {
55
- return context.options.server ?? Bun.env.WEFT_ADDR ?? context.profile?.server ?? context.runLockfile?.server ?? context.runLockfile?.url ?? DEFAULT_WEFT_ADDRESS;
54
+ return context.options.server ?? readEnvironmentVariable("WEFT_ADDR") ?? context.profile?.server ?? context.runLockfile?.server ?? context.runLockfile?.url ?? DEFAULT_WEFT_ADDRESS;
55
+ }
56
+ function loadFsModule() {
57
+ return tryLoadNodeBuiltin("node:fs");
58
+ }
59
+ function loadFsPromisesModule() {
60
+ return tryLoadNodeBuiltin("node:fs/promises");
56
61
  }
57
62
  export async function writeRunLockfile(server) {
58
- await mkdir(weftHome(), { recursive: !0 });
59
- await Bun.write(runLockfilePath(), `${JSON.stringify({ server }, null, 2)}
60
- `);
63
+ const fsPromises = loadFsPromisesModule();
64
+ if (fsPromises === void 0)
65
+ throw Error("writeRunLockfile requires Bun or Node 22.5+ (process.getBuiltinModule); not available in this runtime.");
66
+ await fsPromises.mkdir(weftHome(), { recursive: !0 });
67
+ const contents = `${JSON.stringify({ server }, null, 2)}
68
+ `;
69
+ if (isBunRuntime()) {
70
+ await Bun.write(runLockfilePath(), contents);
71
+ return;
72
+ }
73
+ await fsPromises.writeFile(runLockfilePath(), contents, "utf8");
61
74
  }
62
75
  export async function removeRunLockfile(server) {
63
76
  const lockfile = readRunLockfile();
@@ -65,15 +78,23 @@ export async function removeRunLockfile(server) {
65
78
  return;
66
79
  if ((lockfile.server ?? lockfile.url) !== server)
67
80
  return;
68
- await rm(runLockfilePath(), { force: !0 });
81
+ const fsPromises = loadFsPromisesModule();
82
+ if (fsPromises === void 0)
83
+ return;
84
+ await fsPromises.rm(runLockfilePath(), { force: !0 });
69
85
  }
70
86
  function readWeftConfiguration() {
87
+ if (!isBunRuntime())
88
+ return {};
89
+ const fs = loadFsModule();
90
+ if (fs === void 0)
91
+ return {};
71
92
  const path = configurationPath();
72
- if (!existsSync(path))
93
+ if (!fs.existsSync(path))
73
94
  return {};
74
95
  let parsed;
75
96
  try {
76
- parsed = Bun.TOML.parse(readFileSync(path, "utf8"));
97
+ parsed = Bun.TOML.parse(fs.readFileSync(path, "utf8"));
77
98
  } catch (error) {
78
99
  const message = error instanceof Error ? error.message : String(error);
79
100
  throw new ConnectionConfigurationError(`Failed to read connection configuration at ${path}: ${message}`);
@@ -81,10 +102,13 @@ function readWeftConfiguration() {
81
102
  return normalizeConfiguration(parsed);
82
103
  }
83
104
  function readRunLockfile() {
105
+ const fs = loadFsModule();
106
+ if (fs === void 0)
107
+ return;
84
108
  const path = runLockfilePath();
85
- if (!existsSync(path))
109
+ if (!fs.existsSync(path))
86
110
  return;
87
- const text = readFileSync(path, "utf8").trim();
111
+ const text = fs.readFileSync(path, "utf8").trim();
88
112
  if (text === "")
89
113
  return;
90
114
  try {
@@ -137,11 +161,11 @@ function normalizeRunLockfile(value) {
137
161
  function resolveToken(token, profile) {
138
162
  const directToken = token ?? profile?.token;
139
163
  if (directToken?.startsWith("env:"))
140
- return Bun.env[directToken.slice(4)];
164
+ return readEnvironmentVariable(directToken.slice(4));
141
165
  if (directToken !== void 0)
142
166
  return directToken;
143
167
  if (profile?.tokenEnv !== void 0)
144
- return Bun.env[profile.tokenEnv];
168
+ return readEnvironmentVariable(profile.tokenEnv);
145
169
  return;
146
170
  }
147
171
  function configurationPath() {
@@ -151,7 +175,7 @@ function runLockfilePath() {
151
175
  return `${weftHome()}/run`;
152
176
  }
153
177
  function weftHome() {
154
- return Bun.env.WEFT_HOME ?? `${Bun.env.HOME ?? "."}/.weft`;
178
+ return readEnvironmentVariable("WEFT_HOME") ?? `${readEnvironmentVariable("HOME") ?? "."}/.weft`;
155
179
  }
156
180
  function stringValue(value) {
157
181
  return typeof value === "string" && value !== "" ? value : void 0;
@@ -77,7 +77,8 @@ export function createLifecycleCallbacks(engine) {
77
77
  enforceHistoryCircuitBreaker: (workflowId) => terminateWorkflow(getInternals(engine), workflowId, "timed-out", createTerminationCallbacks(engine), HISTORY_CIRCUIT_BREAKER_REASON),
78
78
  failWorkflowForUnavailableServices: (workflowId, error) => failWorkflow(getInternals(engine), workflowId, error, createTerminationCallbacks(engine), "system"),
79
79
  failWorkflowForRecoveryHook: (workflowId, error) => failWorkflow(getInternals(engine), workflowId, error, createTerminationCallbacks(engine), "system"),
80
- failWorkflowForCheckpointDecodeError: (workflowId, error) => failWorkflow(getInternals(engine), workflowId, error, createTerminationCallbacks(engine), "system")
80
+ failWorkflowForCheckpointDecodeError: (workflowId, error) => failWorkflow(getInternals(engine), workflowId, error, createTerminationCallbacks(engine), "system"),
81
+ failWorkflowForVersionMismatch: (workflowId, error) => failWorkflow(getInternals(engine), workflowId, error, createTerminationCallbacks(engine), "system")
81
82
  };
82
83
  }
83
84
  export function createTerminationCallbacksWith(engine, handleScheduledWorkflowTerminal) {
@@ -371,6 +371,12 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
371
371
  * deploys or explicit operator storage repair.
372
372
  * When set, unknown workflow types are skipped and reported through
373
373
  * {@link WorkflowRecoverySkippedEvent}.
374
+ *
375
+ * A recovered workflow whose persisted version metadata no longer matches
376
+ * its registered `WorkflowDefinition.version` is isolated per
377
+ * {@link RecoverAllOptions.versionMismatchPolicy} (default `'fail-run'`):
378
+ * only that workflow fails, and `recoverAll()` continues recovering its
379
+ * siblings in the same call.
374
380
  */
375
381
  recoverAll(options?: RecoverAllOptions): Promise<WorkflowHandle[]>;
376
382
  /**
@@ -490,3 +496,40 @@ export declare class Engine<TWorkflows extends object = DefaultWorkflowRegistry,
490
496
  */
491
497
  fireTimer(entry: TimerEntry): Promise<void>;
492
498
  }
499
+ /**
500
+ * `Engine` with its two chained-builder registration methods removed —
501
+ * `register` and `registerWorkflows`, whose return type is itself
502
+ * `Engine<NarrowedRegistry>` (see `register()`'s JSDoc: registering returns
503
+ * "this same engine with the definition added to its phantom type
504
+ * registry"). That self-reference is what makes `Engine` invariant in its
505
+ * registry generics: a concretely narrowed `Engine<Concrete>` (e.g. from
506
+ * `Engine.create({ workflows })`) is not structurally assignable to the
507
+ * plain default `Engine<DefaultWorkflowRegistry>`, even though every other
508
+ * member — including `start` and `startOrSignal`, whose *parameter* types
509
+ * (not return types) reference `TWorkflows` but never produce another
510
+ * `Engine<T>` — is not part of that recursive comparison.
511
+ *
512
+ * `start` and `startOrSignal` are deliberately KEPT (not omitted): hosted
513
+ * transports genuinely call them at runtime (REST/JSON-RPC workflow starts,
514
+ * MCP tool invocations) via `runtimeWorkflowEngine()`'s registry-erased
515
+ * dynamic-name overload, so a value satisfying this type must still provide
516
+ * them — a duck-typed engine substitute lacking `start` correctly fails to
517
+ * satisfy `RegistryAgnosticEngine` (see this type's `.test-d.ts` coverage).
518
+ *
519
+ * Host-facing options that accept an already-constructed `Engine` without
520
+ * needing the two chained-builder registration methods — `serve({ engine })`,
521
+ * the Service Worker helpers, the MCP session/HTTP/stdio surfaces — use this
522
+ * type instead of the bare default `Engine`, so both `new Engine({ storage })`
523
+ * and `Engine.create({ workflows })` are accepted without a call-site cast.
524
+ * See #708.
525
+ *
526
+ * (`Engine<object, object>` looks like the obvious fix — the widest legal
527
+ * instantiation of the registry generics — but TypeScript's structural check
528
+ * on `register()`'s self-referential return type does not reliably resolve
529
+ * that relationship: it can pass or fail for the identical `Engine<A>` /
530
+ * `Engine<B>` pair depending on unrelated compilation context, such as other
531
+ * files in the same program. Removing the registry-typed members entirely,
532
+ * rather than widening their generic arguments, avoids the recursive
533
+ * comparison altogether.)
534
+ */
535
+ export type RegistryAgnosticEngine = Omit<Engine, 'register' | 'registerWorkflows'>;
@@ -57,6 +57,22 @@ export type RecoverAllOptions = {
57
57
  * recovery continues with its siblings.
58
58
  */
59
59
  onRecoveredWorkflow?: (info: RecoveredWorkflowInfo) => void | Promise<void>;
60
+ /**
61
+ * Policy for a recovered workflow whose stored {@link WorkflowVersionTuple}
62
+ * (or legacy checkpoint `version`) no longer matches the registered
63
+ * {@link WorkflowDefinition.version}. The mismatch is detected before
64
+ * `resolveWorkflowServices` and `onRecoveredWorkflow` run for that workflow,
65
+ * so a mismatched run never re-provides services or invokes the hook.
66
+ *
67
+ * - `'fail-run'` (default): fail only the mismatched run to a terminal
68
+ * `failed` state with a `system` failure category carrying the
69
+ * {@link VersionMismatchError} message, then continue recovering its
70
+ * siblings. The run never advances user workflow code.
71
+ * - `'throw'`: preserve the pre-#702 behavior — rethrow the
72
+ * {@link VersionMismatchError} out of `recoverAll()` immediately, aborting
73
+ * recovery for every workflow not yet processed in this batch.
74
+ */
75
+ versionMismatchPolicy?: 'fail-run' | 'throw';
60
76
  };
61
77
  export type LifecycleCallbacks = {
62
78
  dispatchEvent: (event: Event) => void;
@@ -97,6 +113,15 @@ export type LifecycleCallbacks = {
97
113
  * other workflows from the same storage backend.
98
114
  */
99
115
  failWorkflowForCheckpointDecodeError: (workflowId: string, error: Error) => Promise<void>;
116
+ /**
117
+ * Force a recovered workflow to a terminal `failed` state because its
118
+ * persisted version metadata no longer matches the registered
119
+ * `WorkflowDefinition.version` (a {@link VersionMismatchError}). Fails just
120
+ * this run with a `system` failure category so `recoverAll()` can continue
121
+ * recovering other workflows under the default `'fail-run'`
122
+ * {@link RecoverAllOptions.versionMismatchPolicy}.
123
+ */
124
+ failWorkflowForVersionMismatch: (workflowId: string, error: Error) => Promise<void>;
100
125
  };
101
126
  /**
102
127
  * Pre-replay history circuit breaker. Called at every restore-from-checkpoint
@@ -4,6 +4,7 @@ import { RegExpExtensionDecodeError } from "../../codec/extension-codec.js";
4
4
  import { Context, setContextWorkflowInterceptor } from "../../context.js";
5
5
  import { EMPTY_EVENT_HEAD } from "../../event-log.js";
6
6
  import { WorkflowRecoverySkippedEvent, WorkflowStartedEvent } from "../../events.js";
7
+ import { VersionMismatchError } from "../../versioning.js";
7
8
  import { createCancelHandlerRegistration, resetCancelHandlers } from "../cancel-handlers.js";
8
9
  import { forgetCommittedCheckpointBytes } from "../checkpoint-commit-snapshots.js";
9
10
  import { hydrateCheckpointReplayState } from "../checkpoint-replay.js";
@@ -60,6 +61,21 @@ async function preflightRecoverAll(internals, callbacks) {
60
61
  }
61
62
  return result;
62
63
  }
64
+ async function recoverEntryOrIsolateFailure(internals, workflowId, callbacks, options) {
65
+ try {
66
+ return await resume(internals, workflowId, callbacks, options?.onRecoveredWorkflow);
67
+ } catch (error) {
68
+ if (error instanceof RegExpExtensionDecodeError) {
69
+ await callbacks.failWorkflowForCheckpointDecodeError(workflowId, error);
70
+ return null;
71
+ }
72
+ if (error instanceof VersionMismatchError && options?.versionMismatchPolicy !== "throw") {
73
+ await callbacks.failWorkflowForVersionMismatch(workflowId, error);
74
+ return null;
75
+ }
76
+ throw error;
77
+ }
78
+ }
63
79
  export async function recoverAll(internals, callbacks, options) {
64
80
  const preflight = await preflightRecoverAll(internals, callbacks), handles = [];
65
81
  if (preflight.missingWorkflows.length > 0 && options?.acknowledgeUnknownWorkflowTypes !== !0)
@@ -76,15 +92,9 @@ export async function recoverAll(internals, callbacks, options) {
76
92
  callbacks.dispatchEvent(new WorkflowRecoverySkippedEvent(entry.workflow.workflowId, entry.workflow.type, "type-not-registered"));
77
93
  continue;
78
94
  }
79
- try {
80
- handles.push(await resume(internals, entry.workflowId, callbacks, options?.onRecoveredWorkflow));
81
- } catch (error) {
82
- if (error instanceof RegExpExtensionDecodeError) {
83
- await callbacks.failWorkflowForCheckpointDecodeError(entry.workflowId, error);
84
- continue;
85
- }
86
- throw error;
87
- }
95
+ const handle = await recoverEntryOrIsolateFailure(internals, entry.workflowId, callbacks, options);
96
+ if (handle !== null)
97
+ handles.push(handle);
88
98
  }
89
99
  return handles;
90
100
  }
@@ -1,4 +1,15 @@
1
1
  import type { DefinitionSchema } from './definition-schema.ts';
2
+ /**
3
+ * Reset the module-level cached Valibot `toJsonSchema` converter.
4
+ *
5
+ * `cachedValibotConverter` is a process-global singleton shared by every test
6
+ * file that imports this module in the same `bun test` run, so whether it is
7
+ * already warm when a given test executes depends on unrelated test order.
8
+ * Tests that need to exercise the cache-miss path deterministically (e.g. the
9
+ * "no `process.getBuiltinModule`" error) must reset it first.
10
+ * @internal Test-only.
11
+ */
12
+ export declare function resetValibotConverterCacheForTesting(): void;
2
13
  /**
3
14
  * Direction parameter for {@link definitionSchemaToJsonSchema}. `"input"`
4
15
  * produces the JSON Schema describing the validator's accepted input;
@@ -1,6 +1,9 @@
1
- import { createRequire } from "node:module";
2
1
  import { z } from "zod";
2
+ import { tryLoadNodeBuiltin } from "../../runtime/portable.js";
3
3
  let cachedValibotConverter;
4
+ export function resetValibotConverterCacheForTesting() {
5
+ cachedValibotConverter = void 0;
6
+ }
4
7
  export function definitionSchemaToJsonSchema(schema, direction = "input") {
5
8
  const standard = schema["~standard"], vendor = standard.vendor;
6
9
  if (vendor === "zod")
@@ -28,10 +31,16 @@ function convertValibot(schema) {
28
31
  const result = loadValibotConverter()(schema);
29
32
  return stripDialect(requirePlainObject(result, "valibot"));
30
33
  }
34
+ function loadNodeRequire() {
35
+ return tryLoadNodeBuiltin("node:module")?.createRequire(import.meta.url);
36
+ }
31
37
  export function loadValibotConverter(requireModule) {
32
- const shouldUseCache = requireModule === void 0, resolver = requireModule ?? createRequire(import.meta.url);
38
+ const shouldUseCache = requireModule === void 0;
33
39
  if (shouldUseCache && cachedValibotConverter !== void 0)
34
40
  return cachedValibotConverter;
41
+ const resolver = requireModule ?? loadNodeRequire();
42
+ if (resolver === void 0)
43
+ throw Error("definitionSchemaToJsonSchema: converting a Valibot schema requires Bun or Node 22.5+ (process.getBuiltinModule) to resolve `@valibot/to-json-schema`. Not available in browser or edge runtimes; attach a `~standard.jsonSchema` converter to the schema instead.");
35
44
  let valibotModule;
36
45
  try {
37
46
  valibotModule = resolver("@valibot/to-json-schema");
package/dist/http.js ADDED
@@ -0,0 +1,2 @@
1
+ function L(F){let G=[];for(let J=0;J<F.length;J+=512)G.push(String.fromCharCode(...F.subarray(J,J+512)));return btoa(G.join(""))}function A(F){let G=atob(F),J=new Uint8Array(G.length);for(let Q=0;Q<G.length;Q+=1)J[Q]=G.charCodeAt(Q);return J}function U(F){return typeof F==="object"&&F!==null&&!Array.isArray(F)}async function j(F,G){return await F.get(G)!==null}async function*X(F,G,J){for await(let[Q]of F.scan(G,J))yield Q}async function _(F,G){let J=0;for await(let Q of X(F,G))J++;return J}async function H(F,G){let J=[];for await(let Q of X(F,G))J.push({type:"delete",key:Q});if(J.length===0)return 0;return await F.batch(J),J.length}async function W(F,G,J){let Q=[];for await(let Z of X(F,G,J))Q.push({type:"delete",key:Z});if(Q.length===0)return 0;return await F.batch(Q),Q.length}function B(F,G,J){if(!F.capabilities()[G])throw Error(`Feature "${J}" requires storage capability "${G}", but this storage backend does not provide it.`)}var N=1e4;class R extends Error{code="StorageBatchOperationLimitExceededError";cap=N;count;target;constructor(F,G){super(`${F} count ${G} exceeds MAX_BATCH_OPERATIONS (${N}).`);this.name="StorageBatchOperationLimitExceededError",this.target=F,this.count=G}}function M(F,G){if(G>N)throw new R(F,G)}function C(F){return F.length>0?F.slice(0,-1)+String.fromCharCode(F.charCodeAt(F.length-1)+1):"ÿ"}function o(F,G={}){if(G.gt!==void 0&&F<=G.gt)return!1;if(G.gte!==void 0&&F<G.gte)return!1;if(G.lt!==void 0&&F>=G.lt)return!1;if(G.lte!==void 0&&F>G.lte)return!1;return!0}function e(F,G){if(F===null||G===null)return F===G;if(F.byteLength!==G.byteLength)return!1;for(let J=0;J<F.byteLength;J++)if(F[J]!==G[J])return!1;return!0}async function P(F,G){if(F.has)return F.has(G);return j(F,G)}function K(F,G,J){if(F.keys)return F.keys(G,J);return X(F,G,J)}async function S(F,G){if(F.count)return F.count(G);return _(F,G)}async function O(F,G){if(F.deletePrefix)return F.deletePrefix(G);return H(F,G)}async function b(F,G,J){if(M("conditionalBatch conditions",G.length),M("conditionalBatch operations",J.length),B(F,"conditionalBatch","storageConditionalBatch"),!F.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return F.conditionalBatch(G,J)}function c(F){if(F===void 0)return;if(typeof F!=="number"||!Number.isInteger(F)||F<0)throw Error("deleteRange limit must be a finite non-negative integer");return F===0?0:F}function V(F){let G={},J=!1;for(let Z of["gt","gte","lt","lte"]){let $=F[Z];if($===void 0)continue;if(typeof $!=="string")throw Error("deleteRange bounds must be strings");G[Z]=$,J=!0}if(!J)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let Q=c(F.limit);if(Q!==void 0)G.limit=Q;return G}async function v(F,G,J){let Q=V(J);if(F.deleteRange)return F.deleteRange(G,Q);return W(F,G,Q)}function I(F,G){let J={key:F,open:!1};if(G.gte!==void 0&&G.gte>J.key)J.key=G.gte,J.open=!1;if(G.gt!==void 0&&G.gt>=J.key)J.key=G.gt,J.open=!0;return J}function g(F,G){let J={key:C(F),open:!0};if(G.lt!==void 0&&G.lt<=J.key)J.key=G.lt,J.open=!0;if(G.lte!==void 0&&G.lte<J.key)J.key=G.lte,J.open=!1;return J}function QF(F,G){let J=I(F,G),Q=g(F,G);if(J.key>Q.key||J.key===Q.key&&(J.open||Q.open))return null;return{lower:J,upper:Q}}function q(F){return F.replaceAll(/:+$/g,"")}function y(F,G){let J=q(F),Q=q(G);if(J.length===0)return Q;if(Q.length===0)return J;return`${J}:${Q}`}class D{#G;#J;constructor(F,G){this.#G=F,this.#J=q(G)}#F(F){if(this.#J.length===0)return F;return F.length===0?`${this.#J}:`:`${this.#J}:${F}`}#Q(F){if(this.#J.length===0)return F;return F.slice(this.#J.length+1)}#Z(F={}){let G={};if(F.limit!==void 0)G.limit=F.limit;if(F.reverse!==void 0)G.reverse=F.reverse;if(F.gt!==void 0)G.gt=this.#F(F.gt);if(F.gte!==void 0)G.gte=this.#F(F.gte);if(F.lt!==void 0)G.lt=this.#F(F.lt);if(F.lte!==void 0)G.lte=this.#F(F.lte);return G}#M(F){let G={};if(F.limit!==void 0)G.limit=F.limit;if(F.gt!==void 0)G.gt=this.#F(F.gt);if(F.gte!==void 0)G.gte=this.#F(F.gte);if(F.lt!==void 0)G.lt=this.#F(F.lt);if(F.lte!==void 0)G.lte=this.#F(F.lte);return G}capabilities(){return this.#G.capabilities()}scoped(F){return new D(this.#G,y(this.#J,F))}async get(F){return this.#G.get(this.#F(F))}async put(F,G){await this.#G.put(this.#F(F),G)}async delete(F){await this.#G.delete(this.#F(F))}async*scan(F,G){for await(let[J,Q]of this.#G.scan(this.#F(F),this.#Z(G)))yield[this.#Q(J),Q]}async batch(F){M("batch operations",F.length),await this.#G.batch(F.map((G)=>{if(G.type==="put")return{type:"put",key:this.#F(G.key),value:G.value};return{type:"delete",key:this.#F(G.key)}}))}async conditionalBatch(F,G){return b(this.#G,F.map((J)=>({key:this.#F(J.key),expectedValue:J.expectedValue})),G.map((J)=>{if(J.type==="put")return{type:"put",key:this.#F(J.key),value:J.value};return{type:"delete",key:this.#F(J.key)}}))}async has(F){return P(this.#G,this.#F(F))}async deletePrefix(F){return O(this.#G,this.#F(F))}async deleteRange(F,G){let J=this.#M(V(G));return v(this.#G,this.#F(F),J)}async*keys(F,G){for await(let J of K(this.#G,this.#F(F),this.#Z(G)))yield this.#Q(J)}async count(F){return S(this.#G,this.#F(F))}[Symbol.dispose](){this.#G[Symbol.dispose]()}}function h(F,G){return new D(F,G)}var u=67108864;function m(F){if(F.type==="put")return{type:"put",key:F.key,value:L(F.value)};return{type:"delete",key:F.key}}function k(F){return{key:F.key,expectedValue:F.expectedValue===null?null:L(F.expectedValue)}}function w(F){if(!U(F)||typeof F.key!=="string"||typeof F.value!=="string")throw Error("HTTPStorage scan response contained an invalid NDJSON entry.");return{key:F.key,value:F.value}}function f(F){if(!U(F)||typeof F.applied!=="boolean")throw Error('HTTPStorage conditional batch response must include a boolean "applied" field.');return F.applied}function Y(F,G,J){if(J!==void 0)F.searchParams.set(G,String(J))}function l(F){if(F.trim().length===0)return null;let G=w(JSON.parse(F));return[G.key,A(G.value)]}function d(F){if(F>u)throw Error("HTTPStorage scan response exceeded the maximum allowed size.")}async function*p(F){if(F.body===null)return;let G=F.body.getReader(),J=new TextDecoder,Q="",Z=0,$=!1;try{while(!0){let{done:E,value:T}=await G.read();if(E){$=!0;break}Z+=T.byteLength,d(Z),Q+=J.decode(T,{stream:!0});let z=Q.split(`
2
+ `);Q=z.pop()??"";for(let x of z)yield x}if(Q+=J.decode(),Q.length>0)yield Q}finally{try{if(!$)await G.cancel()}catch{}G.releaseLock()}}class a{#G;#J;#F;constructor(F){this.#G=F.baseUrl instanceof URL?F.baseUrl:new URL(F.baseUrl),this.#J={...F.headers},this.#F=F.remoteConditionalBatch??!1}capabilities(){return{persistence:"remote",readAfterWrite:"eventual",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:this.#F,boundedRangeDelete:!1}}#Q(F){let G=this.#G.href.endsWith("/")?this.#G.href:`${this.#G.href}/`;return new URL(F.replace(/^\/+/,""),G)}#Z(F){return this.#Q(`/v1/storage/${encodeURIComponent(F)}`)}#M(F,G){let J=this.#Q("/v1/storage");return J.searchParams.set("prefix",F),Y(J,"limit",G.limit),Y(J,"reverse",G.reverse),Y(J,"gt",G.gt),Y(J,"gte",G.gte),Y(J,"lt",G.lt),Y(J,"lte",G.lte),J}async#$(F,G={},J=[]){let Q=new Headers(this.#J);for(let[$,E]of new Headers(G.headers).entries())Q.set($,E);let Z=await fetch(F,{...G,headers:Q});if(!Z.ok&&!J.includes(Z.status))throw Error(`HTTPStorage request failed: ${G.method??"GET"} ${F.pathname} returned ${String(Z.status)}.`);return Z}async get(F){let G=await this.#$(this.#Z(F),{method:"GET"},[404]);if(G.status===404)return null;return new Uint8Array(await G.arrayBuffer())}async put(F,G){await this.#$(this.#Z(F),{method:"PUT",headers:{"content-type":"application/octet-stream"},body:new Blob([G])})}async delete(F){await this.#$(this.#Z(F),{method:"DELETE"})}async*scan(F,G={}){let J=await this.#$(this.#M(F,G),{method:"GET",headers:{accept:"application/x-ndjson"}});for await(let Q of p(J)){let Z=l(Q);if(Z!==null)yield Z}}async batch(F){M("batch operations",F.length),await this.#$(this.#Q("/v1/storage/-/batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({operations:F.map(m)})})}async conditionalBatch(F,G){M("conditionalBatch conditions",F.length),M("conditionalBatch operations",G.length);let J=await this.#$(this.#Q("/v1/storage/-/conditional-batch"),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({conditions:F.map(k),operations:G.map(m)})});return f(await J.json())}has(F){return j(this,F)}async*keys(F,G){yield*X(this,F,G)}count(F){return _(this,F)}deletePrefix(F){return H(this,F)}deleteRange(F,G){return W(this,F,V(G))}scoped(F){return h(this,F)}[Symbol.dispose](){}}export{a as HTTPStorage};
@@ -0,0 +1 @@
1
+ async function q(G,J){return await G.get(J)!==null}async function*T(G,J,Q){for await(let[X]of G.scan(J,Q))yield X}async function O(G,J){let Q=0;for await(let X of T(G,J))Q++;return Q}async function h(G,J){let Q=[];for await(let X of T(G,J))Q.push({type:"delete",key:X});if(Q.length===0)return 0;return await G.batch(Q),Q.length}async function R(G,J,Q){let X=[];for await(let Y of T(G,J,Q))X.push({type:"delete",key:Y});if(X.length===0)return 0;return await G.batch(X),X.length}function E(G,J,Q){if(!G.capabilities()[J])throw Error(`Feature "${Q}" requires storage capability "${J}", but this storage backend does not provide it.`)}var A=1e4;class v extends Error{code="StorageBatchOperationLimitExceededError";cap=A;count;target;constructor(G,J){super(`${G} count ${J} exceeds MAX_BATCH_OPERATIONS (${A}).`);this.name="StorageBatchOperationLimitExceededError",this.target=G,this.count=J}}function M(G,J){if(J>A)throw new v(G,J)}function j(G){return G.length>0?G.slice(0,-1)+String.fromCharCode(G.charCodeAt(G.length-1)+1):"ÿ"}function S(G,J={}){if(J.gt!==void 0&&G<=J.gt)return!1;if(J.gte!==void 0&&G<J.gte)return!1;if(J.lt!==void 0&&G>=J.lt)return!1;if(J.lte!==void 0&&G>J.lte)return!1;return!0}function b(G,J){if(G===null||J===null)return G===J;if(G.byteLength!==J.byteLength)return!1;for(let Q=0;Q<G.byteLength;Q++)if(G[Q]!==J[Q])return!1;return!0}async function m(G,J){if(G.has)return G.has(J);return q(G,J)}function x(G,J,Q){if(G.keys)return G.keys(J,Q);return T(G,J,Q)}async function y(G,J){if(G.count)return G.count(J);return O(G,J)}async function I(G,J){if(G.deletePrefix)return G.deletePrefix(J);return h(G,J)}async function g(G,J,Q){if(M("conditionalBatch conditions",J.length),M("conditionalBatch operations",Q.length),E(G,"conditionalBatch","storageConditionalBatch"),!G.conditionalBatch)throw Error("This storage backend reports conditionalBatch capability but does not implement the conditionalBatch() method.");return G.conditionalBatch(J,Q)}function l(G){if(G===void 0)return;if(typeof G!=="number"||!Number.isInteger(G)||G<0)throw Error("deleteRange limit must be a finite non-negative integer");return G===0?0:G}function N(G){let J={},Q=!1;for(let Y of["gt","gte","lt","lte"]){let $=G[Y];if($===void 0)continue;if(typeof $!=="string")throw Error("deleteRange bounds must be strings");J[Y]=$,Q=!0}if(!Q)throw Error("deleteRange requires at least one of gt/gte/lt/lte; use deletePrefix to delete a whole prefix");let X=l(G.limit);if(X!==void 0)J.limit=X;return J}async function k(G,J,Q){let X=N(Q);if(G.deleteRange)return G.deleteRange(J,X);return R(G,J,X)}function d(G,J){let Q={key:G,open:!1};if(J.gte!==void 0&&J.gte>Q.key)Q.key=J.gte,Q.open=!1;if(J.gt!==void 0&&J.gt>=Q.key)Q.key=J.gt,Q.open=!0;return Q}function p(G,J){let Q={key:j(G),open:!0};if(J.lt!==void 0&&J.lt<=Q.key)Q.key=J.lt,Q.open=!0;if(J.lte!==void 0&&J.lte<Q.key)Q.key=J.lte,Q.open=!1;return Q}function c(G,J){let Q=d(G,J),X=p(G,J);if(Q.key>X.key||Q.key===X.key&&(Q.open||X.open))return null;return{lower:Q,upper:X}}function P(G){return G.replaceAll(/:+$/g,"")}function a(G,J){let Q=P(G),X=P(J);if(Q.length===0)return X;if(X.length===0)return Q;return`${Q}:${X}`}class C{#J;#Q;constructor(G,J){this.#J=G,this.#Q=P(J)}#G(G){if(this.#Q.length===0)return G;return G.length===0?`${this.#Q}:`:`${this.#Q}:${G}`}#X(G){if(this.#Q.length===0)return G;return G.slice(this.#Q.length+1)}#Y(G={}){let J={};if(G.limit!==void 0)J.limit=G.limit;if(G.reverse!==void 0)J.reverse=G.reverse;if(G.gt!==void 0)J.gt=this.#G(G.gt);if(G.gte!==void 0)J.gte=this.#G(G.gte);if(G.lt!==void 0)J.lt=this.#G(G.lt);if(G.lte!==void 0)J.lte=this.#G(G.lte);return J}#Z(G){let J={};if(G.limit!==void 0)J.limit=G.limit;if(G.gt!==void 0)J.gt=this.#G(G.gt);if(G.gte!==void 0)J.gte=this.#G(G.gte);if(G.lt!==void 0)J.lt=this.#G(G.lt);if(G.lte!==void 0)J.lte=this.#G(G.lte);return J}capabilities(){return this.#J.capabilities()}scoped(G){return new C(this.#J,a(this.#Q,G))}async get(G){return this.#J.get(this.#G(G))}async put(G,J){await this.#J.put(this.#G(G),J)}async delete(G){await this.#J.delete(this.#G(G))}async*scan(G,J){for await(let[Q,X]of this.#J.scan(this.#G(G),this.#Y(J)))yield[this.#X(Q),X]}async batch(G){M("batch operations",G.length),await this.#J.batch(G.map((J)=>{if(J.type==="put")return{type:"put",key:this.#G(J.key),value:J.value};return{type:"delete",key:this.#G(J.key)}}))}async conditionalBatch(G,J){return g(this.#J,G.map((Q)=>({key:this.#G(Q.key),expectedValue:Q.expectedValue})),J.map((Q)=>{if(Q.type==="put")return{type:"put",key:this.#G(Q.key),value:Q.value};return{type:"delete",key:this.#G(Q.key)}}))}async has(G){return m(this.#J,this.#G(G))}async deletePrefix(G){return I(this.#J,this.#G(G))}async deleteRange(G,J){let Q=this.#Z(N(J));return k(this.#J,this.#G(G),Q)}async*keys(G,J){for await(let Q of x(this.#J,this.#G(G),this.#Y(J)))yield this.#X(Q)}async count(G){return y(this.#J,this.#G(G))}[Symbol.dispose](){this.#J[Symbol.dispose]()}}function u(G,J){return new C(G,J)}var U="kv";function s(G=globalThis){let{indexedDB:J,IDBKeyRange:Q}=G;if(J===void 0||Q===void 0)throw Error("IndexedDBStorage requires both indexedDB and IDBKeyRange runtime globals.");return{indexedDB:J,IDBKeyRange:Q}}function K(G){return new Promise((J,Q)=>{G.onsuccess=()=>J(G.result),G.onerror=()=>Q(G.error)})}function w(G,J){let Q=null,X=null,Y=null,$=(Z,V)=>{let H=Z??Error(V);if(X){let L=X;Q=null,X=null,L(H);return}Y=H};return G.onsuccess=()=>{if(!Q)return;let Z=Q;Q=null,X=null,Z(G.result)},G.onerror=()=>{$(G.error,"IndexedDB cursor request failed.")},J.onerror=()=>{$(J.error,"IndexedDB transaction failed.")},J.onabort=()=>{$(J.error,"IndexedDB transaction aborted.")},()=>{return new Promise((Z,V)=>{if(Y){let H=Y;Y=null,V(H);return}if(Q=Z,X=V,G.readyState==="done"){let H=Q;Q=null,X=null,H?.(G.result)}})}}class n{#J;#Q=null;#G;#X;constructor(G="weft",J=s()){this.#J=G,this.#X=J,this.#G=this.#Y()}capabilities(){return{persistence:"local",readAfterWrite:"linearizable",scanConsistency:"best-effort",atomicBatch:!0,conditionalBatch:!0,boundedRangeDelete:!0}}#Y(){let G=this.#X.indexedDB.open(this.#J,1);return G.onupgradeneeded=()=>{let J=G.result;if(!J.objectStoreNames.contains(U))J.createObjectStore(U)},K(G).then((J)=>{return this.#Q=J,J})}async get(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U),Y=await K(X.get(G));return Y===void 0?null:new Uint8Array(Y)}async put(G,J){let Y=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(Y.put(J,G))}async delete(G){let X=(await this.#G).transaction(U,"readwrite").objectStore(U);await K(X.delete(G))}async has(G){let X=(await this.#G).transaction(U,"readonly").objectStore(U);return await K(X.count(G))>0}async deletePrefix(G){let J=await this.#G,Q=j(G),X=this.#X.IDBKeyRange.bound(G,Q,!1,!0);return new Promise((Y,$)=>{let Z=J.transaction(U,"readwrite"),V=Z.objectStore(U),H=0,L=V.count(X);L.onsuccess=()=>{H=L.result,V.delete(X)},Z.oncomplete=()=>Y(H),Z.onerror=()=>$(Z.error)})}async deleteRange(G,J){let Q=N(J),X=c(G,Q);if(X===null)return 0;let Y=this.#X.IDBKeyRange.bound(X.lower.key,X.upper.key,X.lower.open,X.upper.open),$=await this.#G,{limit:Z}=Q;return new Promise((V,H)=>{let L=$.transaction(U,"readwrite"),z=L.objectStore(U),B=0;if(Z===void 0){let F=z.count(Y);F.onsuccess=()=>{B=F.result,z.delete(Y)}}else{let F=z.openCursor(Y,"next");F.onsuccess=()=>{let D=F.result;if(D===null||B>=Z)return;if(D.delete(),B++,B<Z)D.continue()}}L.oncomplete=()=>V(B),L.onerror=()=>H(L.error)})}async*scan(G,J={}){let{limit:Q,reverse:X}=J,Y=await this.#G,$=j(G),Z=this.#X.IDBKeyRange.bound(G,$,!1,!0),V=X?"prev":"next",H=Y.transaction(U,"readonly"),z=H.objectStore(U).openCursor(Z,V),B=0,F=w(z,H),D=!1;try{let W=await F();while(W){if(Q!==void 0&&B>=Q)break;let _=W.key;if(S(_,J))yield[_,new Uint8Array(W.value)],B++;W.continue(),W=await F()}D=!0}finally{if(!D)try{H.abort()}catch{}}}async batch(G){if(M("batch operations",G.length),G.length===0)return;let J=await this.#G;return new Promise((Q,X)=>{let Y=J.transaction(U,"readwrite"),$=Y.objectStore(U);for(let Z of G)if(Z.type==="put")$.put(Z.value,Z.key);else $.delete(Z.key);Y.oncomplete=()=>Q(),Y.onerror=()=>X(Y.error)})}async conditionalBatch(G,J){M("conditionalBatch conditions",G.length),M("conditionalBatch operations",J.length);let Q=await this.#G;return new Promise((X,Y)=>{let $=Q.transaction(U,"readwrite"),Z=$.objectStore(U),V=!1,H=!1,L=(F,D)=>{if(V)return;V=!0,Y(F??Error(D))};$.oncomplete=()=>{if(V)return;V=!0,X(!0)},$.onerror=()=>{L($.error,"IndexedDB conditionalBatch transaction failed.")},$.onabort=()=>{if(V)return;if(V=!0,H){X(!1);return}Y($.error??Error("IndexedDB conditionalBatch transaction aborted."))};let z=()=>{for(let F of J)if(F.type==="put")Z.put(F.value,F.key);else Z.delete(F.key)},B=(F)=>{if(F>=G.length){z();return}let D=G[F],W=Z.get(D.key);W.onsuccess=()=>{let _=W.result,f=_===void 0?null:new Uint8Array(_);if(!b(f,D.expectedValue)){H=!0,$.abort();return}B(F+1)},W.onerror=()=>{L(W.error,"IndexedDB conditionalBatch condition check failed.")}};B(0)})}async*keys(G,J={}){let{limit:Q,reverse:X}=J,Y=await this.#G,$=j(G),Z=this.#X.IDBKeyRange.bound(G,$,!1,!0),V=X?"prev":"next",H=Y.transaction(U,"readonly"),z=H.objectStore(U).openKeyCursor(Z,V),B=0,F=w(z,H),D=!1;try{let W=await F();while(W){if(Q!==void 0&&B>=Q)break;let _=W.key;if(S(_,J))yield _,B++;W.continue(),W=await F()}D=!0}finally{if(!D)try{H.abort()}catch{}}}async count(G){let J=await this.#G,Q=j(G),Y=J.transaction(U,"readonly").objectStore(U);return K(Y.count(this.#X.IDBKeyRange.bound(G,Q,!1,!0)))}scoped(G){return u(this,G)}[Symbol.dispose](){if(this.#Q)this.#Q.close(),this.#Q=null}}export{n as IndexedDBStorage};
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- var b=Object.defineProperty;var w=(j)=>j;function F(j,q){this[j]=w.bind(null,q)}var I=(j,q)=>{for(var z in q)b(j,z,{get:q[z],enumerable:!0,configurable:!0,set:F.bind(q,z)})};var Z=(j,q)=>()=>(j&&(q=j(j=0)),q);var N=import.meta.require,V=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},k=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var U=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve(U).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};function x(j){return typeof j==="string"&&M.has(j)}var X,J,M;var $=Z(()=>{X=class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}};J={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,DurableActivityScopeError:!0,DurableActivityUnsupportedError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,WorkflowTeardownPendingError:!0,IdempotencyKeyPurgedError:!0},M=new Set(Object.keys(J))});async function B(j,q,z){let A=j["~standard"];if(!O(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(T)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
3
- `)}function O(j){return typeof j.validate==="function"}function T(j){return{message:j.message,path:y(j.path)}}function y(j){if(j===void 0||j.length===0)return"";let q="";for(let z of j)q+="/",q+=L(z);return q}function L(j){let q=R(j)?j.key:j;return _(String(q))}function R(j){return j!==null&&typeof j==="object"&&"key"in j}function _(j){return j.replace(/~/g,"~0").replace(/\//g,"~1")}function C(j,q,z){return`Validation failed for ${q===void 0?j:`${q} ${j}`}:
4
- ${Y(z)}`}var K;var D=Z(()=>{$();K=class K extends X{fieldName;operation;issues;constructor(j){super("StandardSchemaValidationError",C(j.fieldName,j.operation,j.issues));this.fieldName=j.fieldName,this.operation=j.operation,this.issues=j.issues}}});D();var S=Y,h=K,v=B;export{v as validateStandardSchema,S as formatStandardSchemaIssues,h as StandardSchemaValidationError};
2
+ var O=import.meta.require,T=(j,q,z)=>{if(q!=null){if(typeof q!=="object"&&typeof q!=="function")throw TypeError('Object expected to be assigned to "using" declaration');let A;if(z)A=q[Symbol.asyncDispose];if(A===void 0)A=q[Symbol.dispose];if(typeof A!=="function")throw TypeError("Object not disposable");j.push([z,A,q])}else if(z)j.push([z]);return q},y=(j,q,z)=>{let A=(G)=>q=z?new SuppressedError(G,q,"An error was suppressed during disposal"):(z=!0,G),H=(G)=>{while(G=j.pop())try{var $=G[1]&&G[1].call(G[2]);if(G[0])return Promise.resolve($).then(H,(Q)=>(A(Q),H()))}catch(Q){A(Q)}if(z)throw q};return H()};class X extends Error{code;constructor(j,q,z){super(q,z);this.code=j,this.name=j}}var B={WorkflowAlreadyExistsError:!0,BulkDeleteRequiresTerminalWorkflowsError:!0,BulkOperationConfirmationError:!0,WorkflowTypeNotRegisteredForRecoveryError:!0,EngineCreateNameMismatchError:!0,EngineDisposedError:!0,WorkflowNotFoundError:!0,WorkflowNotRegisteredError:!0,WorkflowConcurrencyLimitExceededError:!0,WorkflowSuspendNotSupportedError:!0,ActivityResolutionError:!0,BranchTopologyChangedError:!0,PersistedDataIncompatibleError:!0,WorkflowTimeoutError:!0,HttpClientError:!0,WorkerProtocolIncompatibleError:!0,UpdateTimeoutError:!0,UpdateValidationError:!0,WorkflowTerminalError:!0,WorkflowBuilderError:!0,VersionMismatchError:!0,EffectReplayConflictError:!0,ReviewTimeoutError:!0,AtomicStateConflictError:!0,StandardSchemaValidationError:!0,ActivityReconciliationCapabilityError:!0,ActivityReconciliationConflictError:!0,ActivityReconciliationIndeterminateError:!0,DurableActivityScopeError:!0,DurableActivityUnsupportedError:!0,AsyncActivityTokenNotFoundError:!0,ActivityScheduleToCloseTimeoutError:!0,ActivityPerAttemptTimeoutError:!0,PayloadSizeExceededError:!0,StartOrSignalConflictError:!0,WorkflowTeardownPendingError:!0,IdempotencyKeyPurgedError:!0},R=new Set(Object.keys(B));class K extends X{fieldName;operation;issues;constructor(j){super("StandardSchemaValidationError",M(j.fieldName,j.operation,j.issues));this.fieldName=j.fieldName,this.operation=j.operation,this.issues=j.issues}}async function Z(j,q,z){let A=j["~standard"];if(!D(A))throw TypeError(`Schema for ${z.fieldName} does not provide runtime validation. Attach a Standard Schema validator (Zod, Valibot, or another vendor) or supply a runtime-validating schema at this boundary.`);let H=await A.validate(q);if(H.issues===void 0)return H.value;throw new K({fieldName:z.fieldName,operation:z.operation,issues:H.issues.map(U)})}function Y(j){return j.map((q)=>q.path===""?q.message:`${q.path}: ${q.message}`).join(`
3
+ `)}function D(j){return typeof j.validate==="function"}function U(j){return{message:j.message,path:b(j.path)}}function b(j){if(j===void 0||j.length===0)return"";let q="";for(let z of j)q+="/",q+=w(z);return q}function w(j){let q=F(j)?j.key:j;return J(String(q))}function F(j){return j!==null&&typeof j==="object"&&"key"in j}function J(j){return j.replace(/~/g,"~0").replace(/\//g,"~1")}function M(j,q,z){return`Validation failed for ${q===void 0?j:`${q} ${j}`}:
4
+ ${Y(z)}`}var V=Y,k=K,P=Z;export{P as validateStandardSchema,V as formatStandardSchemaIssues,k as StandardSchemaValidationError};