@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.
- package/README.md +54 -1
- package/dist/browser/index.cjs +977 -800
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.mjs +975 -801
- package/dist/browser/index.mjs.map +1 -1
- package/dist/edge/index.cjs +977 -800
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.mjs +975 -801
- package/dist/edge/index.mjs.map +1 -1
- package/dist/node/node.cjs +1994 -2056
- package/dist/node/node.cjs.map +1 -1
- package/dist/node/node.mjs +1990 -2010
- package/dist/node/node.mjs.map +1 -1
- package/dist/types/definers/builders/error/fluent-builder.interface.d.ts +6 -0
- package/dist/types/definers/builders/error/index.d.ts +10 -1
- package/dist/types/definers/builders/error/types.d.ts +2 -0
- package/dist/types/definers/builders/event/utils.d.ts +1 -4
- package/dist/types/definers/builders/hook/utils.d.ts +1 -4
- package/dist/types/definers/builders/middleware/utils.d.ts +1 -4
- package/dist/types/definers/builders/resource/utils.d.ts +1 -4
- package/dist/types/definers/builders/shared/mergeUtils.d.ts +5 -0
- package/dist/types/definers/builders/task/utils.d.ts +1 -4
- package/dist/types/definers/builders/utils.d.ts +1 -1
- package/dist/types/definers/defineError.d.ts +5 -3
- package/dist/types/defs.d.ts +1 -0
- package/dist/types/errors.d.ts +20 -21
- package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
- package/dist/types/models/DependencyProcessor.d.ts +1 -1
- package/dist/types/models/MiddlewareManager.d.ts +2 -2
- package/dist/types/models/RunResult.d.ts +1 -1
- package/dist/types/models/Store.d.ts +15 -13
- package/dist/types/models/StoreRegistry.d.ts +19 -16
- package/dist/types/models/middleware/ResourceMiddlewareComposer.d.ts +2 -2
- package/dist/types/models/utils/buildDependencyGraph.d.ts +12 -0
- package/dist/types/models/utils/dependencyStrategies.d.ts +15 -0
- package/dist/types/models/utils/disposeOrder.d.ts +7 -0
- package/dist/types/node/durable/core/DurableResource.d.ts +24 -9
- package/dist/types/node/durable/core/DurableService.d.ts +15 -9
- package/dist/types/node/durable/core/interfaces/service.d.ts +27 -19
- package/dist/types/node/durable/core/interfaces/store.d.ts +1 -1
- package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +34 -4
- package/dist/types/node/durable/core/managers/ScheduleManager.d.ts +5 -3
- package/dist/types/node/durable/core/managers/TaskRegistry.d.ts +5 -5
- package/dist/types/node/durable/core/managers/WaitManager.d.ts +1 -1
- package/dist/types/node/durable/core/resource.d.ts +5 -5
- package/dist/types/node/durable/index.d.ts +1 -0
- package/dist/types/node/durable/resources/memoryDurableResource.d.ts +5 -5
- package/dist/types/node/durable/resources/redisDurableResource.d.ts +5 -5
- package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +19 -0
- package/dist/types/node/exposure/requestContext.d.ts +1 -1
- package/dist/types/node/exposure/resource.d.ts +7 -7
- package/dist/types/node/http/http-mixed-client.factory.resource.d.ts +1 -1
- package/dist/types/node/http/http-smart-client.factory.resource.d.ts +1 -1
- package/dist/types/node/node.d.ts +1 -184
- package/dist/types/platform/adapters/edge.d.ts +17 -0
- package/dist/types/platform/adapters/universal-generic.d.ts +3 -0
- package/dist/types/platform/index.d.ts +1 -0
- package/dist/types/platform/types.d.ts +7 -1
- package/dist/types/public.d.ts +6 -1
- package/dist/types/testing.d.ts +0 -1
- package/dist/types/tools/LockableMap.d.ts +20 -0
- package/dist/types/types/error.d.ts +22 -1
- package/dist/types/types/resource.d.ts +2 -4
- package/dist/types/types/symbols.d.ts +3 -3
- package/dist/types/types/tagged.d.ts +18 -0
- package/dist/types/types/task.d.ts +1 -1
- package/dist/ui/assets/index-Bo7Gi6Vq.js +141 -0
- package/dist/ui/assets/index-Y_9aLumt.css +1 -0
- package/dist/ui/index.html +2 -3
- package/dist/universal/index.cjs +977 -800
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.mjs +975 -801
- package/dist/universal/index.mjs.map +1 -1
- package/package.json +1 -2
- package/readmes/AI.md +59 -12
- package/dist/ui/assets/index-2cb8f39f.js +0 -141
- package/dist/ui/assets/index-b1f988bf.css +0 -1
- /package/dist/types/{tunnels → tools}/buildUniversalManifest.d.ts +0 -0
- /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.
|
|
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)
|
|
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
|
|
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 schema
|
|
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
|
-
|
|
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
|
|
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 isn
|
|
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
|
-
//
|
|
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 don
|
|
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; won
|
|
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
|
|
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
|
|
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
|
|