@bluelibs/runner 5.2.1 → 5.3.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 (43) hide show
  1. package/README.md +54 -1
  2. package/dist/browser/index.cjs +149 -59
  3. package/dist/browser/index.cjs.map +1 -1
  4. package/dist/browser/index.mjs +149 -60
  5. package/dist/browser/index.mjs.map +1 -1
  6. package/dist/edge/index.cjs +149 -59
  7. package/dist/edge/index.cjs.map +1 -1
  8. package/dist/edge/index.mjs +149 -60
  9. package/dist/edge/index.mjs.map +1 -1
  10. package/dist/node/node.cjs +383 -201
  11. package/dist/node/node.cjs.map +1 -1
  12. package/dist/node/node.mjs +382 -202
  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/defineError.d.ts +5 -3
  18. package/dist/types/defs.d.ts +1 -0
  19. package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
  20. package/dist/types/models/Store.d.ts +5 -3
  21. package/dist/types/models/StoreRegistry.d.ts +5 -3
  22. package/dist/types/node/durable/core/DurableResource.d.ts +23 -9
  23. package/dist/types/node/durable/core/DurableService.d.ts +15 -9
  24. package/dist/types/node/durable/core/interfaces/service.d.ts +27 -19
  25. package/dist/types/node/durable/core/interfaces/store.d.ts +1 -1
  26. package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +34 -4
  27. package/dist/types/node/durable/core/managers/ScheduleManager.d.ts +5 -3
  28. package/dist/types/node/durable/core/managers/TaskRegistry.d.ts +5 -5
  29. package/dist/types/node/durable/core/managers/WaitManager.d.ts +1 -1
  30. package/dist/types/node/durable/index.d.ts +1 -0
  31. package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +14 -0
  32. package/dist/types/public.d.ts +4 -1
  33. package/dist/types/testing.d.ts +0 -1
  34. package/dist/types/types/error.d.ts +22 -1
  35. package/dist/types/types/resource.d.ts +2 -4
  36. package/dist/types/types/symbols.d.ts +2 -2
  37. package/dist/types/types/tagged.d.ts +18 -0
  38. package/dist/universal/index.cjs +149 -59
  39. package/dist/universal/index.cjs.map +1 -1
  40. package/dist/universal/index.mjs +149 -60
  41. package/dist/universal/index.mjs.map +1 -1
  42. package/package.json +1 -1
  43. package/readmes/AI.md +25 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluelibs/runner",
3
- "version": "5.2.1",
3
+ "version": "5.3.0",
4
4
  "description": "BlueLibs Runner",
5
5
  "sideEffects": false,
6
6
  "main": "dist/universal/index.cjs",
package/readmes/AI.md CHANGED
@@ -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
@@ -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
@@ -347,22 +349,36 @@ import { r } from "@bluelibs/runner";
347
349
  // Fluent builder
348
350
  const AppError = r
349
351
  .error<{ code: number; message: string }>("app.errors.AppError")
352
+ .httpCode(400)
350
353
  .dataSchema({ parse: (value) => value })
354
+ .format((d) => `[${d.code}] ${d.message}`)
355
+ .remediation("Check the request payload and retry with valid data.")
351
356
  .build();
352
357
 
353
358
  try {
354
359
  AppError.throw({ code: 400, message: "Oops" });
355
360
  } catch (err) {
356
361
  if (AppError.is(err)) {
357
- // Do something
362
+ // err.message -> "[400] Oops\n\nRemediation: Check the request payload and retry with valid data."
363
+ // err.httpCode -> 400
364
+ // err.remediation -> "Check the request payload and retry with valid data."
365
+ // AppError.httpCode -> 400
358
366
  }
359
367
  }
360
368
  ```
361
369
 
362
370
  - 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)`.
371
+ - The thrown `Error` has `name = id` and `message = format(data)`. If you don't provide `.format(...)`, the default is `JSON.stringify(data)`.
372
+ - `.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`.
373
+ - `.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
374
  - `message` is not required in the data unless your custom formatter expects it.
365
375
  - Declare a task/resource error contract with `.throws([AppError])` (or ids). This is declarative only and does not imply DI.
376
+ - 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:
377
+ ```ts
378
+ if (r.error.is(err)) {
379
+ console.error(`Runner error: ${err.id} (${err.httpCode || "N/A"})`);
380
+ }
381
+ ```
366
382
  - For HTTP/tunnel clients, you can pass an `errorRegistry` to rethrow remote errors as your typed helpers (optional):
367
383
 
368
384
  ```ts
@@ -405,7 +421,7 @@ const app = r
405
421
 
406
422
  - `run(root, options)` wires dependencies, initializes resources, and returns helpers: `runTask`, `emitEvent`, `getResourceValue`, `getResourceConfig`, `store`, `logger`, and `dispose`.
407
423
  - 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).
424
+ - 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
425
  - Shutdown hooks: install signal listeners to call `dispose` (default in `run`).
410
426
  - Unhandled errors: `onUnhandledError` receives a structured context (kind and source) for telemetry or controlled shutdown.
411
427
 
@@ -457,7 +473,7 @@ const app = r
457
473
 
458
474
  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
475
 
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`.
476
+ 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
477
 
462
478
  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
479
 
@@ -500,7 +516,7 @@ const serializerSetup = r
500
516
 
501
517
  Use `new Serializer()` when you need a standalone instance outside DI.
502
518
 
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.
519
+ 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
520
 
505
521
  ## Testing
506
522