@bluelibs/runner 5.3.0 → 5.5.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 +23 -22
- package/dist/browser/index.cjs +6159 -3049
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.mjs +6157 -3046
- package/dist/browser/index.mjs.map +1 -1
- package/dist/edge/index.cjs +6159 -3049
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.mjs +6157 -3046
- package/dist/edge/index.mjs.map +1 -1
- package/dist/node/node.cjs +10843 -10814
- package/dist/node/node.cjs.map +1 -1
- package/dist/node/node.mjs +10801 -10770
- package/dist/node/node.mjs.map +1 -1
- package/dist/types/definers/builders/asyncContext/fluent-builder.interface.d.ts +4 -0
- package/dist/types/definers/builders/error/fluent-builder.interface.d.ts +8 -1
- package/dist/types/definers/builders/error/index.d.ts +1 -0
- package/dist/types/definers/builders/error/types.d.ts +2 -1
- package/dist/types/definers/builders/error/utils.d.ts +1 -0
- package/dist/types/definers/builders/event/fluent-builder.interface.d.ts +10 -0
- package/dist/types/definers/builders/event/utils.d.ts +1 -4
- package/dist/types/definers/builders/hook/fluent-builder.interface.d.ts +3 -0
- package/dist/types/definers/builders/hook/types.d.ts +3 -0
- package/dist/types/definers/builders/hook/utils.d.ts +1 -4
- package/dist/types/definers/builders/middleware/index.d.ts +4 -2
- package/dist/types/definers/builders/middleware/resource.interface.d.ts +7 -0
- package/dist/types/definers/builders/middleware/task.interface.d.ts +7 -0
- package/dist/types/definers/builders/middleware/types.d.ts +22 -3
- package/dist/types/definers/builders/middleware/utils.d.ts +3 -4
- package/dist/types/definers/builders/override/hook.d.ts +3 -0
- package/dist/types/definers/builders/override/index.d.ts +5 -0
- package/dist/types/definers/builders/resource/fluent-builder.interface.d.ts +15 -0
- package/dist/types/definers/builders/resource/index.d.ts +0 -1
- package/dist/types/definers/builders/resource/types.d.ts +1 -0
- 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/tag/fluent-builder.interface.d.ts +4 -0
- package/dist/types/definers/builders/tag/utils.d.ts +1 -0
- package/dist/types/definers/builders/task/fluent-builder.interface.d.ts +4 -0
- package/dist/types/definers/builders/task/index.d.ts +2 -2
- package/dist/types/definers/builders/task/utils.d.ts +1 -4
- package/dist/types/definers/builders/utils.d.ts +7 -2
- package/dist/types/definers/defineError.d.ts +12 -2
- package/dist/types/definers/resourceFork.d.ts +3 -1
- package/dist/types/definers/tools.d.ts +12 -12
- package/dist/types/errors/domain-error-ids.d.ts +44 -0
- package/dist/types/errors/domain-runtime.errors.d.ts +136 -0
- package/dist/types/errors/foundation.errors.d.ts +125 -0
- package/dist/types/errors/generic.errors.d.ts +1 -0
- package/dist/types/errors/model-runtime.errors.d.ts +20 -0
- package/dist/types/errors.d.ts +5 -74
- package/dist/types/globals/cron/cron-parser.d.ts +4 -0
- package/dist/types/globals/cron/cron.errors.d.ts +8 -0
- package/dist/types/globals/cron/cron.resource.d.ts +12 -0
- package/dist/types/globals/cron/cron.tag.d.ts +2 -0
- package/dist/types/globals/cron/types.d.ts +25 -0
- package/dist/types/globals/globalMiddleware.d.ts +87 -13
- package/dist/types/globals/globalResources.d.ts +110 -24
- package/dist/types/globals/globalTags.d.ts +10 -2
- package/dist/types/globals/middleware/cache.middleware.d.ts +173 -13
- package/dist/types/globals/middleware/circuitBreaker.middleware.d.ts +5 -1
- package/dist/types/globals/middleware/concurrency.middleware.d.ts +3 -0
- package/dist/types/globals/middleware/fallback.middleware.d.ts +8 -1
- package/dist/types/globals/middleware/rateLimit.middleware.d.ts +4 -1
- package/dist/types/globals/middleware/retry.middleware.d.ts +5 -0
- package/dist/types/globals/middleware/temporal.middleware.d.ts +19 -19
- package/dist/types/globals/middleware/timeout.middleware.d.ts +4 -1
- package/dist/types/globals/resources/eventManager.resource.d.ts +2 -0
- package/dist/types/globals/resources/logger.resource.d.ts +2 -0
- package/dist/types/globals/resources/middlewareManager.resource.d.ts +2 -0
- package/dist/types/globals/resources/queue.resource.d.ts +2 -0
- package/dist/types/globals/resources/runtime.resource.d.ts +2 -0
- package/dist/types/globals/resources/serializer.resource.d.ts +2 -0
- package/dist/types/globals/resources/store.resource.d.ts +2 -0
- package/dist/types/globals/resources/taskRunner.resource.d.ts +2 -0
- package/dist/types/globals/resources/tunnel/plan.d.ts +6 -3
- package/dist/types/globals/resources/tunnel/tunnel.policy.tag.d.ts +2 -0
- package/dist/types/globals/resources/tunnel/types.d.ts +7 -2
- package/dist/types/globals/types.d.ts +6 -1
- package/dist/types/models/BuiltinsRegistry.d.ts +2 -0
- package/dist/types/models/DependencyProcessor.d.ts +27 -6
- package/dist/types/models/EventManager.d.ts +9 -9
- package/dist/types/models/LogPrinter.d.ts +5 -5
- package/dist/types/models/Logger.d.ts +16 -16
- package/dist/types/models/MiddlewareManager.d.ts +20 -2
- package/dist/types/models/OverrideManager.d.ts +4 -1
- package/dist/types/models/Queue.d.ts +2 -0
- package/dist/types/models/RunResult.d.ts +290 -26
- package/dist/types/models/Store.d.ts +29 -12
- package/dist/types/models/StoreRegistry.d.ts +16 -13
- package/dist/types/models/StoreValidator.d.ts +2 -0
- package/dist/types/models/TaskRunner.d.ts +3 -1
- package/dist/types/models/VisibilityTracker.d.ts +75 -0
- package/dist/types/models/dependency-processor/DependencyExtractor.d.ts +23 -0
- package/dist/types/models/dependency-processor/HookEventBuffer.d.ts +15 -0
- package/dist/types/models/dependency-processor/ResourceScheduler.d.ts +10 -0
- package/dist/types/models/event/EmissionExecutor.d.ts +5 -4
- package/dist/types/models/event/ListenerRegistry.d.ts +2 -0
- package/dist/types/models/index.d.ts +1 -3
- package/dist/types/models/middleware/InterceptorRegistry.d.ts +13 -4
- package/dist/types/models/middleware/ResourceMiddlewareComposer.d.ts +3 -2
- package/dist/types/models/middleware/ValidationHelper.d.ts +3 -6
- 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 +11 -0
- package/dist/types/models/utils/resourceDependencyIds.d.ts +1 -0
- package/dist/types/node/durable/bus/MemoryEventBus.d.ts +10 -1
- package/dist/types/node/durable/bus/NoopEventBus.d.ts +1 -1
- package/dist/types/node/durable/bus/RedisEventBus.d.ts +8 -2
- package/dist/types/node/durable/core/CronParser.d.ts +2 -13
- package/dist/types/node/durable/core/DurableResource.d.ts +15 -32
- package/dist/types/node/durable/core/DurableService.d.ts +1 -0
- package/dist/types/node/durable/core/DurableWorker.d.ts +4 -2
- package/dist/types/node/durable/core/createRunnerDurableRuntime.d.ts +2 -0
- package/dist/types/node/durable/core/interfaces/bus.d.ts +1 -1
- package/dist/types/node/durable/core/interfaces/resource.d.ts +61 -0
- package/dist/types/node/durable/core/interfaces/service.d.ts +2 -0
- package/dist/types/node/durable/core/interfaces/store.d.ts +5 -0
- package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +1 -0
- package/dist/types/node/durable/core/managers/PollingManager.d.ts +3 -1
- package/dist/types/node/durable/core/resource.d.ts +6 -5
- package/dist/types/node/durable/core/utils.d.ts +11 -1
- package/dist/types/node/durable/queue/RabbitMQQueue.d.ts +4 -0
- package/dist/types/node/durable/resources/memoryDurableResource.d.ts +7 -5
- package/dist/types/node/durable/resources/redisDurableResource.d.ts +7 -5
- package/dist/types/node/durable/store/MemoryStore.d.ts +2 -0
- package/dist/types/node/durable/store/RedisStore.d.ts +1 -0
- package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +6 -1
- package/dist/types/node/exposure/allowList.d.ts +2 -1
- package/dist/types/node/exposure/handlers/contextWrapper.d.ts +6 -2
- package/dist/types/node/exposure/handlers/errorHandlers.d.ts +1 -0
- package/dist/types/node/exposure/handlers/eventHandler.d.ts +1 -0
- package/dist/types/node/exposure/handlers/taskHandler.d.ts +3 -1
- package/dist/types/node/exposure/logging.d.ts +1 -0
- package/dist/types/node/exposure/requestContext.d.ts +1 -1
- package/dist/types/node/exposure/requestHandlers.d.ts +3 -1
- package/dist/types/node/exposure/requestIdentity.d.ts +3 -0
- package/dist/types/node/exposure/resource.d.ts +7 -7
- package/dist/types/node/exposure/resourceTypes.d.ts +6 -0
- package/dist/types/node/exposure/serverLifecycle.d.ts +2 -0
- 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/node/tunnel/allowlist.d.ts +10 -1
- package/dist/types/platform/adapters/browser.d.ts +1 -1
- package/dist/types/platform/adapters/edge.d.ts +17 -0
- package/dist/types/platform/adapters/node-als.d.ts +1 -1
- package/dist/types/platform/adapters/node.d.ts +1 -1
- package/dist/types/platform/adapters/universal-generic.d.ts +5 -2
- package/dist/types/platform/adapters/universal.d.ts +1 -1
- package/dist/types/platform/index.d.ts +3 -2
- package/dist/types/platform/types.d.ts +8 -2
- package/dist/types/public.d.ts +376 -20
- package/dist/types/run.d.ts +17 -1
- package/dist/types/serializer/Serializer.d.ts +1 -41
- package/dist/types/serializer/errors.d.ts +8 -0
- package/dist/types/serializer/marker-key-escapes.d.ts +2 -0
- package/dist/types/serializer/serialize-utils.d.ts +1 -1
- package/dist/types/serializer/type-registry.d.ts +0 -1
- package/dist/types/serializer/types.d.ts +7 -6
- package/dist/types/testing.d.ts +1 -0
- package/dist/types/tools/LockableMap.d.ts +20 -0
- package/dist/types/tools/getAllThrows.d.ts +13 -0
- package/dist/types/tools/throws.d.ts +1 -1
- package/dist/types/types/error.d.ts +25 -2
- package/dist/types/types/event.d.ts +35 -0
- package/dist/types/types/hook.d.ts +8 -0
- package/dist/types/types/resource.d.ts +13 -1
- package/dist/types/types/resourceMiddleware.d.ts +8 -0
- package/dist/types/types/runner.d.ts +56 -0
- package/dist/types/types/storeTypes.d.ts +5 -1
- package/dist/types/types/symbols.d.ts +1 -1
- package/dist/types/types/tag.d.ts +1 -0
- package/dist/types/types/tagged.d.ts +2 -2
- package/dist/types/types/task.d.ts +3 -3
- package/dist/types/types/taskMiddleware.d.ts +8 -0
- package/dist/types/types/utilities.d.ts +25 -3
- package/dist/ui/assets/index-B4lZaXFJ.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 +6159 -3049
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.mjs +6157 -3046
- package/dist/universal/index.mjs.map +1 -1
- package/package.json +18 -15
- package/readmes/AI.md +181 -45
- 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.5.0",
|
|
4
4
|
"description": "BlueLibs Runner",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "dist/universal/index.cjs",
|
|
@@ -96,6 +96,9 @@
|
|
|
96
96
|
"typecheck": "tsc -p config/ts/tsconfig.json --noEmit",
|
|
97
97
|
"lint": "eslint --config config/eslint/eslint.config.mjs ./src",
|
|
98
98
|
"lint:fix": "eslint --config config/eslint/eslint.config.mjs ./src --fix",
|
|
99
|
+
"audit": "npm audit --audit-level=moderate",
|
|
100
|
+
"audit:prod": "npm audit --omit=dev --audit-level=high",
|
|
101
|
+
"audit:full": "npm audit --audit-level=moderate",
|
|
99
102
|
"qa": "npm run coverage:ai && npm run lint:fix && npm run typecheck && npm run build",
|
|
100
103
|
"prepublishOnly": "npm run clean && npm run build && npm run build:dashboard",
|
|
101
104
|
"postbuild": "npm run guide:compose",
|
|
@@ -106,23 +109,23 @@
|
|
|
106
109
|
"guide:compose": "node ./scripts/compose-guide.mjs"
|
|
107
110
|
},
|
|
108
111
|
"devDependencies": {
|
|
112
|
+
"@eslint/js": "^10.0.1",
|
|
109
113
|
"@types/amqplib": "^0.10.7",
|
|
110
114
|
"@types/benchmark": "^2.1.5",
|
|
111
115
|
"@types/busboy": "^1.5.4",
|
|
112
116
|
"@types/express": "^5.0.3",
|
|
113
|
-
"@types/graphql": "^0.11.3",
|
|
114
117
|
"@types/jest": "^27.0.0",
|
|
115
|
-
"@types/node": "^
|
|
116
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
117
|
-
"@typescript-eslint/parser": "8.
|
|
118
|
+
"@types/node": "^25.2.3",
|
|
119
|
+
"@typescript-eslint/eslint-plugin": "8.56.0",
|
|
120
|
+
"@typescript-eslint/parser": "8.56.0",
|
|
118
121
|
"benchmark": "^2.1.4",
|
|
119
122
|
"busboy": "^1.6.0",
|
|
120
|
-
"eslint": "^
|
|
123
|
+
"eslint": "^10.0.0",
|
|
121
124
|
"eslint-config-prettier": "^10.1.8",
|
|
122
|
-
"eslint-plugin-jest": "^29.
|
|
123
|
-
"eslint-plugin-prettier": "^5.5.
|
|
124
|
-
"eslint-plugin-unused-imports": "^4.
|
|
125
|
-
"express": "^5.1
|
|
125
|
+
"eslint-plugin-jest": "^29.15.0",
|
|
126
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
127
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
128
|
+
"express": "^5.2.1",
|
|
126
129
|
"globals": "^16.5.0",
|
|
127
130
|
"jest": "^29.0.0",
|
|
128
131
|
"jest-environment-jsdom": "^30.1.2",
|
|
@@ -133,11 +136,11 @@
|
|
|
133
136
|
"tailwindcss": "^4.1.12",
|
|
134
137
|
"ts-jest": "^29.0.0",
|
|
135
138
|
"tsup": "^8.0.0",
|
|
136
|
-
"typedoc": "^0.
|
|
137
|
-
"typedoc-material-theme": "^1.1
|
|
139
|
+
"typedoc": "^0.28.17",
|
|
140
|
+
"typedoc-material-theme": "^1.4.1",
|
|
138
141
|
"typedoc-plugin-pages": "^1.1.0",
|
|
139
|
-
"typescript": "^5.
|
|
140
|
-
"typescript-eslint": "^8.
|
|
142
|
+
"typescript": "^5.9.3",
|
|
143
|
+
"typescript-eslint": "^8.56.0",
|
|
141
144
|
"zod": "^4.0.17"
|
|
142
145
|
},
|
|
143
146
|
"typings": "dist/types/index.d.ts",
|
|
@@ -156,12 +159,12 @@
|
|
|
156
159
|
],
|
|
157
160
|
"license": "MIT",
|
|
158
161
|
"dependencies": {
|
|
162
|
+
"cron-parser": "^5.4.0",
|
|
159
163
|
"lru-cache": "^11.1.0"
|
|
160
164
|
},
|
|
161
165
|
"optionalDependencies": {
|
|
162
166
|
"amqplib": "^0.10.9",
|
|
163
167
|
"busboy": "^1.6.0",
|
|
164
|
-
"cron-parser": "^5.4.0",
|
|
165
168
|
"ioredis": "^5.7.0"
|
|
166
169
|
},
|
|
167
170
|
"prettier": {
|
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
|
|
|
@@ -12,13 +12,21 @@ When deserializing untrusted payloads, configure the serializer to restrict
|
|
|
12
12
|
symbol handling so payloads cannot grow the global Symbol registry.
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
import { Serializer
|
|
15
|
+
import { Serializer } from "@bluelibs/runner";
|
|
16
16
|
|
|
17
17
|
const serializer = new Serializer({
|
|
18
|
-
symbolPolicy:
|
|
18
|
+
symbolPolicy: "well-known-only",
|
|
19
19
|
});
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
`Serializer` also supports hardening knobs:
|
|
23
|
+
|
|
24
|
+
- `allowedTypes` to allow-list runtime type ids during deserialize
|
|
25
|
+
- `maxDepth` to cap recursion depth
|
|
26
|
+
- `maxRegExpPatternLength` and `allowUnsafeRegExp` to guard RegExp payloads
|
|
27
|
+
|
|
28
|
+
Default behavior note: `symbolPolicy` defaults to `"allow-all"`; use `"well-known-only"` (or stricter) for untrusted inputs.
|
|
29
|
+
|
|
22
30
|
## Resources
|
|
23
31
|
|
|
24
32
|
```ts
|
|
@@ -29,6 +37,8 @@ import { nodeExposure } from "@bluelibs/runner/node";
|
|
|
29
37
|
const server = r
|
|
30
38
|
.resource<{ port: number }>("app.server")
|
|
31
39
|
.context(() => ({ app: express() }))
|
|
40
|
+
// .schema is an alias for .configSchema / .inputSchema / .payloadSchema etc.
|
|
41
|
+
.schema(z.object({ port: z.number().default(3000) }))
|
|
32
42
|
.init(async ({ port }, _deps, ctx) => {
|
|
33
43
|
ctx.app.use(express.json());
|
|
34
44
|
const listener = ctx.app.listen(port);
|
|
@@ -40,8 +50,8 @@ const server = r
|
|
|
40
50
|
const createUser = r
|
|
41
51
|
.task("app.tasks.createUser")
|
|
42
52
|
.dependencies({ logger: globals.resources.logger })
|
|
43
|
-
.
|
|
44
|
-
.resultSchema<{ id: string; name: string }>({ parse: (value) => value })
|
|
53
|
+
.schema<{ name: string }>({ parse: (value) => value }) // parses the input
|
|
54
|
+
.resultSchema<{ id: string; name: string }>({ parse: (value) => value }) // parses the response
|
|
45
55
|
.run(async (input, { logger }) => {
|
|
46
56
|
await logger.info(`Creating user ${input.name}`);
|
|
47
57
|
return { id: "user-1", name: input.name };
|
|
@@ -75,9 +85,16 @@ await runtime.runTask(createUser, { name: "Ada" });
|
|
|
75
85
|
// runtime.dispose() when you are done.
|
|
76
86
|
```
|
|
77
87
|
|
|
78
|
-
-
|
|
88
|
+
- `.with(config)` exists on configurable built definitions (for example resources, task/resource middleware, and tags). Fluent builders use chained methods plus `.build()`.
|
|
89
|
+
- Entry generics are supported for convenience: `r.resource<Config>(id)` seeds config typing even before `.schema()`/`.configSchema()`/`.init(...)`; config-only resources can omit `.init()` when they only orchestrate `.register((config) => ...)`.
|
|
79
90
|
- `r.*.fork(newId, { register: "keep" | "drop" | "deep", reId })` creates a new resource with a different id but the same definition. Use `register: "drop"` to avoid re-registering nested items, or `register: "deep"` to deep-fork **registered resources** with new ids via `reId` (other registerables are not kept; resource dependencies pointing to deep-forked resources are remapped to those forks). Export forked resources to use as dependencies.
|
|
80
|
-
-
|
|
91
|
+
- Resource boundaries can be narrowed with `.exports([...])` (on object definitions or fluent builders) to enforce encapsulation:
|
|
92
|
+
- **Omit `.exports()`**: Everything remains public (backward compatible).
|
|
93
|
+
- **`.exports([])`**: Nothing is public. Everything else in that resource subtree is private across boundaries (tasks, hooks, middleware, etc.).
|
|
94
|
+
- **Scoping**: Provides safer refactors as internal items cannot be referenced from outside. It also scopes `.everywhere()` middleware: non-exported middleware only applies inside its own registration subtree.
|
|
95
|
+
- **Transitive Visibility**: If a resource exports a child resource, that child's own exported surface is visible transitively, but each intermediate boundary must allow the path (e.g., `A -> B -> C` is blocked if `B.exports([])`).
|
|
96
|
+
- **Validation**: Visibility is validated at `run(...)` init time. IDs remain globally unique even for private items.
|
|
97
|
+
- `run(root)` wires dependencies, runs `init`, emits lifecycle events, and returns a runtime object (`IRuntime`) with helpers such as `runTask`, `emitEvent`, `getResourceValue`, `getLazyResourceValue`, `getResourceConfig`, `getRootId`, `getRootConfig`, `getRootValue`, and `dispose`.
|
|
81
98
|
- Enable verbose logging with `run(root, { debug: "verbose" })`.
|
|
82
99
|
|
|
83
100
|
### Resource Forking
|
|
@@ -97,7 +114,7 @@ import { r } from "@bluelibs/runner";
|
|
|
97
114
|
// Assuming: userService, loggingMiddleware, and tracingMiddleware are defined elsewhere
|
|
98
115
|
const sendEmail = r
|
|
99
116
|
.task("app.tasks.sendEmail")
|
|
100
|
-
.
|
|
117
|
+
.schema<{ to: string; subject: string; body: string }>({
|
|
101
118
|
parse: (value) => value,
|
|
102
119
|
})
|
|
103
120
|
.dependencies({ emailer: userService })
|
|
@@ -114,8 +131,12 @@ const sendEmail = r
|
|
|
114
131
|
- `.dependencies()` accepts a literal map or function `(config) => deps`; appends (shallow-merge) by default
|
|
115
132
|
- `.middleware()` appends by default
|
|
116
133
|
- `.tags()` appends by default
|
|
134
|
+
- `.schema()` is a unified alias for `inputSchema`, `configSchema`, `payloadSchema`, and `dataSchema` (errors).
|
|
135
|
+
- For tasks, `.schema()` maps to `inputSchema` only; keep output validation explicit with `.resultSchema()`.
|
|
136
|
+
- Entry generic is supported for convenience: `r.task<Input>(id)` seeds input typing. Later explicit typing in `.schema()`/`.inputSchema()`/`.run((input: ...))` still has priority.
|
|
117
137
|
- Pass `{ override: true }` to any of these methods to replace instead of append
|
|
118
138
|
- Provide result validation with `.resultSchema()` when the function returns structured data
|
|
139
|
+
- All builders support `.meta({ ... })` for documentation and tooling metadata.
|
|
119
140
|
|
|
120
141
|
## Events and Hooks
|
|
121
142
|
|
|
@@ -157,7 +178,7 @@ const sendWelcomeEmail = r
|
|
|
157
178
|
.build();
|
|
158
179
|
```
|
|
159
180
|
|
|
160
|
-
- Use `.on(onAnyOf(...))` to listen to several events while keeping inference.
|
|
181
|
+
- Use `.on(onAnyOf(...))` to listen to several events while keeping inference. Import `onAnyOf` from `@bluelibs/runner/defs` (or `@bluelibs/runner` if you already re-export it in your local facade).
|
|
161
182
|
- Hooks can set `.order(priority)`; lower numbers run first. Call `event.stopPropagation()` inside `run` to cancel downstream hooks.
|
|
162
183
|
- Wildcard hooks use `.on("*")` and receive every emission except events tagged with `globals.tags.excludeFromGlobalHooks`.
|
|
163
184
|
- Use `.parallel(true)` on event definitions to enable batched parallel execution:
|
|
@@ -166,6 +187,30 @@ const sendWelcomeEmail = r
|
|
|
166
187
|
- All listeners in a failing batch run to completion; if multiple fail, an `AggregateError` with all errors is thrown
|
|
167
188
|
- Propagation is checked between batches only (not mid-batch since parallel listeners can't be stopped mid-flight)
|
|
168
189
|
- If any listener throws, subsequent batches will not run
|
|
190
|
+
- Event emitters (dependency-injected or `runtime.emitEvent`) support options:
|
|
191
|
+
- `failureMode: "fail-fast" | "aggregate"`
|
|
192
|
+
- `throwOnError` (default `true`)
|
|
193
|
+
- `report` (when `true`, returns `IEventEmitReport`)
|
|
194
|
+
- `report: true` is useful when you want to aggregate hook failures without throwing immediately:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { r } from "@bluelibs/runner";
|
|
198
|
+
|
|
199
|
+
const notify = r.event("app.events.notify").build();
|
|
200
|
+
|
|
201
|
+
const task = r
|
|
202
|
+
.task("app.tasks.notify")
|
|
203
|
+
.dependencies({ notify })
|
|
204
|
+
.run(async (_input, { notify }) => {
|
|
205
|
+
const report = await notify(undefined, {
|
|
206
|
+
report: true,
|
|
207
|
+
throwOnError: false,
|
|
208
|
+
failureMode: "aggregate",
|
|
209
|
+
});
|
|
210
|
+
return report.failedListeners;
|
|
211
|
+
})
|
|
212
|
+
.build();
|
|
213
|
+
```
|
|
169
214
|
|
|
170
215
|
## Middleware
|
|
171
216
|
|
|
@@ -205,6 +250,7 @@ const cacheResources = r.middleware
|
|
|
205
250
|
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
251
|
|
|
207
252
|
- 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.
|
|
253
|
+
- Entry generic convenience is available for middleware too: `r.middleware.task<Input>(id)` seeds task input contract typing and `r.middleware.resource<Config>(id)` seeds middleware config typing. The explicit multi-generic form (`<Config, Input, Output>`) remains available.
|
|
208
254
|
|
|
209
255
|
```ts
|
|
210
256
|
type AuthConfig = { requiredRole: string };
|
|
@@ -279,7 +325,7 @@ const getHealth = r
|
|
|
279
325
|
|
|
280
326
|
Retrieve tagged items by using `globals.resources.store` inside a hook or resource and calling `store.getTasksWithTag(tag)`.
|
|
281
327
|
|
|
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 }`.
|
|
328
|
+
**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(...)`) and these are the current, non-deprecated methods. The legacy aliases `durable.startExecution(...)`, `durable.execute(...)`, and `durable.executeStrict(...)` remain available as deprecated compatibility methods (`startExecution` -> `start`, `execute` -> `startAndWait(...).data`, `executeStrict` -> `startAndWait`). The tag is discovery metadata only; `startAndWait(...)` provides the unified result envelope `{ durable: { executionId }, data }`.
|
|
283
329
|
|
|
284
330
|
- 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.
|
|
285
331
|
- 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.
|
|
@@ -296,11 +342,50 @@ const getUser = r
|
|
|
296
342
|
.build();
|
|
297
343
|
```
|
|
298
344
|
|
|
345
|
+
## Cron Scheduling
|
|
346
|
+
|
|
347
|
+
Use `globals.tags.cron` to schedule tasks with cron expressions. The scheduler lives in `globals.resources.cron` and is registered by default, so tagged tasks begin scheduling automatically at runtime startup.
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
import { r, globals } from "@bluelibs/runner";
|
|
351
|
+
|
|
352
|
+
const cleanupTask = r
|
|
353
|
+
.task("app.tasks.cleanup")
|
|
354
|
+
.tags([
|
|
355
|
+
globals.tags.cron.with({
|
|
356
|
+
expression: "*/5 * * * *",
|
|
357
|
+
immediate: true,
|
|
358
|
+
onError: "continue",
|
|
359
|
+
}),
|
|
360
|
+
])
|
|
361
|
+
.run(async () => {
|
|
362
|
+
// cleanup logic
|
|
363
|
+
})
|
|
364
|
+
.build();
|
|
365
|
+
|
|
366
|
+
const app = r.resource("app").register([cleanupTask]).build();
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`globals.tags.cron.with({...})` options:
|
|
370
|
+
|
|
371
|
+
- `expression` (required): 5-field cron expression
|
|
372
|
+
- `input`: static input passed to the task on each run
|
|
373
|
+
- `timezone`: timezone for scheduling
|
|
374
|
+
- `immediate`: run once immediately at startup, then continue schedule
|
|
375
|
+
- `enabled`: disable schedule when `false`
|
|
376
|
+
- `onError`: `"continue"` (default) or `"stop"`
|
|
377
|
+
- `silent`: suppress all cron log output for this task when `true` (default `false`)
|
|
378
|
+
|
|
379
|
+
Notes:
|
|
380
|
+
|
|
381
|
+
- One cron tag per task is supported. If you need multiple schedules, use task forking and tag each fork.
|
|
382
|
+
- Cron startup logs are emitted through `globals.resources.logger`.
|
|
383
|
+
|
|
299
384
|
## Async Context
|
|
300
385
|
|
|
301
|
-
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.
|
|
386
|
+
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.
|
|
302
387
|
|
|
303
|
-
> **Platform Note**: `AsyncLocalStorage
|
|
388
|
+
> **Platform Note**: Async Context requires `AsyncLocalStorage`. It is available in Node.js and Deno (when exposed), and unavailable in browser runtimes.
|
|
304
389
|
|
|
305
390
|
```ts
|
|
306
391
|
import { r } from "@bluelibs/runner";
|
|
@@ -339,9 +424,40 @@ const whoAmI = r
|
|
|
339
424
|
const app = r.resource("app").register([requestContext, whoAmI]).build();
|
|
340
425
|
```
|
|
341
426
|
|
|
427
|
+
## Queue
|
|
428
|
+
|
|
429
|
+
`Queue` is a cooperative FIFO task queue. Tasks run one-after-another, with dead-lock detection and graceful disposal.
|
|
430
|
+
|
|
431
|
+
The global resource `globals.resources.queue` provides a named queue factory — each `id` gets its own isolated `Queue` instance.
|
|
432
|
+
|
|
433
|
+
**Key methods:**
|
|
434
|
+
|
|
435
|
+
- `queue.run(id, task)` — schedule `task` (receives an `AbortSignal`) on the queue identified by `id`; creates the queue lazily.
|
|
436
|
+
|
|
437
|
+
**Event lifecycle:** `enqueue` → `start` → `finish` | `error`. On disposal: `disposed`. On cancel: `cancel`.
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { r, run, globals } from "@bluelibs/runner";
|
|
441
|
+
|
|
442
|
+
const processOrder = r
|
|
443
|
+
.task("app.tasks.processOrder")
|
|
444
|
+
.dependencies({ queue: globals.resources.queue })
|
|
445
|
+
.run(async (input: { orderId: string }, { queue }) => {
|
|
446
|
+
// Tasks with the same orderId run sequentially
|
|
447
|
+
return queue.run(input.orderId, async (signal) => {
|
|
448
|
+
if (signal.aborted) return;
|
|
449
|
+
// ... process order
|
|
450
|
+
return { processed: true };
|
|
451
|
+
});
|
|
452
|
+
})
|
|
453
|
+
.build();
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
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`.
|
|
457
|
+
|
|
342
458
|
## Errors
|
|
343
459
|
|
|
344
|
-
Define typed, namespaced errors with a fluent builder. Built helpers expose `
|
|
460
|
+
Define typed, namespaced errors with a fluent builder. Built helpers expose `new`, `create` (alias), `throw`, and `is`:
|
|
345
461
|
|
|
346
462
|
```ts
|
|
347
463
|
import { r } from "@bluelibs/runner";
|
|
@@ -350,21 +466,29 @@ import { r } from "@bluelibs/runner";
|
|
|
350
466
|
const AppError = r
|
|
351
467
|
.error<{ code: number; message: string }>("app.errors.AppError")
|
|
352
468
|
.httpCode(400)
|
|
353
|
-
.
|
|
469
|
+
// .schema is an alias for .dataSchema in errors
|
|
470
|
+
.schema(z.object({ code: z.number(), message: z.string() }))
|
|
354
471
|
.format((d) => `[${d.code}] ${d.message}`)
|
|
355
472
|
.remediation("Check the request payload and retry with valid data.")
|
|
473
|
+
.tags([criticalTag])
|
|
356
474
|
.build();
|
|
357
475
|
|
|
358
476
|
try {
|
|
359
477
|
AppError.throw({ code: 400, message: "Oops" });
|
|
360
478
|
} catch (err) {
|
|
361
|
-
if (AppError.is(err)) {
|
|
479
|
+
if (AppError.is(err, { code: 400 })) {
|
|
480
|
+
// Narrowed to RunnerError<TData>
|
|
362
481
|
// err.message -> "[400] Oops\n\nRemediation: Check the request payload and retry with valid data."
|
|
363
482
|
// err.httpCode -> 400
|
|
364
483
|
// err.remediation -> "Check the request payload and retry with valid data."
|
|
365
484
|
// AppError.httpCode -> 400
|
|
366
485
|
}
|
|
367
486
|
}
|
|
487
|
+
|
|
488
|
+
const error = AppError.new({ code: 400, message: "Oops" });
|
|
489
|
+
throw error;
|
|
490
|
+
|
|
491
|
+
// .create() is deprecated; use .new() or .throw() for better DX.
|
|
368
492
|
```
|
|
369
493
|
|
|
370
494
|
- Recommended ids: `{domain}.errors.{PascalCaseName}` (for example: `app.errors.InvalidCredentials`).
|
|
@@ -372,10 +496,17 @@ try {
|
|
|
372
496
|
- `.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
497
|
- `.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`.
|
|
374
498
|
- `message` is not required in the data unless your custom formatter expects it.
|
|
499
|
+
- `helper.new(data)` constructs and returns a typed `RunnerError` without throwing (useful for `throw helper.new(data)` semantics).
|
|
500
|
+
- `helper.is(err, partialData?)` accepts an optional partial data filter and performs shallow strict matching (`===`) on each provided key.
|
|
501
|
+
- `helper.tags` and `helper.meta` expose documentation metadata for introspection.
|
|
502
|
+
- For circular dependency detection, use `circularDependencyError`. Backward-compatible aliases `circularDependenciesError` and `dependencyCycleError` remain exported as deprecated names.
|
|
375
503
|
- Declare a task/resource error contract with `.throws([AppError])` (or ids). This is declarative only and does not imply DI.
|
|
504
|
+
- `.throws()` is also available on hooks, task middleware, and resource middleware builders — same semantics.
|
|
505
|
+
- `.throws([...])` accepts error helpers or string ids, normalizes to ids, and deduplicates repeated declarations.
|
|
506
|
+
- `store.getAllThrows(task | resource)` aggregates all declared error ids from a task or resource and its full dependency chain: own throws, local + everywhere middleware throws, resource dependency throws (with their middleware), and — for tasks — hook throws for events the task can emit. Returns a deduplicated `readonly string[]`.
|
|
376
507
|
- 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
508
|
```ts
|
|
378
|
-
if (r.error.is(err)) {
|
|
509
|
+
if (r.error.is(err, { code: 400 })) {
|
|
379
510
|
console.error(`Runner error: ${err.id} (${err.httpCode || "N/A"})`);
|
|
380
511
|
}
|
|
381
512
|
```
|
|
@@ -401,27 +532,40 @@ try {
|
|
|
401
532
|
Override a task/resource/hook/middleware while preserving `id`. Use the helper or the fluent override builder:
|
|
402
533
|
|
|
403
534
|
```ts
|
|
404
|
-
const mockMailer = r
|
|
535
|
+
const mockMailer = r.override(realMailer, async () => new MockMailer());
|
|
536
|
+
|
|
537
|
+
const tracedMailer = r
|
|
405
538
|
.override(realMailer)
|
|
406
|
-
.init(async () =>
|
|
539
|
+
.init(async (config, deps) => {
|
|
540
|
+
const base = await realMailer.init(config, deps);
|
|
541
|
+
return { ...base, trace: true };
|
|
542
|
+
})
|
|
407
543
|
.build();
|
|
408
544
|
|
|
409
545
|
const app = r
|
|
410
546
|
.resource("app")
|
|
411
547
|
.register([realMailer])
|
|
412
|
-
.overrides([mockMailer])
|
|
548
|
+
.overrides([mockMailer, tracedMailer])
|
|
413
549
|
.build();
|
|
414
550
|
```
|
|
415
551
|
|
|
552
|
+
- `r.override(base, fn)` is a typed shorthand for common behavior swaps:
|
|
553
|
+
- task/hook/task-middleware/resource-middleware: replaces `run`
|
|
554
|
+
- resource: replaces `init`
|
|
416
555
|
- `r.override(base)` starts from the base definition and applies fluent mutations using the same composition rules as the base builder.
|
|
556
|
+
- `r.override(...)` creates replacement definitions; `.overrides([...])` applies them in a specific container during bootstrap.
|
|
557
|
+
- Registering only the replacement definition is valid; registering both base and replacement in `.register([...])` causes duplicate-id errors.
|
|
558
|
+
- `.overrides([...])` requires the target id to already be present in the graph; if you wanted a second resource instance instead of replacement, use `.fork("new.id")`.
|
|
417
559
|
- Hook overrides keep the same `.on` target; only behavior/metadata is overridable.
|
|
418
560
|
- The `override(base, patch)` helper remains for direct, shallow patches.
|
|
419
561
|
|
|
420
562
|
## Runtime & Lifecycle
|
|
421
563
|
|
|
422
|
-
- `run(root, options)` wires dependencies, initializes resources, and returns
|
|
423
|
-
-
|
|
424
|
-
-
|
|
564
|
+
- `run(root, options)` wires dependencies, initializes resources, and returns the runtime object: `runTask`, `emitEvent`, `getResourceValue`, `getLazyResourceValue`, `getResourceConfig`, `getRootId`, `getRootConfig`, `getRootValue`, `store`, `logger`, and `dispose`. `getLazyResourceValue` is available only when `run(..., { lazy: true })` is enabled.
|
|
565
|
+
- `emitEvent(event, payload, options?)` accepts the same emission options (`failureMode`, `throwOnError`, `report`) as dependency emitters.
|
|
566
|
+
- Run options highlights: `debug` (normal/verbose or custom config), `logs` (printThreshold/strategy/buffer), `errorBoundary` and `onUnhandledError`, `shutdownHooks`, `dryRun`, `lazy`, and `initMode` (`"sequential"` or `"parallel"`; string literal values work without importing enums).
|
|
567
|
+
- `lazy` + `initMode: "parallel"`: startup still parallelizes resources that are actually needed during bootstrap (respecting dependency readiness); only startup-unused resources stay deferred for `getLazyResourceValue(...)`.
|
|
568
|
+
- 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). Use `deps.someTask.getInterceptingResourceIds()` to inspect which resources registered local interceptors (unique ids, registration order).
|
|
425
569
|
- Shutdown hooks: install signal listeners to call `dispose` (default in `run`).
|
|
426
570
|
- Unhandled errors: `onUnhandledError` receives a structured context (kind and source) for telemetry or controlled shutdown.
|
|
427
571
|
|
|
@@ -473,10 +617,14 @@ const app = r
|
|
|
473
617
|
|
|
474
618
|
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.
|
|
475
619
|
|
|
620
|
+
Important boundary: tunnels are for inter-runner/service-to-service communication, not for exposing a public browser-facing API directly.
|
|
621
|
+
|
|
476
622
|
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`.
|
|
477
623
|
|
|
478
624
|
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).
|
|
479
625
|
|
|
626
|
+
Node exposure hardening: use `x-runner-request-id` for request correlation, set `allowAsyncContext: false` on server tunnel resources unless context propagation is required, and enforce rate limiting at the edge/proxy layer.
|
|
627
|
+
|
|
480
628
|
## Serialization
|
|
481
629
|
|
|
482
630
|
Runner ships with a serializer that round-trips Dates, RegExp, binary, and custom shapes across Node and web.
|
|
@@ -498,18 +646,15 @@ const serializerSetup = r
|
|
|
498
646
|
public value: number,
|
|
499
647
|
public unit: string,
|
|
500
648
|
) {}
|
|
501
|
-
typeName() {
|
|
502
|
-
return "Distance";
|
|
503
|
-
}
|
|
504
|
-
toJSONValue() {
|
|
505
|
-
return { value: this.value, unit: this.unit };
|
|
506
|
-
}
|
|
507
649
|
}
|
|
508
650
|
|
|
509
|
-
serializer.addType(
|
|
510
|
-
"Distance",
|
|
511
|
-
(
|
|
512
|
-
|
|
651
|
+
serializer.addType({
|
|
652
|
+
id: "Distance",
|
|
653
|
+
is: (obj): obj is Distance => obj instanceof Distance,
|
|
654
|
+
serialize: (d) => ({ value: d.value, unit: d.unit }),
|
|
655
|
+
deserialize: (json) => new Distance(json.value, json.unit),
|
|
656
|
+
strategy: "value",
|
|
657
|
+
});
|
|
513
658
|
})
|
|
514
659
|
.build();
|
|
515
660
|
```
|
|
@@ -544,13 +689,15 @@ test("sends welcome email", async () => {
|
|
|
544
689
|
- `globals.resources.logger` exposes the framework logger; register your own logger resource and override it at the root to capture logs centrally.
|
|
545
690
|
- Hooks and tasks emit metadata through `globals.resources.store`. Query it for dashboards or editor plugins.
|
|
546
691
|
- Use middleware for tracing (`r.middleware.task("...").run(...)`) to wrap every task call.
|
|
692
|
+
- Global infra resources are split into dedicated modules under `src/globals/resources/*.resource.ts` (for example `store.resource.ts`, `logger.resource.ts`, `eventManager.resource.ts`), while public consumption remains `globals.resources.*`.
|
|
547
693
|
- `Semaphore` and `Queue` publish local lifecycle events through isolated `EventManager` instances (`on/once`). These are separate from the global EventManager used for business-level application events. Event names: semaphore → `queued/acquired/released/timeout/aborted/disposed`; queue → `enqueue/start/finish/error/cancel/disposed`.
|
|
548
694
|
|
|
549
695
|
## Metadata & Namespacing
|
|
550
696
|
|
|
551
697
|
- Meta: `.meta({ title, description })` on tasks/resources/events/middleware for human-friendly docs and tooling; extend meta types via module augmentation when needed.
|
|
552
698
|
- Namespacing: keep ids consistent with `domain.resources.name`, `domain.tasks.name`, `domain.events.name`, `domain.hooks.on-name`, `domain.middleware.{task|resource}.name`, `domain.errors.ErrorName`, and `domain.ctx.name`.
|
|
553
|
-
-
|
|
699
|
+
- File Structure: While not strictly enforced, prefer co-locating definitions by domain in a feature-driven folder structure (e.g., `src/domains/users/tasks/createUser.task.ts`) and naming files after the item type (`*.task.ts`, `*.resource.ts`, `*.event.ts`) for easier navigation and AI context ingestion.
|
|
700
|
+
- Runtime validation: `inputSchema`, `resultSchema`, `payloadSchema`, `configSchema` share the same `parse(input)` contract; config validation happens on `.with()`, task/event validation happens on call/emit. Use `.schema()` as a unified alias (input/payload/schema/data) for simplicity.
|
|
554
701
|
|
|
555
702
|
## Advanced Patterns
|
|
556
703
|
|
|
@@ -558,15 +705,4 @@ test("sends welcome email", async () => {
|
|
|
558
705
|
- **Conditional registration:** `.register((config) => (config.enableFeature ? [featureResource] : []))`.
|
|
559
706
|
- **Async coordination:** `Semaphore` (O(1) linked queue for heavy contention) and `Queue` live in the main package. Both use isolated EventManagers internally for their lifecycle events, separate from the global EventManager used for business-level application events.
|
|
560
707
|
- **Event safety:** Runner detects event emission cycles and throws an `EventCycleError` with the offending chain.
|
|
561
|
-
- **Internal services:**
|
|
562
|
-
|
|
563
|
-
## Interop With Classic APIs
|
|
564
|
-
|
|
565
|
-
Existing code that uses `resource({ ... })`, `task({ ... })`, or `defineX` keeps working. You can gradually migrate:
|
|
566
|
-
|
|
567
|
-
```ts
|
|
568
|
-
import { r, resource as classicResource } from "@bluelibs/runner";
|
|
569
|
-
|
|
570
|
-
const classic = classicResource({ id: "legacy", init: async () => "ok" });
|
|
571
|
-
const modern = r.resource("modern").register([classic]).build();
|
|
572
|
-
```
|
|
708
|
+
- **Internal services:** `globals.resources.runtime` resolves to the same runtime object returned by `run(...)`. It supports `runTask`, `emitEvent`, `getResourceValue`, `getLazyResourceValue`, `getResourceConfig`, `getRootId`, `getRootConfig`, `getRootValue`, and `dispose`. Bootstrap note: when injected inside a resource `init()`, only that resource's dependencies are guaranteed initialized; unrelated resources may still be pending.
|