@bluelibs/runner 1.0.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.
Files changed (66) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +797 -0
  3. package/dist/DependencyProcessor.d.ts +49 -0
  4. package/dist/DependencyProcessor.js +178 -0
  5. package/dist/DependencyProcessor.js.map +1 -0
  6. package/dist/EventManager.d.ts +13 -0
  7. package/dist/EventManager.js +58 -0
  8. package/dist/EventManager.js.map +1 -0
  9. package/dist/ResourceInitializer.d.ts +13 -0
  10. package/dist/ResourceInitializer.js +54 -0
  11. package/dist/ResourceInitializer.js.map +1 -0
  12. package/dist/Store.d.ts +62 -0
  13. package/dist/Store.js +186 -0
  14. package/dist/Store.js.map +1 -0
  15. package/dist/TaskRunner.d.ts +22 -0
  16. package/dist/TaskRunner.js +93 -0
  17. package/dist/TaskRunner.js.map +1 -0
  18. package/dist/define.d.ts +10 -0
  19. package/dist/define.js +111 -0
  20. package/dist/define.js.map +1 -0
  21. package/dist/defs.d.ts +127 -0
  22. package/dist/defs.js +12 -0
  23. package/dist/defs.js.map +1 -0
  24. package/dist/errors.d.ts +8 -0
  25. package/dist/errors.js +12 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/globalEvents.d.ts +36 -0
  28. package/dist/globalEvents.js +45 -0
  29. package/dist/globalEvents.js.map +1 -0
  30. package/dist/globalResources.d.ts +8 -0
  31. package/dist/globalResources.js +19 -0
  32. package/dist/globalResources.js.map +1 -0
  33. package/dist/index.d.ts +49 -0
  34. package/dist/index.js +25 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/run.d.ts +32 -0
  37. package/dist/run.js +39 -0
  38. package/dist/run.js.map +1 -0
  39. package/dist/tools/findCircularDependencies.d.ts +16 -0
  40. package/dist/tools/findCircularDependencies.js +53 -0
  41. package/dist/tools/findCircularDependencies.js.map +1 -0
  42. package/package.json +50 -0
  43. package/src/DependencyProcessor.ts +243 -0
  44. package/src/EventManager.ts +84 -0
  45. package/src/ResourceInitializer.ts +69 -0
  46. package/src/Store.ts +250 -0
  47. package/src/TaskRunner.ts +135 -0
  48. package/src/__tests__/EventManager.test.ts +96 -0
  49. package/src/__tests__/ResourceInitializer.test.ts +109 -0
  50. package/src/__tests__/Store.test.ts +143 -0
  51. package/src/__tests__/TaskRunner.test.ts +135 -0
  52. package/src/__tests__/benchmark/benchmark.test.ts +146 -0
  53. package/src/__tests__/errors.test.ts +268 -0
  54. package/src/__tests__/globalEvents.test.ts +99 -0
  55. package/src/__tests__/index.ts +9 -0
  56. package/src/__tests__/run.hooks.test.ts +110 -0
  57. package/src/__tests__/run.test.ts +614 -0
  58. package/src/__tests__/tools/findCircularDependencies.test.ts +217 -0
  59. package/src/define.ts +142 -0
  60. package/src/defs.ts +221 -0
  61. package/src/errors.ts +22 -0
  62. package/src/globalEvents.ts +64 -0
  63. package/src/globalResources.ts +19 -0
  64. package/src/index.ts +28 -0
  65. package/src/run.ts +98 -0
  66. package/src/tools/findCircularDependencies.ts +69 -0
@@ -0,0 +1,217 @@
1
+ import {
2
+ findCircularDependencies,
3
+ IDependentNode,
4
+ } from "../../tools/findCircularDependencies";
5
+
6
+ describe("checkCircularDependencies", () => {
7
+ test("should detect a simple cycle (A -> B -> C -> A)", () => {
8
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
9
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
10
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
11
+
12
+ nodeA.dependencies["B"] = nodeB;
13
+ nodeB.dependencies["C"] = nodeC;
14
+ nodeC.dependencies["A"] = nodeA;
15
+
16
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC];
17
+ const result = findCircularDependencies(nodes);
18
+
19
+ expect(result.cycles).toEqual(["A -> B -> C -> A"]);
20
+ expect(result.missingDependencies).toEqual([]);
21
+ });
22
+
23
+ test("should detect multiple cycles", () => {
24
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
25
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
26
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
27
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
28
+
29
+ nodeA.dependencies["B"] = nodeB;
30
+ nodeB.dependencies["C"] = nodeC;
31
+ nodeC.dependencies["A"] = nodeA; // Cycle: A -> B -> C -> A
32
+ nodeC.dependencies["D"] = nodeD;
33
+ nodeD.dependencies["B"] = nodeB; // Cycle: B -> C -> D -> B
34
+
35
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD];
36
+ const result = findCircularDependencies(nodes);
37
+
38
+ // The order of cycles in the result array might vary
39
+ expect(result.cycles).toContain("A -> B -> C -> A");
40
+ expect(result.cycles).toContain("B -> C -> D -> B");
41
+ expect(result.cycles.length).toBe(2);
42
+ expect(result.missingDependencies).toEqual([]);
43
+ });
44
+
45
+ test("should return empty arrays when there are no cycles", () => {
46
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
47
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
48
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
49
+
50
+ nodeA.dependencies["B"] = nodeB;
51
+ nodeB.dependencies["C"] = nodeC;
52
+
53
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC];
54
+ const result = findCircularDependencies(nodes);
55
+
56
+ expect(result.cycles).toEqual([]);
57
+ expect(result.missingDependencies).toEqual([]);
58
+ });
59
+
60
+ test("should detect a self-referential cycle (A -> A)", () => {
61
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
62
+ nodeA.dependencies["A"] = nodeA; // Cycle: A -> A
63
+
64
+ const nodes: IDependentNode[] = [nodeA];
65
+ const result = findCircularDependencies(nodes);
66
+
67
+ expect(result.cycles).toEqual(["A -> A"]);
68
+ expect(result.missingDependencies).toEqual([]);
69
+ });
70
+
71
+ test("should handle complex graphs with shared nodes", () => {
72
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
73
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
74
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
75
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
76
+ const nodeE: IDependentNode = { id: "E", dependencies: {} };
77
+
78
+ nodeA.dependencies["B"] = nodeB;
79
+ nodeB.dependencies["C"] = nodeC;
80
+ nodeC.dependencies["D"] = nodeD;
81
+ nodeD.dependencies["B"] = nodeB; // Cycle: B -> C -> D -> B
82
+ nodeC.dependencies["E"] = nodeE;
83
+ nodeE.dependencies["C"] = nodeC; // Cycle: C -> E -> C
84
+
85
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD, nodeE];
86
+ const result = findCircularDependencies(nodes);
87
+
88
+ expect(result.cycles).toContain("B -> C -> D -> B");
89
+ expect(result.cycles).toContain("C -> E -> C");
90
+ expect(result.cycles.length).toBe(2);
91
+ expect(result.missingDependencies).toEqual([]);
92
+ });
93
+
94
+ test("should handle multiple independent cycles", () => {
95
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
96
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
97
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
98
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
99
+ const nodeE: IDependentNode = { id: "E", dependencies: {} };
100
+ const nodeF: IDependentNode = { id: "F", dependencies: {} };
101
+
102
+ // Cycle 1: A -> B -> A
103
+ nodeA.dependencies["B"] = nodeB;
104
+ nodeB.dependencies["A"] = nodeA;
105
+
106
+ // Cycle 2: C -> D -> E -> C
107
+ nodeC.dependencies["D"] = nodeD;
108
+ nodeD.dependencies["E"] = nodeE;
109
+ nodeE.dependencies["C"] = nodeC;
110
+
111
+ // Cycle 3: F -> F
112
+ nodeF.dependencies["F"] = nodeF;
113
+
114
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD, nodeE, nodeF];
115
+ const result = findCircularDependencies(nodes);
116
+
117
+ expect(result.cycles).toContain("A -> B -> A");
118
+ expect(result.cycles).toContain("C -> D -> E -> C");
119
+ expect(result.cycles).toContain("F -> F");
120
+ expect(result.cycles.length).toBe(3);
121
+ expect(result.missingDependencies).toEqual([]);
122
+ });
123
+
124
+ test("should handle disconnected graphs", () => {
125
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
126
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
127
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
128
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
129
+
130
+ // Cycle 1: A -> B -> A
131
+ nodeA.dependencies["B"] = nodeB;
132
+ nodeB.dependencies["A"] = nodeA;
133
+
134
+ // Cycle 2: C -> D -> C
135
+ nodeC.dependencies["D"] = nodeD;
136
+ nodeD.dependencies["C"] = nodeC;
137
+
138
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD];
139
+ const result = findCircularDependencies(nodes);
140
+
141
+ expect(result.cycles).toContain("A -> B -> A");
142
+ expect(result.cycles).toContain("C -> D -> C");
143
+ expect(result.cycles.length).toBe(2);
144
+ expect(result.missingDependencies).toEqual([]);
145
+ });
146
+
147
+ test("should handle nodes with no dependencies", () => {
148
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
149
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
150
+
151
+ const nodes: IDependentNode[] = [nodeA, nodeB];
152
+ const result = findCircularDependencies(nodes);
153
+
154
+ expect(result.cycles).toEqual([]);
155
+ expect(result.missingDependencies).toEqual([]);
156
+ });
157
+
158
+ test("should handle dependencies that do not form cycles", () => {
159
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
160
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
161
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
162
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
163
+
164
+ nodeA.dependencies["B"] = nodeB;
165
+ nodeB.dependencies["C"] = nodeC;
166
+ nodeC.dependencies["D"] = nodeD;
167
+
168
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD];
169
+ const result = findCircularDependencies(nodes);
170
+
171
+ expect(result.cycles).toEqual([]);
172
+ expect(result.missingDependencies).toEqual([]);
173
+ });
174
+
175
+ test("should report missing dependencies", () => {
176
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
177
+ // nodeB is missing
178
+ nodeA.dependencies["B"] = undefined as any; // Force undefined dependency
179
+
180
+ const nodes: IDependentNode[] = [nodeA];
181
+ const result = findCircularDependencies(nodes);
182
+
183
+ expect(result.cycles).toEqual([]);
184
+ expect(result.missingDependencies).toEqual([
185
+ { nodeId: "A", dependencyId: "B" },
186
+ ]);
187
+ });
188
+
189
+ test("should handle complex graph with multiple overlapping cycles", () => {
190
+ const nodeA: IDependentNode = { id: "A", dependencies: {} };
191
+ const nodeB: IDependentNode = { id: "B", dependencies: {} };
192
+ const nodeC: IDependentNode = { id: "C", dependencies: {} };
193
+ const nodeD: IDependentNode = { id: "D", dependencies: {} };
194
+ const nodeE: IDependentNode = { id: "E", dependencies: {} };
195
+ const nodeF: IDependentNode = { id: "F", dependencies: {} };
196
+
197
+ // Construct cycles:
198
+ // A -> B -> C -> A
199
+ // C -> D -> E -> F -> C
200
+
201
+ nodeA.dependencies["B"] = nodeB;
202
+ nodeB.dependencies["C"] = nodeC;
203
+ nodeC.dependencies["A"] = nodeA;
204
+ nodeC.dependencies["D"] = nodeD;
205
+ nodeD.dependencies["E"] = nodeE;
206
+ nodeE.dependencies["F"] = nodeF;
207
+ nodeF.dependencies["C"] = nodeC;
208
+
209
+ const nodes: IDependentNode[] = [nodeA, nodeB, nodeC, nodeD, nodeE, nodeF];
210
+ const result = findCircularDependencies(nodes);
211
+
212
+ expect(result.cycles).toContain("A -> B -> C -> A");
213
+ expect(result.cycles).toContain("C -> D -> E -> F -> C");
214
+ expect(result.cycles.length).toBe(2);
215
+ expect(result.missingDependencies).toEqual([]);
216
+ });
217
+ });
package/src/define.ts ADDED
@@ -0,0 +1,142 @@
1
+ import {
2
+ ITask,
3
+ ITaskDefinition,
4
+ IResource,
5
+ IResourceWithConfig,
6
+ IResourceDefinintion,
7
+ IEventDefinition,
8
+ IMiddlewareDefinition,
9
+ symbols,
10
+ DependencyMapType,
11
+ DependencyValuesType,
12
+ IMiddleware,
13
+ } from "./defs";
14
+ import { Errors } from "./errors";
15
+
16
+ export function defineTask<
17
+ Input = undefined,
18
+ Output extends Promise<any> = any,
19
+ Deps extends DependencyMapType = any,
20
+ Test = any
21
+ >(
22
+ config: ITaskDefinition<Input, Output, Deps, Test>
23
+ ): ITask<Input, Output, Deps, Test> {
24
+ const autorun: any = {};
25
+ // if (config.autorun) {
26
+ // if (config.autorun.on) {
27
+ // autorun.on = Array.isArray(config.autorun.on);
28
+ // autorun.schedule = config.autorun.schedule;
29
+ // }
30
+ // }
31
+
32
+ return {
33
+ [symbols.task]: true,
34
+ id: config.id,
35
+ dependencies: config.dependencies || ({} as Deps),
36
+ middleware: config.middleware || [],
37
+ run: config.run,
38
+ on: config.on,
39
+ events: {
40
+ beforeRun: defineEvent({
41
+ id: `${config.id}.beforeRun`,
42
+ }),
43
+ afterRun: defineEvent({
44
+ id: `${config.id}.afterRun`,
45
+ }),
46
+ onError: defineEvent({
47
+ id: `${config.id}.onError`,
48
+ }),
49
+ },
50
+ // autorun,
51
+ };
52
+ }
53
+
54
+ export function defineResource<
55
+ TConfig = void,
56
+ TValue = any,
57
+ TDeps extends DependencyMapType = {}
58
+ >(
59
+ constConfig: IResourceDefinintion<TConfig, TValue, TDeps>
60
+ ): IResource<TConfig, TValue, TDeps> {
61
+ return {
62
+ [symbols.resource]: true,
63
+ id: constConfig.id,
64
+ dependencies: constConfig.dependencies,
65
+ hooks: constConfig.hooks || [],
66
+ register: constConfig.register || [],
67
+ init: constConfig.init,
68
+ with: function (config: TConfig) {
69
+ return {
70
+ [symbols.resourceWithConfig]: true,
71
+ resource: this,
72
+ config,
73
+ };
74
+ },
75
+
76
+ events: {
77
+ beforeInit: defineEvent({
78
+ id: `${constConfig.id}.beforeInit`,
79
+ }),
80
+ afterInit: defineEvent({
81
+ id: `${constConfig.id}.afterInit`,
82
+ }),
83
+ onError: defineEvent({
84
+ id: `${constConfig.id}.onError`,
85
+ }),
86
+ },
87
+ };
88
+ }
89
+
90
+ export function defineEvent<TPayload = any>(
91
+ config: IEventDefinition<TPayload>
92
+ ): IEventDefinition<TPayload> {
93
+ return {
94
+ [symbols.event]: true,
95
+ ...config,
96
+ };
97
+ }
98
+
99
+ export function defineMiddleware<TDeps extends DependencyMapType = {}>(
100
+ config: IMiddlewareDefinition<TDeps>
101
+ ): IMiddleware<TDeps> {
102
+ const object = {
103
+ [symbols.middleware]: true,
104
+ ...config,
105
+ dependencies: config.dependencies || ({} as TDeps),
106
+ };
107
+
108
+ return {
109
+ ...object,
110
+ global() {
111
+ return {
112
+ ...object,
113
+ [symbols.middlewareGlobal]: true,
114
+ global() {
115
+ throw Errors.middlewareAlreadyGlobal(config.id);
116
+ },
117
+ };
118
+ },
119
+ };
120
+ }
121
+
122
+ export function isTask(definition: any): definition is ITask {
123
+ return definition && definition[symbols.task];
124
+ }
125
+
126
+ export function isResource(definition: any): definition is IResource {
127
+ return definition && definition[symbols.resource];
128
+ }
129
+
130
+ export function isResourceWithConfig(
131
+ definition: any
132
+ ): definition is IResourceWithConfig {
133
+ return definition && definition[symbols.resourceWithConfig];
134
+ }
135
+
136
+ export function isEvent(definition: any): definition is IEventDefinition {
137
+ return definition && definition[symbols.event];
138
+ }
139
+
140
+ export function isMiddleware(definition: any): definition is IMiddleware {
141
+ return definition && definition[symbols.middleware];
142
+ }
package/src/defs.ts ADDED
@@ -0,0 +1,221 @@
1
+ export const symbols = {
2
+ task: Symbol("task"),
3
+ resource: Symbol("resource"),
4
+ resourceWithConfig: Symbol("resourceWithConfig"),
5
+ event: Symbol("event"),
6
+ middleware: Symbol("middleware"),
7
+ middlewareGlobal: Symbol("middlewareGlobal"),
8
+ };
9
+
10
+ export interface IMeta {
11
+ title?: string;
12
+ description?: string;
13
+ tags: string[];
14
+ }
15
+
16
+ export interface ITaskMeta extends IMeta {}
17
+ export interface IResourceMeta extends IMeta {}
18
+ export interface IEventMeta extends IMeta {}
19
+ export interface IMiddlewareMeta extends IMeta {}
20
+
21
+ // DependencyMap types
22
+ export type DependencyMapType = Record<
23
+ string,
24
+ ITask | IResource | IEventDefinition
25
+ >;
26
+
27
+ export type DependencyValueType<T> = T extends ITask<
28
+ infer I,
29
+ infer O,
30
+ /** The infer D, while not used is crucial for making this work correctly, otherwise it forces input: unknown to a dependency that has a dependency. */
31
+ infer D
32
+ >
33
+ ? (...args: I extends unknown ? [] : [I]) => O
34
+ : T extends IResource<any, infer V>
35
+ ? V
36
+ : T extends IEventDefinition<infer P>
37
+ ? (input: P) => Promise<void> | never
38
+ : never;
39
+
40
+ export type DependencyValuesType<T extends DependencyMapType> = {
41
+ [K in keyof T]: DependencyValueType<T[K]>;
42
+ };
43
+
44
+ // RegisterableItems Type with Conditional Inclusion
45
+ export type RegisterableItems =
46
+ | IResource<void> // Always include IResource<void>
47
+ | IResourceWithConfig<any>
48
+ | ITaskDefinition
49
+ | IMiddlewareDefinition
50
+ | IEventDefinition;
51
+
52
+ export interface ITaskDefinition<
53
+ TInput = any,
54
+ TOutput extends Promise<any> = any,
55
+ TDependencies extends DependencyMapType = {},
56
+ TEventDefinitionInput = null
57
+ > {
58
+ id: string;
59
+ dependencies?: TDependencies | (() => TDependencies);
60
+ middleware?: IMiddlewareDefinition[];
61
+ on?: IEventDefinition<TEventDefinitionInput>;
62
+ meta?: ITaskMeta;
63
+ run: (
64
+ input: TEventDefinitionInput extends null ? TInput : TEventDefinitionInput,
65
+ dependencies: DependencyValuesType<TDependencies>
66
+ ) => TOutput;
67
+ }
68
+
69
+ export type BeforeRunEventPayload<TInput> = {
70
+ input: TInput;
71
+ };
72
+
73
+ export type AfterRunEventPayload<TInput, TOutput> = {
74
+ input: TInput;
75
+ output: TOutput;
76
+ };
77
+
78
+ export type OnErrorEventPayload = {
79
+ error: any;
80
+ };
81
+
82
+ export type BeforeInitEventPayload<TConfig> = {
83
+ config: TConfig;
84
+ };
85
+
86
+ export type AfterInitEventPayload<TConfig, TValue> = {
87
+ config: TConfig;
88
+ value: TValue;
89
+ };
90
+
91
+ /**
92
+ * This is the response after the definition has been prepared. TODO: better naming?
93
+ */
94
+ export interface ITask<
95
+ TInput = any,
96
+ TOutput extends Promise<any> = any,
97
+ TDependencies extends DependencyMapType = {},
98
+ TEventDefinitionInput = null
99
+ > extends ITaskDefinition<
100
+ TInput,
101
+ TOutput,
102
+ TDependencies,
103
+ TEventDefinitionInput
104
+ > {
105
+ dependencies: TDependencies | (() => TDependencies);
106
+ computedDependencies?: DependencyValuesType<TDependencies>;
107
+ middleware: IMiddlewareDefinition[];
108
+ /**
109
+ * These events are automatically populated after the task has been defined.
110
+ */
111
+ events: {
112
+ beforeRun: IEventDefinition<BeforeRunEventPayload<TInput>>;
113
+ afterRun: IEventDefinition<AfterRunEventPayload<TInput, TOutput>>;
114
+ onError: IEventDefinition<OnErrorEventPayload>;
115
+ };
116
+ }
117
+ // Resource interfaces
118
+ export interface IResourceDefinintion<
119
+ TConfig = void,
120
+ TValue = unknown,
121
+ TDependencies extends DependencyMapType = {}
122
+ > {
123
+ id: string;
124
+ dependencies?: TDependencies | ((config: TConfig) => TDependencies);
125
+ hooks?:
126
+ | IHookDefinition<TDependencies>[]
127
+ | ((config: TConfig) => IHookDefinition<TDependencies>[]);
128
+ register?:
129
+ | Array<RegisterableItems>
130
+ | ((config: TConfig) => Array<RegisterableItems>);
131
+ init?: (
132
+ config: TConfig,
133
+ dependencies: DependencyValuesType<TDependencies>
134
+ ) => Promise<TValue>;
135
+ meta?: IResourceMeta;
136
+ }
137
+
138
+ export interface IResource<
139
+ TConfig = void,
140
+ TValue = any,
141
+ TDependencies extends DependencyMapType = any
142
+ > extends IResourceDefinintion<TConfig, TValue, TDependencies> {
143
+ with(config: TConfig): IResourceWithConfig<TConfig, TValue, TDependencies>;
144
+ register:
145
+ | Array<RegisterableItems>
146
+ | ((config: TConfig) => Array<RegisterableItems>);
147
+ /**
148
+ * These events are automatically populated after the task has been defined.
149
+ */
150
+ events: {
151
+ beforeInit: IEventDefinition<BeforeInitEventPayload<TConfig>>;
152
+ afterInit: IEventDefinition<AfterInitEventPayload<TConfig, TValue>>;
153
+ onError: IEventDefinition<OnErrorEventPayload>;
154
+ };
155
+ hooks:
156
+ | IHookDefinition<TDependencies>[]
157
+ | ((config: TConfig) => IHookDefinition<TDependencies>[]);
158
+ }
159
+
160
+ export interface IResourceWithConfig<
161
+ TConfig = any,
162
+ TValue = any,
163
+ TDependencies extends DependencyMapType = any
164
+ > {
165
+ resource: IResource<TConfig, TValue, TDependencies>;
166
+ config: TConfig;
167
+ }
168
+
169
+ export interface IEvent<TPayload = any> {
170
+ id: string;
171
+ data: TPayload;
172
+ }
173
+
174
+ export type EventHandlerType<T = any> = (
175
+ event: IEvent<T>
176
+ ) => any | Promise<any>;
177
+
178
+ // Other necessary interfaces
179
+ export interface IEventDefinition<TPayload = void> {
180
+ id: string;
181
+ meta?: IEventMeta;
182
+ }
183
+
184
+ export interface IMiddlewareDefinition<
185
+ TDependencies extends DependencyMapType = any
186
+ > {
187
+ id: string;
188
+ dependencies?: TDependencies | (() => TDependencies);
189
+ run: (
190
+ input: IMiddlewareExecutionInput,
191
+ dependencies: DependencyValuesType<TDependencies>
192
+ ) => Promise<any>;
193
+ meta?: IMiddlewareMeta;
194
+ }
195
+
196
+ export interface IMiddleware<TDependencies extends DependencyMapType = any>
197
+ extends IMiddlewareDefinition<TDependencies> {
198
+ dependencies: TDependencies | (() => TDependencies);
199
+ global(): IMiddleware<TDependencies>;
200
+ }
201
+
202
+ export interface IMiddlewareDefinitionConfigured<
203
+ C extends Record<string, any> = {}
204
+ > {
205
+ middleware: IMiddleware<C>;
206
+ config?: C;
207
+ }
208
+
209
+ export interface IMiddlewareExecutionInput {
210
+ taskDefinition?: ITask;
211
+ input: any;
212
+ next: (input?: any) => Promise<any>;
213
+ }
214
+
215
+ export interface IHookDefinition<D extends DependencyMapType = {}> {
216
+ event: IEventDefinition;
217
+ run: (
218
+ event: IEvent,
219
+ dependencies: DependencyValuesType<D>
220
+ ) => Promise<void> | void;
221
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { ITask, IResource } from "./defs";
2
+
3
+ export const Errors = {
4
+ duplicateRegistration: (type: string, id: string) =>
5
+ new Error(`${type} "${id}" already registered`),
6
+
7
+ dependencyNotFound: (key: string) =>
8
+ new Error(
9
+ `Dependency ${key} not found. Did you forget to register it through a resource?`
10
+ ),
11
+
12
+ unknownItemType: (item: any) => new Error(`Unknown item type: ${item}`),
13
+
14
+ circularDependencies: (cycles: string[]) =>
15
+ new Error(`Circular dependencies detected: ${cycles.join(", ")}`),
16
+
17
+ eventNotFound: (id: string) =>
18
+ new Error(`Event "${id}" not found. Did you forget to register it?`),
19
+
20
+ middlewareAlreadyGlobal: (id: string) =>
21
+ new Error("Cannot call global on a global middleware: " + id),
22
+ };
@@ -0,0 +1,64 @@
1
+ import { defineEvent } from "./define";
2
+ import { ITask, IResource } from "./defs";
3
+
4
+ export const globalEvents = {
5
+ beforeInit: defineEvent({
6
+ id: "global.beforeInit",
7
+ }),
8
+ afterInit: defineEvent({
9
+ id: "global.afterInit",
10
+ }),
11
+ tasks: {
12
+ beforeRun: defineEvent<{
13
+ task: ITask<any, any, any>;
14
+ input: any;
15
+ }>({
16
+ id: "global.tasks.beforeRun",
17
+ }),
18
+ afterRun: defineEvent<{
19
+ task: ITask<any, any, any>;
20
+ input: any;
21
+ output: any;
22
+ }>({
23
+ id: "global.tasks.afterRun",
24
+ }),
25
+ onError: defineEvent<{
26
+ error: Error;
27
+ task: ITask<any, any, any>;
28
+ }>({
29
+ id: "global.tasks.onError",
30
+ }),
31
+ },
32
+ resources: {
33
+ beforeInit: defineEvent<{
34
+ resource: IResource<any, any, any>;
35
+ config: any;
36
+ }>({
37
+ id: "global.resources.beforeInit",
38
+ }),
39
+ afterInit: defineEvent<{
40
+ resource: IResource<any, any, any>;
41
+ config: any;
42
+ value: any;
43
+ }>({
44
+ id: "global.resources.afterInit",
45
+ }),
46
+ onError: defineEvent<{
47
+ error: Error;
48
+ resource: IResource<any, any, any>;
49
+ }>({
50
+ id: "global.resources.onError",
51
+ }),
52
+ },
53
+ };
54
+
55
+ export const globalEventsArray = [
56
+ globalEvents.beforeInit,
57
+ globalEvents.afterInit,
58
+ globalEvents.tasks.beforeRun,
59
+ globalEvents.tasks.afterRun,
60
+ globalEvents.tasks.onError,
61
+ globalEvents.resources.beforeInit,
62
+ globalEvents.resources.afterInit,
63
+ globalEvents.resources.onError,
64
+ ];
@@ -0,0 +1,19 @@
1
+ import { defineResource } from "./define";
2
+ import { EventManager } from "./EventManager";
3
+ import { Store } from "./Store";
4
+ import { TaskRunner } from "./TaskRunner";
5
+
6
+ export const globalResources = {
7
+ store: defineResource<Store>({
8
+ id: "global.store",
9
+ init: async (store) => store,
10
+ }),
11
+ eventManager: defineResource<EventManager>({
12
+ id: "global.eventManager",
13
+ init: async (em) => em,
14
+ }),
15
+ taskRunner: defineResource<TaskRunner>({
16
+ id: "global.taskRunner",
17
+ init: async (runner) => runner,
18
+ }),
19
+ };