@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,614 @@
1
+ import {
2
+ defineTask,
3
+ defineResource,
4
+ defineEvent,
5
+ defineMiddleware,
6
+ } from "../define";
7
+ import { run } from "../run";
8
+
9
+ describe("run", () => {
10
+ // Tasks
11
+ describe("Tasks", () => {
12
+ it("should be able to register an task and execute it", async () => {
13
+ const testTask = defineTask({
14
+ id: "test.task",
15
+ run: async () => "Hello, World!",
16
+ });
17
+
18
+ const app = defineResource({
19
+ id: "app",
20
+ dependencies: { testTask },
21
+ register: [testTask],
22
+ async init(_, { testTask }) {
23
+ const result = await testTask();
24
+ expect(result).toBe("Hello, World!");
25
+ },
26
+ });
27
+
28
+ await run(app);
29
+ });
30
+
31
+ it("should be able to register an task with dependencies and execute it", async () => {
32
+ const dependencyTask = defineTask({
33
+ id: "dependency.task",
34
+ run: async (_, { d1 }) => {
35
+ return "Dependency";
36
+ },
37
+ });
38
+
39
+ const testTask = defineTask({
40
+ id: "test.task",
41
+ dependencies: { dependencyTask },
42
+ run: async (_, deps) => {
43
+ const dep = await deps.dependencyTask();
44
+ return `Hello, ${dep}!`;
45
+ },
46
+ });
47
+
48
+ const app = defineResource({
49
+ id: "app",
50
+ register: [dependencyTask, testTask],
51
+ dependencies: { testTask },
52
+ async init(_, { testTask }) {
53
+ const result = await testTask();
54
+ expect(result).toBe("Hello, Dependency!");
55
+ },
56
+ });
57
+
58
+ await run(app);
59
+ });
60
+
61
+ it("should be able to register an task that emits an event", async () => {
62
+ const testEvent = defineEvent({ id: "test.event" });
63
+ const eventHandler = jest.fn();
64
+
65
+ const testTask = defineTask({
66
+ id: "test.task",
67
+ dependencies: { testEvent },
68
+ run: async (_, { testEvent }) => {
69
+ await testEvent({ message: "Event emitted" });
70
+ return "Task completed";
71
+ },
72
+ });
73
+
74
+ const app = defineResource({
75
+ id: "app",
76
+ register: [testEvent, testTask],
77
+ hooks: [
78
+ {
79
+ event: testEvent,
80
+ run: eventHandler,
81
+ },
82
+ ],
83
+ dependencies: { testTask },
84
+ async init(_, { testTask }) {
85
+ await testTask();
86
+ expect(eventHandler).toHaveBeenCalled();
87
+ },
88
+ });
89
+
90
+ await run(app);
91
+ });
92
+
93
+ it("should emit the proper beforeRun, afterRun, onError events and I can listen to them", async () => {
94
+ const beforeRunHandler = jest.fn();
95
+ const afterRunHandler = jest.fn();
96
+ const onErrorHandler = jest.fn();
97
+
98
+ const testTask = defineTask({
99
+ id: "test.task",
100
+ run: async () => "Task executed",
101
+ });
102
+
103
+ const app = defineResource({
104
+ id: "app",
105
+ register: [testTask],
106
+ dependencies: { testTask },
107
+ hooks: [
108
+ { event: testTask.events.beforeRun, run: beforeRunHandler },
109
+ { event: testTask.events.afterRun, run: afterRunHandler },
110
+ { event: testTask.events.onError, run: onErrorHandler },
111
+ ],
112
+ async init(_, { testTask }) {
113
+ await testTask();
114
+ expect(beforeRunHandler).toHaveBeenCalled();
115
+ expect(afterRunHandler).toHaveBeenCalled();
116
+ expect(onErrorHandler).not.toHaveBeenCalled();
117
+ },
118
+ });
119
+
120
+ await run(app);
121
+ });
122
+
123
+ it("should be able to register an task with middleware and execute it, ensuring the middleware is called in the correct order", async () => {
124
+ const order: string[] = [];
125
+
126
+ const testMiddleware1 = defineMiddleware({
127
+ id: "test.middleware1",
128
+ run: async ({ next }) => {
129
+ order.push("middleware1 before");
130
+ const result = await next();
131
+ order.push("middleware1 after");
132
+ return result;
133
+ },
134
+ });
135
+
136
+ const testMiddleware2 = defineMiddleware({
137
+ id: "test.middleware2",
138
+ run: async ({ next }) => {
139
+ order.push("middleware2 before");
140
+ const result = await next();
141
+ order.push("middleware2 after");
142
+ return result;
143
+ },
144
+ });
145
+
146
+ const testTask = defineTask({
147
+ id: "test.task",
148
+ middleware: [testMiddleware1, testMiddleware2],
149
+ run: async () => {
150
+ order.push("task");
151
+ return "Task executed";
152
+ },
153
+ });
154
+
155
+ const app = defineResource({
156
+ id: "app",
157
+ register: [testMiddleware1, testMiddleware2, testTask],
158
+ dependencies: { testTask },
159
+ async init(_, { testTask }) {
160
+ await testTask();
161
+ },
162
+ });
163
+
164
+ await run(app);
165
+
166
+ expect(order).toEqual([
167
+ "middleware1 before",
168
+ "middleware2 before",
169
+ "task",
170
+ "middleware2 after",
171
+ "middleware1 after",
172
+ ]);
173
+ });
174
+
175
+ it("should be able to register an task with middleware that has dependencies and execute it", async () => {
176
+ const dependencyResource = defineResource({
177
+ id: "dependency.resource",
178
+ init: async () => "Dependency Value",
179
+ });
180
+
181
+ const testMiddleware = defineMiddleware({
182
+ id: "test.middleware",
183
+ dependencies: { dependencyResource },
184
+ run: async ({ next }, { dependencyResource }) => {
185
+ const result = await next();
186
+ return `${result} - ${dependencyResource}`;
187
+ },
188
+ });
189
+
190
+ const testTask = defineTask({
191
+ id: "test.task",
192
+ middleware: [testMiddleware],
193
+ run: async () => "Task executed",
194
+ });
195
+
196
+ const app = defineResource({
197
+ id: "app",
198
+ dependencies: { testTask },
199
+ register: [dependencyResource, testMiddleware, testTask],
200
+ async init(_, { testTask }) {
201
+ const result = await testTask();
202
+ expect(result).toBe("Task executed - Dependency Value");
203
+ },
204
+ });
205
+
206
+ await run(app);
207
+ });
208
+
209
+ it("should throw an error if there's an infinite dependency", async () => {
210
+ const task1 = defineTask({
211
+ id: "task1",
212
+ dependencies: () => ({ task2 }), // Corrected line
213
+ run: async () => "Task 1",
214
+ });
215
+
216
+ const task2 = defineTask({
217
+ id: "task2",
218
+ dependencies: { task1 },
219
+ run: async () => "Task 2",
220
+ });
221
+
222
+ // define circular dependency resources
223
+ const resource1 = defineResource({
224
+ id: "resource1",
225
+ dependencies: () => ({
226
+ resource2,
227
+ }),
228
+ init: async () => "Resource 1",
229
+ });
230
+
231
+ const resource2 = defineResource({
232
+ id: "resource2",
233
+ dependencies: { resource1 },
234
+ init: async () => "Resource 2",
235
+ });
236
+
237
+ task1.dependencies.task2 = task2;
238
+
239
+ const app = defineResource({
240
+ id: "app",
241
+ register: [resource1, resource2],
242
+ });
243
+
244
+ await expect(run(app)).rejects.toThrow(/Circular dependencies detected/);
245
+ });
246
+ });
247
+
248
+ it("should be able to listen to an event through the 'on' property", async () => {
249
+ const testEvent = defineEvent<{ message: string }>({ id: "test.event" });
250
+ const eventHandler = jest.fn();
251
+
252
+ const task = defineTask({
253
+ id: "app",
254
+ on: testEvent,
255
+ async run(event) {
256
+ eventHandler();
257
+ },
258
+ });
259
+
260
+ const app = defineResource({
261
+ id: "app.resource",
262
+ register: [testEvent, task],
263
+ dependencies: { task, testEvent },
264
+ async init(_, deps) {
265
+ await deps.testEvent({ message: "Event emitted" });
266
+ },
267
+ });
268
+
269
+ await run(app);
270
+ expect(eventHandler).toHaveBeenCalled();
271
+ });
272
+
273
+ // Resources
274
+ describe("Resources", () => {
275
+ it("should be able to register a resource and get its value", async () => {
276
+ const testResource = defineResource({
277
+ id: "test.resource",
278
+ init: async () => "Resource Value",
279
+ });
280
+
281
+ const app = defineResource({
282
+ id: "app",
283
+ register: [testResource],
284
+ dependencies: { testResource },
285
+ async init(_, { testResource }) {
286
+ expect(testResource).toBe("Resource Value");
287
+ },
288
+ });
289
+
290
+ await run(app);
291
+ });
292
+
293
+ it("should be able to register a resource with dependencies and get its value", async () => {
294
+ const dependencyResource = defineResource({
295
+ id: "dependency.resource",
296
+ init: async () => "Dependency",
297
+ });
298
+
299
+ const testResource = defineResource({
300
+ id: "test.resource",
301
+ dependencies: { dependencyResource },
302
+ init: async (_, { dependencyResource }) =>
303
+ `Hello, ${dependencyResource}!`,
304
+ });
305
+
306
+ const app = defineResource({
307
+ id: "app",
308
+ register: [dependencyResource, testResource],
309
+ dependencies: { testResource },
310
+ async init(_, { testResource }) {
311
+ expect(testResource).toBe("Hello, Dependency!");
312
+ },
313
+ });
314
+
315
+ await run(app);
316
+ });
317
+
318
+ it("should allow to register a resource without an init task", async () => {
319
+ const mockFn = jest.fn();
320
+ const testResourceWithInit = defineResource({
321
+ id: "test.resource.with.init",
322
+ init: mockFn,
323
+ });
324
+
325
+ const testResource = defineResource({
326
+ id: "test.resource",
327
+ register: [testResourceWithInit],
328
+ });
329
+
330
+ const app = defineResource({
331
+ id: "app",
332
+ register: [testResource],
333
+ });
334
+
335
+ await run(app);
336
+ expect(mockFn).toHaveBeenCalled();
337
+ });
338
+
339
+ it("should be able to register a resource with configuration and get its value", async () => {
340
+ const testResource = defineResource({
341
+ id: "test.resource",
342
+ init: async (config: { prefix: string }) => `${config.prefix} World!`,
343
+ });
344
+
345
+ const t2 = defineResource({
346
+ id: "test.r2",
347
+ async init() {},
348
+ });
349
+
350
+ const typeTest = defineResource({
351
+ id: "typeTest",
352
+ register: [t2, testResource],
353
+ });
354
+
355
+ const app = defineResource({
356
+ id: "app",
357
+ dependencies: { testResource },
358
+ register: [testResource.with({ prefix: "Hello," })],
359
+ async init(_, { testResource }) {
360
+ expect(testResource).toBe("Hello, World!");
361
+ },
362
+ });
363
+
364
+ await run(app);
365
+ });
366
+ });
367
+
368
+ // Middleware
369
+ describe("Middleware", () => {
370
+ it("should be able to register the middleware and execute it", async () => {
371
+ const testMiddleware = defineMiddleware({
372
+ id: "test.middleware",
373
+ run: async ({ next }) => {
374
+ const result = await next();
375
+ return `Middleware: ${result}`;
376
+ },
377
+ });
378
+
379
+ const testTask = defineTask({
380
+ id: "test.task",
381
+ middleware: [testMiddleware],
382
+ run: async () => "Task executed",
383
+ });
384
+
385
+ const app = defineResource({
386
+ id: "app",
387
+ register: [testMiddleware, testTask],
388
+ dependencies: { testTask },
389
+ async init(_, { testTask }) {
390
+ const result = await testTask();
391
+ expect(result).toBe("Middleware: Task executed");
392
+ },
393
+ });
394
+
395
+ await run(app);
396
+ });
397
+
398
+ it("should work with global middleware", async () => {
399
+ const globalMiddleware = defineMiddleware({
400
+ id: "global.middleware",
401
+ run: async ({ next }) => {
402
+ const result = await next();
403
+ return `global.middleware: ${result}`;
404
+ },
405
+ });
406
+
407
+ const testMiddleware = defineMiddleware({
408
+ id: "test.middleware",
409
+ run: async ({ next }) => {
410
+ const result = await next();
411
+ return `Middleware: ${result}`;
412
+ },
413
+ });
414
+
415
+ const testTask = defineTask({
416
+ id: "test.task",
417
+ middleware: [testMiddleware],
418
+ run: async () => "Task executed",
419
+ });
420
+
421
+ const app = defineResource({
422
+ id: "app",
423
+ register: [globalMiddleware.global(), testMiddleware, testTask],
424
+ dependencies: { testTask },
425
+ async init(_, { testTask }) {
426
+ const result = await testTask();
427
+ expect(result).toBe("global.middleware: Middleware: Task executed");
428
+ },
429
+ });
430
+
431
+ await run(app);
432
+ });
433
+
434
+ it("should work with global middleware but local one should have priority", async () => {
435
+ const createMiddleware = (id: string) =>
436
+ defineMiddleware({
437
+ id: "middleware",
438
+ run: async ({ next }) => {
439
+ const result = await next();
440
+ return `${id}: ${result}`;
441
+ },
442
+ });
443
+ const globalMiddleware = createMiddleware("global.middleware");
444
+
445
+ const testMiddleware = defineMiddleware({
446
+ id: "test.middleware",
447
+ run: async ({ next }) => {
448
+ const result = await next();
449
+ return `Middleware: ${result}`;
450
+ },
451
+ });
452
+
453
+ const testTask = defineTask({
454
+ id: "test.task",
455
+ middleware: [testMiddleware],
456
+ run: async () => "Task executed",
457
+ });
458
+
459
+ const app = defineResource({
460
+ id: "app",
461
+ register: [globalMiddleware.global(), testMiddleware, testTask],
462
+ dependencies: { testTask },
463
+ async init(_, { testTask }) {
464
+ const result = await testTask();
465
+ expect(result).toBe("global.middleware: Middleware: Task executed");
466
+ },
467
+ });
468
+
469
+ await run(app);
470
+ });
471
+ it("should work with a middleware with functional() dependencies", async () => {
472
+ const task = defineTask({
473
+ id: "task",
474
+ run: async () => "Task executed",
475
+ });
476
+
477
+ const testMiddleware = defineMiddleware({
478
+ id: "test.middleware",
479
+ dependencies: () => ({ task }),
480
+ run: async ({ next }, { task }) => {
481
+ const result = await next();
482
+ expect(result).toBe(await task());
483
+ expect(result).toBe("Task executed");
484
+ return `Middleware: ${result}`;
485
+ },
486
+ });
487
+
488
+ const testTask = defineTask({
489
+ id: "test.task",
490
+ middleware: [testMiddleware],
491
+ run: async () => "Task executed",
492
+ });
493
+
494
+ let allSolved = jest.fn();
495
+ const app = defineResource({
496
+ id: "app",
497
+ register: [testMiddleware, testTask, task],
498
+ dependencies: { testTask },
499
+ async init(_, { testTask }) {
500
+ const result = await testTask();
501
+ expect(result).toBe("Middleware: Task executed");
502
+ allSolved();
503
+ },
504
+ });
505
+
506
+ await run(app);
507
+ expect(allSolved).toHaveBeenCalled();
508
+ });
509
+ });
510
+ it("should be able to register as a function", async () => {
511
+ const mockFn = jest.fn();
512
+ const testResource = defineResource({
513
+ id: "test.resource",
514
+ init: mockFn,
515
+ });
516
+
517
+ const app = defineResource({
518
+ id: "app",
519
+ register: () => [testResource],
520
+ });
521
+
522
+ await run(app);
523
+ expect(mockFn).toHaveBeenCalled();
524
+ });
525
+
526
+ it("should be able to register a dependency via function", async () => {
527
+ const mockFn = jest.fn();
528
+ const testResource = defineResource({
529
+ id: "test.resource",
530
+ init: async () => {
531
+ mockFn();
532
+ return "XXX";
533
+ },
534
+ });
535
+
536
+ const app = defineResource({
537
+ id: "app",
538
+ dependencies: () => ({ testResource }),
539
+ register: () => [testResource],
540
+ async init(_, { testResource }) {
541
+ expect(testResource).toBe("XXX"); // that's what the mock function returns.
542
+ },
543
+ });
544
+
545
+ await run(app);
546
+ expect(mockFn).toHaveBeenCalled();
547
+ });
548
+
549
+ it("resources - should be able to register a dependency via function", async () => {
550
+ const mockFn = jest.fn();
551
+ const testResource = defineResource({
552
+ id: "test.resource",
553
+ init: async () => {
554
+ mockFn();
555
+ return "XXX";
556
+ },
557
+ });
558
+
559
+ const middle = defineResource({
560
+ id: "middle",
561
+ dependencies: () => ({ testResource }),
562
+ register: () => [testResource],
563
+ async init(_, { testResource }) {
564
+ expect(testResource).toBe("XXX");
565
+ return "middle";
566
+ },
567
+ });
568
+
569
+ const app = defineResource({
570
+ id: "app",
571
+ dependencies: () => ({ middle }),
572
+ register: () => [middle],
573
+ async init(_, { middle }) {
574
+ expect(middle).toBe("middle");
575
+ },
576
+ });
577
+
578
+ await run(app);
579
+ expect(mockFn).toHaveBeenCalled();
580
+ });
581
+
582
+ it("tasks - should be able to register a dependency via function", async () => {
583
+ const mockFn = jest.fn();
584
+ const testTask = defineTask({
585
+ id: "test.task",
586
+ run: async () => {
587
+ mockFn();
588
+ return "XXX";
589
+ },
590
+ });
591
+
592
+ const middle = defineTask({
593
+ id: "middle",
594
+ dependencies: () => ({ testTask }),
595
+ async run(_, { testTask }) {
596
+ expect(await testTask()).toBe("XXX");
597
+ return "middle";
598
+ },
599
+ });
600
+
601
+ const app = defineResource({
602
+ id: "app",
603
+ dependencies: () => ({ middle }),
604
+ register: () => [middle, testTask],
605
+ async init(_, { middle }) {
606
+ console.log(middle.toString());
607
+ expect(await middle()).toBe("middle");
608
+ },
609
+ });
610
+
611
+ await run(app);
612
+ expect(mockFn).toHaveBeenCalled();
613
+ });
614
+ });