@bluelibs/runner 2.2.3 → 3.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.
- package/README.md +1315 -942
- 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 +22 -3
- package/dist/define.js +52 -8
- package/dist/define.js.map +1 -1
- package/dist/defs.d.ts +52 -31
- package/dist/defs.js +10 -2
- package/dist/defs.js.map +1 -1
- package/dist/errors.js +1 -1
- 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 +45 -9
- 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 +2 -1
- package/dist/models/DependencyProcessor.js +11 -13
- package/dist/models/DependencyProcessor.js.map +1 -1
- package/dist/models/EventManager.d.ts +5 -0
- package/dist/models/EventManager.js +44 -2
- package/dist/models/EventManager.js.map +1 -1
- package/dist/models/Logger.d.ts +30 -12
- package/dist/models/Logger.js +130 -42
- 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 +20 -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 +17 -72
- 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.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/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 +144 -0
- 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.middleware.test.ts +166 -12
- package/src/__tests__/run.overrides.test.ts +13 -10
- package/src/__tests__/run.test.ts +363 -12
- package/src/__tests__/setOutput.test.ts +244 -0
- package/src/__tests__/tools/getCallerFile.test.ts +9 -9
- package/src/__tests__/typesafety.test.ts +54 -39
- package/src/context.ts +86 -0
- package/src/define.ts +84 -14
- package/src/defs.ts +91 -41
- package/src/errors.ts +3 -1
- 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 +36 -40
- package/src/models/EventManager.ts +45 -5
- package/src/models/Logger.ts +170 -48
- package/src/models/OverrideManager.ts +84 -0
- package/src/models/Queue.ts +66 -0
- package/src/models/ResourceInitializer.ts +38 -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 +217 -0
- package/src/models/StoreTypes.ts +46 -0
- package/src/models/StoreValidator.ts +38 -0
- package/src/models/TaskRunner.ts +53 -40
- package/src/models/index.ts +3 -0
- package/src/run.ts +7 -4
- package/src/__tests__/index.ts +0 -15
- package/src/examples/express-mongo/index.ts +0 -1
|
@@ -5,7 +5,40 @@ import {
|
|
|
5
5
|
defineMiddleware,
|
|
6
6
|
} from "../define";
|
|
7
7
|
import { run } from "../run";
|
|
8
|
-
import { globalResources } from "../globalResources";
|
|
8
|
+
import { globalResources } from "../globals/globalResources";
|
|
9
|
+
|
|
10
|
+
describe("main exports", () => {
|
|
11
|
+
it("should export all public APIs correctly", async () => {
|
|
12
|
+
// Test main index exports for 100% coverage
|
|
13
|
+
const mainExports = await import("../index");
|
|
14
|
+
|
|
15
|
+
expect(typeof mainExports.task).toBe("function");
|
|
16
|
+
expect(typeof mainExports.resource).toBe("function");
|
|
17
|
+
expect(typeof mainExports.event).toBe("function");
|
|
18
|
+
expect(typeof mainExports.middleware).toBe("function");
|
|
19
|
+
expect(typeof mainExports.run).toBe("function");
|
|
20
|
+
expect(typeof mainExports.globals).toBe("object");
|
|
21
|
+
expect(typeof mainExports.definitions).toBe("object");
|
|
22
|
+
expect(typeof mainExports.Store).toBe("function");
|
|
23
|
+
expect(typeof mainExports.EventManager).toBe("function");
|
|
24
|
+
expect(typeof mainExports.TaskRunner).toBe("function");
|
|
25
|
+
|
|
26
|
+
// Test that aliases work the same as direct imports
|
|
27
|
+
const directTask = defineTask({ id: "test", run: async () => "direct" });
|
|
28
|
+
const aliasTask = mainExports.task({
|
|
29
|
+
id: "test2",
|
|
30
|
+
run: async () => "alias",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(directTask.id).toBe("test");
|
|
34
|
+
expect(aliasTask.id).toBe("test2");
|
|
35
|
+
|
|
36
|
+
// Test globals sub-properties for complete coverage
|
|
37
|
+
expect(typeof mainExports.globals.events).toBe("object");
|
|
38
|
+
expect(typeof mainExports.globals.resources).toBe("object");
|
|
39
|
+
expect(typeof mainExports.globals.middlewares).toBe("object");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
9
42
|
|
|
10
43
|
describe("run", () => {
|
|
11
44
|
// Tasks
|
|
@@ -251,22 +284,22 @@ describe("run", () => {
|
|
|
251
284
|
});
|
|
252
285
|
|
|
253
286
|
it("should throw an error if there's an infinite dependency", async () => {
|
|
254
|
-
const task1 = defineTask({
|
|
287
|
+
const task1: any = defineTask({
|
|
255
288
|
id: "task1",
|
|
256
|
-
dependencies: () => ({ task2 }), // Corrected line
|
|
289
|
+
dependencies: (): any => ({ task2 }), // Corrected line
|
|
257
290
|
run: async () => "Task 1",
|
|
258
291
|
});
|
|
259
292
|
|
|
260
|
-
const task2 = defineTask({
|
|
293
|
+
const task2: any = defineTask({
|
|
261
294
|
id: "task2",
|
|
262
295
|
dependencies: { task1 },
|
|
263
296
|
run: async () => "Task 2",
|
|
264
297
|
});
|
|
265
298
|
|
|
266
299
|
// define circular dependency resources
|
|
267
|
-
const resource1 = defineResource({
|
|
300
|
+
const resource1: any = defineResource({
|
|
268
301
|
id: "resource1",
|
|
269
|
-
dependencies: () => ({
|
|
302
|
+
dependencies: (): any => ({
|
|
270
303
|
resource2,
|
|
271
304
|
}),
|
|
272
305
|
init: async () => "Resource 1",
|
|
@@ -532,7 +565,8 @@ describe("run", () => {
|
|
|
532
565
|
},
|
|
533
566
|
});
|
|
534
567
|
|
|
535
|
-
await
|
|
568
|
+
const result = await run(app);
|
|
569
|
+
expect(result.value).toBe("ok");
|
|
536
570
|
expect(supressMock).toHaveBeenCalledTimes(2);
|
|
537
571
|
});
|
|
538
572
|
});
|
|
@@ -653,18 +687,335 @@ describe("run", () => {
|
|
|
653
687
|
const app = defineResource({
|
|
654
688
|
id: "app",
|
|
655
689
|
register: [testResource],
|
|
656
|
-
dependencies: { testResource
|
|
657
|
-
async init(_, { testResource
|
|
690
|
+
dependencies: { testResource },
|
|
691
|
+
async init(_, { testResource }) {
|
|
658
692
|
expect(testResource).toBe("Resource Value");
|
|
693
|
+
return testResource;
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
const result = await run(app);
|
|
698
|
+
expect(result.value).toBe("Resource Value");
|
|
699
|
+
await result.dispose();
|
|
700
|
+
expect(disposeFn).toHaveBeenCalledWith(
|
|
701
|
+
"Resource Value",
|
|
702
|
+
{},
|
|
703
|
+
{},
|
|
704
|
+
undefined
|
|
705
|
+
);
|
|
706
|
+
});
|
|
659
707
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
708
|
+
it("should work with primitive return values", async () => {
|
|
709
|
+
const disposeFn = jest.fn();
|
|
710
|
+
const testResource = defineResource({
|
|
711
|
+
id: "test.resource",
|
|
712
|
+
dispose: disposeFn,
|
|
713
|
+
init: async () => "Resource Value",
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
const app = defineResource({
|
|
717
|
+
id: "app",
|
|
718
|
+
register: [testResource],
|
|
719
|
+
dependencies: { testResource },
|
|
720
|
+
async init(_, { testResource }) {
|
|
721
|
+
return 42; // primitive number
|
|
663
722
|
},
|
|
664
723
|
});
|
|
665
724
|
|
|
666
725
|
const result = await run(app);
|
|
726
|
+
expect(result.value + 1).toBe(43); // should work as number
|
|
727
|
+
expect(result.value).toBe(42);
|
|
667
728
|
await result.dispose();
|
|
729
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it("should work with object return values", async () => {
|
|
733
|
+
const disposeFn = jest.fn();
|
|
734
|
+
const testResource = defineResource({
|
|
735
|
+
id: "test.resource",
|
|
736
|
+
dispose: disposeFn,
|
|
737
|
+
init: async () => "Resource Value",
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
const app = defineResource({
|
|
741
|
+
id: "app",
|
|
742
|
+
register: [testResource],
|
|
743
|
+
dependencies: { testResource },
|
|
744
|
+
async init(_, { testResource }) {
|
|
745
|
+
return { api: "server", value: 42 };
|
|
746
|
+
},
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
const result = await run(app);
|
|
750
|
+
expect(result.value.api).toBe("server");
|
|
751
|
+
expect(result.value.value).toBe(42);
|
|
752
|
+
await result.dispose();
|
|
753
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it("should work with null return values", async () => {
|
|
757
|
+
const disposeFn = jest.fn();
|
|
758
|
+
const testResource = defineResource({
|
|
759
|
+
id: "test.resource",
|
|
760
|
+
dispose: disposeFn,
|
|
761
|
+
init: async () => "Resource Value",
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
const app = defineResource({
|
|
765
|
+
id: "app",
|
|
766
|
+
register: [testResource],
|
|
767
|
+
dependencies: { testResource },
|
|
768
|
+
async init(_, { testResource }) {
|
|
769
|
+
return null; // null return value
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
const result = await run(app);
|
|
774
|
+
expect(result.value).toBe(null);
|
|
775
|
+
await result.dispose();
|
|
776
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
it("should work with undefined return values", async () => {
|
|
780
|
+
const disposeFn = jest.fn();
|
|
781
|
+
const testResource = defineResource({
|
|
782
|
+
id: "test.resource",
|
|
783
|
+
dispose: disposeFn,
|
|
784
|
+
init: async () => "Resource Value",
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const app = defineResource({
|
|
788
|
+
id: "app",
|
|
789
|
+
register: [testResource],
|
|
790
|
+
dependencies: { testResource },
|
|
791
|
+
async init(_, { testResource }) {
|
|
792
|
+
return undefined; // undefined return value
|
|
793
|
+
},
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
const result = await run(app);
|
|
797
|
+
expect(result.value).toBe(undefined);
|
|
798
|
+
await result.dispose();
|
|
799
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it("should work with boolean return values", async () => {
|
|
803
|
+
const disposeFn = jest.fn();
|
|
804
|
+
const testResource = defineResource({
|
|
805
|
+
id: "test.resource",
|
|
806
|
+
dispose: disposeFn,
|
|
807
|
+
init: async () => "Resource Value",
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
const app = defineResource({
|
|
811
|
+
id: "app",
|
|
812
|
+
register: [testResource],
|
|
813
|
+
dependencies: { testResource },
|
|
814
|
+
async init(_, { testResource }) {
|
|
815
|
+
return true; // boolean return value
|
|
816
|
+
},
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
const result = await run(app);
|
|
820
|
+
expect(result.value).toBe(true);
|
|
821
|
+
await result.dispose();
|
|
822
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
it("should forward string methods correctly", async () => {
|
|
826
|
+
const disposeFn = jest.fn();
|
|
827
|
+
const testResource = defineResource({
|
|
828
|
+
id: "test.resource",
|
|
829
|
+
dispose: disposeFn,
|
|
830
|
+
init: async () => "Resource Value",
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
const app = defineResource({
|
|
834
|
+
id: "app",
|
|
835
|
+
register: [testResource],
|
|
836
|
+
dependencies: { testResource },
|
|
837
|
+
async init(_, { testResource }) {
|
|
838
|
+
return "hello world test"; // string return value
|
|
839
|
+
},
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
const result = await run(app);
|
|
843
|
+
expect(result.value).toBe("hello world test");
|
|
844
|
+
await result.dispose();
|
|
845
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it("should work with symbol return values", async () => {
|
|
849
|
+
const disposeFn = jest.fn();
|
|
850
|
+
const testResource = defineResource({
|
|
851
|
+
id: "test.resource",
|
|
852
|
+
dispose: disposeFn,
|
|
853
|
+
init: async () => "Resource Value",
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
const app = defineResource({
|
|
857
|
+
id: "app",
|
|
858
|
+
register: [testResource],
|
|
859
|
+
dependencies: { testResource },
|
|
860
|
+
async init(_, { testResource }) {
|
|
861
|
+
return Symbol("test"); // symbol return value
|
|
862
|
+
},
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
const result = await run(app);
|
|
866
|
+
expect(typeof result.value).toBe("symbol");
|
|
867
|
+
expect(result.value.toString()).toBe("Symbol(test)");
|
|
868
|
+
await result.dispose();
|
|
869
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it("should work with bigint return values", async () => {
|
|
873
|
+
const disposeFn = jest.fn();
|
|
874
|
+
const testResource = defineResource({
|
|
875
|
+
id: "test.resource",
|
|
876
|
+
dispose: disposeFn,
|
|
877
|
+
init: async () => "Resource Value",
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
const app = defineResource({
|
|
881
|
+
id: "app",
|
|
882
|
+
register: [testResource],
|
|
883
|
+
dependencies: { testResource },
|
|
884
|
+
async init(_, { testResource }) {
|
|
885
|
+
return BigInt(123); // bigint return value
|
|
886
|
+
},
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
const result = await run(app);
|
|
890
|
+
expect(typeof result.value).toBe("bigint");
|
|
891
|
+
expect(result.value).toBe(BigInt(123));
|
|
892
|
+
expect(result.value.toString()).toBe("123");
|
|
893
|
+
await result.dispose();
|
|
894
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
describe("private context resources", () => {
|
|
899
|
+
it("should share private context between init and dispose", async () => {
|
|
900
|
+
const disposeFn = jest.fn();
|
|
901
|
+
const dbResource = defineResource({
|
|
902
|
+
id: "db.resource",
|
|
903
|
+
context: () => ({ connections: [] as string[] }),
|
|
904
|
+
async init(config, deps, context) {
|
|
905
|
+
context.connections.push("main-db");
|
|
906
|
+
// @ts-expect-error - should not allow access to non-existent properties
|
|
907
|
+
context.nonExistentProperty;
|
|
908
|
+
// @ts-expect-error - should not allow writing to non-existent properties
|
|
909
|
+
context.anotherProperty = "test";
|
|
910
|
+
return "connected";
|
|
911
|
+
},
|
|
912
|
+
async dispose(value, config, deps, context) {
|
|
913
|
+
expect(context.connections).toEqual(["main-db"]);
|
|
914
|
+
disposeFn();
|
|
915
|
+
|
|
916
|
+
context.connections.length = 0; // cleanup
|
|
917
|
+
// @ts-expect-error - should not allow access to non-existent properties in dispose
|
|
918
|
+
context.undefinedProperty;
|
|
919
|
+
},
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
const app = defineResource({
|
|
923
|
+
id: "app",
|
|
924
|
+
register: [dbResource],
|
|
925
|
+
dependencies: { dbResource },
|
|
926
|
+
async init(_, { dbResource }) {
|
|
927
|
+
return dbResource;
|
|
928
|
+
},
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
const result = await run(app);
|
|
932
|
+
await result.dispose();
|
|
933
|
+
|
|
934
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
it("should work without context", async () => {
|
|
938
|
+
const simpleResource = defineResource({
|
|
939
|
+
id: "simple.resource",
|
|
940
|
+
async init(config, deps) {
|
|
941
|
+
return "simple value";
|
|
942
|
+
},
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
const app = defineResource({
|
|
946
|
+
id: "app",
|
|
947
|
+
register: [simpleResource],
|
|
948
|
+
dependencies: { simpleResource },
|
|
949
|
+
async init(_, { simpleResource }) {
|
|
950
|
+
return simpleResource;
|
|
951
|
+
},
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
const result = await run(app);
|
|
955
|
+
expect(result.value).toBe("simple value");
|
|
956
|
+
await result.dispose();
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it("should work with private context and dispose only", async () => {
|
|
960
|
+
const disposeFn = jest.fn();
|
|
961
|
+
|
|
962
|
+
// Test dispose function with private context but no init
|
|
963
|
+
const contextOnlyResource = defineResource({
|
|
964
|
+
id: "context.only",
|
|
965
|
+
context: () => ({ cleanupTasks: ["task1", "task2"] }),
|
|
966
|
+
// This resource only has dispose, testing the private context in dispose scenario
|
|
967
|
+
dispose: async function (value, config, deps, context) {
|
|
968
|
+
// When there's no init, dispose still gets called but private context should be available
|
|
969
|
+
// Note: This won't have private context since init wasn't called
|
|
970
|
+
disposeFn();
|
|
971
|
+
},
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
const app = defineResource({
|
|
975
|
+
id: "app",
|
|
976
|
+
register: [contextOnlyResource],
|
|
977
|
+
dependencies: { contextOnlyResource },
|
|
978
|
+
async init(_, { contextOnlyResource }) {
|
|
979
|
+
// Resource without init should be undefined
|
|
980
|
+
expect(contextOnlyResource).toBeUndefined();
|
|
981
|
+
return "app started";
|
|
982
|
+
},
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
const result = await run(app);
|
|
986
|
+
expect(result.value).toBe("app started");
|
|
987
|
+
await result.dispose();
|
|
988
|
+
expect(disposeFn).toHaveBeenCalled();
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
it("should handle resources without init method and proper disposal types", async () => {
|
|
992
|
+
const disposeFn = jest.fn();
|
|
993
|
+
|
|
994
|
+
// Resource without init - just registers other resources
|
|
995
|
+
const registrationOnlyResource = defineResource({
|
|
996
|
+
id: "registration.only",
|
|
997
|
+
// No init method
|
|
998
|
+
dispose: disposeFn,
|
|
999
|
+
register: [],
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
const app = defineResource({
|
|
1003
|
+
id: "app",
|
|
1004
|
+
register: [registrationOnlyResource],
|
|
1005
|
+
dependencies: { registrationOnlyResource },
|
|
1006
|
+
async init(_, { registrationOnlyResource }) {
|
|
1007
|
+
// registrationOnlyResource should be undefined since no init
|
|
1008
|
+
expect(registrationOnlyResource).toBeUndefined();
|
|
1009
|
+
return 42;
|
|
1010
|
+
},
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
const result = await run(app);
|
|
1014
|
+
expect(result.value).toBe(42);
|
|
1015
|
+
await result.dispose();
|
|
1016
|
+
|
|
1017
|
+
// dispose should be called with undefined value since no init
|
|
1018
|
+
expect(disposeFn).toHaveBeenCalledWith(undefined, {}, {}, {});
|
|
668
1019
|
});
|
|
669
1020
|
});
|
|
670
1021
|
});
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { defineTask, defineResource } from "../define";
|
|
2
|
+
import { run } from "../run";
|
|
3
|
+
|
|
4
|
+
describe("setOutput functionality", () => {
|
|
5
|
+
it("should allow afterRun event listeners to modify task output", async () => {
|
|
6
|
+
const originalTask = defineTask({
|
|
7
|
+
id: "original.task",
|
|
8
|
+
run: async (input: string) => `Hello, ${input}!`,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const transformListener = defineTask({
|
|
12
|
+
id: "transform.listener",
|
|
13
|
+
on: originalTask.events.afterRun,
|
|
14
|
+
run: async (event) => {
|
|
15
|
+
const transformedOutput = event.data.output.toUpperCase();
|
|
16
|
+
event.data.setOutput(transformedOutput);
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const app = defineResource({
|
|
21
|
+
id: "app",
|
|
22
|
+
register: [originalTask, transformListener],
|
|
23
|
+
dependencies: { originalTask },
|
|
24
|
+
async init(_, { originalTask }) {
|
|
25
|
+
const result = await originalTask("World");
|
|
26
|
+
expect(result).toBe("HELLO, WORLD!");
|
|
27
|
+
return result;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await run(app);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should preserve original output if setOutput is not called", async () => {
|
|
35
|
+
const originalTask = defineTask({
|
|
36
|
+
id: "original.task",
|
|
37
|
+
run: async (input: string) => `Hello, ${input}!`,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const observerListener = defineTask({
|
|
41
|
+
id: "observer.listener",
|
|
42
|
+
on: originalTask.events.afterRun,
|
|
43
|
+
run: async (event) => {
|
|
44
|
+
// Just observe, don't modify
|
|
45
|
+
expect(event.data.output).toBe("Hello, World!");
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const app = defineResource({
|
|
50
|
+
id: "app",
|
|
51
|
+
register: [originalTask, observerListener],
|
|
52
|
+
dependencies: { originalTask },
|
|
53
|
+
async init(_, { originalTask }) {
|
|
54
|
+
const result = await originalTask("World");
|
|
55
|
+
expect(result).toBe("Hello, World!");
|
|
56
|
+
return result;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
await run(app);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should handle multiple afterRun listeners with setOutput (last one wins)", async () => {
|
|
64
|
+
const originalTask = defineTask({
|
|
65
|
+
id: "original.task",
|
|
66
|
+
run: async (input: number) => input * 2,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const firstTransform = defineTask({
|
|
70
|
+
id: "first.transform",
|
|
71
|
+
on: originalTask.events.afterRun,
|
|
72
|
+
listenerOrder: 1,
|
|
73
|
+
run: async (event) => {
|
|
74
|
+
event.data.setOutput(event.data.output + 10);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const secondTransform = defineTask({
|
|
79
|
+
id: "second.transform",
|
|
80
|
+
on: originalTask.events.afterRun,
|
|
81
|
+
listenerOrder: 2,
|
|
82
|
+
run: async (event) => {
|
|
83
|
+
event.data.setOutput(event.data.output * 3);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const app = defineResource({
|
|
88
|
+
id: "app",
|
|
89
|
+
register: [originalTask, firstTransform, secondTransform],
|
|
90
|
+
dependencies: { originalTask },
|
|
91
|
+
async init(_, { originalTask }) {
|
|
92
|
+
const result = await originalTask(5);
|
|
93
|
+
// Original: 5 * 2 = 10
|
|
94
|
+
// First transform: 10 + 10 = 20
|
|
95
|
+
// Second transform: 20 * 3 = 60
|
|
96
|
+
expect(result).toBe(60);
|
|
97
|
+
return result;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await run(app);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should work with external library scenario", async () => {
|
|
105
|
+
// Simulate an external library task
|
|
106
|
+
const externalLibraryTask = defineTask({
|
|
107
|
+
id: "external.library.task",
|
|
108
|
+
run: async (data: { name: string; age: number }) => {
|
|
109
|
+
return {
|
|
110
|
+
id: Math.random(),
|
|
111
|
+
name: data.name,
|
|
112
|
+
age: data.age,
|
|
113
|
+
timestamp: Date.now(),
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Create a transformer for the external task
|
|
119
|
+
const resultTransformer = defineTask({
|
|
120
|
+
id: "result.transformer",
|
|
121
|
+
on: externalLibraryTask.events.afterRun,
|
|
122
|
+
run: async (event) => {
|
|
123
|
+
const result = event.data.output;
|
|
124
|
+
// Add some computed fields
|
|
125
|
+
const enrichedResult = {
|
|
126
|
+
...result,
|
|
127
|
+
displayName: `${result.name} (${result.age} years old)`,
|
|
128
|
+
isAdult: result.age >= 18,
|
|
129
|
+
};
|
|
130
|
+
event.data.setOutput(enrichedResult);
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const app = defineResource({
|
|
135
|
+
id: "app",
|
|
136
|
+
register: [externalLibraryTask, resultTransformer],
|
|
137
|
+
dependencies: { externalLibraryTask },
|
|
138
|
+
async init(_, { externalLibraryTask }) {
|
|
139
|
+
const result = await externalLibraryTask({ name: "Alice", age: 25 });
|
|
140
|
+
|
|
141
|
+
expect(result).toMatchObject({
|
|
142
|
+
name: "Alice",
|
|
143
|
+
age: 25,
|
|
144
|
+
displayName: "Alice (25 years old)",
|
|
145
|
+
isAdult: true,
|
|
146
|
+
});
|
|
147
|
+
expect(result.id).toBeDefined();
|
|
148
|
+
expect(result.timestamp).toBeDefined();
|
|
149
|
+
|
|
150
|
+
return result;
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
await run(app);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("should handle type safety with setOutput", async () => {
|
|
158
|
+
interface TaskOutput {
|
|
159
|
+
message: string;
|
|
160
|
+
count: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const typedTask = defineTask({
|
|
164
|
+
id: "typed.task",
|
|
165
|
+
run: async (input: string): Promise<TaskOutput> => ({
|
|
166
|
+
message: `Hello, ${input}!`,
|
|
167
|
+
count: input.length,
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const typedTransformer = defineTask({
|
|
172
|
+
id: "typed.transformer",
|
|
173
|
+
on: typedTask.events.afterRun,
|
|
174
|
+
run: async (event) => {
|
|
175
|
+
const result = event.data.output;
|
|
176
|
+
const newResult: TaskOutput = {
|
|
177
|
+
message: result.message.toUpperCase(),
|
|
178
|
+
count: result.count * 2,
|
|
179
|
+
};
|
|
180
|
+
event.data.setOutput(newResult);
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const app = defineResource({
|
|
185
|
+
id: "app",
|
|
186
|
+
register: [typedTask, typedTransformer],
|
|
187
|
+
dependencies: { typedTask },
|
|
188
|
+
async init(_, { typedTask }) {
|
|
189
|
+
const result = await typedTask("World");
|
|
190
|
+
expect(result).toEqual({
|
|
191
|
+
message: "HELLO, WORLD!",
|
|
192
|
+
count: 10, // "World".length * 2
|
|
193
|
+
});
|
|
194
|
+
return result;
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await run(app);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should work with middleware and setOutput together", async () => {
|
|
202
|
+
const { defineMiddleware } = await import("../define");
|
|
203
|
+
|
|
204
|
+
const testMiddleware = defineMiddleware({
|
|
205
|
+
id: "test.middleware",
|
|
206
|
+
run: async ({ next, task }) => {
|
|
207
|
+
const result = await next(task?.input);
|
|
208
|
+
return `[middleware: ${result}]`;
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const originalTask = defineTask({
|
|
213
|
+
id: "original.task",
|
|
214
|
+
middleware: [testMiddleware],
|
|
215
|
+
run: async (input: string) => `Hello, ${input}!`,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const outputTransformer = defineTask({
|
|
219
|
+
id: "output.transformer",
|
|
220
|
+
on: originalTask.events.afterRun,
|
|
221
|
+
run: async (event) => {
|
|
222
|
+
// The output here already includes middleware processing
|
|
223
|
+
const transformed = event.data.output.replace(
|
|
224
|
+
"middleware:",
|
|
225
|
+
"processed:"
|
|
226
|
+
);
|
|
227
|
+
event.data.setOutput(transformed);
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const app = defineResource({
|
|
232
|
+
id: "app",
|
|
233
|
+
register: [testMiddleware, originalTask, outputTransformer],
|
|
234
|
+
dependencies: { originalTask },
|
|
235
|
+
async init(_, { originalTask }) {
|
|
236
|
+
const result = await originalTask("World");
|
|
237
|
+
expect(result).toBe("[processed: Hello, World!]");
|
|
238
|
+
return result;
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
await run(app);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
@@ -38,14 +38,14 @@ describe("getCallerFile", () => {
|
|
|
38
38
|
id: "event",
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
expect(task[symbols.filePath]).toBeDefined();
|
|
42
|
-
expect(resource[symbols.filePath]).toBeDefined();
|
|
43
|
-
expect(middleware[symbols.filePath]).toBeDefined();
|
|
44
|
-
expect(event[symbols.filePath]).toBeDefined();
|
|
45
|
-
|
|
46
|
-
expect(task[symbols.filePath]).toContain("getCallerFile.test");
|
|
47
|
-
expect(resource[symbols.filePath]).toContain("getCallerFile.test");
|
|
48
|
-
expect(middleware[symbols.filePath]).toContain("getCallerFile.test");
|
|
49
|
-
expect(event[symbols.filePath]).toContain("getCallerFile.test");
|
|
41
|
+
expect((task as any)[symbols.filePath]).toBeDefined();
|
|
42
|
+
expect((resource as any)[symbols.filePath]).toBeDefined();
|
|
43
|
+
expect((middleware as any)[symbols.filePath]).toBeDefined();
|
|
44
|
+
expect((event as any)[symbols.filePath]).toBeDefined();
|
|
45
|
+
|
|
46
|
+
expect((task as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
47
|
+
expect((resource as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
48
|
+
expect((middleware as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
49
|
+
expect((event as any)[symbols.filePath]).toContain("getCallerFile.test");
|
|
50
50
|
});
|
|
51
51
|
});
|