@bluelibs/runner 5.2.1 → 5.4.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 (79) hide show
  1. package/README.md +54 -1
  2. package/dist/browser/index.cjs +977 -800
  3. package/dist/browser/index.cjs.map +1 -1
  4. package/dist/browser/index.mjs +975 -801
  5. package/dist/browser/index.mjs.map +1 -1
  6. package/dist/edge/index.cjs +977 -800
  7. package/dist/edge/index.cjs.map +1 -1
  8. package/dist/edge/index.mjs +975 -801
  9. package/dist/edge/index.mjs.map +1 -1
  10. package/dist/node/node.cjs +1994 -2056
  11. package/dist/node/node.cjs.map +1 -1
  12. package/dist/node/node.mjs +1990 -2010
  13. package/dist/node/node.mjs.map +1 -1
  14. package/dist/types/definers/builders/error/fluent-builder.interface.d.ts +6 -0
  15. package/dist/types/definers/builders/error/index.d.ts +10 -1
  16. package/dist/types/definers/builders/error/types.d.ts +2 -0
  17. package/dist/types/definers/builders/event/utils.d.ts +1 -4
  18. package/dist/types/definers/builders/hook/utils.d.ts +1 -4
  19. package/dist/types/definers/builders/middleware/utils.d.ts +1 -4
  20. package/dist/types/definers/builders/resource/utils.d.ts +1 -4
  21. package/dist/types/definers/builders/shared/mergeUtils.d.ts +5 -0
  22. package/dist/types/definers/builders/task/utils.d.ts +1 -4
  23. package/dist/types/definers/builders/utils.d.ts +1 -1
  24. package/dist/types/definers/defineError.d.ts +5 -3
  25. package/dist/types/defs.d.ts +1 -0
  26. package/dist/types/errors.d.ts +20 -21
  27. package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
  28. package/dist/types/models/DependencyProcessor.d.ts +1 -1
  29. package/dist/types/models/MiddlewareManager.d.ts +2 -2
  30. package/dist/types/models/RunResult.d.ts +1 -1
  31. package/dist/types/models/Store.d.ts +15 -13
  32. package/dist/types/models/StoreRegistry.d.ts +19 -16
  33. package/dist/types/models/middleware/ResourceMiddlewareComposer.d.ts +2 -2
  34. package/dist/types/models/utils/buildDependencyGraph.d.ts +12 -0
  35. package/dist/types/models/utils/dependencyStrategies.d.ts +15 -0
  36. package/dist/types/models/utils/disposeOrder.d.ts +7 -0
  37. package/dist/types/node/durable/core/DurableResource.d.ts +24 -9
  38. package/dist/types/node/durable/core/DurableService.d.ts +15 -9
  39. package/dist/types/node/durable/core/interfaces/service.d.ts +27 -19
  40. package/dist/types/node/durable/core/interfaces/store.d.ts +1 -1
  41. package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +34 -4
  42. package/dist/types/node/durable/core/managers/ScheduleManager.d.ts +5 -3
  43. package/dist/types/node/durable/core/managers/TaskRegistry.d.ts +5 -5
  44. package/dist/types/node/durable/core/managers/WaitManager.d.ts +1 -1
  45. package/dist/types/node/durable/core/resource.d.ts +5 -5
  46. package/dist/types/node/durable/index.d.ts +1 -0
  47. package/dist/types/node/durable/resources/memoryDurableResource.d.ts +5 -5
  48. package/dist/types/node/durable/resources/redisDurableResource.d.ts +5 -5
  49. package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +19 -0
  50. package/dist/types/node/exposure/requestContext.d.ts +1 -1
  51. package/dist/types/node/exposure/resource.d.ts +7 -7
  52. package/dist/types/node/http/http-mixed-client.factory.resource.d.ts +1 -1
  53. package/dist/types/node/http/http-smart-client.factory.resource.d.ts +1 -1
  54. package/dist/types/node/node.d.ts +1 -184
  55. package/dist/types/platform/adapters/edge.d.ts +17 -0
  56. package/dist/types/platform/adapters/universal-generic.d.ts +3 -0
  57. package/dist/types/platform/index.d.ts +1 -0
  58. package/dist/types/platform/types.d.ts +7 -1
  59. package/dist/types/public.d.ts +6 -1
  60. package/dist/types/testing.d.ts +0 -1
  61. package/dist/types/tools/LockableMap.d.ts +20 -0
  62. package/dist/types/types/error.d.ts +22 -1
  63. package/dist/types/types/resource.d.ts +2 -4
  64. package/dist/types/types/symbols.d.ts +3 -3
  65. package/dist/types/types/tagged.d.ts +18 -0
  66. package/dist/types/types/task.d.ts +1 -1
  67. package/dist/ui/assets/index-Bo7Gi6Vq.js +141 -0
  68. package/dist/ui/assets/index-Y_9aLumt.css +1 -0
  69. package/dist/ui/index.html +2 -3
  70. package/dist/universal/index.cjs +977 -800
  71. package/dist/universal/index.cjs.map +1 -1
  72. package/dist/universal/index.mjs +975 -801
  73. package/dist/universal/index.mjs.map +1 -1
  74. package/package.json +1 -2
  75. package/readmes/AI.md +59 -12
  76. package/dist/ui/assets/index-2cb8f39f.js +0 -141
  77. package/dist/ui/assets/index-b1f988bf.css +0 -1
  78. /package/dist/types/{tunnels → tools}/buildUniversalManifest.d.ts +0 -0
  79. /package/dist/types/{processHooks.d.ts → tools/processShutdownHooks.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluelibs/runner",
3
- "version": "5.2.1",
3
+ "version": "5.4.0",
4
4
  "description": "BlueLibs Runner",
5
5
  "sideEffects": false,
6
6
  "main": "dist/universal/index.cjs",
@@ -110,7 +110,6 @@
110
110
  "@types/benchmark": "^2.1.5",
111
111
  "@types/busboy": "^1.5.4",
112
112
  "@types/express": "^5.0.3",
113
- "@types/graphql": "^0.11.3",
114
113
  "@types/jest": "^27.0.0",
115
114
  "@types/node": "^20.0.0",
116
115
  "@typescript-eslint/eslint-plugin": "8.39.0",
package/readmes/AI.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  For the landing overview, see [README.md](../README.md). For the complete guide, see [FULL_GUIDE.md](./FULL_GUIDE.md).
6
6
 
7
- **Durable Workflows (Node-only):** For persistence and crash recovery, see `DURABLE_WORKFLOWS.md`. Includes `ctx.switch()` (replay-safe branching) and `durable.describe()` (DI-accurate flow shape export) — see `DURABLE_WORKFLOWS_AI.md` for quick reference.
7
+ **Durable Workflows (Node-only):** For persistence and crash recovery, see `DURABLE_WORKFLOWS.md`. Includes `ctx.switch()` (replay-safe branching), `durable.describe()` (DI-accurate flow shape export), and `durableWorkflowTag.defaults` (default input for `describe(task)` when omitted) — see `DURABLE_WORKFLOWS_AI.md` for quick reference.
8
8
 
9
9
  ## Serializer Safety
10
10
 
@@ -85,7 +85,7 @@ await runtime.runTask(createUser, { name: "Ada" });
85
85
  Use `.fork(newId, { register, reId })` to clone a resource definition under a new id (handy for multi-instance patterns).
86
86
  Forks keep the same implementation/types but get separate runtime instances (no shared state). Use `register: "drop"` to clear registered items, or `register: "deep"` to deep-fork **registered resources** (resource tree) with new ids (non-resource registerables are not cloned/kept).
87
87
  Prefer exporting forks so other tasks/resources can depend on them.
88
- Forked resources also expose provenance at `[definitions.symbolResourceForkedFrom]` (`fromId`, `forkedAtFilePath`) for tooling/debugging.
88
+ Forked resources expose provenance at `[definitions.symbolForkedFrom]` (`fromId`) for tooling/debugging.
89
89
 
90
90
  ## Tasks
91
91
 
@@ -204,7 +204,7 @@ const cacheResources = r.middleware
204
204
 
205
205
  Attach middleware using `.middleware([auditTasks])` on the definition that owns it, and register the middleware alongside the target resource or task at the root.
206
206
 
207
- - Contract middleware: middleware can declare `Config`, `Input`, `Output` generics; tasks using it must conform (contracts intersect across `.middleware([...])` and `.tags([...])`). Collisions surface as `InputContractViolationError` / `OutputContractViolationError` in TypeScript; if you add `.inputSchema()`, ensure the schemas inferred type includes the contract shape.
207
+ - Contract middleware: middleware can declare `Config`, `Input`, `Output` generics; tasks using it must conform (contracts intersect across `.middleware([...])` and `.tags([...])`). Collisions surface as `InputContractViolationError` / `OutputContractViolationError` in TypeScript; if you add `.inputSchema()`, ensure the schema's inferred type includes the contract shape.
208
208
 
209
209
  ```ts
210
210
  type AuthConfig = { requiredRole: string };
@@ -279,7 +279,9 @@ const getHealth = r
279
279
 
280
280
  Retrieve tagged items by using `globals.resources.store` inside a hook or resource and calling `store.getTasksWithTag(tag)`.
281
281
 
282
- - Contract tags (a “smart tag”): define type contracts for task input/output (or resource config/value) via `r.tag<TConfig, TInputContract, TOutputContract>(id)`. They don’t change runtime behavior; they shape the inferred types and compose with contract middleware.
282
+ **Node durable workflows must be tagged** with `durableWorkflowTag` from `@bluelibs/runner/node` to be discoverable via `durable.getWorkflows()` at runtime. This tag is required, not optional. Workflow execution is explicit via the durable API (`durable.start(...)` / `durable.startAndWait(...)`). The tag is discovery metadata only; `startAndWait(...)` provides the unified result envelope `{ durable: { executionId }, data }`.
283
+
284
+ - Contract tags (a "smart tag"): define type contracts for task input/output (or resource config/value) via `r.tag<TConfig, TInputContract, TOutputContract>(id)`. They don't change runtime behavior; they shape the inferred types and compose with contract middleware.
283
285
  - Smart tags: built-in tags like `globals.tags.system`, `globals.tags.debug`, and `globals.tags.excludeFromGlobalHooks` change framework behavior; use them for per-component debug or to opt out of global hooks.
284
286
 
285
287
  ```ts
@@ -296,9 +298,9 @@ const getUser = r
296
298
 
297
299
  ## Async Context
298
300
 
299
- Async Context provides per-request/thread-local state via the platform's `AsyncLocalStorage` (Node). Use the fluent builder under `r.asyncContext` or the classic `asyncContext({ ... })` export.
301
+ Async Context provides per-request/thread-local state via the platform's `AsyncLocalStorage` (Node, and Deno when `AsyncLocalStorage` is available). Use the fluent builder under `r.asyncContext` or the classic `asyncContext({ ... })` export.
300
302
 
301
- > **Platform Note**: `AsyncLocalStorage` is Node.js-only. Async Context is unavailable in browsers/edge runtimes.
303
+ > **Platform Note**: Async Context requires `AsyncLocalStorage`. It is available in Node.js and Deno (when exposed), and unavailable in browser runtimes.
302
304
 
303
305
  ```ts
304
306
  import { r } from "@bluelibs/runner";
@@ -325,7 +327,7 @@ r.task("task").middleware([requestContext.require()]);
325
327
  - `.configSchema(schema)` (optional) validates the value passed to `provide(...)`.
326
328
  - If you don't provide `serialize`/`parse`, Runner uses its default serializer to preserve Dates, RegExp, etc.
327
329
  - You can also inject async contexts as dependencies; the injected value is the helper itself. Contexts must be registered to be used.
328
- - Optional dependencies: `dependencies({ requestContext: requestContext.optional() })` injects `undefined` if the context isnt registered.
330
+ - Optional dependencies: `dependencies({ requestContext: requestContext.optional() })` injects `undefined` if the context isn't registered.
329
331
 
330
332
  ```ts
331
333
  const whoAmI = r
@@ -337,6 +339,37 @@ const whoAmI = r
337
339
  const app = r.resource("app").register([requestContext, whoAmI]).build();
338
340
  ```
339
341
 
342
+ ## Queue
343
+
344
+ `Queue` is a cooperative FIFO task queue. Tasks run one-after-another, with dead-lock detection and graceful disposal.
345
+
346
+ The global resource `globals.resources.queue` provides a named queue factory — each `id` gets its own isolated `Queue` instance.
347
+
348
+ **Key methods:**
349
+
350
+ - `queue.run(id, task)` — schedule `task` (receives an `AbortSignal`) on the queue identified by `id`; creates the queue lazily.
351
+
352
+ **Event lifecycle:** `enqueue` → `start` → `finish` | `error`. On disposal: `disposed`. On cancel: `cancel`.
353
+
354
+ ```ts
355
+ import { r, run, globals } from "@bluelibs/runner";
356
+
357
+ const processOrder = r
358
+ .task("app.tasks.processOrder")
359
+ .dependencies({ queue: globals.resources.queue })
360
+ .run(async (input: { orderId: string }, { queue }) => {
361
+ // Tasks with the same orderId run sequentially
362
+ return queue.run(input.orderId, async (signal) => {
363
+ if (signal.aborted) return;
364
+ // ... process order
365
+ return { processed: true };
366
+ });
367
+ })
368
+ .build();
369
+ ```
370
+
371
+ For advanced usage, import `Queue` directly and use `on(type, handler)` / `once(type, handler)` to observe lifecycle events. Call `dispose({ cancel: true })` to abort in-flight work via the `AbortSignal`.
372
+
340
373
  ## Errors
341
374
 
342
375
  Define typed, namespaced errors with a fluent builder. Built helpers expose `throw` and `is`:
@@ -347,22 +380,36 @@ import { r } from "@bluelibs/runner";
347
380
  // Fluent builder
348
381
  const AppError = r
349
382
  .error<{ code: number; message: string }>("app.errors.AppError")
383
+ .httpCode(400)
350
384
  .dataSchema({ parse: (value) => value })
385
+ .format((d) => `[${d.code}] ${d.message}`)
386
+ .remediation("Check the request payload and retry with valid data.")
351
387
  .build();
352
388
 
353
389
  try {
354
390
  AppError.throw({ code: 400, message: "Oops" });
355
391
  } catch (err) {
356
392
  if (AppError.is(err)) {
357
- // Do something
393
+ // err.message -> "[400] Oops\n\nRemediation: Check the request payload and retry with valid data."
394
+ // err.httpCode -> 400
395
+ // err.remediation -> "Check the request payload and retry with valid data."
396
+ // AppError.httpCode -> 400
358
397
  }
359
398
  }
360
399
  ```
361
400
 
362
401
  - Recommended ids: `{domain}.errors.{PascalCaseName}` (for example: `app.errors.InvalidCredentials`).
363
- - The thrown `Error` has `name = id` and `message = format(data)`. If you dont provide `.format(...)`, the default is `JSON.stringify(data)`.
402
+ - The thrown `Error` has `name = id` and `message = format(data)`. If you don't provide `.format(...)`, the default is `JSON.stringify(data)`.
403
+ - `.httpCode(number)` sets an HTTP status for the error helper (must be an integer in `100..599`). The helper exposes `helper.httpCode`, and thrown typed errors expose `error.httpCode`.
404
+ - `.remediation(stringOrFn)` attaches fix-it advice. Accepts a static string or `(data) => string`. When present, `error.message` and `error.toString()` include `\n\nRemediation: <advice>`. The raw advice is also available via `error.remediation`.
364
405
  - `message` is not required in the data unless your custom formatter expects it.
365
406
  - Declare a task/resource error contract with `.throws([AppError])` (or ids). This is declarative only and does not imply DI.
407
+ - Use `r.error.is(err)` to check if an error is _any_ Runner error (not just a specific one). This type guard narrows to `RunnerError` with `id`, `data`, `httpCode`, and `remediation` properties. Useful in catch blocks or error filters:
408
+ ```ts
409
+ if (r.error.is(err)) {
410
+ console.error(`Runner error: ${err.id} (${err.httpCode || "N/A"})`);
411
+ }
412
+ ```
366
413
  - For HTTP/tunnel clients, you can pass an `errorRegistry` to rethrow remote errors as your typed helpers (optional):
367
414
 
368
415
  ```ts
@@ -405,7 +452,7 @@ const app = r
405
452
 
406
453
  - `run(root, options)` wires dependencies, initializes resources, and returns helpers: `runTask`, `emitEvent`, `getResourceValue`, `getResourceConfig`, `store`, `logger`, and `dispose`.
407
454
  - Run options highlights: `debug` (normal/verbose or custom config), `logs` (printThreshold/strategy/buffer), `errorBoundary` and `onUnhandledError`, `shutdownHooks`, `dryRun`.
408
- - Task interceptors: inside resource init, call `deps.someTask.intercept(async (next, input) => next(input))` to wrap a single task execution at runtime (runs inside middleware; wont run if middleware short-circuits).
455
+ - Task interceptors: inside resource init, call `deps.someTask.intercept(async (next, input) => next(input))` to wrap a single task execution at runtime (runs inside middleware; won't run if middleware short-circuits).
409
456
  - Shutdown hooks: install signal listeners to call `dispose` (default in `run`).
410
457
  - Unhandled errors: `onUnhandledError` receives a structured context (kind and source) for telemetry or controlled shutdown.
411
458
 
@@ -457,7 +504,7 @@ const app = r
457
504
 
458
505
  Tunnels let you call Runner tasks/events across a process boundary over a small HTTP surface (Node-only exposure via `nodeExposure`), while preserving task ids, middleware, validation, typed errors, and async context.
459
506
 
460
- For no call-site changes”, register a client-mode tunnel resource tagged with `globals.tags.tunnel` plus phantom tasks for the remote ids; the tunnel middleware auto-routes selected tasks/events to an HTTP client. For explicit boundaries, create a client once and call `client.task(id, input)` / `client.event(id, payload)` directly. Full guide: `readmes/TUNNELS.md`.
507
+ For "no call-site changes", register a client-mode tunnel resource tagged with `globals.tags.tunnel` plus phantom tasks for the remote ids; the tunnel middleware auto-routes selected tasks/events to an HTTP client. For explicit boundaries, create a client once and call `client.task(id, input)` / `client.event(id, payload)` directly. Full guide: `readmes/TUNNELS.md`.
461
508
 
462
509
  Node client note: prefer `createHttpMixedClient` (it uses the serialized-JSON path via Runner `Serializer` + `fetch` when possible and switches to the streaming-capable Smart path when needed). If a task may return a stream even for plain JSON inputs (ex: downloads), set `forceSmart` on Mixed (or use `createHttpSmartClient` directly).
463
510
 
@@ -500,7 +547,7 @@ const serializerSetup = r
500
547
 
501
548
  Use `new Serializer()` when you need a standalone instance outside DI.
502
549
 
503
- Note on files: The File you see in tunnels is not a custom serializer type. Runner uses a dedicated `$runnerFile: "File"` sentinel in inputs which the tunnel client/server convert to multipart streams via a manifest. File handling is performed by the tunnel layer (manifest hydration and multipart), not by the serializer. Keep using `createWebFile`/`createNodeFile` for uploads.
550
+ Note on files: The "File" you see in tunnels is not a custom serializer type. Runner uses a dedicated `$runnerFile: "File"` sentinel in inputs which the tunnel client/server convert to multipart streams via a manifest. File handling is performed by the tunnel layer (manifest hydration and multipart), not by the serializer. Keep using `createWebFile`/`createNodeFile` for uploads.
504
551
 
505
552
  ## Testing
506
553