@lostgradient/weft 0.10.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 (102) hide show
  1. package/README.md +10 -2
  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/checkpoint/serialization.js +2 -2
  14. package/dist/core/engine/callback-creators-core.js +2 -1
  15. package/dist/core/engine/disposal.js +4 -0
  16. package/dist/core/engine/engine-leak-warnings.d.ts +2 -0
  17. package/dist/core/engine/engine-leak-warnings.js +7 -1
  18. package/dist/core/engine/index.d.ts +49 -3
  19. package/dist/core/engine/index.js +19 -3
  20. package/dist/core/engine/inline-parking.js +14 -1
  21. package/dist/core/engine/internals.d.ts +21 -0
  22. package/dist/core/engine/lifecycle/shared.d.ts +25 -0
  23. package/dist/core/engine/lifecycle/transition.js +19 -9
  24. package/dist/core/engine/operations-time.js +8 -28
  25. package/dist/core/engine/sleep-timer-acknowledgements.d.ts +15 -0
  26. package/dist/core/engine/sleep-timer-acknowledgements.js +144 -0
  27. package/dist/core/engine/termination/cleanup.js +3 -0
  28. package/dist/core/types/definition-schema-to-json.d.ts +11 -0
  29. package/dist/core/types/definition-schema-to-json.js +11 -2
  30. package/dist/http.js +2 -0
  31. package/dist/indexeddb.js +1 -0
  32. package/dist/json-schema.js +3 -3
  33. package/dist/mcp/cli.js +63 -61
  34. package/dist/mcp/http.d.ts +7 -2
  35. package/dist/mcp/session.d.ts +3 -3
  36. package/dist/mcp/session.js +3 -2
  37. package/dist/mcp/stdio.d.ts +7 -2
  38. package/dist/observability/index.js +2 -2
  39. package/dist/runtime/portable.d.ts +33 -0
  40. package/dist/runtime/portable.js +17 -6
  41. package/dist/server/engine-event-feed-backend.d.ts +22 -3
  42. package/dist/server/fleet-event-feed.d.ts +65 -0
  43. package/dist/server/handler/index.d.ts +11 -2
  44. package/dist/server/handler/index.js +7 -0
  45. package/dist/server/handler.js +1 -56
  46. package/dist/server/index.d.ts +12 -1
  47. package/dist/server/index.js +87 -64
  48. package/dist/server/operations/submit-review-decision.d.ts +2 -0
  49. package/dist/server/operations/submit-review-decision.js +19 -2
  50. package/dist/server/replay-live-feed-internals.d.ts +19 -0
  51. package/dist/server/replay-live-feed-internals.js +76 -0
  52. package/dist/server/runtime/authentication-bridge.d.ts +2 -2
  53. package/dist/server/serve-internals.js +3 -3
  54. package/dist/server/workflow-event-feed.d.ts +100 -11
  55. package/dist/server/workflow-event-feed.js +1 -140
  56. package/dist/service-worker/index.d.ts +8 -2
  57. package/dist/service-worker/index.js +37 -56
  58. package/dist/service-worker/scheduler.js +1 -0
  59. package/dist/service-worker/setup.d.ts +6 -2
  60. package/dist/storage/auto.js +1 -1
  61. package/dist/storage/bun-sql.js +3 -3
  62. package/dist/storage/cloudflare-durable-object-sql.d.ts +75 -0
  63. package/dist/storage/cloudflare-durable-object-sql.js +0 -0
  64. package/dist/storage/cloudflare-value-codec.d.ts +65 -0
  65. package/dist/storage/cloudflare-value-codec.js +46 -0
  66. package/dist/storage/cloudflare.d.ts +140 -0
  67. package/dist/storage/cloudflare.js +127 -0
  68. package/dist/storage/compressed-storage.js +1 -1
  69. package/dist/storage/http.js +200 -2
  70. package/dist/storage/index.d.ts +1 -0
  71. package/dist/storage/indexeddb.js +311 -1
  72. package/dist/storage/interface.js +1 -1
  73. package/dist/storage/lazy-postgres-pool.d.ts +38 -0
  74. package/dist/storage/lazy-postgres-pool.js +38 -0
  75. package/dist/storage/lmdb.js +1 -1
  76. package/dist/storage/memory.js +1 -1
  77. package/dist/storage/neon-batch.d.ts +8 -8
  78. package/dist/storage/neon.d.ts +26 -79
  79. package/dist/storage/neon.js +4 -4
  80. package/dist/storage/node-sqlite.js +3 -3
  81. package/dist/storage/postgres-key-value-queries.d.ts +7 -1
  82. package/dist/storage/postgres-key-value-queries.js +2 -3
  83. package/dist/storage/postgres-key-value-storage.d.ts +110 -0
  84. package/dist/storage/postgres-key-value-storage.js +204 -0
  85. package/dist/storage/postgres.d.ts +59 -0
  86. package/dist/storage/postgres.js +15 -0
  87. package/dist/storage/resolve.js +1 -1
  88. package/dist/storage/scoped-storage.js +1 -1
  89. package/dist/storage/sql-identifier.d.ts +35 -0
  90. package/dist/storage/sql-identifier.js +5 -0
  91. package/dist/storage/sqlite-key-value-queries.d.ts +11 -6
  92. package/dist/storage/sqlite-key-value-queries.js +3 -3
  93. package/dist/storage/testing.js +1 -1
  94. package/dist/storage/turso.js +2 -2
  95. package/dist/storage/typed-storage.js +138 -2
  96. package/dist/storage/web-extension.js +298 -1
  97. package/dist/testing/index.js +21 -60
  98. package/dist/version.d.ts +1 -1
  99. package/dist/version.js +1 -1
  100. package/dist/web-extension.js +1 -0
  101. package/dist/worker/protocol.js +1 -1
  102. package/package.json +23 -5
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Weft
2
2
 
3
- A Bun-native durable execution engine. Current release: `0.10.0`.
3
+ A Bun-native durable execution engine. Current release: `0.12.0`.
4
4
 
5
5
  Install the library from npm as `@lostgradient/weft`:
6
6
 
@@ -105,6 +105,8 @@ That's the core loop: `workflow({ name })` is a **chained builder** that co-loca
105
105
 
106
106
  Passing an explicitly empty workflow map is the same default-registry boot shape as omitting `workflows`: `Engine.create({ workflows: {} })` recovers after registration and returns an engine whose TypeScript type is compatible with default-registry consumers such as `serve({ engine })`. Use a non-empty `workflows` map when you want TypeScript to narrow `engine.start(...)` to the registered names.
107
107
 
108
+ Serverless hosts that cannot keep process-local intervals alive can set `backgroundTasks: 'manual'` and call `await engine.runMaintenance()` from an alarm or Cron wake-up to drive timers, scheduled starts, update-response cleanup, retention, and alert evaluation. Await each maintenance cycle before starting another one so host-driven ticks do not overlap.
109
+
108
110
  If you'd rather wire things up by hand — useful for tests, isolating engines onto separate storage scopes via `ScopedStorage`, or adding new workflows after the engine starts up — `new Engine({ storage })`, `engine.register(workflow)` or `engine.registerWorkflows({ ... })`, and `await engine.recoverAll()` are the underlying primitives. Each `engine.register(workflow)` call returns the engine with that workflow's name and types baked in, so `engine.start('welcome', ...)` autocompletes immediately.
109
111
 
110
112
  When a workflow needs a live host capability that cannot be checkpointed, pass it as per-run `services`:
@@ -115,6 +117,8 @@ const handle = await engine.start('welcome', { name: 'Steve' }, { services: { cr
115
117
 
116
118
  Inside inline workflows, read that value from `ctx.services` and narrow it to your application type. Weft never writes the service object into checkpoints; it persists only a presence marker so `Engine.create({ resolveWorkflowServices })` can rebuild the service value during fresh-process recovery before the generator advances. Scheduled runs also carry `schedule.id` and, when the grid timestamp is retained, `schedule.occurrence` into the resolver across recovery, so host services can branch on schedule origin without duplicating that identity in workflow input. Do not use `services` for durable data, and do not pass it in Worker execution mode — non-serializable values cannot cross to a Worker.
117
119
 
120
+ When recovery also needs to rebuild live host surfaces such as progress emitters or adapters, opt out of automatic recovery first with `Engine.create({ recover: false, ... })`, then call `await engine.recoverAll({ onRecoveredWorkflow })` after any host surfaces are ready. Weft awaits the hook after services are re-provided and before the recovered generator advances, and a hook failure fails only that recovered run with a system failure category.
121
+
118
122
  > [!NOTE]
119
123
  > The chained builder also accepts `.signals({...})`, `.updates({...})`, `.queries({...})`, and `.searchAttributes({...})`. Each can be called at most once before `.execute(fn)`; the type system flips a phantom flag so a duplicate call fails to typecheck, and the runtime mirrors the same invariant. These maps don't introduce new runtime gating — they're type hints that thread into `ctx.run()`, `ctx.waitForSignal()`, `ctx.waitForUpdate()`, and friends so your editor autocompletes and your code typechecks. The underlying dispatch paths are unchanged.
120
124
 
@@ -147,7 +151,9 @@ Because recovery never re-executes the workflow from the beginning, your workflo
147
151
 
148
152
  ### Durable Workflows
149
153
 
150
- Generator functions with automatic checkpointing at every `yield*` boundary. Activities, sleeps, signals, condition gates with `ctx.waitUntil()`, queries, updates, structured logs with `ctx.log`, parallel execution via `ctx.all()`, race semantics via `ctx.race()`, memoization via `ctx.memo()`, sagas via `ctx.saga()`, child workflows, and forks. Plain async helpers called from inline `ctx.memo()` callbacks can use `durableActivity()` for activity-level retry, heartbeat, reconciliation, and observability without converting the helper stack to generators. `ctx.sleep()` uses replay-stable durable timer keys, so a workflow that crashes while parked on a sleep resumes the same timer instead of orphaning the old one. `ctx.all()` and `ctx.race()` can branch over activities, sleeps, and signal waits; use `ctx.race([ctx.waitForSignal(name), ctx.sleep(timeout)])` for signal timeouts instead of placing an unbounded signal wait directly in `ctx.all()`.
154
+ Generator functions with automatic checkpointing at every `yield*` boundary. Activities, sleeps, signals, condition gates with `ctx.waitUntil()`, queries, updates, structured logs with `ctx.log`, parallel execution via `ctx.all()`, race semantics via `ctx.race()` and `ctx.raceKeyed()`, memoization via `ctx.memo()`, sagas via `ctx.saga()`, child workflows, and forks. Plain async helpers called from inline `ctx.memo()` callbacks can use `durableActivity()` for activity-level retry, heartbeat, reconciliation, and observability without converting the helper stack to generators. `ctx.sleep()` uses replay-stable durable timer keys, so a workflow that crashes while parked on a sleep resumes the same timer instead of orphaning the old one. Fired sleep timers are acknowledged only after the awakened inline workflow reaches its next durable checkpoint or terminal state, which lets Service Worker schedulers retry a timer instead of deleting the only wake-up record during an eviction window. `ctx.all()`, `ctx.race()`, and `ctx.raceKeyed()` can branch over activities, sleeps, and signal waits; use `ctx.race([ctx.waitForSignal(name), ctx.sleep(timeout)])` for signal timeouts instead of placing an unbounded signal wait directly in `ctx.all()`.
155
+
156
+ Use `ctx.raceKeyed()` when the workflow needs to know which branch won without encoding branch identity into the payload. The result is a discriminated `{ key, value }` pair, so checking `winner.key` narrows `winner.value`. For non-blocking signal drains, race a direct `ctx.waitForSignal(name)` branch against a literal `ctx.sleep(0)`: Weft checks the durable signal buffer first, consumes one already-buffered signal if present, and otherwise continues immediately. The stronger ordering is specific to zero-duration sleeps; positive timeouts keep ordinary race behavior.
151
157
 
152
158
  Every workflow context exposes `ctx.workflowId` and `ctx.workflowType`. `workflowType` is the registered name from `workflow({ name })`, so shared workflow code can log, tag, or branch on the current workflow type without closing over definition-site state.
153
159
 
@@ -250,6 +256,8 @@ The idempotency mapping intentionally outlives terminal cleanup. If retention re
250
256
 
251
257
  For stable-id re-sync flows, `engine.startOrSignal()` can replace a terminal prior run with `{ id, onTerminalConflict: 'start-new' }` when the initial signal also carries a deterministic `signalId`. Non-terminal runs are still signalled, not replaced, and restart-capable calls reject `idempotencyKey` because idempotency keys are permanent at-most-once mappings. Signal identifiers are treated as opaque user identifiers before storage-key construction, so caller-provided values that contain separator-looking text such as `anonymous:` stay explicit signal IDs instead of colliding with Weft's generated anonymous-signal sequence.
252
258
 
259
+ When a workflow drains signals with `ctx.race([ctx.waitForSignal(name), ctx.sleep(0)])` and then returns, use that stable `id`, a deterministic per-event `signalId`, and `onTerminalConflict: 'start-new'` for the corresponding `startOrSignal` calls. Delivery is serialized against terminal completion, so a signal arriving across the completion boundary is consumed by the current run or handed to its successor; a positive drain window is not needed for correctness.
260
+
253
261
  ### Search Attributes
254
262
 
255
263
  Attach indexed metadata to a workflow at runtime, then list and filter on it.