@lostgradient/weft 0.13.0 → 0.15.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 (114) hide show
  1. package/README.md +3 -8
  2. package/dist/cli/generated/operation-catalog.snapshot.json +977 -77
  3. package/dist/cli/generated/operation-client.generated.d.ts +14 -21
  4. package/dist/cli/help-text.d.ts +3 -3
  5. package/dist/cli/help-text.js +4 -4
  6. package/dist/cli/operation-catalog-snapshot.d.ts +1 -1
  7. package/dist/cli/schedule.js +1 -3
  8. package/dist/cli/serve-registrations.d.ts +3 -3
  9. package/dist/cli/serve-registrations.js +2 -2
  10. package/dist/cli/shutdown.d.ts +11 -0
  11. package/dist/cli/shutdown.js +25 -0
  12. package/dist/cli/validate.d.ts +1 -1
  13. package/dist/cli/version-check.d.ts +1 -1
  14. package/dist/cli/version-check.js +1 -1
  15. package/dist/cli-main.js +10 -29
  16. package/dist/client/http-client-storage.js +7 -35
  17. package/dist/client/http-request.d.ts +8 -9
  18. package/dist/client/http-request.js +3 -25
  19. package/dist/core/aggregate-validation.d.ts +2 -5
  20. package/dist/core/aggregate-validation.js +2 -10
  21. package/dist/core/atomic-state.js +21 -24
  22. package/dist/core/context/parallel-operations.d.ts +0 -2
  23. package/dist/core/context/parallel-operations.js +0 -7
  24. package/dist/core/engine/callback-checkpoint-persistence.d.ts +2 -0
  25. package/dist/core/engine/callback-checkpoint-persistence.js +6 -3
  26. package/dist/core/engine/callback-creators.js +4 -22
  27. package/dist/core/engine/checkpoint-io.d.ts +1 -1
  28. package/dist/core/engine/operations-coordination.js +1 -1
  29. package/dist/core/engine/parallel-dispatch.d.ts +1 -1
  30. package/dist/core/engine/parallel-dispatch.js +1 -1
  31. package/dist/core/engine/review-list-entries.d.ts +1 -1
  32. package/dist/core/engine/review-list-entries.js +6 -30
  33. package/dist/core/engine/schedules.js +4 -1
  34. package/dist/core/engine/timeline-coordinator-detail.d.ts +1 -1
  35. package/dist/core/inline-execution-strategy.js +8 -46
  36. package/dist/core/list-filter-validation.d.ts +2 -5
  37. package/dist/core/list-filter-validation.js +2 -10
  38. package/dist/core/review/index.d.ts +87 -0
  39. package/dist/core/review/index.js +29 -0
  40. package/dist/core/types/schedules.d.ts +13 -31
  41. package/dist/core/validation-issues.d.ts +13 -0
  42. package/dist/core/validation-issues.js +9 -0
  43. package/dist/diagnostics/validate.d.ts +9 -30
  44. package/dist/diagnostics/validate.js +83 -21
  45. package/dist/diagnostics/version-check.d.ts +2 -2
  46. package/dist/http.js +2 -2
  47. package/dist/indexeddb.js +1 -1
  48. package/dist/mcp/session.js +2 -10
  49. package/dist/mcp/tools.js +2 -10
  50. package/dist/mcp/tuple-list-equality.d.ts +1 -0
  51. package/dist/mcp/tuple-list-equality.js +12 -0
  52. package/dist/server/openapi-error-responses.js +0 -14
  53. package/dist/server/operation-catalog/index.d.ts +1 -1
  54. package/dist/server/operation-catalog/raise-fault.d.ts +2 -1
  55. package/dist/server/operation-catalog/types.d.ts +3 -2
  56. package/dist/server/operation-fault.d.ts +1 -4
  57. package/dist/server/operation-fault.js +1 -1
  58. package/dist/server/operation-registry.d.ts +2 -31
  59. package/dist/server/operations/bulk-filter-helpers.d.ts +17 -17
  60. package/dist/server/operations/bulk-filter-helpers.js +16 -37
  61. package/dist/server/operations/bulk-mutate-workflow-tags.d.ts +27 -27
  62. package/dist/server/operations/bulk-signal-workflows.d.ts +26 -26
  63. package/dist/server/operations/create-schedule.js +7 -19
  64. package/dist/server/operations/event-stream-contracts.d.ts +20 -0
  65. package/dist/server/operations/event-stream-contracts.js +67 -0
  66. package/dist/server/operations/fleet-events-sse.js +21 -50
  67. package/dist/server/operations/fleet-events-subscription.js +8 -12
  68. package/dist/server/operations/list-reviews.js +2 -19
  69. package/dist/server/operations/list-workflows.d.ts +43 -43
  70. package/dist/server/operations/list-workflows.js +9 -30
  71. package/dist/server/operations/purge-workflows.d.ts +11 -11
  72. package/dist/server/operations/recover-all.js +1 -10
  73. package/dist/server/operations/schedule-rest-body.d.ts +11 -0
  74. package/dist/server/operations/schedule-rest-body.js +28 -0
  75. package/dist/server/operations/single-workflow-control-operation.d.ts +2 -1
  76. package/dist/server/operations/update-schedule.js +7 -19
  77. package/dist/server/operations/workflow-events-sse.js +29 -67
  78. package/dist/server/operations/workflow-events-subscription.js +9 -27
  79. package/dist/server/runtime/task-polling.js +3 -5
  80. package/dist/server/task-state.d.ts +2 -8
  81. package/dist/server/task-state.js +10 -7
  82. package/dist/storage/bounded-ndjson-response.d.ts +5 -0
  83. package/dist/storage/bounded-ndjson-response.js +33 -0
  84. package/dist/storage/bun-sql.js +52 -49
  85. package/dist/storage/conditional-batch-body.d.ts +7 -0
  86. package/dist/storage/conditional-batch-body.js +14 -0
  87. package/dist/storage/http.js +5 -37
  88. package/dist/storage/lmdb.js +1 -1
  89. package/dist/storage/memory.js +1 -1
  90. package/dist/storage/neon.d.ts +3 -16
  91. package/dist/storage/neon.js +2 -2
  92. package/dist/storage/node-sqlite-loader.d.ts +2 -2
  93. package/dist/storage/node-sqlite.js +32 -17
  94. package/dist/storage/postgres.js +1 -1
  95. package/dist/storage/resolve.js +1 -1
  96. package/dist/storage/scoped-storage.js +1 -1
  97. package/dist/storage/turso.js +1 -1
  98. package/dist/version.d.ts +1 -1
  99. package/dist/version.js +1 -1
  100. package/dist/web-extension.js +1 -1
  101. package/dist/worker/execute-with-interceptors.d.ts +1 -1
  102. package/dist/worker/execute-with-interceptors.js +2 -2
  103. package/dist/worker/index.js +5 -5
  104. package/dist/worker/long-poll.js +3 -4
  105. package/dist/worker/protocol-messages.d.ts +11 -19
  106. package/dist/worker/protocol-schemas.d.ts +8 -8
  107. package/dist/worker/protocol-schemas.js +4 -4
  108. package/dist/worker/protocol-task-result.d.ts +1 -0
  109. package/dist/worker/protocol-task-result.js +1 -3
  110. package/dist/worker/protocol.js +1 -1
  111. package/dist/worker/registry/types.d.ts +2 -7
  112. package/dist/worker/registry.d.ts +3 -9
  113. package/dist/worker/registry.js +2 -5
  114. package/package.json +1 -1
@@ -19,8 +19,8 @@ export declare const CLIENT_REST_OPERATION_BINDINGS: {
19
19
  };
20
20
  };
21
21
  };
22
- type SharedAttributesBulkConcurrenc_f62debcf = {
23
- readonly attributes?: ReadonlyArray<SharedGtGteKey_896a0c41>;
22
+ type SharedAttributesBulkConcurrenc_ff397168 = {
23
+ readonly attributes?: ReadonlyArray<SharedGtGteKey_e658e64c>;
24
24
  readonly bulkConcurrency?: number;
25
25
  readonly confirmationToken?: string;
26
26
  readonly createdAt?: SharedGtGteLt_d9a61361;
@@ -39,8 +39,8 @@ type SharedAttributesBulkConcurrenc_f62debcf = {
39
39
  readonly type?: string;
40
40
  readonly updatedAt?: SharedGtGteLt_d9a61361;
41
41
  };
42
- type SharedAttributesCreatedAtExecu_2af7f9ff = {
43
- readonly attributes?: ReadonlyArray<SharedGtGteKey_896a0c41>;
42
+ type SharedAttributesCreatedAtExecu_0caddefc = {
43
+ readonly attributes?: ReadonlyArray<SharedGtGteKey_e658e64c>;
44
44
  readonly createdAt?: SharedGtGteLt_d9a61361;
45
45
  readonly executionDeadline?: SharedGtGteLt_d9a61361;
46
46
  readonly failureCategory?: unknown;
@@ -55,10 +55,10 @@ type SharedAttributesCreatedAtExecu_2af7f9ff = {
55
55
  readonly type?: string;
56
56
  readonly updatedAt?: SharedGtGteLt_d9a61361;
57
57
  };
58
- type SharedGtGteKey_896a0c41 = {
58
+ type SharedGtGteKey_e658e64c = {
59
59
  readonly gt?: unknown;
60
60
  readonly gte?: unknown;
61
- readonly key: string;
61
+ readonly key: unknown;
62
62
  readonly lt?: unknown;
63
63
  readonly lte?: unknown;
64
64
  readonly value?: unknown;
@@ -411,14 +411,7 @@ export type ClientOperationTypes = {
411
411
  };
412
412
  'weft.workflows.aggregate': {
413
413
  readonly input: {
414
- readonly attributes?: ReadonlyArray<{
415
- readonly gt?: unknown;
416
- readonly gte?: unknown;
417
- readonly key: unknown;
418
- readonly lt?: unknown;
419
- readonly lte?: unknown;
420
- readonly value?: unknown;
421
- }>;
414
+ readonly attributes?: ReadonlyArray<SharedGtGteKey_e658e64c>;
422
415
  readonly createdAt?: SharedGtGteLt_d9a61361;
423
416
  readonly executionDeadline?: SharedGtGteLt_d9a61361;
424
417
  readonly failureCategory?: unknown;
@@ -461,23 +454,23 @@ export type ClientOperationTypes = {
461
454
  readonly faults: never;
462
455
  };
463
456
  'weft.workflows.bulk.cancel': {
464
- readonly input: SharedAttributesBulkConcurrenc_f62debcf;
457
+ readonly input: SharedAttributesBulkConcurrenc_ff397168;
465
458
  readonly output: unknown;
466
459
  readonly faults: never;
467
460
  };
468
461
  'weft.workflows.bulk.delete': {
469
- readonly input: SharedAttributesBulkConcurrenc_f62debcf;
462
+ readonly input: SharedAttributesBulkConcurrenc_ff397168;
470
463
  readonly output: unknown;
471
464
  readonly faults: 'Unprocessable';
472
465
  };
473
466
  'weft.workflows.bulk.retryfailed': {
474
- readonly input: SharedAttributesBulkConcurrenc_f62debcf;
467
+ readonly input: SharedAttributesBulkConcurrenc_ff397168;
475
468
  readonly output: unknown;
476
469
  readonly faults: never;
477
470
  };
478
471
  'weft.workflows.bulk.signal': {
479
472
  readonly input: {
480
- readonly attributes?: ReadonlyArray<SharedGtGteKey_896a0c41>;
473
+ readonly attributes?: ReadonlyArray<SharedGtGteKey_e658e64c>;
481
474
  readonly bulkConcurrency?: number;
482
475
  readonly confirmationToken?: string;
483
476
  readonly createdAt?: SharedGtGteLt_d9a61361;
@@ -506,7 +499,7 @@ export type ClientOperationTypes = {
506
499
  readonly bulkConcurrency?: number;
507
500
  readonly confirmationToken?: string;
508
501
  readonly dryRun?: boolean;
509
- readonly filter?: SharedAttributesCreatedAtExecu_2af7f9ff;
502
+ readonly filter?: SharedAttributesCreatedAtExecu_0caddefc;
510
503
  readonly operation: 'add' | 'remove';
511
504
  readonly requestId?: string;
512
505
  readonly tags: ReadonlyArray<string>;
@@ -569,7 +562,7 @@ export type ClientOperationTypes = {
569
562
  };
570
563
  'weft.workflows.list': {
571
564
  readonly input: {
572
- readonly attributes?: ReadonlyArray<SharedGtGteKey_896a0c41>;
565
+ readonly attributes?: ReadonlyArray<SharedGtGteKey_e658e64c>;
573
566
  readonly createdAt?: SharedGtGteLt_d9a61361;
574
567
  readonly executionDeadline?: SharedGtGteLt_d9a61361;
575
568
  readonly failureCategory?: unknown;
@@ -589,7 +582,7 @@ export type ClientOperationTypes = {
589
582
  readonly faults: 'Unprocessable';
590
583
  };
591
584
  'weft.workflows.purge': {
592
- readonly input: SharedAttributesCreatedAtExecu_2af7f9ff;
585
+ readonly input: SharedAttributesCreatedAtExecu_0caddefc;
593
586
  readonly output: unknown;
594
587
  readonly faults: never;
595
588
  };
@@ -1,11 +1,11 @@
1
- export declare const HELP_TEXT = "\nweft - Bun-native durable execution engine\n\nUsage: weft [command] [options]\n\nCommands:\n serve Start the Weft server (default)\n doctor Run diagnostics on the Weft database\n conformance Run RemoteWorker protocol conformance checks\n schedule Manage recurring schedules\n timeline Inspect workflow timeline and replay history\n version:check Check workflow version compatibility\n validate Lint workflow registrations for design-time anti-patterns\n codegen Generate TypeScript declarations from a registry snapshot\n api Inspect and invoke catalog operations on a running server\n server Inspect a running server's health and operation surface\n workflow List, inspect, start, signal, and cancel workflows on a server\n tail Stream a workflow's events as Server-Sent Events\n completions Generate or install shell completion scripts\n version Print the installed Weft version\n\nVersion (leading token only; a subcommand owns its own option line):\n weft version | weft --version | weft -v Print the installed Weft version\n\nServe Options:\n -p, --port <port> Server port (default: 7233)\n -d, --database <path> Database file path (default: ./weft.db)\n -s, --storage <backend> Storage backend: sqlite, lmdb, memory (default: sqlite)\n -w, --workflows <path> Path to workflow module to register on startup\n -h, --help Show this help message\n";
1
+ export declare const HELP_TEXT = "\nweft - Bun-native durable execution engine\n\nUsage: weft [command] [options]\n\nCommands:\n serve Start the Weft server (default)\n doctor Run diagnostics on the Weft database\n conformance Run RemoteWorker protocol conformance checks\n schedule Manage recurring schedules\n timeline Inspect workflow timeline and replay history\n version:check Check workflow version compatibility\n validate Lint workflow definitions for design-time anti-patterns\n codegen Generate TypeScript declarations from a registry snapshot\n api Inspect and invoke catalog operations on a running server\n server Inspect a running server's health and operation surface\n workflow List, inspect, start, signal, and cancel workflows on a server\n tail Stream a workflow's events as Server-Sent Events\n completions Generate or install shell completion scripts\n version Print the installed Weft version\n\nVersion (leading token only; a subcommand owns its own option line):\n weft version | weft --version | weft -v Print the installed Weft version\n\nServe Options:\n -p, --port <port> Server port (default: 7233)\n -d, --database <path> Database file path (default: ./weft.db)\n -s, --storage <backend> Storage backend: sqlite, lmdb, memory (default: sqlite)\n -w, --workflows <path> Path to workflow module to register on startup\n -h, --help Show this help message\n";
2
2
  export declare const API_HELP_TEXT = "\nweft api - Inspect and invoke catalog operations on a running server\n\nUsage:\n weft api --list [options]\n weft api --describe <operation-name> [options]\n weft api <operation-name> [--input <json> | --input-file <path|->] [options]\n\nOptions:\n --server <url> Server URL (default: WEFT_ADDR, profile, run lockfile, or localhost)\n --token <token> Bearer token (default: WEFT_TOKEN)\n --profile <name> Profile from ~/.weft/config (default: WEFT_PROFILE or default_profile)\n --input <json> JSON object input for the operation\n --input-file <path> Read JSON object input from a file, or '-' for stdin\n --list List catalog operations\n --describe <name> Print one operation's schema and metadata\n --yes Confirm destructive operations without prompting\n -j, --json Emit machine-readable JSON output\n -h, --help Show this help message\n\nExit codes:\n 0 Success\n 1 Operation failed or destructive operation was not confirmed\n 2 Connection error\n 3 Usage or input validation error\n";
3
3
  export declare const CONFORMANCE_HELP_TEXT = "\nweft conformance - Run RemoteWorker protocol conformance checks\n\nUsage: weft conformance [options] -- <worker-command> [args...]\n\nThe worker command receives:\n WEFT_WORKER_URL\n WEFT_WORKER_QUEUE\n WEFT_WORKER_ACTIVITIES\n WEFT_WORKER_PROTOCOL_VERSION\n\nOptions:\n --timeout <ms> Per-check timeout in milliseconds (default: 15000)\n -j, --json Output results as JSON\n -h, --help Show this help message\n";
4
4
  export declare const DOCTOR_HELP_TEXT = "\nweft doctor - Run diagnostics on the Weft database\n\nUsage: weft doctor [options]\n\nOptions:\n -d, --database <path> Database file path (default: ./weft.db)\n -j, --json Output results as JSON\n -h, --help Show this help message\n";
5
5
  export declare const VERSION_CHECK_HELP_TEXT = "\nweft version:check - Check workflow version compatibility\n\nUsage: weft version:check [options]\n\nOptions:\n -d, --database <path> Database file path (default: ./weft.db)\n -w, --workflows <path> Path to workflows module\n -j, --json Output results as JSON\n -h, --help Show this help message\n";
6
6
  export declare const TIMELINE_HELP_TEXT = "\nweft timeline - Inspect workflow timeline and replay history\n\nUsage:\n weft timeline <workflowId> [options]\n weft timeline <workflowId> --diff <fromStep> <toStep> [options]\n\nOptions:\n -d, --database <path> Database file path (default: ./weft.db)\n --step <step> Show replay details for one checkpoint step\n --diff Diff two checkpoint steps (requires two positional step numbers)\n -h, --help Show this help message\n";
7
- export declare const SCHEDULE_HELP_TEXT = "\nweft schedule - Manage recurring schedules\n\nUsage:\n weft schedule list [options]\n weft schedule create <workflowType> <cronExpression> [options]\n weft schedule create <workflowType> --every <duration> [options]\n weft schedule pause <scheduleId> [options]\n weft schedule resume <scheduleId> [options]\n weft schedule cancel <scheduleId> [options]\n\nProvide a cron expression positional OR an --every interval (e.g. \"30s\", \"1h\"),\nbut not both. Interval schedules fire one period after creation, then every\nperiod after that.\n\nOptions:\n -d, --database <path> Database file path (default: ./weft.db)\n -s, --storage <backend> Storage backend: sqlite, lmdb (default: sqlite)\n -w, --workflows <path> Path to workflow registrations module (required for create)\n --every <duration> Interval cadence for create (e.g. 30s, 5m, 1h); mutually exclusive with the cron positional\n --input <json> JSON input payload for create (default: null)\n --id <id> Custom schedule id for create\n --overlap <policy> Overlap policy: skip, queue, cancel-running, allow\n --backfill Run missed ticks on recovery\n --jitter <duration> Deterministic dispatch jitter for create (e.g. 30s, 5m)\n -j, --json Output results as JSON\n -h, --help Show this help message\n";
8
- export declare const VALIDATE_HELP_TEXT = "\nweft validate - Lint workflow registrations for design-time anti-patterns\n\nUsage: weft validate <entry.ts>... [options]\n\nArguments:\n <entry.ts>... One or more TypeScript modules or glob patterns that\n resolve to workflow registrations and/or activity\n definitions.\n\nOptions:\n -j, --json Output results as JSON\n -h, --help Show this help message\n\nExit codes:\n 0 No errors (warnings may be present)\n 1 One or more errors detected\n 2 Entry file could not be loaded (takes precedence over validation errors)\n\nJSON output:\n { entries, valid, hasLoadErrors, hasValidationErrors }\n\nChecks performed:\n unbounded-retry Activity retry.maxAttempts is Infinity\n stateful-without-compensator Non-idempotent activity has no compensate fn\n";
7
+ export declare const SCHEDULE_HELP_TEXT = "\nweft schedule - Manage recurring schedules\n\nUsage:\n weft schedule list [options]\n weft schedule create <workflowType> <cronExpression> [options]\n weft schedule create <workflowType> --every <duration> [options]\n weft schedule pause <scheduleId> [options]\n weft schedule resume <scheduleId> [options]\n weft schedule cancel <scheduleId> [options]\n\nProvide a cron expression positional OR an --every interval (e.g. \"30s\", \"1h\"),\nbut not both. Interval schedules fire one period after creation, then every\nperiod after that.\n\nOptions:\n -d, --database <path> Database file path (default: ./weft.db)\n -s, --storage <backend> Storage backend: sqlite, lmdb (default: sqlite)\n -w, --workflows <path> Path to workflow definitions module (required for create)\n --every <duration> Interval cadence for create (e.g. 30s, 5m, 1h); mutually exclusive with the cron positional\n --input <json> JSON input payload for create (default: null)\n --id <id> Custom schedule id for create\n --overlap <policy> Overlap policy: skip, queue, cancel-running, allow\n --backfill Run missed ticks on recovery\n --jitter <duration> Deterministic dispatch jitter for create (e.g. 30s, 5m)\n -j, --json Output results as JSON\n -h, --help Show this help message\n";
8
+ export declare const VALIDATE_HELP_TEXT = "\nweft validate - Lint workflow definitions for design-time anti-patterns\n\nUsage: weft validate <entry.ts>... [options]\n\nArguments:\n <entry.ts>... One or more TypeScript modules or glob patterns that\n resolve to workflow definitions and/or activity\n definitions.\n\nOptions:\n -j, --json Output results as JSON\n -h, --help Show this help message\n\nExit codes:\n 0 No errors (warnings may be present)\n 1 One or more errors detected\n 2 Entry file could not be loaded (takes precedence over validation errors)\n\nJSON output:\n { entries, valid, hasLoadErrors, hasValidationErrors }\n\nChecks performed:\n unbounded-retry Activity retry.maxAttempts is Infinity\n stateful-without-compensator Non-idempotent activity has no compensate fn\n";
9
9
  export declare const SERVER_HELP_TEXT = "\nweft server - Inspect a running server's health and operation surface\n\nUsage:\n weft server health [options]\n weft server info [options]\n\nOptions:\n --server <url> Server URL (default: WEFT_ADDR, profile, run lockfile, or localhost)\n --token <token> Bearer token (default: WEFT_TOKEN)\n --profile <name> Profile from ~/.weft/config\n --wait (health) Poll until the server is reachable\n --wait-timeout <ms> (health) Maximum time to wait, in milliseconds (default: 30000)\n -j, --json Emit machine-readable JSON output\n -q, --quiet Suppress success/error text (use the exit code)\n -h, --help Show this help message\n\nExit codes:\n 0 Healthy\n 1 Unreachable / unhealthy\n 2 Connection error\n";
10
10
  export declare const WORKFLOW_HELP_TEXT = "\nweft workflow - List, inspect, start, signal, and cancel workflows on a server\n\nUsage:\n weft workflow ls [--type <type>] [--status <status>] [--limit <n>] [options]\n weft workflow get <workflow-id> [options]\n weft workflow events <workflow-id> [options]\n weft workflow start <workflow-type> [--input <json> | --input-file <path|->] [--id <id>] [options]\n weft workflow cancel <workflow-id> [--yes] [--dry-run] [options]\n weft workflow signal <workflow-id> <signal-name> [--input <json> | --input-file <path|->] [options]\n\nOptions:\n --server <url> Server URL (default: WEFT_ADDR, profile, run lockfile, or localhost)\n --token <token> Bearer token (default: WEFT_TOKEN)\n --profile <name> Profile from ~/.weft/config\n --type <type> (ls) Filter by workflow type\n --status <status> (ls) Filter by workflow status\n --limit <n> (ls) Maximum number of rows\n --input <json> (start/signal) JSON input payload\n --input-file <path> (start/signal) Read JSON input from a file, or '-' for stdin\n --id <id> (start) Explicit workflow id\n -y, --yes (cancel) Confirm without prompting\n --dry-run (cancel) Print affected count without cancelling\n -j, --json Emit machine-readable output (NDJSON for lists)\n -q, --quiet Print ids only / suppress success text\n -h, --help Show this help message\n\nExit codes:\n 0 Success\n 1 Operation failed or destructive operation was not confirmed\n 2 Connection error\n 3 Usage or input error\n 4 Operation unavailable on this server (version skew)\n";
11
11
  export declare const TAIL_HELP_TEXT = "\nweft tail - Stream a workflow's events as Server-Sent Events\n\nUsage:\n weft tail <workflow-id> [options]\n\nOptions:\n --server <url> Server URL (default: WEFT_ADDR, profile, run lockfile, or localhost)\n --token <token> Bearer token (default: WEFT_TOKEN)\n --profile <name> Profile from ~/.weft/config\n -j, --json Emit one JSON object per line (NDJSON)\n -q, --quiet Do not echo events to stdout\n -h, --help Show this help message\n\nPress Ctrl-C to stop tailing; the stream ends cleanly with exit code 0.\n";
@@ -10,7 +10,7 @@ Commands:
10
10
  schedule Manage recurring schedules
11
11
  timeline Inspect workflow timeline and replay history
12
12
  version:check Check workflow version compatibility
13
- validate Lint workflow registrations for design-time anti-patterns
13
+ validate Lint workflow definitions for design-time anti-patterns
14
14
  codegen Generate TypeScript declarations from a registry snapshot
15
15
  api Inspect and invoke catalog operations on a running server
16
16
  server Inspect a running server's health and operation surface
@@ -117,7 +117,7 @@ period after that.
117
117
  Options:
118
118
  -d, --database <path> Database file path (default: ./weft.db)
119
119
  -s, --storage <backend> Storage backend: sqlite, lmdb (default: sqlite)
120
- -w, --workflows <path> Path to workflow registrations module (required for create)
120
+ -w, --workflows <path> Path to workflow definitions module (required for create)
121
121
  --every <duration> Interval cadence for create (e.g. 30s, 5m, 1h); mutually exclusive with the cron positional
122
122
  --input <json> JSON input payload for create (default: null)
123
123
  --id <id> Custom schedule id for create
@@ -127,13 +127,13 @@ Options:
127
127
  -j, --json Output results as JSON
128
128
  -h, --help Show this help message
129
129
  `, VALIDATE_HELP_TEXT = `
130
- weft validate - Lint workflow registrations for design-time anti-patterns
130
+ weft validate - Lint workflow definitions for design-time anti-patterns
131
131
 
132
132
  Usage: weft validate <entry.ts>... [options]
133
133
 
134
134
  Arguments:
135
135
  <entry.ts>... One or more TypeScript modules or glob patterns that
136
- resolve to workflow registrations and/or activity
136
+ resolve to workflow definitions and/or activity
137
137
  definitions.
138
138
 
139
139
  Options:
@@ -1,5 +1,5 @@
1
+ import type { FaultCode } from '../core/fault-code.ts';
1
2
  import type { ErasedOperation, OperationKind, TransportAvailability } from '../server/operation-catalog.ts';
2
- import type { FaultCode } from '../server/operation-fault.ts';
3
3
  import type { ParamSource, ResponseShape } from '../server/rest-binding.ts';
4
4
  export type CatalogRestBindingSnapshot = {
5
5
  readonly method: string;
@@ -51,10 +51,8 @@ function parseScheduleInput(input) {
51
51
  }
52
52
  async function registerScheduleWorkflows(engine, workflowsPath, loadRegistrationsFromModule) {
53
53
  const loaded = await loadRegistrationsFromModule(workflowsPath);
54
- for (const [workflowType, registration] of Object.entries(loaded.registrations)) {
55
- const definition = { ...registration, name: workflowType };
54
+ for (const definition of Object.values(loaded.registrations))
56
55
  registerOnRuntimeEngine(runtimeWorkflowEngine(engine), definition);
57
- }
58
56
  }
59
57
  async function executeScheduleCreate(options, engine, loadRegistrationsFromModule) {
60
58
  const validationError = getScheduleCreateValidationError(options);
@@ -1,6 +1,6 @@
1
1
  import type { ActivityDefinition } from '../core/activity.ts';
2
2
  import type { Engine } from '../core/engine.ts';
3
- import type { WorkflowRegistration } from '../diagnostics/validate.ts';
3
+ import type { WorkflowDefinition } from '../core/types.ts';
4
4
  /**
5
5
  * Convert a plain-object `ActivityDefinition` into a callable function that
6
6
  * satisfies the engine's `isActivityDefinition` check (which requires a
@@ -8,8 +8,8 @@ import type { WorkflowRegistration } from '../diagnostics/validate.ts';
8
8
  */
9
9
  export declare function toActivityCallable(definition: ActivityDefinition): (...args: unknown[]) => unknown;
10
10
  /**
11
- * Register a map of workflow registrations and a list of activity definitions
11
+ * Register a map of workflow definitions and a list of activity definitions
12
12
  * on an engine instance. Plain-object activities are normalized to callables
13
13
  * before registration.
14
14
  */
15
- export declare function registerModuleExports(engine: Engine, registrations: Record<string, WorkflowRegistration>, activities: ActivityDefinition[]): void;
15
+ export declare function registerModuleExports(engine: Engine, registrations: Record<string, WorkflowDefinition>, activities: ActivityDefinition[]): void;
@@ -13,8 +13,8 @@ export function toActivityCallable(definition) {
13
13
  }
14
14
  export function registerModuleExports(engine, registrations, activities) {
15
15
  const runtime = runtimeWorkflowEngine(engine);
16
- for (const [workflowType, registration] of Object.entries(registrations))
17
- registerOnRuntimeEngine(runtime, { ...registration, name: workflowType });
16
+ for (const definition of Object.values(registrations))
17
+ registerOnRuntimeEngine(runtime, definition);
18
18
  for (const activity of activities)
19
19
  if (typeof activity === "function")
20
20
  engine.register(activity);
@@ -0,0 +1,11 @@
1
+ export declare const CLI_SHUTDOWN_SIGNALS: readonly ["SIGINT", "SIGTERM"];
2
+ export type CliShutdownSignal = (typeof CLI_SHUTDOWN_SIGNALS)[number];
3
+ export type CliShutdownDependencies = {
4
+ readonly stopServer: () => Promise<void>;
5
+ readonly removeRunLockfile: () => Promise<void>;
6
+ readonly disposeStorage: () => void;
7
+ readonly log: (message: string) => void;
8
+ readonly reportError: (message: string, error: unknown) => void;
9
+ readonly exit: (code: 0 | 1) => void;
10
+ };
11
+ export declare function createCliShutdownHandler(dependencies: CliShutdownDependencies): (signal: CliShutdownSignal) => Promise<void>;
@@ -0,0 +1,25 @@
1
+ export const CLI_SHUTDOWN_SIGNALS = ["SIGINT", "SIGTERM"];
2
+ async function runCliShutdown(signal, dependencies) {
3
+ dependencies.log(`
4
+ Received ${signal}; shutting down...`);
5
+ try {
6
+ await dependencies.stopServer();
7
+ try {
8
+ await dependencies.removeRunLockfile();
9
+ } catch (error) {
10
+ dependencies.reportError("[weft] Failed to remove run lockfile:", error);
11
+ }
12
+ dependencies.disposeStorage();
13
+ dependencies.exit(0);
14
+ } catch (error) {
15
+ dependencies.reportError("[weft] Shutdown error:", error);
16
+ dependencies.exit(1);
17
+ }
18
+ }
19
+ export function createCliShutdownHandler(dependencies) {
20
+ let shutdownPromise;
21
+ return (signal) => {
22
+ shutdownPromise ??= runCliShutdown(signal, dependencies);
23
+ return shutdownPromise;
24
+ };
25
+ }
@@ -1,5 +1,5 @@
1
1
  import type { CommandOutput } from './types.ts';
2
- /** Validates workflow registration modules and reports load or design-time errors. */
2
+ /** Validates workflow definition modules and reports load or design-time errors. */
3
3
  export declare function executeValidate(options: {
4
4
  entryPaths: string[];
5
5
  json: boolean;
@@ -1,5 +1,5 @@
1
1
  import type { CommandOutput } from './types.ts';
2
- /** Checks stored workflow history against the current workflow registrations. */
2
+ /** Checks stored workflow history against the current workflow definitions. */
3
3
  export declare function executeVersionCheck(options: {
4
4
  database: string;
5
5
  workflows: string;
@@ -8,7 +8,7 @@ export async function executeVersionCheck(options) {
8
8
  };
9
9
  const { runVersionCheck } = await import("../diagnostics/version-check.js"), { formatVersionCheckReport } = await import("../diagnostics/format.js"), { BunSQLiteStorage } = await import("../storage/bun-sql.js"), storage = new BunSQLiteStorage(options.database);
10
10
  try {
11
- const registrations = await import(resolve(process.cwd(), options.workflows)), report = await runVersionCheck(storage, registrations.default);
11
+ const workflowsPath = resolve(process.cwd(), options.workflows), { loadRegistrationsFromModule } = await import("../diagnostics/validate.js"), { registrations } = await loadRegistrationsFromModule(workflowsPath), report = await runVersionCheck(storage, registrations);
12
12
  return { stdout: options.json ? JSON.stringify(report, null, 2) : formatVersionCheckReport(report), exitCode: 0 };
13
13
  } finally {
14
14
  storage[Symbol.dispose]();
package/dist/cli-main.js CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  WORKFLOW_HELP_TEXT,
33
33
  writeRunLockfile
34
34
  } from "./cli/index.js";
35
+ import { CLI_SHUTDOWN_SIGNALS, createCliShutdownHandler } from "./cli/shutdown.js";
35
36
  import { Engine } from "./core/engine.js";
36
37
  import { serve } from "./server/index.js";
37
38
  const parsedArguments = (() => {
@@ -67,36 +68,16 @@ if (parsedArguments.command === "version") {
67
68
  console.log(`Health check: ${new URL("/v1/health", server.url).href}`);
68
69
  console.log(`Storage: ${parsedArguments.storage}`);
69
70
  console.log(`Database: ${parsedArguments.database}`);
70
- process.on("SIGINT", () => {
71
- console.log(`
72
- Shutting down...`);
73
- server.stop().then(async () => {
74
- try {
75
- await removeRunLockfile(server.url);
76
- } catch (error) {
77
- console.error("[weft] Failed to remove run lockfile:", error);
78
- }
79
- storage[Symbol.dispose]();
80
- process.exit(0);
81
- }).catch((error) => {
82
- console.error("[weft] Shutdown error:", error);
83
- process.exit(1);
84
- });
85
- });
86
- process.on("SIGTERM", () => {
87
- server.stop().then(async () => {
88
- try {
89
- await removeRunLockfile(server.url);
90
- } catch (error) {
91
- console.error("[weft] Failed to remove run lockfile:", error);
92
- }
93
- storage[Symbol.dispose]();
94
- process.exit(0);
95
- }).catch((error) => {
96
- console.error("[weft] Shutdown error:", error);
97
- process.exit(1);
98
- });
71
+ const shutdown = createCliShutdownHandler({
72
+ stopServer: () => server.stop(),
73
+ removeRunLockfile: () => removeRunLockfile(server.url),
74
+ disposeStorage: () => storage[Symbol.dispose](),
75
+ log: (message) => console.log(message),
76
+ reportError: (message, error) => console.error(message, error),
77
+ exit: (code) => process.exit(code)
99
78
  });
79
+ for (const signal of CLI_SHUTDOWN_SIGNALS)
80
+ process.on(signal, () => void shutdown(signal));
100
81
  } else if (parsedArguments.command === "doctor") {
101
82
  if (parsedArguments.help) {
102
83
  console.log(DOCTOR_HELP_TEXT);
@@ -1,3 +1,4 @@
1
+ import { readBoundedNdjsonResponse } from "../storage/bounded-ndjson-response.js";
1
2
  import { decodeBase64ToBytes, encodeBytesToBase64, isRecord } from "../storage/byte-encoding.js";
2
3
  import {
3
4
  assertStorageBatchOperationCount
@@ -19,42 +20,13 @@ function scanEntry(value) {
19
20
  throw Error("HttpClient storage scan response contained an invalid NDJSON entry.");
20
21
  return [value.key, decodeBase64ToBytes(value.value)];
21
22
  }
22
- async function releaseScanReader(reader, reachedEndOfStream) {
23
- if (!reachedEndOfStream)
24
- try {
25
- await reader.cancel();
26
- } catch {}
27
- reader.releaseLock();
28
- }
29
23
  async function* scanResponse(response) {
30
- if (response.body === null)
31
- return;
32
- const reader = response.body.getReader(), decoder = new TextDecoder;
33
- let bufferedText = "", bytesRead = 0, reachedEndOfStream = !1;
34
- try {
35
- while (!0) {
36
- const { done, value } = await reader.read();
37
- if (done) {
38
- reachedEndOfStream = !0;
39
- break;
40
- }
41
- bytesRead += value.byteLength;
42
- if (bytesRead > MAX_SCAN_RESPONSE_BYTES)
43
- throw Error("HttpClient storage scan response exceeded the maximum allowed size.");
44
- bufferedText += decoder.decode(value, { stream: !0 });
45
- const lines = bufferedText.split(`
46
- `);
47
- bufferedText = lines.pop() ?? "";
48
- for (const line of lines)
49
- if (line.trim().length > 0)
50
- yield scanEntry(JSON.parse(line));
51
- }
52
- bufferedText += decoder.decode();
53
- if (bufferedText.trim().length > 0)
54
- yield scanEntry(JSON.parse(bufferedText));
55
- } finally {
56
- await releaseScanReader(reader, reachedEndOfStream);
57
- }
24
+ for await (const line of readBoundedNdjsonResponse(response, {
25
+ maximumBytes: MAX_SCAN_RESPONSE_BYTES,
26
+ sizeLimitError: () => Error("HttpClient storage scan response exceeded the maximum allowed size.")
27
+ }))
28
+ if (line.trim().length > 0)
29
+ yield scanEntry(JSON.parse(line));
58
30
  }
59
31
  export function createHttpClientStorage(baseUrl, headers) {
60
32
  return {
@@ -63,7 +63,7 @@ export declare function resolveHttpClientConnection(options: HttpClientOptions):
63
63
  /**
64
64
  * Error thrown when the server returns a non-2xx response.
65
65
  *
66
- * When the server sends a fault body with a recognized coarse `code`, the wire
66
+ * When a transport response carries a recognized coarse fault code, the wire
67
67
  * value is surfaced as {@link HttpClientError.faultCode} with a derived
68
68
  * {@link HttpClientError.category}, so callers can branch without
69
69
  * string-matching `message`. Both are `undefined` for the production flat REST
@@ -96,8 +96,8 @@ export declare class HttpClientError extends WeftError<'HttpClientError'> {
96
96
  /** HTTP status code of the failed response. */
97
97
  readonly status: number;
98
98
  /**
99
- * The server's stable wire fault code, when the response carried a structured
100
- * fault body. `undefined` for plain-string error bodies or unrecognized codes.
99
+ * The server's stable wire fault code, when the transport response carried a
100
+ * recognized code. `undefined` for flat REST error bodies or unrecognized codes.
101
101
  */
102
102
  readonly faultCode?: FaultCode | undefined;
103
103
  /**
@@ -107,18 +107,17 @@ export declare class HttpClientError extends WeftError<'HttpClientError'> {
107
107
  readonly category?: FailureCategory | undefined;
108
108
  /**
109
109
  * The fine-grained originating public {@link WeftErrorCode} the server
110
- * attached (e.g. `WorkflowNotFoundError`), when the structured fault carried
111
- * one. This is what makes cross-transport branching work: a producer can call
110
+ * attached (e.g. `WorkflowNotFoundError`), when the response carried one.
111
+ * This is what makes cross-transport branching work: a producer can call
112
112
  * `isWeftFault(error, 'WorkflowNotFoundError')` and have it match over HTTP
113
113
  * exactly as it matches the in-process typed error. `undefined` when the fault
114
- * carried only the coarse {@link faultCode} (most faults) or no structured body.
114
+ * carried only the coarse {@link faultCode} (most faults) or no recognized code.
115
115
  */
116
116
  readonly weftCode?: WeftErrorCode | undefined;
117
117
  /**
118
118
  * The fault's wire `data` payload, when the response carried a structured
119
- * body (`{ error, data }` for production REST, nested `{ error: { data } }`
120
- * for structured REST responses, or `error.data` for JSON-RPC). Shape is
121
- * fault-code-dependent — see {@link FaultCode} and the server's
119
+ * body (`{ error, data }` for production REST or `error.data` for JSON-RPC).
120
+ * Shape is fault-code-dependent see {@link FaultCode} and the server's
122
121
  * `OperationFault` per-code `data` union for what each code carries (e.g.
123
122
  * `InvalidParams.data.issues`, `NotFound.data.resource`). `undefined` for
124
123
  * plain-string error bodies, bodies with no `data` field, or a `data` field
@@ -1,5 +1,5 @@
1
1
  import { resolveConnection } from "../connection.js";
2
- import { failureCategoryForFaultCode, isFaultCode } from "../core/fault-code.js";
2
+ import { failureCategoryForFaultCode } from "../core/fault-code.js";
3
3
  import { isWeftErrorCode, WeftError } from "../core/weft-error.js";
4
4
  export function resolveHttpClientConnection(options) {
5
5
  const connection = resolveConnection({
@@ -49,34 +49,12 @@ function parseFlatErrorBody(body) {
49
49
  ...data === void 0 ? {} : { data }
50
50
  };
51
51
  }
52
- function isStructuredErrorBody(value) {
53
- if (value === null || typeof value !== "object")
54
- return !1;
55
- const error = value.error;
56
- if (error === null || typeof error !== "object")
57
- return !1;
58
- return typeof error.message === "string";
59
- }
60
- function weftCodeFromData(data) {
61
- if (data === null || typeof data !== "object")
62
- return;
63
- const candidate = data.weftCode;
64
- return isWeftErrorCode(candidate) ? candidate : void 0;
65
- }
66
52
  function isRecord(value) {
67
53
  return typeof value === "object" && value !== null && !Array.isArray(value);
68
54
  }
69
55
  async function parseErrorBody(response) {
70
56
  try {
71
57
  const body = await response.json();
72
- if (isStructuredErrorBody(body)) {
73
- const { code, message, data } = body.error, weftCode = weftCodeFromData(data), base = { message };
74
- if (weftCode !== void 0)
75
- base.weftCode = weftCode;
76
- if (isRecord(data))
77
- base.data = data;
78
- return isFaultCode(code) ? { ...base, faultCode: code } : base;
79
- }
80
58
  if (isFlatErrorBody(body) && body.error)
81
59
  return parseFlatErrorBody(body);
82
60
  return { message: response.statusText };
@@ -97,8 +75,8 @@ export async function requestResponse(baseUrl, path, baseHeaders, options) {
97
75
  if (response.status === 404 && (!options?.method || options.method === "GET"))
98
76
  return null;
99
77
  if (!response.ok) {
100
- const { message, faultCode, weftCode, data } = await parseErrorBody(response);
101
- throw new HttpClientError(response.status, message, { faultCode, weftCode, data });
78
+ const { message, weftCode, data } = await parseErrorBody(response);
79
+ throw new HttpClientError(response.status, message, { weftCode, data });
102
80
  }
103
81
  return response;
104
82
  }
@@ -11,6 +11,7 @@
11
11
  * @module core/aggregate-validation
12
12
  */
13
13
  import { z } from 'zod';
14
+ import { type ValidationIssue } from './validation-issues.ts';
14
15
  import { WeftError } from './weft-error.ts';
15
16
  /**
16
17
  * Group-by dimension for `engine.aggregate()`. Either a fixed structural
@@ -54,11 +55,7 @@ export declare const aggregateOptionsObjectSchema: z.ZodObject<{
54
55
  limit: z.ZodOptional<z.ZodNumber>;
55
56
  }, z.core.$strict>;
56
57
  /** A flattened Zod issue suitable for cross-transport serialization. */
57
- export type AggregateOptionsValidationIssue = {
58
- readonly path: ReadonlyArray<string | number>;
59
- readonly message: string;
60
- readonly code: string;
61
- };
58
+ export type AggregateOptionsValidationIssue = ValidationIssue;
62
59
  /**
63
60
  * Thrown by {@link normalizeAggregateOptions} when input fails validation.
64
61
  * Carries flattened Zod issues so transport adapters can map directly to
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { flattenZodIssue } from "./validation-issues.js";
2
3
  import { WeftError } from "./weft-error.js";
3
4
  const attributeDimensionSchema = z.object({ attribute: z.string().min(1) }).strict(), groupBySchema = z.union([
4
5
  z.literal("status"),
@@ -10,15 +11,6 @@ export const AGGREGATE_DEFAULT_LIMIT = 1000, AGGREGATE_MAX_LIMIT = 1e4, MAX_AGGR
10
11
  groupBy: groupBySchema,
11
12
  limit: z.number().int().min(1).max(AGGREGATE_MAX_LIMIT).optional()
12
13
  }).strict();
13
- function flattenIssue(issue) {
14
- const path = [];
15
- for (const segment of issue.path)
16
- if (typeof segment === "string" || typeof segment === "number")
17
- path.push(segment);
18
- else
19
- path.push(String(segment));
20
- return { path, message: issue.message, code: issue.code };
21
- }
22
14
 
23
15
  export class AggregateOptionsValidationError extends WeftError {
24
16
  issues;
@@ -42,7 +34,7 @@ export class AggregateDistinctKeyCapExceededError extends WeftError {
42
34
  export function normalizeAggregateOptions(input) {
43
35
  const result = aggregateOptionsObjectSchema.safeParse(input ?? {});
44
36
  if (!result.success)
45
- throw new AggregateOptionsValidationError(result.error.issues.map(flattenIssue));
37
+ throw new AggregateOptionsValidationError(result.error.issues.map(flattenZodIssue));
46
38
  const { groupBy, limit } = result.data;
47
39
  return { groupBy, ...limit !== void 0 && { limit } };
48
40
  }
@@ -121,35 +121,18 @@ export class AtomicState extends EventTarget {
121
121
  return (await this.#read()).value;
122
122
  }
123
123
  async update(updater) {
124
- for (let attempt = 0;attempt < this.#maxRetries; attempt++) {
125
- const snapshot = await this.#read(), nextValue = updater(snapshot.value), commit = await commitAtomicStateValue(this.#storage, this.#dataKey, snapshot.version, nextValue);
126
- if (commit.applied) {
127
- dispatchAtomicStateEvent(this, new AtomicStateChangeEvent(nextValue, snapshot.value, commit.version));
128
- return nextValue;
129
- }
130
- dispatchAtomicStateEvent(this, new AtomicStateConflictEvent(this.#stateKey, attempt + 1));
131
- if (attempt < this.#maxRetries - 1)
132
- await this.#sleep(retryDelay(attempt));
133
- }
134
- dispatchAtomicStateEvent(this, new AtomicStateExhaustedEvent(this.#stateKey, this.#maxRetries));
135
- throw new AtomicStateConflictError(this.#stateKey, this.#maxRetries);
124
+ return this.#runOptimisticMutation(async (snapshot) => {
125
+ const nextValue = updater(snapshot.value);
126
+ return { commit: await commitAtomicStateValue(this.#storage, this.#dataKey, snapshot.version, nextValue), result: nextValue, eventValue: nextValue };
127
+ });
136
128
  }
137
129
  async set(value) {
138
130
  return this.update(() => value);
139
131
  }
140
132
  async delete() {
141
- for (let attempt = 0;attempt < this.#maxRetries; attempt++) {
142
- const snapshot = await this.#read(), commit = await commitAtomicStateDelete(this.#storage, this.#dataKey, snapshot.version);
143
- if (commit.applied) {
144
- dispatchAtomicStateEvent(this, new AtomicStateChangeEvent(void 0, snapshot.value, commit.version));
145
- return;
146
- }
147
- dispatchAtomicStateEvent(this, new AtomicStateConflictEvent(this.#stateKey, attempt + 1));
148
- if (attempt < this.#maxRetries - 1)
149
- await this.#sleep(retryDelay(attempt));
150
- }
151
- dispatchAtomicStateEvent(this, new AtomicStateExhaustedEvent(this.#stateKey, this.#maxRetries));
152
- throw new AtomicStateConflictError(this.#stateKey, this.#maxRetries);
133
+ await this.#runOptimisticMutation(async (snapshot) => {
134
+ return { commit: await commitAtomicStateDelete(this.#storage, this.#dataKey, snapshot.version), result: void 0, eventValue: void 0 };
135
+ });
153
136
  }
154
137
  increment(amount = 1) {
155
138
  return this.update((current) => (current ?? 0) + amount);
@@ -225,6 +208,20 @@ export class AtomicState extends EventTarget {
225
208
  this.removeEventListener("exhausted", listener);
226
209
  }
227
210
  }
211
+ async#runOptimisticMutation(attemptMutation) {
212
+ for (let attempt = 0;attempt < this.#maxRetries; attempt++) {
213
+ const snapshot = await this.#read(), mutation = await attemptMutation(snapshot);
214
+ if (mutation.commit.applied) {
215
+ dispatchAtomicStateEvent(this, new AtomicStateChangeEvent(mutation.eventValue, snapshot.value, mutation.commit.version));
216
+ return mutation.result;
217
+ }
218
+ dispatchAtomicStateEvent(this, new AtomicStateConflictEvent(this.#stateKey, attempt + 1));
219
+ if (attempt < this.#maxRetries - 1)
220
+ await this.#sleep(retryDelay(attempt));
221
+ }
222
+ dispatchAtomicStateEvent(this, new AtomicStateExhaustedEvent(this.#stateKey, this.#maxRetries));
223
+ throw new AtomicStateConflictError(this.#stateKey, this.#maxRetries);
224
+ }
228
225
  async#read() {
229
226
  return readAtomicStateSnapshot(this.#storage, this.#dataKey, this.#options);
230
227
  }
@@ -1,8 +1,6 @@
1
1
  import type { Context } from './index.ts';
2
2
  import type { ContextInternals } from './internals.ts';
3
3
  import type { ContextOperationRequest } from './operation-request.ts';
4
- import { assertValidParallelOperationCacheEntry, BranchTopologyChangedError, createParallelOperationCacheEntry, isParallelOperationCacheEntry, type ParallelBranchSlot, type ParallelOperationCacheEntry } from './parallel-cache-entry.ts';
5
- export { assertValidParallelOperationCacheEntry, BranchTopologyChangedError, createParallelOperationCacheEntry, isParallelOperationCacheEntry, type ParallelBranchSlot, type ParallelOperationCacheEntry, };
6
4
  export declare function all(context: Context, internals: ContextInternals, operations: Generator<ContextOperationRequest, unknown, unknown>[]): Generator<ContextOperationRequest, unknown[], unknown>;
7
5
  export declare function race(context: Context, internals: ContextInternals, operations: Generator<ContextOperationRequest, unknown, unknown>[], branchNames?: string[]): Generator<ContextOperationRequest, unknown, unknown>;
8
6
  export declare function memo<T>(context: Context, internals: ContextInternals, key: string, fn: () => T | Promise<T>): Generator<ContextOperationRequest, T, unknown>;