@bluelibs/runner 5.2.0 → 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.
- package/README.md +73 -10
- package/dist/browser/index.cjs +149 -59
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.mjs +149 -60
- package/dist/browser/index.mjs.map +1 -1
- package/dist/edge/index.cjs +149 -59
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.mjs +149 -60
- package/dist/edge/index.mjs.map +1 -1
- package/dist/node/node.cjs +384 -201
- package/dist/node/node.cjs.map +1 -1
- package/dist/node/node.mjs +382 -202
- 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/defineError.d.ts +5 -3
- package/dist/types/defs.d.ts +2 -0
- package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
- package/dist/types/models/Store.d.ts +5 -3
- package/dist/types/models/StoreRegistry.d.ts +5 -3
- package/dist/types/node/durable/core/DurableResource.d.ts +23 -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/index.d.ts +1 -0
- package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +14 -0
- package/dist/types/node/node.d.ts +2 -1
- package/dist/types/public.d.ts +4 -1
- package/dist/types/testing.d.ts +0 -1
- 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 +2 -2
- package/dist/types/types/tagged.d.ts +18 -0
- package/dist/universal/index.cjs +149 -59
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.mjs +149 -60
- package/dist/universal/index.mjs.map +1 -1
- package/package.json +2 -2
- package/readmes/AI.md +25 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluelibs/runner",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "BlueLibs Runner",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "dist/universal/index.cjs",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"benchmark": "jest --config config/jest/jest.bench.config.js --verbose --runInBand",
|
|
104
104
|
"benchmark:json": "BENCHMARK_OUTPUT=./config/benchmarks/benchmark-results.json NODE_OPTIONS=--expose-gc npm run benchmark",
|
|
105
105
|
"benchmark:compare": "node ./scripts/compare-benchmarks.mjs ./config/benchmarks/baseline.json ./config/benchmarks/benchmark-results.json ./config/benchmarks/benchmarks.config.json",
|
|
106
|
-
"guide:compose": "node ./scripts/compose-
|
|
106
|
+
"guide:compose": "node ./scripts/compose-guide.mjs"
|
|
107
107
|
},
|
|
108
108
|
"devDependencies": {
|
|
109
109
|
"@types/amqplib": "^0.10.7",
|
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
|
|
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
|
|
@@ -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
|
|
@@ -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
|
-
//
|
|
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 don
|
|
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; won
|
|
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
|
|
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
|
|
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
|
|