@bluelibs/runner 4.5.9 → 4.6.0-alpha
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 +48 -102
- package/README.md +50 -6
- package/dist/browser/index.mjs +4492 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/{index.mjs → edge/index.mjs} +473 -306
- package/dist/edge/index.mjs.map +1 -0
- package/dist/{index.d.ts → index.d.mts} +14 -9
- package/dist/index.unused.js +4344 -0
- package/dist/index.unused.js.map +1 -0
- package/dist/{index.cjs → node/index.cjs} +473 -312
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.mjs +4492 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/universal/index.cjs +4524 -0
- package/dist/universal/index.cjs.map +1 -0
- package/dist/universal/index.mjs +4524 -0
- package/dist/universal/index.mjs.map +1 -0
- package/package.json +49 -11
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
package/AI.md
CHANGED
|
@@ -184,9 +184,7 @@ import { globals, task } from "@bluelibs/runner";
|
|
|
184
184
|
|
|
185
185
|
const critical = task({
|
|
186
186
|
id: "app.tasks.critical",
|
|
187
|
-
tags: [
|
|
188
|
-
globals.tags.debug.with({ logTaskInput: true, logTaskOutput: true }),
|
|
189
|
-
],
|
|
187
|
+
tags: [globals.tags.debug.with({ logTaskInput: true, logTaskOutput: true })],
|
|
190
188
|
run: async () => "ok",
|
|
191
189
|
});
|
|
192
190
|
```
|
|
@@ -203,7 +201,7 @@ const logsExtension = resource({
|
|
|
203
201
|
logger.info("test", { example: 123 }); // "trace", "debug", "info", "warn", "error", "critical"
|
|
204
202
|
const sublogger = logger.with({
|
|
205
203
|
source: "app.logs",
|
|
206
|
-
|
|
204
|
+
additionalContext: {},
|
|
207
205
|
});
|
|
208
206
|
logger.onLog((log) => {
|
|
209
207
|
// ship or transform
|
|
@@ -238,7 +236,8 @@ export async function getRunner() {
|
|
|
238
236
|
// handler.ts
|
|
239
237
|
export const handler = async (event: any, context: any) => {
|
|
240
238
|
const rr: any = await getRunner();
|
|
241
|
-
const method =
|
|
239
|
+
const method =
|
|
240
|
+
event?.requestContext?.http?.method ?? event?.httpMethod ?? "GET";
|
|
242
241
|
const path = event?.rawPath || event?.path || "/";
|
|
243
242
|
const rawBody = event?.body
|
|
244
243
|
? event.isBase64Encoded
|
|
@@ -247,9 +246,12 @@ export const handler = async (event: any, context: any) => {
|
|
|
247
246
|
: undefined;
|
|
248
247
|
const body = rawBody ? JSON.parse(rawBody) : undefined;
|
|
249
248
|
|
|
250
|
-
return RequestCtx.provide(
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
return RequestCtx.provide(
|
|
250
|
+
{ requestId: context?.awsRequestId ?? "local", method, path },
|
|
251
|
+
async () => {
|
|
252
|
+
// route and call rr.runTask(...)
|
|
253
|
+
},
|
|
254
|
+
);
|
|
253
255
|
};
|
|
254
256
|
```
|
|
255
257
|
|
|
@@ -396,6 +398,21 @@ const { dispose } = await run(app, {
|
|
|
396
398
|
});
|
|
397
399
|
```
|
|
398
400
|
|
|
401
|
+
## Type Helpers
|
|
402
|
+
|
|
403
|
+
Extract generics from tasks, resources, and events without re-declaring their shapes and avoid use of 'any'.
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
import { task, resource, event } from "@bluelibs/runner";
|
|
407
|
+
import type {
|
|
408
|
+
ExtractTaskInput, // ExtractTaskInput(typeof myTask)
|
|
409
|
+
ExtractTaskOutput,
|
|
410
|
+
ExtractResourceConfig,
|
|
411
|
+
ExtractResourceValue,
|
|
412
|
+
ExtractEventPayload,
|
|
413
|
+
} from "@bluelibs/runner";
|
|
414
|
+
```
|
|
415
|
+
|
|
399
416
|
## Run Options (high‑level)
|
|
400
417
|
|
|
401
418
|
- debug: "normal" | "verbose" | DebugConfig
|
|
@@ -521,115 +538,40 @@ Coverage tip: the script name is `npm run coverage`.
|
|
|
521
538
|
|
|
522
539
|
## Metadata & Tags
|
|
523
540
|
|
|
541
|
+
Tags and meta can be applied to all elements.
|
|
542
|
+
|
|
524
543
|
```ts
|
|
525
544
|
import { tag, globals, task, resource } from "@bluelibs/runner";
|
|
526
545
|
|
|
527
|
-
// Simple tags and debug/system globals
|
|
528
|
-
const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
|
|
529
546
|
const contractTag = tag<void, void, { result: string }>({ id: "contract" });
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
id: "app.tasks.pay",
|
|
533
|
-
tags: [perf.with({ warnAboveMs: 1000 })],
|
|
534
|
-
meta: {
|
|
535
|
-
title: "Process Payment",
|
|
536
|
-
description: "Detailed",
|
|
537
|
-
},
|
|
538
|
-
run: async () => {
|
|
539
|
-
/* ... */
|
|
540
|
-
},
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
const internalSvc = resource({
|
|
544
|
-
id: "app.resources.internal",
|
|
545
|
-
register: [perf],
|
|
546
|
-
tags: [globals.tags.system],
|
|
547
|
-
init: async () => ({}),
|
|
548
|
-
});
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
### Tag Contracts (type‑enforced returns)
|
|
552
|
-
|
|
553
|
-
```ts
|
|
554
|
-
// Contract enforces the awaited return type (Config, Input, Output)
|
|
555
|
-
const userContract = tag<void, void, { name: string }>({ id: "contract.user" });
|
|
556
|
-
|
|
557
|
-
const getProfile = task({
|
|
558
|
-
id: "app.tasks.getProfile",
|
|
559
|
-
tags: [userContract],
|
|
560
|
-
run: async () => ({ name: "Ada" }), // must contain { name: string }
|
|
547
|
+
const httpRouteTag = tag<{ method: "GET" | "POST"; path: string }>({
|
|
548
|
+
id: "httpRoute",
|
|
561
549
|
});
|
|
562
550
|
|
|
563
|
-
const
|
|
564
|
-
id: "app.
|
|
565
|
-
tags: [
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
### Tag Extraction (behavior flags)
|
|
571
|
-
|
|
572
|
-
```ts
|
|
573
|
-
const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
|
|
574
|
-
|
|
575
|
-
const perfMiddleware = taskMiddleware({
|
|
576
|
-
id: "app.middleware.perf",
|
|
577
|
-
run: async ({ task, next }) => {
|
|
578
|
-
const cfg = perf.extract(task.definition);
|
|
579
|
-
// use perf.exists(task.definition) to check for existence
|
|
580
|
-
if (!cfg) return next(task.input);
|
|
581
|
-
// performance hooks here ...
|
|
551
|
+
const task = task({
|
|
552
|
+
id: "app.tasks.myTask",
|
|
553
|
+
tags: [contractTag, httpRouteTag.with({ method: "POST", path: "/do" })],
|
|
554
|
+
run: async () => ({ result: "ok" }), // must return { result: string }
|
|
555
|
+
meta: {
|
|
556
|
+
title: "My Task",
|
|
557
|
+
description: "Does something important", // multi-line description, markdown
|
|
582
558
|
},
|
|
583
559
|
});
|
|
584
560
|
```
|
|
585
561
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
Use a hook on `globals.events.ready` to discover and intercept tasks by tag:
|
|
562
|
+
Usage:
|
|
589
563
|
|
|
590
564
|
```ts
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const apiTag = tag<void>({ id: "api" });
|
|
594
|
-
|
|
595
|
-
const addTracingToApiTasks = hook({
|
|
596
|
-
id: "app.hooks.traceApis",
|
|
565
|
+
const onReady = hook({
|
|
566
|
+
id: "app.hooks.onReady",
|
|
597
567
|
on: globals.events.ready,
|
|
598
568
|
dependencies: { store: globals.resources.store },
|
|
599
569
|
run: async (_, { store }) => {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
taskDef.intercept(async (next, input) => {
|
|
603
|
-
// ...
|
|
604
|
-
});
|
|
605
|
-
});
|
|
606
|
-
// Apply same concept to routing like fastify, express routes based on tag config to your fastify instance.
|
|
607
|
-
},
|
|
608
|
-
});
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
### Route registration via tags (ready hook)
|
|
612
|
-
|
|
613
|
-
```ts
|
|
614
|
-
import { hook, globals } from "@bluelibs/runner";
|
|
615
|
-
import { httpTag } from "./http.tag"; // your structured tag (method, path, schemas, etc.)
|
|
616
|
-
import { expressServer } from "./expressServer"; // resource that returns { app, port }
|
|
617
|
-
|
|
618
|
-
const registerRoutes = hook({
|
|
619
|
-
id: "app.hooks.registerRoutes",
|
|
620
|
-
on: globals.events.ready,
|
|
621
|
-
dependencies: { store: globals.resources.store, server: expressServer },
|
|
622
|
-
run: async (_, { store, server }) => {
|
|
623
|
-
const tasks = store.getTasksWithTag(httpTag);
|
|
570
|
+
// Same concept for resources
|
|
571
|
+
const tasks = store.getTasksWithTag(httpRouteTag); // uses httpRouteTag.exists(component);
|
|
624
572
|
tasks.forEach((t) => {
|
|
625
|
-
const cfg =
|
|
626
|
-
|
|
627
|
-
const { method, path } = cfg.config;
|
|
628
|
-
if (!method || !path) return;
|
|
629
|
-
(server.app as any)[method.toLowerCase()](path, async (req, res) => {
|
|
630
|
-
const result = await t({ ...req.body, ...req.query, ...req.params });
|
|
631
|
-
res.json(result);
|
|
632
|
-
});
|
|
573
|
+
const cfg = httpRouteTag.extract(tasks); // { method, path }
|
|
574
|
+
// you can even do t
|
|
633
575
|
});
|
|
634
576
|
},
|
|
635
577
|
});
|
|
@@ -704,7 +646,7 @@ interface IValidationSchema<T> {
|
|
|
704
646
|
}
|
|
705
647
|
```
|
|
706
648
|
|
|
707
|
-
Works out of the box with Zod (`z.object(...).parse`), and can be adapted for Yup/Joi with small wrappers.
|
|
649
|
+
Works out of the box with Zod (`z.object(...).parse`), and can be adapted for Yup/Joi with small wrappers. As it only needs a parse() method.
|
|
708
650
|
|
|
709
651
|
```ts
|
|
710
652
|
import { z } from "zod";
|
|
@@ -728,6 +670,10 @@ event({
|
|
|
728
670
|
payloadSchema, // Runs on event emission
|
|
729
671
|
});
|
|
730
672
|
|
|
673
|
+
tag({
|
|
674
|
+
configSchema, // Tag config validation (runs on .with())
|
|
675
|
+
});
|
|
676
|
+
|
|
731
677
|
// Middleware config validation (runs on .with())
|
|
732
678
|
middleware({
|
|
733
679
|
// ...
|
package/README.md
CHANGED
|
@@ -19,8 +19,13 @@ _Or: How I Learned to Stop Worrying and Love Dependency Injection_
|
|
|
19
19
|
| [Migrate from 3.x.x to 4.x.x](https://github.com/bluelibs/runner/blob/main/readmes/MIGRATION.md) | Guide | Step-by-step upgrade from v3 to v4 |
|
|
20
20
|
| [Runner Lore](https://github.com/bluelibs/runner/blob/main/readmes) | Docs | Design notes, deep dives, and context |
|
|
21
21
|
| [Example: Express + OpenAPI + SQLite](https://github.com/bluelibs/runner/tree/main/examples/express-openapi-sqlite) | Example | Full Express + OpenAPI + SQLite demo |
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
|| [Example: Fastify + MikroORM + PostgreSQL](https://github.com/bluelibs/runner/tree/main/examples/fastify-mikroorm) | Example | Full Fastify + MikroORM + PostgreSQL demo |
|
|
23
|
+
|| [OpenAI Runner Chatbot](https://chatgpt.com/g/g-68b756abec648191aa43eaa1ea7a7945-runner?model=gpt-5-thinking) | Chatbot | Ask questions interactively, or feed README.md to your own AI |
|
|
24
|
+
|
|
25
|
+
### Community & Policies
|
|
26
|
+
- Code of Conduct: see [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
|
|
27
|
+
- Contributing: see [CONTRIBUTING](./CONTRIBUTING.md)
|
|
28
|
+
- Security: see [SECURITY](./SECURITY.md)
|
|
24
29
|
|
|
25
30
|
Welcome to BlueLibs Runner, where we've taken the chaos of modern application architecture and turned it into something that won't make you question your life choices at 3am. This isn't just another framework – it's your new best friend who actually understands that code should be readable, testable, and not require a PhD in abstract nonsense to maintain.
|
|
26
31
|
|
|
@@ -958,6 +963,43 @@ const handleRequest = resource({
|
|
|
958
963
|
});
|
|
959
964
|
```
|
|
960
965
|
|
|
966
|
+
## Type Helpers
|
|
967
|
+
|
|
968
|
+
These utility types help you extract the generics from tasks, resources, and events without re-declaring them. Import them from `@bluelibs/runner`.
|
|
969
|
+
|
|
970
|
+
```ts
|
|
971
|
+
import { task, resource, event } from "@bluelibs/runner";
|
|
972
|
+
import type {
|
|
973
|
+
ExtractTaskInput,
|
|
974
|
+
ExtractTaskOutput,
|
|
975
|
+
ExtractResourceConfig,
|
|
976
|
+
ExtractResourceValue,
|
|
977
|
+
ExtractEventPayload,
|
|
978
|
+
} from "@bluelibs/runner";
|
|
979
|
+
|
|
980
|
+
// Task example
|
|
981
|
+
const add = task({
|
|
982
|
+
id: "calc.add",
|
|
983
|
+
run: async (input: { a: number; b: number }) => input.a + input.b,
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
type AddInput = ExtractTaskInput<typeof add>; // { a: number; b: number }
|
|
987
|
+
type AddOutput = ExtractTaskOutput<typeof add>; // number
|
|
988
|
+
|
|
989
|
+
// Resource example
|
|
990
|
+
const config = resource({
|
|
991
|
+
id: "app.config",
|
|
992
|
+
init: async (cfg: { baseUrl: string }) => ({ baseUrl: cfg.baseUrl }),
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
type ConfigInput = ExtractResourceConfig<typeof config>; // { baseUrl: string }
|
|
996
|
+
type ConfigValue = ExtractResourceValue<typeof config>; // { baseUrl: string }
|
|
997
|
+
|
|
998
|
+
// Event example
|
|
999
|
+
const userRegistered = event<{ userId: string; email: string }>({ id: "app.events.userRegistered" });
|
|
1000
|
+
type UserRegisteredPayload = ExtractEventPayload<typeof userRegistered>; // { userId: string; email: string }
|
|
1001
|
+
```
|
|
1002
|
+
|
|
961
1003
|
### Context with Middleware
|
|
962
1004
|
|
|
963
1005
|
Context shines when combined with middleware for request-scoped data:
|
|
@@ -1328,7 +1370,7 @@ BlueLibs Runner achieves high performance while providing enterprise features:
|
|
|
1328
1370
|
| Event System | ~0.013ms | Loose coupling, observability |
|
|
1329
1371
|
| Middleware Chain | ~0.0003ms/middleware | Cross-cutting concerns |
|
|
1330
1372
|
| Resource Management | One-time init | Singleton pattern, lifecycle |
|
|
1331
|
-
| Built-in Caching |
|
|
1373
|
+
| Built-in Caching | Variable speedup | Automatic optimization |
|
|
1332
1374
|
|
|
1333
1375
|
**Bottom line**: The framework adds minimal overhead (~0.005ms per task) while providing significant architectural benefits.
|
|
1334
1376
|
|
|
@@ -1552,10 +1594,12 @@ const requestHandler = task({
|
|
|
1552
1594
|
const request = RequestContext.use();
|
|
1553
1595
|
|
|
1554
1596
|
// Create a contextual logger with bound metadata with source and context
|
|
1555
|
-
const requestLogger = logger.with(
|
|
1597
|
+
const requestLogger = logger.with({
|
|
1556
1598
|
source: requestHandler.id,
|
|
1557
|
-
|
|
1558
|
-
|
|
1599
|
+
additionalContext: {
|
|
1600
|
+
requestId: request.requestId,
|
|
1601
|
+
userId: request.userId,
|
|
1602
|
+
},
|
|
1559
1603
|
});
|
|
1560
1604
|
|
|
1561
1605
|
// All logs from this logger will include the bound context
|