@bluelibs/runner 4.7.0 → 4.8.1
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/AI.md +88 -95
- package/README.md +205 -144
- package/dist/browser/index.cjs +739 -487
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.mjs +736 -487
- package/dist/browser/index.mjs.map +1 -1
- package/dist/define.d.ts +1 -1
- package/dist/definers/builders/asyncContext.d.ts +11 -0
- package/dist/definers/builders/error.d.ts +13 -0
- package/dist/definers/builders/middleware.d.ts +3 -3
- package/dist/definers/defineAsyncContext.d.ts +15 -0
- package/dist/definers/defineError.d.ts +26 -0
- package/dist/definers/tools.d.ts +6 -0
- package/dist/edge/index.cjs +739 -487
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.mjs +736 -487
- package/dist/edge/index.mjs.map +1 -1
- package/dist/errors.d.ts +60 -102
- package/dist/globals/debug.d.ts +10 -0
- package/dist/globals/globalMiddleware.d.ts +3 -3
- package/dist/globals/middleware/requireContext.middleware.d.ts +2 -2
- package/dist/globals/resources/tunnel/protocol.d.ts +8 -1
- package/dist/globals/resources/tunnel/serializer.d.ts +1 -1
- package/dist/globals/types.d.ts +1 -0
- package/dist/http-client.d.ts +4 -0
- package/dist/http-fetch-tunnel.resource.d.ts +11 -0
- package/dist/index.d.ts +14 -5
- package/dist/models/Store.d.ts +6 -2
- package/dist/models/StoreRegistry.d.ts +6 -0
- package/dist/node/exposure/httpResponse.d.ts +1 -1
- package/dist/node/exposure/requestContext.d.ts +1 -1
- package/dist/node/{mixed-http-client.node.d.ts → http-mixed-client.d.ts} +1 -1
- package/dist/node/http-smart-client.model.d.ts +2 -0
- package/dist/node/node.cjs +918 -4296
- package/dist/node/node.cjs.map +1 -1
- package/dist/node/node.mjs +914 -4296
- package/dist/node/node.mjs.map +1 -1
- package/dist/types/asyncContext.d.ts +39 -0
- package/dist/types/error.d.ts +34 -0
- package/dist/types/runner.d.ts +13 -0
- package/dist/types/symbols.d.ts +6 -0
- package/dist/types/utilities.d.ts +6 -4
- package/dist/universal/index.cjs +739 -487
- package/dist/universal/index.cjs.map +1 -1
- package/dist/universal/index.mjs +736 -487
- package/dist/universal/index.mjs.map +1 -1
- package/dist/utils/detectRunnerMode.d.ts +9 -0
- package/package.json +1 -1
- package/dist/context.d.ts +0 -31
package/AI.md
CHANGED
|
@@ -3,23 +3,23 @@
|
|
|
3
3
|
> Token-friendly (<5000 tokens). This guide spotlights the fluent builder API (`r.*`) that ships with Runner 4.x. Classic `defineX` / `resource({...})` remain supported for backwards compatibility, but fluent builders are the default throughout.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
10
|
-
- [Core Concepts](#core-concepts)
|
|
6
|
+
|
|
7
|
+
- [BlueLibs Runner: Fluent Builder Field Guide](#bluelibs-runner-fluent-builder-field-guide)
|
|
8
|
+
- [Table of Contents](#table-of-contents)
|
|
9
|
+
- [Install](#install)
|
|
11
10
|
- [Resources](#resources)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
11
|
+
- [Tasks](#tasks)
|
|
12
|
+
- [Events and Hooks](#events-and-hooks)
|
|
13
|
+
- [Middleware](#middleware)
|
|
14
|
+
- [Tags](#tags)
|
|
15
|
+
- [Async Context](#async-context)
|
|
16
|
+
- [Errors](#errors)
|
|
17
|
+
- [HTTP \& Tunnels](#http--tunnels)
|
|
18
|
+
- [Serialization](#serialization)
|
|
19
|
+
- [Testing](#testing)
|
|
20
|
+
- [Observability \& Debugging](#observability--debugging)
|
|
21
|
+
- [Advanced Patterns](#advanced-patterns)
|
|
22
|
+
- [Interop With Classic APIs](#interop-with-classic-apis)
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
@@ -27,9 +27,7 @@
|
|
|
27
27
|
npm install @bluelibs/runner
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
## Quick Start
|
|
30
|
+
## Resources
|
|
33
31
|
|
|
34
32
|
```ts
|
|
35
33
|
import express from "express";
|
|
@@ -89,72 +87,6 @@ await runtime.runTask(createUser, { name: "Ada" });
|
|
|
89
87
|
- `run(root)` wires dependencies, runs `init`, emits lifecycle events, and returns helpers such as `runTask`, `getResourceValue`, and `dispose`.
|
|
90
88
|
- Enable verbose logging with `run(root, { debug: "verbose" })`.
|
|
91
89
|
|
|
92
|
-
## Platform Matrix
|
|
93
|
-
|
|
94
|
-
| Capability | Node.js | Browser | Workers (e.g. Cloudflare) |
|
|
95
|
-
| --- | --- | --- | --- |
|
|
96
|
-
| `run()` lifecycle | ✅ | ✅ | ✅ |
|
|
97
|
-
| `nodeExposure`, Node tunnels | ✅ | ❌ | ❌ |
|
|
98
|
-
| `createHttpClient` | ✅ | ✅ | ✅ |
|
|
99
|
-
| Duplex upload (`createHttpSmartClient`) | ✅ | ❌ | ❌ |
|
|
100
|
-
| File helpers (`createNodeFile`, `createWebFile`) | Node only | Browser only | Browser only |
|
|
101
|
-
| Async context (AsyncLocalStorage) | ✅ | n/a | n/a |
|
|
102
|
-
|
|
103
|
-
- Browser environments rely on `globalThis.__ENV__` or `globalThis.env` for configuration; Node uses `process.env`.
|
|
104
|
-
- Runner will throw if you call Node-only helpers in the browser; keep shared code inside `src/` and Node-specific logic under `src/node/`.
|
|
105
|
-
|
|
106
|
-
## Fluent Builder Primer
|
|
107
|
-
|
|
108
|
-
The fluent API lives under the single `r` namespace:
|
|
109
|
-
|
|
110
|
-
```ts
|
|
111
|
-
import { r } from "@bluelibs/runner";
|
|
112
|
-
|
|
113
|
-
const task = r.task("demo.tasks.hello").run(async ({ input }) => input).build();
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Key rules:
|
|
117
|
-
|
|
118
|
-
- Builders are immutable; every fluent call returns a new builder with tightened typings.
|
|
119
|
-
- Call `.build()` once you finish configuring the definition.
|
|
120
|
-
- `with(config)` clones the built definition with typed config overrides.
|
|
121
|
-
- Use the same pattern across tasks, resources, hooks, middleware, and tags for consistent DX.
|
|
122
|
-
|
|
123
|
-
## Core Concepts
|
|
124
|
-
|
|
125
|
-
### Resources
|
|
126
|
-
|
|
127
|
-
Resources encapsulate long-lived values such as database connections or service facades.
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
import { MongoClient } from "mongodb";
|
|
131
|
-
import { r } from "@bluelibs/runner";
|
|
132
|
-
|
|
133
|
-
const database = r
|
|
134
|
-
.resource<{ url: string }>("app.resources.database")
|
|
135
|
-
.init(async ({ url }) => {
|
|
136
|
-
const client = new MongoClient(url);
|
|
137
|
-
await client.connect();
|
|
138
|
-
return client;
|
|
139
|
-
})
|
|
140
|
-
.dispose(async (client) => client.close())
|
|
141
|
-
.build();
|
|
142
|
-
|
|
143
|
-
const userService = r
|
|
144
|
-
.resource("app.resources.userService")
|
|
145
|
-
.dependencies({ database })
|
|
146
|
-
.init(async (_config, { database }) => ({
|
|
147
|
-
async create(user: { email: string }) {
|
|
148
|
-
return database.db().collection("users").insertOne(user);
|
|
149
|
-
},
|
|
150
|
-
}))
|
|
151
|
-
.build();
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
- `context(fn)` gives you a private object that survives `init` → `dispose`.
|
|
155
|
-
- `configSchema` and `resultSchema` accept anything with a `parse()` method (Zod, custom validators).
|
|
156
|
-
- Register resources inside other resources via `.register([...])`. Repeated calls append unless you pass `{ override: true }`.
|
|
157
|
-
|
|
158
90
|
### Tasks
|
|
159
91
|
|
|
160
92
|
Tasks are your business actions. They are plain async functions with DI, middleware, and validation.
|
|
@@ -280,6 +212,67 @@ const getHealth = r
|
|
|
280
212
|
|
|
281
213
|
Retrieve tagged items by using `globals.resources.store` inside a hook or resource and calling `store.getTasksWithTag(tag)`.
|
|
282
214
|
|
|
215
|
+
### Async Context
|
|
216
|
+
|
|
217
|
+
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.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { r } from "@bluelibs/runner";
|
|
221
|
+
|
|
222
|
+
const requestContext = r
|
|
223
|
+
.asyncContext<{ requestId: string }>("app.ctx.request")
|
|
224
|
+
// below is optional
|
|
225
|
+
.configSchema(z.object({ ... }))
|
|
226
|
+
.serialize((data) => JSON.stringify(data))
|
|
227
|
+
.parse((raw) => JSON.parse(raw))
|
|
228
|
+
.build();
|
|
229
|
+
|
|
230
|
+
// Provide and read within an async boundary
|
|
231
|
+
await requestContext.provide({ requestId: "abc" }, async () => {
|
|
232
|
+
const ctx = requestContext.use(); // { requestId: "abc" }
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Require middleware for tasks that need the context
|
|
236
|
+
r.task('task').middleware([requestContext.require()]);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
- If you don't provide `serialize`/`parse`, Runner uses its default EJSON serializer to preserve Dates, RegExp, etc.
|
|
240
|
+
- You can also inject async contexts as dependencies; the injected value is the helper itself. Contexts must be registered to be used.
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const whoAmI = r
|
|
244
|
+
.task("app.tasks.whoAmI")
|
|
245
|
+
.dependencies({ requestContext })
|
|
246
|
+
.run(async (_input, { requestContext }) => requestContext.use().requestId)
|
|
247
|
+
.build();
|
|
248
|
+
|
|
249
|
+
const app = r.resource("app").register([requestContext, whoAmI]).build();
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Errors
|
|
253
|
+
|
|
254
|
+
Define typed, namespaced errors with a fluent builder. Built helpers expose `throw`, `is`, and `toString`:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { r } from "@bluelibs/runner";
|
|
258
|
+
|
|
259
|
+
// Fluent builder
|
|
260
|
+
const AppError = r
|
|
261
|
+
.error<{ code: number; message: string }>("app.errors.AppError")
|
|
262
|
+
.dataSchema(zod) // or { parse(obj) => obj }
|
|
263
|
+
.build();
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
AppError.throw({ code: 400, message: "Oops" });
|
|
267
|
+
} catch (err) {
|
|
268
|
+
if (AppError.is(err)) {
|
|
269
|
+
// Do something
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- Error data must include a `message: string`. The thrown `Error` has `name = id` and `message = data.message` for predictable matching and logging.
|
|
275
|
+
|
|
283
276
|
## HTTP & Tunnels
|
|
284
277
|
|
|
285
278
|
Run Node exposures and connect to remote Runners with fluent resources.
|
|
@@ -325,6 +318,7 @@ import { createFile as createWebFile } from "@bluelibs/runner/platform/createFil
|
|
|
325
318
|
const client = createHttpClient({
|
|
326
319
|
baseUrl: "/__runner",
|
|
327
320
|
auth: { token: "secret" },
|
|
321
|
+
serializer: JSON,
|
|
328
322
|
});
|
|
329
323
|
|
|
330
324
|
await client.task("app.tasks.getHealth");
|
|
@@ -336,6 +330,7 @@ await client.task("app.tasks.upload", { file });
|
|
|
336
330
|
- `createHttpSmartClient` (Node only) supports duplex streams.
|
|
337
331
|
- For Node-specific features such as `useExposureContext` for handling aborts and streaming in exposed tasks, see TUNNELS.md.
|
|
338
332
|
- Register authentication middleware or rate limiting on the exposure via middleware tags and filters.
|
|
333
|
+
- Single-owner policy: a task may be tunneled by exactly one tunnel resource. Runner enforces exclusivity at init time and throws if two tunnels select the same task. This is tracked via an internal symbol on the task linking it to the owning tunnel.
|
|
339
334
|
|
|
340
335
|
## Serialization
|
|
341
336
|
|
|
@@ -358,7 +353,10 @@ const serializerSetup = r
|
|
|
358
353
|
}
|
|
359
354
|
}
|
|
360
355
|
|
|
361
|
-
serializer.addType(
|
|
356
|
+
serializer.addType(
|
|
357
|
+
"Distance",
|
|
358
|
+
(json) => new Distance(json.value, json.unit),
|
|
359
|
+
);
|
|
362
360
|
})
|
|
363
361
|
.build();
|
|
364
362
|
```
|
|
@@ -379,7 +377,10 @@ Example:
|
|
|
379
377
|
import { run } from "@bluelibs/runner";
|
|
380
378
|
|
|
381
379
|
test("sends welcome email", async () => {
|
|
382
|
-
const app = r
|
|
380
|
+
const app = r
|
|
381
|
+
.resource("spec.app")
|
|
382
|
+
.register([sendWelcomeEmail, registerUser])
|
|
383
|
+
.build();
|
|
383
384
|
const runtime = await run(app);
|
|
384
385
|
await runtime.runTask(registerUser, { email: "user@example.com" });
|
|
385
386
|
await runtime.dispose();
|
|
@@ -412,11 +413,3 @@ const modern = r.resource("modern").register([classic]).build();
|
|
|
412
413
|
```
|
|
413
414
|
|
|
414
415
|
Fluent builders produce the exact same runtime definitions, so you can mix both styles within one project.
|
|
415
|
-
|
|
416
|
-
## Reference Links
|
|
417
|
-
|
|
418
|
-
- `readmes/FLUENT_BUILDERS.md` – deep dive into fluent APIs.
|
|
419
|
-
- `readmes/TUNNELS.md` – streaming, authentication, deployment tips.
|
|
420
|
-
- `README.md` – project overview and additional examples.
|
|
421
|
-
- `AI.md` (this file) – copy/paste friendly summary.
|
|
422
|
-
- `MULTIPLATFORM.md` – cross-platform gotchas and recommended structure.
|