@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.
Files changed (49) hide show
  1. package/AI.md +88 -95
  2. package/README.md +205 -144
  3. package/dist/browser/index.cjs +739 -487
  4. package/dist/browser/index.cjs.map +1 -1
  5. package/dist/browser/index.mjs +736 -487
  6. package/dist/browser/index.mjs.map +1 -1
  7. package/dist/define.d.ts +1 -1
  8. package/dist/definers/builders/asyncContext.d.ts +11 -0
  9. package/dist/definers/builders/error.d.ts +13 -0
  10. package/dist/definers/builders/middleware.d.ts +3 -3
  11. package/dist/definers/defineAsyncContext.d.ts +15 -0
  12. package/dist/definers/defineError.d.ts +26 -0
  13. package/dist/definers/tools.d.ts +6 -0
  14. package/dist/edge/index.cjs +739 -487
  15. package/dist/edge/index.cjs.map +1 -1
  16. package/dist/edge/index.mjs +736 -487
  17. package/dist/edge/index.mjs.map +1 -1
  18. package/dist/errors.d.ts +60 -102
  19. package/dist/globals/debug.d.ts +10 -0
  20. package/dist/globals/globalMiddleware.d.ts +3 -3
  21. package/dist/globals/middleware/requireContext.middleware.d.ts +2 -2
  22. package/dist/globals/resources/tunnel/protocol.d.ts +8 -1
  23. package/dist/globals/resources/tunnel/serializer.d.ts +1 -1
  24. package/dist/globals/types.d.ts +1 -0
  25. package/dist/http-client.d.ts +4 -0
  26. package/dist/http-fetch-tunnel.resource.d.ts +11 -0
  27. package/dist/index.d.ts +14 -5
  28. package/dist/models/Store.d.ts +6 -2
  29. package/dist/models/StoreRegistry.d.ts +6 -0
  30. package/dist/node/exposure/httpResponse.d.ts +1 -1
  31. package/dist/node/exposure/requestContext.d.ts +1 -1
  32. package/dist/node/{mixed-http-client.node.d.ts → http-mixed-client.d.ts} +1 -1
  33. package/dist/node/http-smart-client.model.d.ts +2 -0
  34. package/dist/node/node.cjs +918 -4296
  35. package/dist/node/node.cjs.map +1 -1
  36. package/dist/node/node.mjs +914 -4296
  37. package/dist/node/node.mjs.map +1 -1
  38. package/dist/types/asyncContext.d.ts +39 -0
  39. package/dist/types/error.d.ts +34 -0
  40. package/dist/types/runner.d.ts +13 -0
  41. package/dist/types/symbols.d.ts +6 -0
  42. package/dist/types/utilities.d.ts +6 -4
  43. package/dist/universal/index.cjs +739 -487
  44. package/dist/universal/index.cjs.map +1 -1
  45. package/dist/universal/index.mjs +736 -487
  46. package/dist/universal/index.mjs.map +1 -1
  47. package/dist/utils/detectRunnerMode.d.ts +9 -0
  48. package/package.json +1 -1
  49. 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
- - [Install](#install)
7
- - [Quick Start](#quick-start)
8
- - [Platform Matrix](#platform-matrix)
9
- - [Fluent Builder Primer](#fluent-builder-primer)
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
- - [Tasks](#tasks)
13
- - [Events and Hooks](#events-and-hooks)
14
- - [Middleware](#middleware)
15
- - [Tags](#tags)
16
- - [HTTP & Tunnels](#http--tunnels)
17
- - [Serialization](#serialization)
18
- - [Testing](#testing)
19
- - [Observability & Debugging](#observability--debugging)
20
- - [Advanced Patterns](#advanced-patterns)
21
- - [Interop With Classic APIs](#interop-with-classic-apis)
22
- - [Reference Links](#reference-links)
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
- Runner auto-detects its runtime. The Node bundle lives under `@bluelibs/runner/node`; browser helpers are under `@bluelibs/runner/platform`.
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("Distance", (json) => new Distance(json.value, json.unit));
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.resource("spec.app").register([sendWelcomeEmail, registerUser]).build();
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.