@bluelibs/runner 3.2.0 → 3.3.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/README.md +482 -34
- package/dist/cli/extract-docs.d.ts +2 -0
- package/dist/cli/extract-docs.js +88 -0
- package/dist/cli/extract-docs.js.map +1 -0
- package/dist/define.d.ts +21 -1
- package/dist/define.js +95 -23
- package/dist/define.js.map +1 -1
- package/dist/defs/core.d.ts +144 -0
- package/dist/defs/core.js +6 -0
- package/dist/defs/core.js.map +1 -0
- package/dist/defs/symbols.d.ts +42 -0
- package/dist/defs/symbols.js +45 -0
- package/dist/defs/symbols.js.map +1 -0
- package/dist/defs/tags.d.ts +70 -0
- package/dist/defs/tags.js +6 -0
- package/dist/defs/tags.js.map +1 -0
- package/dist/defs.d.ts +168 -16
- package/dist/defs.js +41 -14
- package/dist/defs.js.map +1 -1
- package/dist/docs/introspect.d.ts +7 -0
- package/dist/docs/introspect.js +199 -0
- package/dist/docs/introspect.js.map +1 -0
- package/dist/docs/markdown.d.ts +2 -0
- package/dist/docs/markdown.js +148 -0
- package/dist/docs/markdown.js.map +1 -0
- package/dist/docs/model.d.ts +62 -0
- package/dist/docs/model.js +33 -0
- package/dist/docs/model.js.map +1 -0
- package/dist/express/docsRouter.d.ts +12 -0
- package/dist/express/docsRouter.js +54 -0
- package/dist/express/docsRouter.js.map +1 -0
- package/dist/globals/globalMiddleware.d.ts +1 -0
- package/dist/globals/globalMiddleware.js +2 -0
- package/dist/globals/globalMiddleware.js.map +1 -1
- package/dist/globals/middleware/timeout.middleware.d.ts +8 -0
- package/dist/globals/middleware/timeout.middleware.js +35 -0
- package/dist/globals/middleware/timeout.middleware.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/models/DependencyProcessor.js +2 -2
- package/dist/models/DependencyProcessor.js.map +1 -1
- package/dist/models/Store.d.ts +1 -1
- package/dist/models/StoreConstants.d.ts +1 -1
- package/dist/models/StoreConstants.js +2 -1
- package/dist/models/StoreConstants.js.map +1 -1
- package/dist/models/TaskRunner.d.ts +2 -3
- package/dist/models/TaskRunner.js +1 -2
- package/dist/models/TaskRunner.js.map +1 -1
- package/dist/testing.d.ts +24 -0
- package/dist/testing.js +41 -0
- package/dist/testing.js.map +1 -0
- package/dist/types/dependencies.d.ts +47 -18
- package/dist/types/event.d.ts +49 -0
- package/dist/types/event.js +4 -0
- package/dist/types/event.js.map +1 -0
- package/dist/types/index.d.ts +4 -10
- package/dist/types/index.js +8 -7
- package/dist/types/index.js.map +1 -1
- package/dist/types/metadata.d.ts +75 -0
- package/dist/types/metadata.js +3 -0
- package/dist/types/metadata.js.map +1 -0
- package/dist/types/middleware.d.ts +43 -18
- package/dist/types/middleware.js +0 -3
- package/dist/types/middleware.js.map +1 -1
- package/dist/types/resource.d.ts +96 -0
- package/dist/types/resource.js +3 -0
- package/dist/types/resource.js.map +1 -0
- package/dist/types/symbols.d.ts +17 -0
- package/dist/types/symbols.js +18 -3
- package/dist/types/symbols.js.map +1 -1
- package/dist/types/task.d.ts +68 -0
- package/dist/types/task.js +3 -0
- package/dist/types/task.js.map +1 -0
- package/package.json +4 -4
- package/src/__tests__/benchmark/task-benchmark.test.ts +132 -0
- package/src/__tests__/createTestResource.test.ts +139 -0
- package/src/__tests__/globals/timeout.middleware.test.ts +88 -0
- package/src/__tests__/models/EventManager.test.ts +39 -6
- package/src/__tests__/models/Semaphore.test.ts +1 -1
- package/src/__tests__/override.test.ts +104 -0
- package/src/__tests__/run.overrides.test.ts +50 -21
- package/src/__tests__/run.test.ts +19 -0
- package/src/__tests__/tags.test.ts +396 -0
- package/src/__tests__/tools/getCallerFile.test.ts +9 -11
- package/src/__tests__/typesafety.test.ts +109 -1
- package/src/define.ts +128 -24
- package/src/defs.ts +174 -22
- package/src/globals/globalMiddleware.ts +2 -0
- package/src/globals/middleware/timeout.middleware.ts +46 -0
- package/src/index.ts +6 -0
- package/src/models/DependencyProcessor.ts +2 -10
- package/src/models/StoreConstants.ts +2 -1
- package/src/models/TaskRunner.ts +1 -3
- package/src/testing.ts +66 -0
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
defineResource,
|
|
5
5
|
defineEvent,
|
|
6
6
|
defineMiddleware,
|
|
7
|
+
defineOverride,
|
|
7
8
|
} from "../define";
|
|
8
9
|
import { Errors } from "../errors";
|
|
9
10
|
import { run } from "../run";
|
|
@@ -16,8 +17,7 @@ describe("run.overrides", () => {
|
|
|
16
17
|
run: async () => "Task executed",
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
const
|
|
20
|
-
id: "task",
|
|
20
|
+
const overrideTask = defineOverride(task, {
|
|
21
21
|
run: async () => "Task overridden",
|
|
22
22
|
});
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ describe("run.overrides", () => {
|
|
|
27
27
|
dependencies: {
|
|
28
28
|
task,
|
|
29
29
|
},
|
|
30
|
-
overrides: [
|
|
30
|
+
overrides: [overrideTask],
|
|
31
31
|
async init(_, deps) {
|
|
32
32
|
return await deps.task();
|
|
33
33
|
},
|
|
@@ -43,15 +43,14 @@ describe("run.overrides", () => {
|
|
|
43
43
|
run: async () => "Task executed",
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
id: "task",
|
|
46
|
+
const overrideTask = defineOverride(task, {
|
|
48
47
|
run: async () => "Task overridden",
|
|
49
48
|
});
|
|
50
49
|
|
|
51
50
|
const middle = defineResource({
|
|
52
51
|
id: "app",
|
|
53
52
|
register: [task],
|
|
54
|
-
overrides: [
|
|
53
|
+
overrides: [overrideTask],
|
|
55
54
|
});
|
|
56
55
|
|
|
57
56
|
const root = defineResource({
|
|
@@ -73,15 +72,14 @@ describe("run.overrides", () => {
|
|
|
73
72
|
run: async () => "Task executed",
|
|
74
73
|
});
|
|
75
74
|
|
|
76
|
-
const
|
|
77
|
-
id: "task",
|
|
75
|
+
const overrideTask = defineOverride(task, {
|
|
78
76
|
run: async () => "Task overridden",
|
|
79
77
|
});
|
|
80
78
|
|
|
81
79
|
const middle = defineResource<{ test: string }>({
|
|
82
80
|
id: "app",
|
|
83
81
|
register: [task],
|
|
84
|
-
overrides: [
|
|
82
|
+
overrides: [overrideTask],
|
|
85
83
|
});
|
|
86
84
|
|
|
87
85
|
const root = defineResource({
|
|
@@ -103,8 +101,7 @@ describe("run.overrides", () => {
|
|
|
103
101
|
run: async () => "Task executed",
|
|
104
102
|
});
|
|
105
103
|
|
|
106
|
-
const
|
|
107
|
-
id: "task",
|
|
104
|
+
const overrideTask = defineOverride(task, {
|
|
108
105
|
run: async () => "Task overridden",
|
|
109
106
|
});
|
|
110
107
|
|
|
@@ -112,10 +109,9 @@ describe("run.overrides", () => {
|
|
|
112
109
|
id: "resource",
|
|
113
110
|
});
|
|
114
111
|
|
|
115
|
-
const resourceOverride = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
};
|
|
112
|
+
const resourceOverride = defineOverride(resource, {
|
|
113
|
+
overrides: [overrideTask],
|
|
114
|
+
});
|
|
119
115
|
|
|
120
116
|
const middle = defineResource({
|
|
121
117
|
id: "app",
|
|
@@ -142,8 +138,7 @@ describe("run.overrides", () => {
|
|
|
142
138
|
run: async () => "Task executed",
|
|
143
139
|
});
|
|
144
140
|
|
|
145
|
-
const
|
|
146
|
-
id: "task",
|
|
141
|
+
const overrideTask = defineOverride(task, {
|
|
147
142
|
run: async () => "Task overridden",
|
|
148
143
|
});
|
|
149
144
|
|
|
@@ -153,7 +148,7 @@ describe("run.overrides", () => {
|
|
|
153
148
|
|
|
154
149
|
const resourceOverride: definitions.IResource<any> = {
|
|
155
150
|
...resource,
|
|
156
|
-
overrides: [
|
|
151
|
+
overrides: [overrideTask],
|
|
157
152
|
async init(config: { test: string }) {
|
|
158
153
|
return "Resource init";
|
|
159
154
|
},
|
|
@@ -186,8 +181,7 @@ describe("run.overrides", () => {
|
|
|
186
181
|
},
|
|
187
182
|
});
|
|
188
183
|
|
|
189
|
-
const
|
|
190
|
-
id: "middleware",
|
|
184
|
+
const middlewareOverride = defineOverride(middleware, {
|
|
191
185
|
run: async ({ next }) => {
|
|
192
186
|
return `Override: ${await next()}`;
|
|
193
187
|
},
|
|
@@ -203,7 +197,7 @@ describe("run.overrides", () => {
|
|
|
203
197
|
id: "resource",
|
|
204
198
|
register: [middleware, task],
|
|
205
199
|
dependencies: { task },
|
|
206
|
-
overrides: [
|
|
200
|
+
overrides: [middlewareOverride],
|
|
207
201
|
async init(_, deps) {
|
|
208
202
|
return deps.task();
|
|
209
203
|
},
|
|
@@ -392,4 +386,39 @@ describe("run.overrides", () => {
|
|
|
392
386
|
const result = await run(app);
|
|
393
387
|
expect(result.value).toBe("Task overriden.");
|
|
394
388
|
});
|
|
389
|
+
|
|
390
|
+
it("should choose precedence when two overrides target the same id", async () => {
|
|
391
|
+
const baseTask = defineTask({
|
|
392
|
+
id: "task.same",
|
|
393
|
+
run: async () => "Original",
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const middleOverride = defineOverride(baseTask, {
|
|
397
|
+
run: async () => "Middle",
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const rootOverride = defineOverride(baseTask, {
|
|
401
|
+
run: async () => "Root",
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
const middle = defineResource({
|
|
405
|
+
id: "middle",
|
|
406
|
+
register: [baseTask],
|
|
407
|
+
overrides: [middleOverride],
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
const app = defineResource({
|
|
411
|
+
id: "app",
|
|
412
|
+
register: [middle],
|
|
413
|
+
dependencies: { t: baseTask },
|
|
414
|
+
overrides: [rootOverride],
|
|
415
|
+
async init(_, deps) {
|
|
416
|
+
return await deps.t();
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
const result = await run(app);
|
|
421
|
+
// Since root is visited after middle, its override takes precedence.
|
|
422
|
+
expect(result.value).toBe("Root");
|
|
423
|
+
});
|
|
395
424
|
});
|
|
@@ -16,12 +16,17 @@ describe("main exports", () => {
|
|
|
16
16
|
expect(typeof mainExports.resource).toBe("function");
|
|
17
17
|
expect(typeof mainExports.event).toBe("function");
|
|
18
18
|
expect(typeof mainExports.middleware).toBe("function");
|
|
19
|
+
expect(typeof mainExports.index).toBe("function");
|
|
20
|
+
expect(typeof mainExports.tag).toBe("function");
|
|
19
21
|
expect(typeof mainExports.run).toBe("function");
|
|
22
|
+
expect(typeof mainExports.createContext).toBe("function");
|
|
20
23
|
expect(typeof mainExports.globals).toBe("object");
|
|
21
24
|
expect(typeof mainExports.definitions).toBe("object");
|
|
22
25
|
expect(typeof mainExports.Store).toBe("function");
|
|
23
26
|
expect(typeof mainExports.EventManager).toBe("function");
|
|
24
27
|
expect(typeof mainExports.TaskRunner).toBe("function");
|
|
28
|
+
expect(typeof mainExports.Queue).toBe("function");
|
|
29
|
+
expect(typeof mainExports.Semaphore).toBe("function");
|
|
25
30
|
|
|
26
31
|
// Test that aliases work the same as direct imports
|
|
27
32
|
const directTask = defineTask({ id: "test", run: async () => "direct" });
|
|
@@ -33,6 +38,20 @@ describe("main exports", () => {
|
|
|
33
38
|
expect(directTask.id).toBe("test");
|
|
34
39
|
expect(aliasTask.id).toBe("test2");
|
|
35
40
|
|
|
41
|
+
// Test tag exports work
|
|
42
|
+
const testTag = mainExports.tag<{ value: number }>({ id: "test.tag" });
|
|
43
|
+
const testTag2 = mainExports.tag<{ name: string }>({ id: "test.tag2" });
|
|
44
|
+
|
|
45
|
+
expect(testTag.id).toBe("test.tag");
|
|
46
|
+
expect(testTag2.id).toBe("test.tag2");
|
|
47
|
+
expect(typeof testTag.with).toBe("function");
|
|
48
|
+
expect(typeof testTag2.extract).toBe("function");
|
|
49
|
+
|
|
50
|
+
// Test createContext export
|
|
51
|
+
const TestContext = mainExports.createContext<string>("test.context");
|
|
52
|
+
expect(typeof TestContext.provide).toBe("function");
|
|
53
|
+
expect(typeof TestContext.use).toBe("function");
|
|
54
|
+
|
|
36
55
|
// Test globals sub-properties for complete coverage
|
|
37
56
|
expect(typeof mainExports.globals.events).toBe("object");
|
|
38
57
|
expect(typeof mainExports.globals.resources).toBe("object");
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineTag,
|
|
3
|
+
defineTask,
|
|
4
|
+
defineResource,
|
|
5
|
+
defineEvent,
|
|
6
|
+
defineMiddleware,
|
|
7
|
+
} from "../define";
|
|
8
|
+
import { run } from "../run";
|
|
9
|
+
|
|
10
|
+
describe("Configurable Tags", () => {
|
|
11
|
+
describe("Tag Definition", () => {
|
|
12
|
+
it("should create a tag with string id", () => {
|
|
13
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
14
|
+
id: "performance.track",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
expect(performanceTag.id).toBe("performance.track");
|
|
18
|
+
expect(typeof performanceTag.with).toBe("function");
|
|
19
|
+
expect(typeof performanceTag.extract).toBe("function");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should create a tag with symbol id", () => {
|
|
23
|
+
const symbolId = Symbol("test.tag");
|
|
24
|
+
const testTag = defineTag<{ value: string }>({ id: symbolId });
|
|
25
|
+
|
|
26
|
+
expect(testTag.id).toBe(symbolId);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should create a tag without configuration", () => {
|
|
30
|
+
const simpleTag = defineTag({ id: "simple.tag" });
|
|
31
|
+
|
|
32
|
+
expect(simpleTag.id).toBe("simple.tag");
|
|
33
|
+
expect(typeof simpleTag.with).toBe("function");
|
|
34
|
+
expect(typeof simpleTag.extract).toBe("function");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("Tag Configuration with .with()", () => {
|
|
39
|
+
it("should create a configured tag instance", () => {
|
|
40
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
41
|
+
id: "performance.track",
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const configuredTag = performanceTag.with({ alertAboveMs: 200 });
|
|
45
|
+
|
|
46
|
+
expect(configuredTag.id).toBe("performance.track");
|
|
47
|
+
expect(configuredTag.config).toEqual({ alertAboveMs: 200 });
|
|
48
|
+
expect(configuredTag.tag).toBe(performanceTag);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should allow multiple configurations of the same tag", () => {
|
|
52
|
+
const cacheTag = defineTag<{ ttl: number }>({ id: "cache.config" });
|
|
53
|
+
|
|
54
|
+
const shortCache = cacheTag.with({ ttl: 300 });
|
|
55
|
+
const longCache = cacheTag.with({ ttl: 3600 });
|
|
56
|
+
|
|
57
|
+
expect(shortCache.config.ttl).toBe(300);
|
|
58
|
+
expect(longCache.config.ttl).toBe(3600);
|
|
59
|
+
expect(shortCache.tag).toBe(cacheTag);
|
|
60
|
+
expect(longCache.tag).toBe(cacheTag);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("Tag Extraction with .extract()", () => {
|
|
65
|
+
it("should extract configured tag from tags array", () => {
|
|
66
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
67
|
+
id: "performance.track",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const tags = [
|
|
71
|
+
"simple-string-tag",
|
|
72
|
+
performanceTag.with({ alertAboveMs: 200 }),
|
|
73
|
+
"another-string",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const extracted = performanceTag.extract(tags);
|
|
77
|
+
|
|
78
|
+
expect(extracted).not.toBeNull();
|
|
79
|
+
expect(extracted?.id).toBe("performance.track");
|
|
80
|
+
expect(extracted?.config).toEqual({ alertAboveMs: 200 });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should extract unconfigured tag from tags array", () => {
|
|
84
|
+
const simpleTag = defineTag({ id: "simple.tag" });
|
|
85
|
+
|
|
86
|
+
const tags = ["string-tag", simpleTag, "another-string"];
|
|
87
|
+
|
|
88
|
+
const extracted = simpleTag.extract(tags);
|
|
89
|
+
|
|
90
|
+
expect(extracted).not.toBeNull();
|
|
91
|
+
expect(extracted?.id).toBe("simple.tag");
|
|
92
|
+
expect(extracted?.config).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should return null if tag not found", () => {
|
|
96
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
97
|
+
id: "performance.track",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const tags = ["string-tag", "another-string"];
|
|
101
|
+
|
|
102
|
+
const extracted = performanceTag.extract(tags);
|
|
103
|
+
|
|
104
|
+
expect(extracted).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should ignore string tags during extraction", () => {
|
|
108
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
109
|
+
id: "performance.track",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const tags = [
|
|
113
|
+
"performance.track", // This is a string, not the tag
|
|
114
|
+
performanceTag.with({ alertAboveMs: 100 }),
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
const extracted = performanceTag.extract(tags);
|
|
118
|
+
|
|
119
|
+
expect(extracted).not.toBeNull();
|
|
120
|
+
expect(extracted?.config).toEqual({ alertAboveMs: 100 });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should handle symbol ids correctly", () => {
|
|
124
|
+
const symbolId = Symbol("test.tag");
|
|
125
|
+
const testTag = defineTag<{ data: string }>({ id: symbolId });
|
|
126
|
+
|
|
127
|
+
const tags = [testTag.with({ data: "test" })];
|
|
128
|
+
|
|
129
|
+
const extracted = testTag.extract(tags);
|
|
130
|
+
|
|
131
|
+
expect(extracted).not.toBeNull();
|
|
132
|
+
expect(extracted?.id).toBe(symbolId);
|
|
133
|
+
expect(extracted?.config).toEqual({ data: "test" });
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should extract configured tag from a taggable object (task.definition)", () => {
|
|
137
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
138
|
+
id: "performance.track",
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const task = defineTask({
|
|
142
|
+
id: "task.with.tags",
|
|
143
|
+
meta: {
|
|
144
|
+
tags: [performanceTag.with({ alertAboveMs: 123 })],
|
|
145
|
+
},
|
|
146
|
+
run: async () => "ok",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const extracted = performanceTag.extract(task);
|
|
150
|
+
expect(extracted).not.toBeNull();
|
|
151
|
+
expect(extracted?.config).toEqual({ alertAboveMs: 123 });
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("should return null when taggable has no tags", () => {
|
|
155
|
+
const t = defineTag({ id: "x" });
|
|
156
|
+
const task = defineTask({ id: "no.tags", run: async () => "ok" });
|
|
157
|
+
expect(t.extract(task)).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should work with a simple taggable carrying meta.tags directly", () => {
|
|
161
|
+
const t = defineTag<{ p: number }>({ id: "pp" });
|
|
162
|
+
const taggable = { meta: { tags: [t.with({ p: 9 })] } } as any;
|
|
163
|
+
const extracted = t.extract(taggable);
|
|
164
|
+
expect(extracted?.config).toEqual({ p: 9 });
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe("Integration with Tasks", () => {
|
|
169
|
+
it("should work with task metadata", () => {
|
|
170
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
171
|
+
id: "performance.track",
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const testTask = defineTask({
|
|
175
|
+
id: "test.task",
|
|
176
|
+
meta: {
|
|
177
|
+
tags: [
|
|
178
|
+
"api",
|
|
179
|
+
performanceTag.with({ alertAboveMs: 200 }),
|
|
180
|
+
"important",
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
run: async () => {
|
|
184
|
+
return "success";
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(testTask.meta?.tags).toHaveLength(3);
|
|
189
|
+
expect(testTask.meta?.tags?.[0]).toBe("api");
|
|
190
|
+
expect(testTask.meta?.tags?.[2]).toBe("important");
|
|
191
|
+
|
|
192
|
+
const extracted = performanceTag.extract(testTask.meta?.tags || []);
|
|
193
|
+
expect(extracted).not.toBeNull();
|
|
194
|
+
expect(extracted?.config).toEqual({ alertAboveMs: 200 });
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should work with middleware checking tags", async () => {
|
|
198
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
199
|
+
id: "performance.track",
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const middlewareExecutions: Array<{ taskId: string; config: any }> = [];
|
|
203
|
+
|
|
204
|
+
const performanceMiddleware = defineMiddleware({
|
|
205
|
+
id: "performance.middleware",
|
|
206
|
+
run: async ({ task, next }) => {
|
|
207
|
+
if (task?.definition.meta?.tags) {
|
|
208
|
+
const extracted = performanceTag.extract(task.definition.meta.tags);
|
|
209
|
+
if (extracted) {
|
|
210
|
+
middlewareExecutions.push({
|
|
211
|
+
taskId: task.definition.id as string,
|
|
212
|
+
config: extracted.config,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return next(task?.input);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const fastTask = defineTask({
|
|
221
|
+
id: "fast.task",
|
|
222
|
+
meta: {
|
|
223
|
+
tags: [performanceTag.with({ alertAboveMs: 100 })],
|
|
224
|
+
},
|
|
225
|
+
run: async () => "fast",
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const slowTask = defineTask({
|
|
229
|
+
id: "slow.task",
|
|
230
|
+
meta: {
|
|
231
|
+
tags: [performanceTag.with({ alertAboveMs: 500 })],
|
|
232
|
+
},
|
|
233
|
+
run: async () => "slow",
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const normalTask = defineTask({
|
|
237
|
+
id: "normal.task",
|
|
238
|
+
meta: {
|
|
239
|
+
tags: ["just-a-string"],
|
|
240
|
+
},
|
|
241
|
+
run: async () => "normal",
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const app = defineResource({
|
|
245
|
+
id: "test.app",
|
|
246
|
+
register: [
|
|
247
|
+
fastTask,
|
|
248
|
+
slowTask,
|
|
249
|
+
normalTask,
|
|
250
|
+
performanceMiddleware.everywhere(),
|
|
251
|
+
],
|
|
252
|
+
dependencies: { fastTask, slowTask, normalTask },
|
|
253
|
+
init: async (_, { fastTask, slowTask, normalTask }) => {
|
|
254
|
+
await fastTask();
|
|
255
|
+
await slowTask();
|
|
256
|
+
await normalTask();
|
|
257
|
+
return "done";
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const { dispose } = await run(app);
|
|
262
|
+
await dispose();
|
|
263
|
+
|
|
264
|
+
expect(middlewareExecutions).toHaveLength(2);
|
|
265
|
+
expect(middlewareExecutions).toEqual([
|
|
266
|
+
{ taskId: "fast.task", config: { alertAboveMs: 100 } },
|
|
267
|
+
{ taskId: "slow.task", config: { alertAboveMs: 500 } },
|
|
268
|
+
]);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("Integration with Resources", () => {
|
|
273
|
+
it("should work with resource metadata", () => {
|
|
274
|
+
const dbTag = defineTag<{ connectionTimeout: number }>({
|
|
275
|
+
id: "db.config",
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const database = defineResource({
|
|
279
|
+
id: "database",
|
|
280
|
+
meta: {
|
|
281
|
+
tags: ["database", dbTag.with({ connectionTimeout: 5000 })],
|
|
282
|
+
},
|
|
283
|
+
init: async () => ({ query: () => "result" }),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
expect(database.meta?.tags).toHaveLength(2);
|
|
287
|
+
const extracted = dbTag.extract(database.meta?.tags || []);
|
|
288
|
+
expect(extracted?.config).toEqual({ connectionTimeout: 5000 });
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("Integration with Events", () => {
|
|
293
|
+
it("should work with event metadata", () => {
|
|
294
|
+
const auditTag = defineTag<{ sensitive: boolean }>({
|
|
295
|
+
id: "audit.config",
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const userEvent = defineEvent<{ userId: string }>({
|
|
299
|
+
id: "user.created",
|
|
300
|
+
meta: {
|
|
301
|
+
tags: ["user-event", auditTag.with({ sensitive: true })],
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
expect(userEvent.meta?.tags).toHaveLength(2);
|
|
306
|
+
const extracted = auditTag.extract(userEvent.meta?.tags || []);
|
|
307
|
+
expect(extracted?.config).toEqual({ sensitive: true });
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe("Integration with Middleware", () => {
|
|
312
|
+
it("should work with middleware metadata", () => {
|
|
313
|
+
const rateLimitTag = defineTag<{ requestsPerMinute: number }>({
|
|
314
|
+
id: "rate-limit",
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const rateLimitMiddleware = defineMiddleware({
|
|
318
|
+
id: "rate.limit.middleware",
|
|
319
|
+
meta: {
|
|
320
|
+
tags: ["security", rateLimitTag.with({ requestsPerMinute: 60 })],
|
|
321
|
+
},
|
|
322
|
+
run: async ({ next, task }) => {
|
|
323
|
+
return next(task?.input);
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
expect(rateLimitMiddleware.meta?.tags).toHaveLength(2);
|
|
328
|
+
const extracted = rateLimitTag.extract(
|
|
329
|
+
rateLimitMiddleware.meta?.tags || []
|
|
330
|
+
);
|
|
331
|
+
expect(extracted?.config).toEqual({ requestsPerMinute: 60 });
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe("Backward Compatibility", () => {
|
|
336
|
+
it("should work with existing string tags", () => {
|
|
337
|
+
const task = defineTask({
|
|
338
|
+
id: "legacy.task",
|
|
339
|
+
meta: {
|
|
340
|
+
tags: ["api", "legacy", "important"],
|
|
341
|
+
},
|
|
342
|
+
run: async () => "success",
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// String tags should still work
|
|
346
|
+
expect(task.meta?.tags).toEqual(["api", "legacy", "important"]);
|
|
347
|
+
|
|
348
|
+
// New tags should not interfere with string tags
|
|
349
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
350
|
+
id: "performance.track",
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const extracted = performanceTag.extract(task.meta?.tags || []);
|
|
354
|
+
expect(extracted).toBeNull(); // Should not find the tag
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("should allow mixing string tags and configurable tags", () => {
|
|
358
|
+
const performanceTag = defineTag<{ alertAboveMs: number }>({
|
|
359
|
+
id: "performance.track",
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const task = defineTask({
|
|
363
|
+
id: "mixed.task",
|
|
364
|
+
meta: {
|
|
365
|
+
tags: [
|
|
366
|
+
"api", // string tag
|
|
367
|
+
performanceTag.with({ alertAboveMs: 200 }), // configurable tag
|
|
368
|
+
"important", // string tag
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
run: async () => "success",
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
expect(task.meta?.tags).toHaveLength(3);
|
|
375
|
+
expect(task.meta?.tags?.[0]).toBe("api");
|
|
376
|
+
expect(task.meta?.tags?.[2]).toBe("important");
|
|
377
|
+
|
|
378
|
+
const extracted = performanceTag.extract(task.meta?.tags || []);
|
|
379
|
+
expect(extracted?.config).toEqual({ alertAboveMs: 200 });
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe("Edge Cases", () => {
|
|
384
|
+
it("should handle null/undefined config", () => {
|
|
385
|
+
const optionalTag = defineTag<{ value?: string }>({
|
|
386
|
+
id: "optional.config",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const configuredTag = optionalTag.with({});
|
|
390
|
+
expect(configuredTag.config).toEqual({});
|
|
391
|
+
|
|
392
|
+
const extracted = optionalTag.extract([configuredTag]);
|
|
393
|
+
expect(extracted?.config).toEqual({});
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
});
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
defineMiddleware,
|
|
5
5
|
defineEvent,
|
|
6
6
|
} from "../../define";
|
|
7
|
-
import {
|
|
7
|
+
import { symbolFilePath } from "../../defs";
|
|
8
8
|
import {
|
|
9
9
|
getCallerFile,
|
|
10
10
|
generateCallerIdFromFile,
|
|
@@ -41,17 +41,15 @@ describe("getCallerFile", () => {
|
|
|
41
41
|
id: "event",
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
expect((task as any)[
|
|
45
|
-
expect((resource as any)[
|
|
46
|
-
expect((middleware as any)[
|
|
47
|
-
expect((event as any)[
|
|
44
|
+
expect((task as any)[symbolFilePath]).toBeDefined();
|
|
45
|
+
expect((resource as any)[symbolFilePath]).toBeDefined();
|
|
46
|
+
expect((middleware as any)[symbolFilePath]).toBeDefined();
|
|
47
|
+
expect((event as any)[symbolFilePath]).toBeDefined();
|
|
48
48
|
|
|
49
|
-
expect((task as any)[
|
|
50
|
-
expect((resource as any)[
|
|
51
|
-
expect((middleware as any)[
|
|
52
|
-
|
|
53
|
-
);
|
|
54
|
-
expect((event as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
49
|
+
expect((task as any)[symbolFilePath]).toContain("getCallerFile.test");
|
|
50
|
+
expect((resource as any)[symbolFilePath]).toContain("getCallerFile.test");
|
|
51
|
+
expect((middleware as any)[symbolFilePath]).toContain("getCallerFile.test");
|
|
52
|
+
expect((event as any)[symbolFilePath]).toContain("getCallerFile.test");
|
|
55
53
|
});
|
|
56
54
|
});
|
|
57
55
|
|