@bluelibs/runner 4.5.8 → 4.5.10

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 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
- context: {},
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 = event?.requestContext?.http?.method ?? event?.httpMethod ?? "GET";
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({ requestId: context?.awsRequestId ?? "local", method, path }, async () => {
251
- // route and call rr.runTask(...)
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
- const processPayment = task({
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 profileService = resource({
564
- id: "app.resources.profile",
565
- tags: [userContract],
566
- init: async () => ({ name: "Ada" }),
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
- ### Intercept Tasks via Tags (programmatic wiring)
587
-
588
- Use a hook on `globals.events.ready` to discover and intercept tasks by tag:
562
+ Usage:
589
563
 
590
564
  ```ts
591
- import { hook, globals, tag } from "@bluelibs/runner";
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
- const apiTasks = store.getTasksWithTag(apiTag); // tag object or string id
601
- apiTasks.forEach((taskDef) => {
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 = httpTag.extract(t.meta?.tags || []);
626
- if (!cfg?.config) return;
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
@@ -958,6 +958,43 @@ const handleRequest = resource({
958
958
  });
959
959
  ```
960
960
 
961
+ ## Type Helpers
962
+
963
+ These utility types help you extract the generics from tasks, resources, and events without re-declaring them. Import them from `@bluelibs/runner`.
964
+
965
+ ```ts
966
+ import { task, resource, event } from "@bluelibs/runner";
967
+ import type {
968
+ ExtractTaskInput,
969
+ ExtractTaskOutput,
970
+ ExtractResourceConfig,
971
+ ExtractResourceValue,
972
+ ExtractEventPayload,
973
+ } from "@bluelibs/runner";
974
+
975
+ // Task example
976
+ const add = task({
977
+ id: "calc.add",
978
+ run: async (input: { a: number; b: number }) => input.a + input.b,
979
+ });
980
+
981
+ type AddInput = ExtractTaskInput<typeof add>; // { a: number; b: number }
982
+ type AddOutput = ExtractTaskOutput<typeof add>; // number
983
+
984
+ // Resource example
985
+ const config = resource({
986
+ id: "app.config",
987
+ init: async (cfg: { baseUrl: string }) => ({ baseUrl: cfg.baseUrl }),
988
+ });
989
+
990
+ type ConfigInput = ExtractResourceConfig<typeof config>; // { baseUrl: string }
991
+ type ConfigValue = ExtractResourceValue<typeof config>; // { baseUrl: string }
992
+
993
+ // Event example
994
+ const userRegistered = event<{ userId: string; email: string }>({ id: "app.events.userRegistered" });
995
+ type UserRegisteredPayload = ExtractEventPayload<typeof userRegistered>; // { userId: string; email: string }
996
+ ```
997
+
961
998
  ### Context with Middleware
962
999
 
963
1000
  Context shines when combined with middleware for request-scoped data:
@@ -1328,7 +1365,7 @@ BlueLibs Runner achieves high performance while providing enterprise features:
1328
1365
  | Event System | ~0.013ms | Loose coupling, observability |
1329
1366
  | Middleware Chain | ~0.0003ms/middleware | Cross-cutting concerns |
1330
1367
  | Resource Management | One-time init | Singleton pattern, lifecycle |
1331
- | Built-in Caching | 1.8x speedup | Automatic optimization |
1368
+ | Built-in Caching | Variable speedup | Automatic optimization |
1332
1369
 
1333
1370
  **Bottom line**: The framework adds minimal overhead (~0.005ms per task) while providing significant architectural benefits.
1334
1371
 
@@ -1552,10 +1589,12 @@ const requestHandler = task({
1552
1589
  const request = RequestContext.use();
1553
1590
 
1554
1591
  // Create a contextual logger with bound metadata with source and context
1555
- const requestLogger = logger.with("api.handler", {
1592
+ const requestLogger = logger.with({
1556
1593
  source: requestHandler.id,
1557
- requestId: request.requestId,
1558
- userId: request.userId,
1594
+ additionalContext: {
1595
+ requestId: request.requestId,
1596
+ userId: request.userId,
1597
+ },
1559
1598
  });
1560
1599
 
1561
1600
  // All logs from this logger will include the bound context
package/dist/index.cjs CHANGED
@@ -4105,13 +4105,7 @@ async function run(resourceOrResourceWithConfig, options) {
4105
4105
  store2.lock();
4106
4106
  eventManager.lock();
4107
4107
  await logger.lock();
4108
- await eventManager.emit(
4109
- globalEvents.ready,
4110
- {
4111
- root: store2.root.resource
4112
- },
4113
- "system"
4114
- );
4108
+ await eventManager.emit(globalEvents.ready, void 0, "run");
4115
4109
  await boundedLogger.info("Runner online. Awaiting tasks and events.");
4116
4110
  if (shutdownHooks) {
4117
4111
  unhookShutdown = registerShutdownHook(() => store2.dispose());