@bluelibs/runner 2.2.4 → 3.1.0
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 +1409 -935
- package/dist/common.types.d.ts +20 -0
- package/dist/common.types.js +4 -0
- package/dist/common.types.js.map +1 -0
- package/dist/context.d.ts +34 -0
- package/dist/context.js +58 -0
- package/dist/context.js.map +1 -0
- package/dist/define.d.ts +24 -5
- package/dist/define.js +89 -20
- package/dist/define.js.map +1 -1
- package/dist/defs.d.ts +109 -73
- package/dist/defs.js +12 -2
- package/dist/defs.js.map +1 -1
- package/dist/errors.d.ts +5 -5
- package/dist/errors.js +6 -5
- package/dist/errors.js.map +1 -1
- package/dist/event.types.d.ts +18 -0
- package/dist/event.types.js +4 -0
- package/dist/event.types.js.map +1 -0
- package/dist/examples/registrator-example.d.ts +122 -0
- package/dist/examples/registrator-example.js +147 -0
- package/dist/examples/registrator-example.js.map +1 -0
- package/dist/globals/globalEvents.d.ts +41 -0
- package/dist/globals/globalEvents.js +94 -0
- package/dist/globals/globalEvents.js.map +1 -0
- package/dist/globals/globalMiddleware.d.ts +23 -0
- package/dist/globals/globalMiddleware.js +15 -0
- package/dist/globals/globalMiddleware.js.map +1 -0
- package/dist/globals/globalResources.d.ts +27 -0
- package/dist/globals/globalResources.js +47 -0
- package/dist/globals/globalResources.js.map +1 -0
- package/dist/globals/middleware/cache.middleware.d.ts +34 -0
- package/dist/globals/middleware/cache.middleware.js +85 -0
- package/dist/globals/middleware/cache.middleware.js.map +1 -0
- package/dist/globals/middleware/requireContext.middleware.d.ts +6 -0
- package/dist/globals/middleware/requireContext.middleware.js +25 -0
- package/dist/globals/middleware/requireContext.middleware.js.map +1 -0
- package/dist/globals/middleware/retry.middleware.d.ts +20 -0
- package/dist/globals/middleware/retry.middleware.js +34 -0
- package/dist/globals/middleware/retry.middleware.js.map +1 -0
- package/dist/globals/resources/queue.resource.d.ts +7 -0
- package/dist/globals/resources/queue.resource.js +31 -0
- package/dist/globals/resources/queue.resource.js.map +1 -0
- package/dist/index.d.ts +54 -18
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/dist/middleware.types.d.ts +40 -0
- package/dist/middleware.types.js +4 -0
- package/dist/middleware.types.js.map +1 -0
- package/dist/models/DependencyProcessor.d.ts +6 -5
- package/dist/models/DependencyProcessor.js +13 -15
- package/dist/models/DependencyProcessor.js.map +1 -1
- package/dist/models/EventManager.d.ts +9 -4
- package/dist/models/EventManager.js +44 -2
- package/dist/models/EventManager.js.map +1 -1
- package/dist/models/Logger.d.ts +30 -13
- package/dist/models/Logger.js +132 -54
- package/dist/models/Logger.js.map +1 -1
- package/dist/models/OverrideManager.d.ts +13 -0
- package/dist/models/OverrideManager.js +70 -0
- package/dist/models/OverrideManager.js.map +1 -0
- package/dist/models/Queue.d.ts +25 -0
- package/dist/models/Queue.js +54 -0
- package/dist/models/Queue.js.map +1 -0
- package/dist/models/ResourceInitializer.d.ts +5 -2
- package/dist/models/ResourceInitializer.js +22 -14
- package/dist/models/ResourceInitializer.js.map +1 -1
- package/dist/models/Semaphore.d.ts +61 -0
- package/dist/models/Semaphore.js +166 -0
- package/dist/models/Semaphore.js.map +1 -0
- package/dist/models/Store.d.ts +18 -73
- package/dist/models/Store.js +71 -269
- package/dist/models/Store.js.map +1 -1
- package/dist/models/StoreConstants.d.ts +11 -0
- package/dist/models/StoreConstants.js +18 -0
- package/dist/models/StoreConstants.js.map +1 -0
- package/dist/models/StoreRegistry.d.ts +25 -0
- package/dist/models/StoreRegistry.js +171 -0
- package/dist/models/StoreRegistry.js.map +1 -0
- package/dist/models/StoreTypes.d.ts +21 -0
- package/dist/models/StoreTypes.js +3 -0
- package/dist/models/StoreTypes.js.map +1 -0
- package/dist/models/StoreValidator.d.ts +10 -0
- package/dist/models/StoreValidator.js +41 -0
- package/dist/models/StoreValidator.js.map +1 -0
- package/dist/models/TaskRunner.d.ts +1 -1
- package/dist/models/TaskRunner.js +39 -24
- package/dist/models/TaskRunner.js.map +1 -1
- package/dist/models/VarStore.d.ts +17 -0
- package/dist/models/VarStore.js +60 -0
- package/dist/models/VarStore.js.map +1 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/index.js.map +1 -1
- package/dist/resource.types.d.ts +31 -0
- package/dist/resource.types.js +3 -0
- package/dist/resource.types.js.map +1 -0
- package/dist/run.d.ts +4 -1
- package/dist/run.js +6 -3
- package/dist/run.js.map +1 -1
- package/dist/symbols.d.ts +24 -0
- package/dist/symbols.js +29 -0
- package/dist/symbols.js.map +1 -0
- package/dist/task.types.d.ts +55 -0
- package/dist/task.types.js +23 -0
- package/dist/task.types.js.map +1 -0
- package/dist/tools/getCallerFile.d.ts +9 -1
- package/dist/tools/getCallerFile.js +41 -0
- package/dist/tools/getCallerFile.js.map +1 -1
- package/dist/tools/registratorId.d.ts +4 -0
- package/dist/tools/registratorId.js +40 -0
- package/dist/tools/registratorId.js.map +1 -0
- package/dist/tools/simpleHash.d.ts +9 -0
- package/dist/tools/simpleHash.js +34 -0
- package/dist/tools/simpleHash.js.map +1 -0
- package/dist/types/base-interfaces.d.ts +18 -0
- package/dist/types/base-interfaces.js +6 -0
- package/dist/types/base-interfaces.js.map +1 -0
- package/dist/types/base.d.ts +13 -0
- package/dist/types/base.js +3 -0
- package/dist/types/base.js.map +1 -0
- package/dist/types/dependencies.d.ts +22 -0
- package/dist/types/dependencies.js +3 -0
- package/dist/types/dependencies.js.map +1 -0
- package/dist/types/dependency-core.d.ts +14 -0
- package/dist/types/dependency-core.js +5 -0
- package/dist/types/dependency-core.js.map +1 -0
- package/dist/types/events.d.ts +52 -0
- package/dist/types/events.js +6 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/hooks.d.ts +16 -0
- package/dist/types/hooks.js +5 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/meta.d.ts +13 -0
- package/dist/types/meta.js +5 -0
- package/dist/types/meta.js.map +1 -0
- package/dist/types/middleware.d.ts +38 -0
- package/dist/types/middleware.js +6 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/registerable.d.ts +10 -0
- package/dist/types/registerable.js +5 -0
- package/dist/types/registerable.js.map +1 -0
- package/dist/types/resources.d.ts +44 -0
- package/dist/types/resources.js +5 -0
- package/dist/types/resources.js.map +1 -0
- package/dist/types/symbols.d.ts +24 -0
- package/dist/types/symbols.js +30 -0
- package/dist/types/symbols.js.map +1 -0
- package/dist/types/tasks.d.ts +41 -0
- package/dist/types/tasks.js +5 -0
- package/dist/types/tasks.js.map +1 -0
- package/dist/types/utilities.d.ts +7 -0
- package/dist/types/utilities.js +5 -0
- package/dist/types/utilities.js.map +1 -0
- package/package.json +10 -6
- package/src/__tests__/benchmark/benchmark.test.ts +1 -1
- package/src/__tests__/context.test.ts +91 -0
- package/src/__tests__/errors.test.ts +8 -5
- package/src/__tests__/globalEvents.test.ts +1 -1
- package/src/__tests__/globals/cache.middleware.test.ts +772 -0
- package/src/__tests__/globals/queue.resource.test.ts +141 -0
- package/src/__tests__/globals/requireContext.middleware.test.ts +98 -0
- package/src/__tests__/globals/retry.middleware.test.ts +157 -0
- package/src/__tests__/index.helper.test.ts +55 -0
- package/src/__tests__/models/EventManager.test.ts +157 -11
- package/src/__tests__/models/Logger.test.ts +291 -34
- package/src/__tests__/models/Queue.test.ts +189 -0
- package/src/__tests__/models/ResourceInitializer.test.ts +8 -6
- package/src/__tests__/models/Semaphore.test.ts +713 -0
- package/src/__tests__/models/Store.test.ts +40 -0
- package/src/__tests__/models/TaskRunner.test.ts +86 -5
- package/src/__tests__/run.anonymous.test.ts +679 -0
- package/src/__tests__/run.middleware.test.ts +312 -12
- package/src/__tests__/run.overrides.test.ts +13 -10
- package/src/__tests__/run.test.ts +364 -13
- package/src/__tests__/setOutput.test.ts +244 -0
- package/src/__tests__/tools/getCallerFile.test.ts +124 -9
- package/src/__tests__/typesafety.test.ts +71 -41
- package/src/context.ts +86 -0
- package/src/define.ts +129 -34
- package/src/defs.ts +156 -119
- package/src/errors.ts +15 -10
- package/src/{globalEvents.ts → globals/globalEvents.ts} +13 -12
- package/src/globals/globalMiddleware.ts +14 -0
- package/src/{globalResources.ts → globals/globalResources.ts} +14 -10
- package/src/globals/middleware/cache.middleware.ts +115 -0
- package/src/globals/middleware/requireContext.middleware.ts +36 -0
- package/src/globals/middleware/retry.middleware.ts +56 -0
- package/src/globals/resources/queue.resource.ts +34 -0
- package/src/index.ts +9 -5
- package/src/models/DependencyProcessor.ts +42 -49
- package/src/models/EventManager.ts +64 -13
- package/src/models/Logger.ts +181 -64
- package/src/models/OverrideManager.ts +84 -0
- package/src/models/Queue.ts +66 -0
- package/src/models/ResourceInitializer.ts +40 -20
- package/src/models/Semaphore.ts +208 -0
- package/src/models/Store.ts +94 -342
- package/src/models/StoreConstants.ts +17 -0
- package/src/models/StoreRegistry.ts +228 -0
- package/src/models/StoreTypes.ts +46 -0
- package/src/models/StoreValidator.ts +43 -0
- package/src/models/TaskRunner.ts +54 -41
- package/src/models/index.ts +3 -0
- package/src/run.ts +7 -4
- package/src/tools/getCallerFile.ts +54 -2
- package/src/__tests__/index.ts +0 -15
- package/src/examples/express-mongo/index.ts +0 -1
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
defineEvent,
|
|
6
6
|
} from "../../define";
|
|
7
7
|
import { symbols } from "../../defs";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
getCallerFile,
|
|
10
|
+
generateCallerIdFromFile,
|
|
11
|
+
} from "../../tools/getCallerFile";
|
|
9
12
|
|
|
10
13
|
describe("getCallerFile", () => {
|
|
11
14
|
it("should return the file name of the caller", () => {
|
|
@@ -38,14 +41,126 @@ describe("getCallerFile", () => {
|
|
|
38
41
|
id: "event",
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
expect(task[symbols.filePath]).toBeDefined();
|
|
42
|
-
expect(resource[symbols.filePath]).toBeDefined();
|
|
43
|
-
expect(middleware[symbols.filePath]).toBeDefined();
|
|
44
|
-
expect(event[symbols.filePath]).toBeDefined();
|
|
44
|
+
expect((task as any)[symbols.filePath]).toBeDefined();
|
|
45
|
+
expect((resource as any)[symbols.filePath]).toBeDefined();
|
|
46
|
+
expect((middleware as any)[symbols.filePath]).toBeDefined();
|
|
47
|
+
expect((event as any)[symbols.filePath]).toBeDefined();
|
|
48
|
+
|
|
49
|
+
expect((task as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
50
|
+
expect((resource as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
51
|
+
expect((middleware as any)[symbols.filePath]).toContain(
|
|
52
|
+
"getCallerFile.test"
|
|
53
|
+
);
|
|
54
|
+
expect((event as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("generateCallerIdFromFile", () => {
|
|
59
|
+
it("should generate a symbol from a path containing src", () => {
|
|
60
|
+
const filePath =
|
|
61
|
+
"/Users/theodordiaconu/Projects/runner/src/globals/resources/queue.resource.ts";
|
|
62
|
+
const expectedDescription = "globals.resources.queue.resource";
|
|
63
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
64
|
+
expectedDescription
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should generate a symbol from a path not containing src", () => {
|
|
69
|
+
const filePath =
|
|
70
|
+
"/Users/theodordiaconu/Projects/runner/dist/globals/resources/queue.resource.ts";
|
|
71
|
+
const expectedDescription = "dist.globals.resources.queue.resource";
|
|
72
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
73
|
+
expectedDescription
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should handle paths with few parts when src is not present", () => {
|
|
78
|
+
const filePath = "a/b/c.ts";
|
|
79
|
+
const expectedDescription = "a.b.c";
|
|
80
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
81
|
+
expectedDescription
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle paths with backslashes", () => {
|
|
86
|
+
const filePath =
|
|
87
|
+
"C:\\Users\\theodordiaconu\\Projects\\runner\\src\\globals\\resources\\queue.resource.ts";
|
|
88
|
+
const expectedDescription = "globals.resources.queue.resource";
|
|
89
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
90
|
+
expectedDescription
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should handle file names without extensions", () => {
|
|
95
|
+
const filePath =
|
|
96
|
+
"/Users/theodordiaconu/Projects/runner/src/globals/resources/queue.resource";
|
|
97
|
+
const expectedDescription = "globals.resources.queue.resource";
|
|
98
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
99
|
+
expectedDescription
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should handle file names with multiple dots", () => {
|
|
104
|
+
const filePath =
|
|
105
|
+
"/Users/theodordiaconu/Projects/runner/src/globals/resources/queue.resource.test.ts";
|
|
106
|
+
const expectedDescription = "globals.resources.queue.resource.test";
|
|
107
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
108
|
+
expectedDescription
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("should generate a symbol from a path containing node_modules", () => {
|
|
113
|
+
const filePath =
|
|
114
|
+
"/Users/theodordiaconu/Projects/runner/node_modules/some-package/dist/index.js";
|
|
115
|
+
const expectedDescription = "some-package.dist.index";
|
|
116
|
+
expect(generateCallerIdFromFile(filePath).description).toEqual(
|
|
117
|
+
expectedDescription
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should respect the fallbackParts argument", () => {
|
|
122
|
+
const filePath = "a/b/c/d/e.ts";
|
|
123
|
+
const expectedDescription = "d.e";
|
|
124
|
+
expect(generateCallerIdFromFile(filePath, "", 2).description).toEqual(
|
|
125
|
+
expectedDescription
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("should append the suffix to the symbol description", () => {
|
|
130
|
+
const filePath = "a/b/c.ts";
|
|
131
|
+
const expectedDescription = "a.b.c.my-suffix";
|
|
132
|
+
expect(generateCallerIdFromFile(filePath, "my-suffix").description).toEqual(
|
|
133
|
+
expectedDescription
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should not append the suffix if it is already in the file name", () => {
|
|
138
|
+
const filePath = "a/b/c.resource.ts";
|
|
139
|
+
const expectedDescription = "a.b.c.resource";
|
|
140
|
+
expect(generateCallerIdFromFile(filePath, "resource").description).toEqual(
|
|
141
|
+
expectedDescription
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("should handle empty path or path with no relevant parts", () => {
|
|
146
|
+
// Test with empty string - this creates relevantParts = [""] which is not empty
|
|
147
|
+
const filePath = "";
|
|
148
|
+
const expectedDescription = ".suffix";
|
|
149
|
+
expect(generateCallerIdFromFile(filePath, "suffix").description).toEqual(
|
|
150
|
+
expectedDescription
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Test case where 'src' is at the end, making relevantParts empty (triggers line 77 else branch)
|
|
154
|
+
const result = generateCallerIdFromFile("/some/path/src", "suffix");
|
|
155
|
+
expect(result.description).toEqual(".suffix");
|
|
156
|
+
});
|
|
45
157
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
158
|
+
it("should use fallback parts when no src or node_modules found", () => {
|
|
159
|
+
// Test path without src or node_modules to trigger the else branch (line 59)
|
|
160
|
+
const filePath = "/some/other/deep/path/file.js";
|
|
161
|
+
const expectedDescription = "other.deep.path.file";
|
|
162
|
+
expect(generateCallerIdFromFile(filePath, "", 4).description).toEqual(
|
|
163
|
+
expectedDescription
|
|
164
|
+
);
|
|
50
165
|
});
|
|
51
166
|
});
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
defineEvent,
|
|
3
|
+
defineTask,
|
|
4
|
+
defineResource,
|
|
5
|
+
defineMiddleware,
|
|
6
|
+
} from "../define";
|
|
7
|
+
import {
|
|
8
|
+
IEventDefinition,
|
|
9
|
+
IMiddlewareDefinition,
|
|
10
|
+
IResource,
|
|
11
|
+
IResourceWithConfig,
|
|
12
|
+
ITaskDefinition,
|
|
13
|
+
RegisterableItems,
|
|
14
|
+
} from "../defs";
|
|
3
15
|
|
|
4
16
|
describe("typesafety", () => {
|
|
5
17
|
it("tasks, resources: should have propper type safety for dependeices", async () => {
|
|
@@ -7,10 +19,43 @@ describe("typesafety", () => {
|
|
|
7
19
|
message: string;
|
|
8
20
|
};
|
|
9
21
|
|
|
22
|
+
const middleware = defineMiddleware({
|
|
23
|
+
id: "middleware",
|
|
24
|
+
run: async (input, deps) => {
|
|
25
|
+
return input;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
type MiddlewareConfig = {
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type MiddlewareOptionalConfig = {
|
|
34
|
+
message?: string;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const middlewareWithConfig = defineMiddleware({
|
|
38
|
+
id: "middleware.config",
|
|
39
|
+
run: async (input, deps, config: MiddlewareConfig) => {
|
|
40
|
+
return input;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const middlewareWithOptionalConfig = defineMiddleware({
|
|
45
|
+
id: "middleware.optional.config",
|
|
46
|
+
run: async (input, deps, config: MiddlewareOptionalConfig) => {
|
|
47
|
+
return input;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
10
51
|
const event = defineEvent<{ message: string }>({
|
|
11
52
|
id: "event",
|
|
12
53
|
});
|
|
13
54
|
|
|
55
|
+
const eventWithoutArguments = defineEvent({
|
|
56
|
+
id: "event",
|
|
57
|
+
});
|
|
58
|
+
|
|
14
59
|
const baseTask = defineTask({
|
|
15
60
|
id: "task",
|
|
16
61
|
run: async (input: InputTask) => "Task executed",
|
|
@@ -58,7 +103,19 @@ describe("typesafety", () => {
|
|
|
58
103
|
|
|
59
104
|
const testResource = defineResource({
|
|
60
105
|
id: "test.resource",
|
|
61
|
-
|
|
106
|
+
middleware: [
|
|
107
|
+
middleware,
|
|
108
|
+
// @ts-expect-error
|
|
109
|
+
middlewareWithConfig,
|
|
110
|
+
middlewareWithConfig.with({ message: "Hello, World!" }),
|
|
111
|
+
// @ts-expect-error
|
|
112
|
+
middlewareWithConfig.with({ message: 123 }),
|
|
113
|
+
middlewareWithOptionalConfig,
|
|
114
|
+
middlewareWithOptionalConfig.with({ message: "Hello, World!" }),
|
|
115
|
+
// @ts-expect-error
|
|
116
|
+
middlewareWithOptionalConfig.with({ message: 123 }),
|
|
117
|
+
],
|
|
118
|
+
dependencies: { task, dummyResource, event, eventWithoutArguments },
|
|
62
119
|
init: async (_, deps) => {
|
|
63
120
|
const result = await deps.task({
|
|
64
121
|
message: "Hello, World!",
|
|
@@ -69,6 +126,10 @@ describe("typesafety", () => {
|
|
|
69
126
|
deps.event();
|
|
70
127
|
// @ts-expect-error
|
|
71
128
|
deps.event({ messagex: "Hello, World!" });
|
|
129
|
+
deps.eventWithoutArguments();
|
|
130
|
+
deps.eventWithoutArguments({});
|
|
131
|
+
// @ts-expect-error
|
|
132
|
+
deps.eventWithoutArguments({ something: false });
|
|
72
133
|
|
|
73
134
|
// @ts-expect-error
|
|
74
135
|
deps.dummyResource as number;
|
|
@@ -82,6 +143,13 @@ describe("typesafety", () => {
|
|
|
82
143
|
deps.task2;
|
|
83
144
|
},
|
|
84
145
|
register: [
|
|
146
|
+
middleware,
|
|
147
|
+
middlewareWithConfig,
|
|
148
|
+
middlewareWithOptionalConfig,
|
|
149
|
+
middlewareWithOptionalConfig.with({ message: "Hello, World!" }),
|
|
150
|
+
middlewareWithConfig.with({ message: "Hello, World!" }),
|
|
151
|
+
// @ts-expect-error
|
|
152
|
+
middlewareWithConfig.with({ message: 123 }),
|
|
85
153
|
dummyResourceNoConfig,
|
|
86
154
|
// @ts-expect-error
|
|
87
155
|
dummyResourceNoConfig.with("hello"),
|
|
@@ -141,42 +209,4 @@ describe("typesafety", () => {
|
|
|
141
209
|
|
|
142
210
|
expect(true).toBe(true);
|
|
143
211
|
});
|
|
144
|
-
|
|
145
|
-
it("should work with resources that register other resources and don't infer an any to them", async () => {
|
|
146
|
-
const resourceBase = defineResource({
|
|
147
|
-
id: "task",
|
|
148
|
-
init: async (_, deps) => {
|
|
149
|
-
deps.resource;
|
|
150
|
-
return "";
|
|
151
|
-
},
|
|
152
|
-
dependencies: () => ({
|
|
153
|
-
resource,
|
|
154
|
-
}),
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const resource = defineResource({
|
|
158
|
-
id: "resource",
|
|
159
|
-
register: [],
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
resource.register = [resourceBase];
|
|
163
|
-
|
|
164
|
-
// @ts-expect-error
|
|
165
|
-
resource.DOES_NOT_EXIST;
|
|
166
|
-
|
|
167
|
-
expect(true).toBe(true);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it("should work to unit test with void arguments?", async () => {
|
|
171
|
-
const resourceBase = defineResource({
|
|
172
|
-
id: "task",
|
|
173
|
-
init: async (_, deps) => {
|
|
174
|
-
return "";
|
|
175
|
-
},
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
if (resourceBase.init) {
|
|
179
|
-
resourceBase.init(undefined, {});
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
212
|
});
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2
|
+
import { defineMiddleware } from "./define";
|
|
3
|
+
import { IMiddleware, IMiddlewareConfigured } from "./defs";
|
|
4
|
+
import { requireContextMiddleware } from "./globals/middleware/requireContext.middleware";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown whenever a requested context is not available.
|
|
8
|
+
*/
|
|
9
|
+
export class ContextError extends Error {
|
|
10
|
+
constructor(message: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "ContextError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The generic Context object returned by `createContext`.
|
|
18
|
+
*/
|
|
19
|
+
export interface Context<T> {
|
|
20
|
+
/** unique symbol used as key in the AsyncLocalStorage map */
|
|
21
|
+
readonly id: symbol;
|
|
22
|
+
/** Retrieve the current context value or throw */
|
|
23
|
+
use(): T;
|
|
24
|
+
/**
|
|
25
|
+
* Provide a value for this context during the lifetime of `fn()`
|
|
26
|
+
*/
|
|
27
|
+
provide<R>(value: T, fn: () => Promise<R> | R): Promise<R> | R;
|
|
28
|
+
/**
|
|
29
|
+
* Generates a middleware that guarantees the context exists (and optionally
|
|
30
|
+
* enforces that certain keys are present on the context object).
|
|
31
|
+
*/
|
|
32
|
+
require<K extends keyof T = never>(
|
|
33
|
+
keys?: K[]
|
|
34
|
+
): IMiddlewareConfigured<{ context: Context<T> }>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The internal storage maps Context identifiers (symbols) to their values
|
|
38
|
+
export const storage = new AsyncLocalStorage<Map<symbol, unknown>>();
|
|
39
|
+
|
|
40
|
+
/** Returns the currently active store or undefined. */
|
|
41
|
+
function getCurrentStore(): Map<symbol, unknown> | undefined {
|
|
42
|
+
return storage.getStore();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a new typed Context. The result contains helpers similar to React’s
|
|
47
|
+
* Context API but adapted for async usage in Runner.
|
|
48
|
+
*/
|
|
49
|
+
export function createContext<T>(name: string = "runner.context"): Context<T> {
|
|
50
|
+
const ctxId = Symbol(name);
|
|
51
|
+
|
|
52
|
+
function use(): T {
|
|
53
|
+
const store = getCurrentStore();
|
|
54
|
+
if (!store || !store.has(ctxId)) {
|
|
55
|
+
throw new ContextError(
|
|
56
|
+
`Context not available for symbol ${ctxId.toString()}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return store.get(ctxId) as T;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function provide<R>(value: T, fn: () => Promise<R> | R): Promise<R> | R {
|
|
63
|
+
const currentStore = getCurrentStore();
|
|
64
|
+
const map = currentStore
|
|
65
|
+
? new Map(currentStore)
|
|
66
|
+
: new Map<symbol, unknown>();
|
|
67
|
+
map.set(ctxId, value);
|
|
68
|
+
|
|
69
|
+
return storage.run(map, fn as any);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generates a middleware that guarantees the context exists (and optionally
|
|
74
|
+
* enforces that certain keys are present on the context object).
|
|
75
|
+
*/
|
|
76
|
+
function require(): IMiddlewareConfigured {
|
|
77
|
+
return requireContextMiddleware.with({ context: this as Context<T> });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
id: ctxId,
|
|
82
|
+
use,
|
|
83
|
+
provide,
|
|
84
|
+
require,
|
|
85
|
+
};
|
|
86
|
+
}
|
package/src/define.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { get } from "node:http";
|
|
2
1
|
import {
|
|
3
2
|
ITask,
|
|
4
3
|
ITaskDefinition,
|
|
@@ -11,13 +10,15 @@ import {
|
|
|
11
10
|
DependencyMapType,
|
|
12
11
|
DependencyValuesType,
|
|
13
12
|
IMiddleware,
|
|
14
|
-
IHookDefinition,
|
|
15
13
|
IEvent,
|
|
16
|
-
IEventDefinitionConfig,
|
|
17
14
|
symbolEvent,
|
|
15
|
+
RegisterableItems,
|
|
16
|
+
symbolMiddlewareConfigured,
|
|
17
|
+
symbolFilePath,
|
|
18
|
+
symbolIndexResource,
|
|
18
19
|
} from "./defs";
|
|
19
20
|
import { Errors } from "./errors";
|
|
20
|
-
import { getCallerFile } from "./tools/getCallerFile";
|
|
21
|
+
import { generateCallerIdFromFile, getCallerFile } from "./tools/getCallerFile";
|
|
21
22
|
|
|
22
23
|
// Helper function to get the caller file
|
|
23
24
|
|
|
@@ -30,30 +31,39 @@ export function defineTask<
|
|
|
30
31
|
taskConfig: ITaskDefinition<Input, Output, Deps, TOn>
|
|
31
32
|
): ITask<Input, Output, Deps, TOn> {
|
|
32
33
|
const filePath = getCallerFile();
|
|
34
|
+
const isAnonymous = !Boolean(taskConfig.id);
|
|
35
|
+
const id = taskConfig.id || generateCallerIdFromFile(filePath, "task");
|
|
33
36
|
return {
|
|
34
37
|
[symbols.task]: true,
|
|
35
38
|
[symbols.filePath]: filePath,
|
|
36
|
-
id
|
|
39
|
+
id,
|
|
37
40
|
dependencies: taskConfig.dependencies || ({} as Deps),
|
|
38
41
|
middleware: taskConfig.middleware || [],
|
|
39
42
|
run: taskConfig.run,
|
|
40
43
|
on: taskConfig.on,
|
|
44
|
+
listenerOrder: taskConfig.listenerOrder,
|
|
41
45
|
events: {
|
|
42
46
|
beforeRun: {
|
|
43
47
|
...defineEvent({
|
|
44
|
-
id:
|
|
48
|
+
id: isAnonymous
|
|
49
|
+
? Symbol(`anonymous-task.events.beforeRun`)
|
|
50
|
+
: `${id as string}.events.beforeRun`,
|
|
45
51
|
}),
|
|
46
52
|
[symbols.filePath]: getCallerFile(),
|
|
47
53
|
},
|
|
48
54
|
afterRun: {
|
|
49
55
|
...defineEvent({
|
|
50
|
-
id:
|
|
56
|
+
id: isAnonymous
|
|
57
|
+
? Symbol(`anonymous-task.events.afterRun`)
|
|
58
|
+
: `${id as string}.events.afterRun`,
|
|
51
59
|
}),
|
|
52
60
|
[symbols.filePath]: getCallerFile(),
|
|
53
61
|
},
|
|
54
62
|
onError: {
|
|
55
63
|
...defineEvent({
|
|
56
|
-
id:
|
|
64
|
+
id: isAnonymous
|
|
65
|
+
? Symbol(`anonymous-task.events.onError`)
|
|
66
|
+
: `${id as string}.events.onError`,
|
|
57
67
|
}),
|
|
58
68
|
[symbols.filePath]: getCallerFile(),
|
|
59
69
|
},
|
|
@@ -67,20 +77,27 @@ export function defineResource<
|
|
|
67
77
|
TConfig = void,
|
|
68
78
|
TValue = any,
|
|
69
79
|
TDeps extends DependencyMapType = {},
|
|
70
|
-
|
|
80
|
+
TPrivate = any
|
|
71
81
|
>(
|
|
72
|
-
constConfig: IResourceDefinition<TConfig, TValue, TDeps,
|
|
73
|
-
): IResource<TConfig, TValue, TDeps> {
|
|
74
|
-
|
|
82
|
+
constConfig: IResourceDefinition<TConfig, TValue, TDeps, TPrivate>
|
|
83
|
+
): IResource<TConfig, TValue, TDeps, TPrivate> {
|
|
84
|
+
// The symbolFilePath might already come from defineIndex() for example
|
|
85
|
+
const filePath: string = constConfig[symbolFilePath] || getCallerFile();
|
|
86
|
+
const isIndexResource = constConfig[symbolIndexResource] || false;
|
|
87
|
+
const isAnonymous = !Boolean(constConfig.id);
|
|
88
|
+
const id =
|
|
89
|
+
constConfig.id ||
|
|
90
|
+
generateCallerIdFromFile(filePath, isIndexResource ? "index" : "resource");
|
|
75
91
|
return {
|
|
76
92
|
[symbols.resource]: true,
|
|
77
93
|
[symbols.filePath]: filePath,
|
|
78
|
-
id
|
|
94
|
+
id,
|
|
79
95
|
dependencies: constConfig.dependencies,
|
|
80
96
|
dispose: constConfig.dispose,
|
|
81
97
|
register: constConfig.register || [],
|
|
82
98
|
overrides: constConfig.overrides || [],
|
|
83
|
-
init: constConfig.init
|
|
99
|
+
init: constConfig.init,
|
|
100
|
+
context: constConfig.context,
|
|
84
101
|
with: function (config: TConfig) {
|
|
85
102
|
return {
|
|
86
103
|
[symbols.resourceWithConfig]: true,
|
|
@@ -93,19 +110,25 @@ export function defineResource<
|
|
|
93
110
|
events: {
|
|
94
111
|
beforeInit: {
|
|
95
112
|
...defineEvent({
|
|
96
|
-
id:
|
|
113
|
+
id: isAnonymous
|
|
114
|
+
? Symbol(`anonymous-resource.events.beforeInit`)
|
|
115
|
+
: `${id as string}.events.beforeInit`,
|
|
97
116
|
}),
|
|
98
117
|
[symbols.filePath]: filePath,
|
|
99
118
|
},
|
|
100
119
|
afterInit: {
|
|
101
120
|
...defineEvent({
|
|
102
|
-
id:
|
|
121
|
+
id: isAnonymous
|
|
122
|
+
? Symbol(`anonymous-resource.events.afterInit`)
|
|
123
|
+
: `${id as string}.events.afterInit`,
|
|
103
124
|
}),
|
|
104
125
|
[symbols.filePath]: filePath,
|
|
105
126
|
},
|
|
106
127
|
onError: {
|
|
107
128
|
...defineEvent({
|
|
108
|
-
id:
|
|
129
|
+
id: isAnonymous
|
|
130
|
+
? Symbol(`anonymous-resource.events.onError`)
|
|
131
|
+
: `${id as string}.events.onError`,
|
|
109
132
|
}),
|
|
110
133
|
[symbols.filePath]: filePath,
|
|
111
134
|
},
|
|
@@ -115,34 +138,106 @@ export function defineResource<
|
|
|
115
138
|
};
|
|
116
139
|
}
|
|
117
140
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
/**
|
|
142
|
+
* Creates an "index" resource which groups multiple registerable items under
|
|
143
|
+
* a single dependency. The resulting resource registers every item, depends
|
|
144
|
+
* on the same items, and returns the resolved dependency map so users can
|
|
145
|
+
* access them naturally: `deps.services.myTask()` or `deps.services.myResource`.
|
|
146
|
+
*/
|
|
147
|
+
export function defineIndex<
|
|
148
|
+
T extends Record<string, RegisterableItems>,
|
|
149
|
+
D extends {
|
|
150
|
+
[K in keyof T]: T[K] extends IResourceWithConfig<any, any, any>
|
|
151
|
+
? T[K]["resource"]
|
|
152
|
+
: T[K];
|
|
153
|
+
} & DependencyMapType
|
|
154
|
+
>(items: T): IResource<void, DependencyValuesType<D>, D> {
|
|
155
|
+
const dependencies = {} as D;
|
|
156
|
+
const register: RegisterableItems[] = [];
|
|
157
|
+
|
|
158
|
+
for (const key of Object.keys(items) as (keyof T)[]) {
|
|
159
|
+
const item = items[key];
|
|
160
|
+
register.push(item);
|
|
161
|
+
|
|
162
|
+
if (isResourceWithConfig(item)) {
|
|
163
|
+
(dependencies as any)[key] = item.resource;
|
|
164
|
+
} else {
|
|
165
|
+
(dependencies as any)[key] = item as any;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const callerFilePath = getCallerFile();
|
|
169
|
+
|
|
170
|
+
return defineResource({
|
|
171
|
+
register,
|
|
172
|
+
dependencies,
|
|
173
|
+
async init(_, deps) {
|
|
174
|
+
return deps as any;
|
|
175
|
+
},
|
|
176
|
+
[symbols.filePath]: callerFilePath,
|
|
177
|
+
[symbols.indexResource]: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function defineEvent<TPayload = void>(
|
|
182
|
+
config: IEventDefinition<TPayload>
|
|
183
|
+
): IEvent<TPayload> {
|
|
184
|
+
const callerFilePath = getCallerFile();
|
|
121
185
|
return {
|
|
122
|
-
[symbols.filePath]: getCallerFile(),
|
|
123
|
-
[symbolEvent]: true,
|
|
124
186
|
...config,
|
|
187
|
+
id: config.id || generateCallerIdFromFile(callerFilePath, "event"),
|
|
188
|
+
[symbols.filePath]: callerFilePath,
|
|
189
|
+
[symbolEvent]: true, // This is a workaround
|
|
125
190
|
};
|
|
126
191
|
}
|
|
127
192
|
|
|
128
|
-
export
|
|
129
|
-
|
|
130
|
-
|
|
193
|
+
export type MiddlewareEverywhereOptions = {
|
|
194
|
+
/**
|
|
195
|
+
* Enable this for tasks. Default is true.
|
|
196
|
+
*/
|
|
197
|
+
tasks?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Enable this for resources. Default is true.
|
|
200
|
+
*/
|
|
201
|
+
resources?: boolean;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export function defineMiddleware<
|
|
205
|
+
TConfig extends Record<string, any>,
|
|
206
|
+
TDependencies extends DependencyMapType
|
|
207
|
+
>(
|
|
208
|
+
middlewareDef: IMiddlewareDefinition<TConfig, TDependencies>
|
|
209
|
+
): IMiddleware<TConfig, TDependencies> {
|
|
210
|
+
const filePath = getCallerFile();
|
|
131
211
|
const object = {
|
|
132
|
-
[symbols.filePath]:
|
|
212
|
+
[symbols.filePath]: filePath,
|
|
133
213
|
[symbols.middleware]: true,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
214
|
+
config: {} as TConfig,
|
|
215
|
+
id: middlewareDef.id || generateCallerIdFromFile(filePath, "middleware"),
|
|
216
|
+
...middlewareDef,
|
|
217
|
+
dependencies: middlewareDef.dependencies || ({} as TDependencies),
|
|
218
|
+
} as IMiddleware<TConfig, TDependencies>;
|
|
137
219
|
|
|
138
220
|
return {
|
|
139
221
|
...object,
|
|
140
|
-
|
|
222
|
+
with: (config: TConfig) => {
|
|
223
|
+
return {
|
|
224
|
+
...object,
|
|
225
|
+
[symbolMiddlewareConfigured]: true,
|
|
226
|
+
config: {
|
|
227
|
+
...object.config,
|
|
228
|
+
...config,
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
},
|
|
232
|
+
everywhere(options: MiddlewareEverywhereOptions = {}) {
|
|
233
|
+
const { tasks = true, resources = true } = options;
|
|
234
|
+
|
|
141
235
|
return {
|
|
142
236
|
...object,
|
|
143
|
-
[symbols.
|
|
144
|
-
|
|
145
|
-
|
|
237
|
+
[symbols.middlewareEverywhereTasks]: tasks,
|
|
238
|
+
[symbols.middlewareEverywhereResources]: resources,
|
|
239
|
+
everywhere() {
|
|
240
|
+
throw Errors.middlewareAlreadyGlobal(object.id);
|
|
146
241
|
},
|
|
147
242
|
};
|
|
148
243
|
},
|
|
@@ -163,7 +258,7 @@ export function isResourceWithConfig(
|
|
|
163
258
|
return definition && definition[symbols.resourceWithConfig];
|
|
164
259
|
}
|
|
165
260
|
|
|
166
|
-
export function isEvent(definition: any): definition is
|
|
261
|
+
export function isEvent(definition: any): definition is IEvent {
|
|
167
262
|
return definition && definition[symbols.event];
|
|
168
263
|
}
|
|
169
264
|
|