@bluelibs/runner 3.4.1 → 3.4.2
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 +77 -281
- package/dist/define.d.ts +67 -3
- package/dist/define.js +84 -20
- package/dist/define.js.map +1 -1
- package/dist/tools/getCallerFile.js +16 -6
- package/dist/tools/getCallerFile.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/globalEvents.test.ts +1 -1
- package/src/__tests__/run.anonymous.test.ts +27 -0
- package/src/__tests__/tools/getCallerFile.test.ts +20 -5
- package/src/define.ts +101 -25
- package/src/tools/getCallerFile.ts +18 -7
package/src/define.ts
CHANGED
|
@@ -41,6 +41,23 @@ import { generateCallerIdFromFile, getCallerFile } from "./tools/getCallerFile";
|
|
|
41
41
|
|
|
42
42
|
// Helper function to get the caller file
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Define a task.
|
|
46
|
+
* Generates a strongly-typed task object with id, lifecycle events, dependencies,
|
|
47
|
+
* middleware, and metadata.
|
|
48
|
+
*
|
|
49
|
+
* - If `id` is omitted, an anonymous, file-based id is generated.
|
|
50
|
+
* - Wires lifecycle events: `beforeRun`, `afterRun`, `onError`.
|
|
51
|
+
* - Carries through dependencies, middleware, input schema, and metadata.
|
|
52
|
+
*
|
|
53
|
+
* @typeParam Input - Input type accepted by the task's `run` function.
|
|
54
|
+
* @typeParam Output - Promise type returned by the `run` function.
|
|
55
|
+
* @typeParam Deps - Dependency map type this task requires.
|
|
56
|
+
* @typeParam TOn - Event type or "*" this task listens to.
|
|
57
|
+
* @typeParam TMeta - Arbitrary metadata type carried by the task.
|
|
58
|
+
* @param taskConfig - The task definition config.
|
|
59
|
+
* @returns A branded task definition usable by the runner.
|
|
60
|
+
*/
|
|
44
61
|
export function defineTask<
|
|
45
62
|
Input = undefined,
|
|
46
63
|
Output extends Promise<any> = any,
|
|
@@ -50,12 +67,6 @@ export function defineTask<
|
|
|
50
67
|
>(
|
|
51
68
|
taskConfig: ITaskDefinition<Input, Output, Deps, TOn, TMeta>
|
|
52
69
|
): ITask<Input, Output, Deps, TOn, TMeta> {
|
|
53
|
-
/**
|
|
54
|
-
* Creates a task definition.
|
|
55
|
-
* - Generates an anonymous id based on file path when `id` is omitted
|
|
56
|
-
* - Wires lifecycle events: beforeRun, afterRun, onError
|
|
57
|
-
* - Carries through dependencies and middleware as declared
|
|
58
|
-
*/
|
|
59
70
|
const filePath = getCallerFile();
|
|
60
71
|
const isAnonymous = !Boolean(taskConfig.id);
|
|
61
72
|
const id = taskConfig.id || generateCallerIdFromFile(filePath, "task");
|
|
@@ -118,10 +129,21 @@ export function defineResource<
|
|
|
118
129
|
>
|
|
119
130
|
): IResource<TConfig, TValue, TDeps, TPrivate, TMeta> {
|
|
120
131
|
/**
|
|
121
|
-
*
|
|
122
|
-
* -
|
|
123
|
-
*
|
|
124
|
-
*
|
|
132
|
+
* Define a resource.
|
|
133
|
+
* Produces a strongly-typed resource with id, lifecycle events, registration hooks,
|
|
134
|
+
* and optional config schema.
|
|
135
|
+
*
|
|
136
|
+
* - If `id` is omitted, an anonymous, file-based id is generated (resource or index flavored).
|
|
137
|
+
* - Wires lifecycle events: `beforeInit`, `afterInit`, `onError`.
|
|
138
|
+
* - Provides `.with(config)` for config-bound registration with optional runtime validation.
|
|
139
|
+
*
|
|
140
|
+
* @typeParam TConfig - Configuration type accepted by the resource.
|
|
141
|
+
* @typeParam TValue - Promise type resolved by the resource `init`.
|
|
142
|
+
* @typeParam TDeps - Dependency map type this resource requires.
|
|
143
|
+
* @typeParam TPrivate - Private context type exposed to middleware during init.
|
|
144
|
+
* @typeParam TMeta - Arbitrary metadata type carried by the resource.
|
|
145
|
+
* @param constConfig - The resource definition config.
|
|
146
|
+
* @returns A branded resource definition usable by the runner.
|
|
125
147
|
*/
|
|
126
148
|
// The symbolFilePath might already come from defineIndex() for example
|
|
127
149
|
const filePath: string = constConfig[symbolFilePath] || getCallerFile();
|
|
@@ -149,10 +171,14 @@ export function defineResource<
|
|
|
149
171
|
try {
|
|
150
172
|
config = this.configSchema.parse(config);
|
|
151
173
|
} catch (error) {
|
|
152
|
-
throw new ValidationError(
|
|
174
|
+
throw new ValidationError(
|
|
175
|
+
"Resource config",
|
|
176
|
+
this.id,
|
|
177
|
+
error instanceof Error ? error : new Error(String(error))
|
|
178
|
+
);
|
|
153
179
|
}
|
|
154
180
|
}
|
|
155
|
-
|
|
181
|
+
|
|
156
182
|
return {
|
|
157
183
|
[symbolResourceWithConfig]: true,
|
|
158
184
|
id: this.id,
|
|
@@ -237,8 +263,13 @@ export function defineEvent<TPayload = void>(
|
|
|
237
263
|
config?: IEventDefinition<TPayload>
|
|
238
264
|
): IEvent<TPayload> {
|
|
239
265
|
/**
|
|
240
|
-
*
|
|
241
|
-
*
|
|
266
|
+
* Define an event.
|
|
267
|
+
* Generates a branded event definition with a stable id (anonymous if omitted)
|
|
268
|
+
* and file path metadata for better debugging.
|
|
269
|
+
*
|
|
270
|
+
* @typeParam TPayload - Payload type carried by the event.
|
|
271
|
+
* @param config - Optional event definition (id, etc.).
|
|
272
|
+
* @returns A branded event definition.
|
|
242
273
|
*/
|
|
243
274
|
const callerFilePath = getCallerFile();
|
|
244
275
|
const eventConfig = config || {};
|
|
@@ -261,18 +292,25 @@ export type MiddlewareEverywhereOptions = {
|
|
|
261
292
|
resources?: boolean;
|
|
262
293
|
};
|
|
263
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Define a middleware.
|
|
297
|
+
* Creates a middleware definition with anonymous id generation, `.with(config)`,
|
|
298
|
+
* and `.everywhere()` helpers.
|
|
299
|
+
*
|
|
300
|
+
* - `.with(config)` merges config (optionally validated via `configSchema`).
|
|
301
|
+
* - `.everywhere()` marks the middleware global (optionally scoping to tasks/resources).
|
|
302
|
+
*
|
|
303
|
+
* @typeParam TConfig - Configuration type accepted by the middleware.
|
|
304
|
+
* @typeParam TDependencies - Dependency map type required by the middleware.
|
|
305
|
+
* @param middlewareDef - The middleware definition config.
|
|
306
|
+
* @returns A branded middleware definition usable by the runner.
|
|
307
|
+
*/
|
|
264
308
|
export function defineMiddleware<
|
|
265
309
|
TConfig extends Record<string, any>,
|
|
266
310
|
TDependencies extends DependencyMapType
|
|
267
311
|
>(
|
|
268
312
|
middlewareDef: IMiddlewareDefinition<TConfig, TDependencies>
|
|
269
313
|
): IMiddleware<TConfig, TDependencies> {
|
|
270
|
-
/**
|
|
271
|
-
* Creates a middleware definition with:
|
|
272
|
-
* - Anonymous id generation when omitted
|
|
273
|
-
* - `.with(config)` to create configured instances
|
|
274
|
-
* - `.everywhere()` to mark as global (optionally scoping to tasks/resources)
|
|
275
|
-
*/
|
|
276
314
|
const filePath = getCallerFile();
|
|
277
315
|
const object = {
|
|
278
316
|
[symbolFilePath]: filePath,
|
|
@@ -291,10 +329,14 @@ export function defineMiddleware<
|
|
|
291
329
|
try {
|
|
292
330
|
config = object.configSchema.parse(config);
|
|
293
331
|
} catch (error) {
|
|
294
|
-
throw new ValidationError(
|
|
332
|
+
throw new ValidationError(
|
|
333
|
+
"Middleware config",
|
|
334
|
+
object.id,
|
|
335
|
+
error instanceof Error ? error : new Error(String(error))
|
|
336
|
+
);
|
|
295
337
|
}
|
|
296
338
|
}
|
|
297
|
-
|
|
339
|
+
|
|
298
340
|
return {
|
|
299
341
|
...object,
|
|
300
342
|
[symbolMiddlewareConfigured]: true,
|
|
@@ -319,31 +361,60 @@ export function defineMiddleware<
|
|
|
319
361
|
};
|
|
320
362
|
}
|
|
321
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Type guard: checks if a definition is a Task.
|
|
366
|
+
* @param definition - Any value to test.
|
|
367
|
+
* @returns True when `definition` is a branded Task.
|
|
368
|
+
*/
|
|
322
369
|
export function isTask(definition: any): definition is ITask {
|
|
323
370
|
return definition && definition[symbolTask];
|
|
324
371
|
}
|
|
325
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Type guard: checks if a definition is a Resource.
|
|
375
|
+
* @param definition - Any value to test.
|
|
376
|
+
* @returns True when `definition` is a branded Resource.
|
|
377
|
+
*/
|
|
326
378
|
export function isResource(definition: any): definition is IResource {
|
|
327
379
|
return definition && definition[symbolResource];
|
|
328
380
|
}
|
|
329
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Type guard: checks if a definition is a Resource that carries config via `.with()`.
|
|
384
|
+
* @param definition - Any value to test.
|
|
385
|
+
* @returns True when `definition` is a branded ResourceWithConfig.
|
|
386
|
+
*/
|
|
330
387
|
export function isResourceWithConfig(
|
|
331
388
|
definition: any
|
|
332
389
|
): definition is IResourceWithConfig {
|
|
333
390
|
return definition && definition[symbolResourceWithConfig];
|
|
334
391
|
}
|
|
335
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Type guard: checks if a definition is an Event.
|
|
395
|
+
* @param definition - Any value to test.
|
|
396
|
+
* @returns True when `definition` is a branded Event.
|
|
397
|
+
*/
|
|
336
398
|
export function isEvent(definition: any): definition is IEvent {
|
|
337
399
|
return definition && definition[symbolEvent];
|
|
338
400
|
}
|
|
339
401
|
|
|
402
|
+
/**
|
|
403
|
+
* Type guard: checks if a definition is a Middleware.
|
|
404
|
+
* @param definition - Any value to test.
|
|
405
|
+
* @returns True when `definition` is a branded Middleware.
|
|
406
|
+
*/
|
|
340
407
|
export function isMiddleware(definition: any): definition is IMiddleware {
|
|
341
408
|
return definition && definition[symbolMiddleware];
|
|
342
409
|
}
|
|
343
410
|
|
|
344
411
|
/**
|
|
345
412
|
* Override helper that preserves the original `id` and returns the same type.
|
|
346
|
-
* You can override any property except `id`.
|
|
413
|
+
* You can override any property except `id`. The override is shallow-merged over the base.
|
|
414
|
+
*
|
|
415
|
+
* @param base - The base definition to override.
|
|
416
|
+
* @param patch - Properties to override (except `id`).
|
|
417
|
+
* @returns A definition of the same kind with overrides applied.
|
|
347
418
|
*/
|
|
348
419
|
export function defineOverride<T extends ITask<any, any, any, any>>(
|
|
349
420
|
base: T,
|
|
@@ -371,9 +442,14 @@ export function defineOverride(
|
|
|
371
442
|
}
|
|
372
443
|
|
|
373
444
|
/**
|
|
374
|
-
*
|
|
445
|
+
* Create a tag definition.
|
|
375
446
|
* - `.with(config)` to create configured instances
|
|
376
|
-
* - `.extract(tags)` to extract this tag from a list of tags
|
|
447
|
+
* - `.extract(tags)` to extract this tag from a list of tags or a taggable's meta
|
|
448
|
+
*
|
|
449
|
+
* @typeParam TConfig - Configuration type carried by configured tags.
|
|
450
|
+
* @typeParam TEnforceContract - Optional helper type to enforce a contract when tags are used.
|
|
451
|
+
* @param definition - The tag definition (id).
|
|
452
|
+
* @returns A tag object with helpers to configure and extract.
|
|
377
453
|
*/
|
|
378
454
|
export function defineTag<TConfig = void, TEnforceContract = void>(
|
|
379
455
|
definition: ITagDefinition<TConfig, TEnforceContract>
|
|
@@ -44,18 +44,29 @@ export function generateCallerIdFromFile(
|
|
|
44
44
|
suffix: string = "",
|
|
45
45
|
fallbackParts: number = 4
|
|
46
46
|
): symbol {
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
const nodeModulesIndex = parts.lastIndexOf("node_modules");
|
|
47
|
+
// Normalize paths for consistency across platforms
|
|
48
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
49
|
+
const cwdNormalized = process.cwd().replace(/\\/g, "/");
|
|
51
50
|
|
|
52
|
-
const
|
|
51
|
+
const parts = normalizedPath.split("/");
|
|
52
|
+
const nodeModulesIndex = parts.lastIndexOf("node_modules");
|
|
53
53
|
|
|
54
54
|
let relevantParts: string[];
|
|
55
55
|
|
|
56
|
-
if (
|
|
57
|
-
|
|
56
|
+
if (nodeModulesIndex !== -1) {
|
|
57
|
+
// If inside node_modules, generate id relative to the package path
|
|
58
|
+
relevantParts = parts.slice(nodeModulesIndex + 1);
|
|
59
|
+
} else if (
|
|
60
|
+
normalizedPath === cwdNormalized ||
|
|
61
|
+
normalizedPath.startsWith(cwdNormalized + "/")
|
|
62
|
+
) {
|
|
63
|
+
// Prefer generating id relative to the workspace root (process.cwd())
|
|
64
|
+
const relativeToCwd = normalizedPath
|
|
65
|
+
.slice(cwdNormalized.length)
|
|
66
|
+
.replace(/^\//, "");
|
|
67
|
+
relevantParts = relativeToCwd.length > 0 ? relativeToCwd.split("/") : [""];
|
|
58
68
|
} else {
|
|
69
|
+
// Fallback: use the last N parts if path is outside cwd and not in node_modules
|
|
59
70
|
relevantParts = parts.slice(-fallbackParts);
|
|
60
71
|
}
|
|
61
72
|
|