@bluelibs/runner 3.3.2 → 3.4.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 +437 -33
- package/dist/define.d.ts +5 -5
- package/dist/define.js +22 -2
- package/dist/define.js.map +1 -1
- package/dist/defs.d.ts +55 -21
- package/dist/defs.js.map +1 -1
- package/dist/defs.returnTag.d.ts +36 -0
- package/dist/defs.returnTag.js +4 -0
- package/dist/defs.returnTag.js.map +1 -0
- package/dist/errors.d.ts +60 -10
- package/dist/errors.js +103 -12
- package/dist/errors.js.map +1 -1
- package/dist/globals/globalMiddleware.d.ts +4 -4
- package/dist/globals/globalResources.d.ts +28 -10
- package/dist/globals/middleware/cache.middleware.d.ts +9 -9
- package/dist/globals/resources/queue.resource.d.ts +5 -2
- package/dist/index.d.ts +33 -14
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/models/DependencyProcessor.js +4 -4
- package/dist/models/DependencyProcessor.js.map +1 -1
- package/dist/models/EventManager.js +10 -1
- package/dist/models/EventManager.js.map +1 -1
- package/dist/models/Logger.d.ts +8 -0
- package/dist/models/Logger.js +24 -0
- package/dist/models/Logger.js.map +1 -1
- package/dist/models/OverrideManager.js +1 -1
- package/dist/models/OverrideManager.js.map +1 -1
- package/dist/models/ResourceInitializer.d.ts +2 -2
- package/dist/models/ResourceInitializer.js.map +1 -1
- package/dist/models/Store.d.ts +2 -2
- package/dist/models/Store.js +1 -1
- package/dist/models/Store.js.map +1 -1
- package/dist/models/StoreConstants.d.ts +6 -3
- package/dist/models/StoreRegistry.d.ts +2 -2
- package/dist/models/StoreRegistry.js +1 -1
- package/dist/models/StoreRegistry.js.map +1 -1
- package/dist/models/StoreTypes.d.ts +1 -1
- package/dist/models/StoreValidator.js +5 -5
- package/dist/models/StoreValidator.js.map +1 -1
- package/dist/models/TaskRunner.js +10 -0
- package/dist/models/TaskRunner.js.map +1 -1
- package/dist/run.d.ts +3 -3
- package/dist/run.js +1 -1
- package/dist/run.js.map +1 -1
- package/dist/t1.d.ts +1 -0
- package/dist/t1.js +13 -0
- package/dist/t1.js.map +1 -0
- package/dist/testing.d.ts +1 -1
- package/package.json +2 -2
- package/src/__tests__/errors.test.ts +92 -11
- package/src/__tests__/models/EventManager.test.ts +0 -1
- package/src/__tests__/models/Logger.test.ts +82 -5
- package/src/__tests__/recursion/c.resource.ts +1 -1
- package/src/__tests__/run.overrides.test.ts +3 -3
- package/src/__tests__/typesafety.test.ts +112 -9
- package/src/__tests__/validation-edge-cases.test.ts +111 -0
- package/src/__tests__/validation-interface.test.ts +428 -0
- package/src/define.ts +47 -15
- package/src/defs.returnTag.ts +91 -0
- package/src/defs.ts +84 -27
- package/src/errors.ts +95 -23
- package/src/index.ts +1 -0
- package/src/models/DependencyProcessor.ts +9 -5
- package/src/models/EventManager.ts +12 -3
- package/src/models/Logger.ts +28 -0
- package/src/models/OverrideManager.ts +2 -7
- package/src/models/ResourceInitializer.ts +8 -3
- package/src/models/Store.ts +3 -3
- package/src/models/StoreRegistry.ts +2 -2
- package/src/models/StoreTypes.ts +1 -1
- package/src/models/StoreValidator.ts +6 -6
- package/src/models/TaskRunner.ts +10 -1
- package/src/run.ts +8 -5
- package/src/testing.ts +1 -1
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":";;AAsDA,
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":";;AAsDA,kBA6DC;AAnHD,oDAAiD;AAUjD,sEAAmE;AACnE,wDAAqD;AACrD,yDAAsD;AACtD,0CAAuC;AACvC,+EAA4E;AAC5E,qCAAqD;AACrD,+DAA4D;AAC5D,4CAAyC;AAqClC,KAAK,UAAU,GAAG,CACvB,QAAiE,EACjE,MAAU;IAKV,MAAM,YAAY,GAAG,IAAI,2BAAY,EAAE,CAAC;IAExC,2FAA2F;IAC3F,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,YAAY,CAAC,CAAC;IAExC,MAAM,KAAK,GAAG,IAAI,aAAK,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,uBAAU,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,yCAAmB,CACvC,KAAK,EACL,YAAY,EACZ,UAAU,EACV,MAAM,CACP,CAAC;IAEF,+FAA+F;IAC/F,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,KAAK,CAAC,gBAAgB,CAAC,iCAAe,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,KAAK,CAAC,gBAAgB,CAAC,iCAAe,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpE,kGAAkG;IAClG,MAAM,cAAc,GAAG,KAAK,CAAC,iBAAiB,EAAE,CAAC;IACjD,MAAM,oBAAoB,GAAG,IAAA,mDAAwB,EAAC,cAAc,CAAC,CAAC;IACtE,IAAI,oBAAoB,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,kCAAyB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACnE,CAAC;IAED,sFAAsF;IACtF,MAAM,KAAK,CAAC,gBAAgB,EAAE,CAAC;IAE/B,iGAAiG;IACjG,MAAM,KAAK,CAAC,sBAAsB,EAAE,CAAC;IACrC,MAAM,SAAS,CAAC,eAAe,EAAE,CAAC;IAClC,MAAM,SAAS,CAAC,sBAAsB,EAAE,CAAC;IAEzC,6DAA6D;IAC7D,MAAM,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAE1D,mFAAmF;IACnF,iCAAiC;IACjC,MAAM,YAAY,CAAC,IAAI,CAAC,2BAAY,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEpE,0CAA0C;IAC1C,MAAM,SAAS,CAAC,cAAc,EAAE,CAAC;IAEjC,MAAM,YAAY,CAAC,IAAI,CAAC,2BAAY,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAE1D,0CAA0C;IAC1C,KAAK,CAAC,IAAI,EAAE,CAAC;IAEb,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK;QACvB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE;KAC/B,CAAC;AACJ,CAAC"}
|
package/dist/t1.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/t1.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const define_1 = require("./define");
|
|
4
|
+
const tag = (0, define_1.defineTag)({ id: "usertag" });
|
|
5
|
+
const tag2 = (0, define_1.defineTag)({ id: "usertag2" });
|
|
6
|
+
const tag3 = (0, define_1.defineTag)({ id: "usertag3" });
|
|
7
|
+
// Preserve as a const tuple for type extraction
|
|
8
|
+
const tags = [tag.with({ value: 123 }), tag2];
|
|
9
|
+
// Build runtime meta by spreading the tuple (meta.tags becomes an array)
|
|
10
|
+
const meta = {
|
|
11
|
+
tags: ["string", tag2, tag3, tag.with({ value: 123 })],
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=t1.js.map
|
package/dist/t1.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"t1.js","sourceRoot":"","sources":["../src/t1.ts"],"names":[],"mappings":";;AAAA,qCAAqC;AAgBrC,MAAM,GAAG,GAAG,IAAA,kBAAS,EAA2B,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;AACnE,MAAM,IAAI,GAAG,IAAA,kBAAS,EAAe,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;AACzD,MAAM,IAAI,GAAG,IAAA,kBAAS,EAAa,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;AAEvD,gDAAgD;AAChD,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAU,CAAC;AAEvD,yEAAyE;AACzE,MAAM,IAAI,GAAG;IACX,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;CACvC,CAAC"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { EventManager, Logger, Store, TaskRunner } from "./models";
|
|
|
7
7
|
*/
|
|
8
8
|
export declare function createTestResource(root: RegisterableItems, options?: {
|
|
9
9
|
overrides?: Array<IResource | ITask | IMiddleware | IResourceWithConfig>;
|
|
10
|
-
}): IResource<void, ReturnType<typeof buildTestFacade
|
|
10
|
+
}): IResource<void, Promise<ReturnType<typeof buildTestFacade>>>;
|
|
11
11
|
declare function buildTestFacade(deps: {
|
|
12
12
|
taskRunner: TaskRunner;
|
|
13
13
|
store: Store;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluelibs/runner",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "BlueLibs Runner",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"coverage": "jest --verbose --coverage",
|
|
16
16
|
"test:clean": "jest --clearCache",
|
|
17
17
|
"testonly": "npm test",
|
|
18
|
-
"test:ci": "
|
|
18
|
+
"test:ci": "jest --verbose --coverage --ci --maxWorkers=2 --reporters=default --reporters=jest-junit",
|
|
19
19
|
"prepublishOnly": "npm run build",
|
|
20
20
|
"typedoc": "typedoc --options typedoc.json",
|
|
21
21
|
"benchmark": "jest --testMatch=\"**/__tests__/benchmark/benchmark.test.ts\" --testTimeout 10000"
|
|
@@ -5,7 +5,20 @@ import {
|
|
|
5
5
|
defineMiddleware,
|
|
6
6
|
} from "../define";
|
|
7
7
|
import { run } from "../run";
|
|
8
|
-
import { Errors } from "
|
|
8
|
+
import { Errors } from "..";
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
RuntimeError,
|
|
12
|
+
DuplicateRegistrationError,
|
|
13
|
+
DependencyNotFoundError,
|
|
14
|
+
UnknownItemTypeError,
|
|
15
|
+
CircularDependenciesError,
|
|
16
|
+
EventNotFoundError,
|
|
17
|
+
MiddlewareAlreadyGlobalError,
|
|
18
|
+
LockedError,
|
|
19
|
+
StoreAlreadyInitializedError,
|
|
20
|
+
ValidationError,
|
|
21
|
+
} = Errors;
|
|
9
22
|
|
|
10
23
|
describe("Errors", () => {
|
|
11
24
|
it("should throw duplicateRegistration error", async () => {
|
|
@@ -18,7 +31,7 @@ describe("Errors", () => {
|
|
|
18
31
|
});
|
|
19
32
|
|
|
20
33
|
await expect(run(app)).rejects.toThrow(
|
|
21
|
-
|
|
34
|
+
new DuplicateRegistrationError("Task", "test.task").message
|
|
22
35
|
);
|
|
23
36
|
});
|
|
24
37
|
|
|
@@ -38,7 +51,9 @@ describe("Errors", () => {
|
|
|
38
51
|
},
|
|
39
52
|
});
|
|
40
53
|
|
|
41
|
-
await expect(run(app)).rejects.toThrow(
|
|
54
|
+
await expect(run(app)).rejects.toThrow(
|
|
55
|
+
new UnknownItemTypeError({}).message
|
|
56
|
+
);
|
|
42
57
|
});
|
|
43
58
|
|
|
44
59
|
it("should throw unknown item type error at resource level", async () => {
|
|
@@ -52,7 +67,9 @@ describe("Errors", () => {
|
|
|
52
67
|
async init(_, {}) {},
|
|
53
68
|
});
|
|
54
69
|
|
|
55
|
-
await expect(run(app)).rejects.toThrow(
|
|
70
|
+
await expect(run(app)).rejects.toThrow(
|
|
71
|
+
new UnknownItemTypeError({}).message
|
|
72
|
+
);
|
|
56
73
|
});
|
|
57
74
|
|
|
58
75
|
it("should throw circularDependencies error", async () => {
|
|
@@ -91,7 +108,7 @@ describe("Errors", () => {
|
|
|
91
108
|
});
|
|
92
109
|
|
|
93
110
|
await expect(run(app)).rejects.toThrow(
|
|
94
|
-
|
|
111
|
+
new EventNotFoundError("non.existent.event").message
|
|
95
112
|
);
|
|
96
113
|
});
|
|
97
114
|
|
|
@@ -148,7 +165,7 @@ describe("Errors", () => {
|
|
|
148
165
|
});
|
|
149
166
|
|
|
150
167
|
await expect(run(app)).rejects.toThrow(
|
|
151
|
-
|
|
168
|
+
new DuplicateRegistrationError("Resource", "res1").message
|
|
152
169
|
);
|
|
153
170
|
});
|
|
154
171
|
|
|
@@ -169,7 +186,7 @@ describe("Errors", () => {
|
|
|
169
186
|
});
|
|
170
187
|
|
|
171
188
|
await expect(run(app)).rejects.toThrow(
|
|
172
|
-
|
|
189
|
+
new DuplicateRegistrationError("Middleware", "middlewarex").message
|
|
173
190
|
);
|
|
174
191
|
});
|
|
175
192
|
|
|
@@ -187,7 +204,7 @@ describe("Errors", () => {
|
|
|
187
204
|
});
|
|
188
205
|
|
|
189
206
|
await expect(run(app)).rejects.toThrow(
|
|
190
|
-
|
|
207
|
+
new DuplicateRegistrationError("Event", "ev1").message
|
|
191
208
|
);
|
|
192
209
|
});
|
|
193
210
|
|
|
@@ -216,7 +233,7 @@ describe("Errors", () => {
|
|
|
216
233
|
});
|
|
217
234
|
|
|
218
235
|
await expect(run(app)).rejects.toThrow(
|
|
219
|
-
|
|
236
|
+
new DependencyNotFoundError("Task test.off.the.grid").message
|
|
220
237
|
);
|
|
221
238
|
});
|
|
222
239
|
|
|
@@ -244,7 +261,7 @@ describe("Errors", () => {
|
|
|
244
261
|
});
|
|
245
262
|
|
|
246
263
|
await expect(run(app)).rejects.toThrow(
|
|
247
|
-
|
|
264
|
+
new DependencyNotFoundError("Resource test.off.the.grid").message
|
|
248
265
|
);
|
|
249
266
|
});
|
|
250
267
|
|
|
@@ -254,7 +271,71 @@ describe("Errors", () => {
|
|
|
254
271
|
run: async () => {},
|
|
255
272
|
}).everywhere();
|
|
256
273
|
expect(() => first.everywhere()).toThrow(
|
|
257
|
-
|
|
274
|
+
new MiddlewareAlreadyGlobalError("x").message
|
|
258
275
|
);
|
|
259
276
|
});
|
|
277
|
+
|
|
278
|
+
describe("Error Classes", () => {
|
|
279
|
+
it("should have correct error names and inheritance", () => {
|
|
280
|
+
// Test base RuntimeError
|
|
281
|
+
const baseError = new RuntimeError("test");
|
|
282
|
+
expect(baseError.name).toBe("RuntimeError");
|
|
283
|
+
expect(baseError).toBeInstanceOf(Error);
|
|
284
|
+
expect(baseError).toBeInstanceOf(RuntimeError);
|
|
285
|
+
|
|
286
|
+
// Test DuplicateRegistrationError
|
|
287
|
+
const dupError = new DuplicateRegistrationError("Task", "test");
|
|
288
|
+
expect(dupError.name).toBe("DuplicateRegistrationError");
|
|
289
|
+
expect(dupError).toBeInstanceOf(Error);
|
|
290
|
+
expect(dupError).toBeInstanceOf(RuntimeError);
|
|
291
|
+
expect(dupError).toBeInstanceOf(DuplicateRegistrationError);
|
|
292
|
+
|
|
293
|
+
// Test DependencyNotFoundError
|
|
294
|
+
const depError = new DependencyNotFoundError("test");
|
|
295
|
+
expect(depError.name).toBe("DependencyNotFoundError");
|
|
296
|
+
expect(depError).toBeInstanceOf(RuntimeError);
|
|
297
|
+
|
|
298
|
+
// Test UnknownItemTypeError
|
|
299
|
+
const unknownError = new UnknownItemTypeError("test");
|
|
300
|
+
expect(unknownError.name).toBe("UnknownItemTypeError");
|
|
301
|
+
expect(unknownError).toBeInstanceOf(RuntimeError);
|
|
302
|
+
|
|
303
|
+
// Test CircularDependenciesError
|
|
304
|
+
const circularError = new CircularDependenciesError(["a", "b"]);
|
|
305
|
+
expect(circularError.name).toBe("CircularDependenciesError");
|
|
306
|
+
expect(circularError).toBeInstanceOf(RuntimeError);
|
|
307
|
+
|
|
308
|
+
// Test EventNotFoundError
|
|
309
|
+
const eventError = new EventNotFoundError("test");
|
|
310
|
+
expect(eventError.name).toBe("EventNotFoundError");
|
|
311
|
+
expect(eventError).toBeInstanceOf(RuntimeError);
|
|
312
|
+
|
|
313
|
+
// Test MiddlewareAlreadyGlobalError
|
|
314
|
+
const middlewareError = new MiddlewareAlreadyGlobalError("test");
|
|
315
|
+
expect(middlewareError.name).toBe("MiddlewareAlreadyGlobalError");
|
|
316
|
+
expect(middlewareError).toBeInstanceOf(RuntimeError);
|
|
317
|
+
|
|
318
|
+
// Test LockedError
|
|
319
|
+
const lockedError = new LockedError("test");
|
|
320
|
+
expect(lockedError.name).toBe("LockedError");
|
|
321
|
+
expect(lockedError).toBeInstanceOf(RuntimeError);
|
|
322
|
+
|
|
323
|
+
// Test StoreAlreadyInitializedError
|
|
324
|
+
const storeError = new StoreAlreadyInitializedError();
|
|
325
|
+
expect(storeError.name).toBe("StoreAlreadyInitializedError");
|
|
326
|
+
expect(storeError).toBeInstanceOf(RuntimeError);
|
|
327
|
+
|
|
328
|
+
// Test ValidationError with Error object
|
|
329
|
+
const validationErrorWithError = new ValidationError("Task input", "test-task", new Error("Required field missing"));
|
|
330
|
+
expect(validationErrorWithError.name).toBe("ValidationError");
|
|
331
|
+
expect(validationErrorWithError.message).toBe("Task input validation failed for test-task: Required field missing");
|
|
332
|
+
expect(validationErrorWithError).toBeInstanceOf(RuntimeError);
|
|
333
|
+
|
|
334
|
+
// Test ValidationError with string
|
|
335
|
+
const validationErrorWithString = new ValidationError("Resource config", Symbol("test-resource"), "Invalid configuration");
|
|
336
|
+
expect(validationErrorWithString.name).toBe("ValidationError");
|
|
337
|
+
expect(validationErrorWithString.message).toBe("Resource config validation failed for Symbol(test-resource): Invalid configuration");
|
|
338
|
+
expect(validationErrorWithString).toBeInstanceOf(RuntimeError);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
260
341
|
});
|
|
@@ -5,12 +5,25 @@ import { globalEvents } from "../../globals/globalEvents";
|
|
|
5
5
|
describe("Logger", () => {
|
|
6
6
|
let logger: Logger;
|
|
7
7
|
let mockEventManager: jest.Mocked<EventManager>;
|
|
8
|
+
let originalEnv: any;
|
|
8
9
|
|
|
9
10
|
beforeEach(() => {
|
|
11
|
+
// Save original environment
|
|
12
|
+
originalEnv = { ...process.env };
|
|
13
|
+
|
|
14
|
+
// Clear environment variables that might affect tests
|
|
15
|
+
delete process.env.RUNNER_DISABLE_LOGS;
|
|
16
|
+
delete process.env.RUNNER_LOG_LEVEL;
|
|
17
|
+
|
|
10
18
|
mockEventManager = new EventManager() as jest.Mocked<EventManager>;
|
|
11
19
|
logger = new Logger(mockEventManager);
|
|
12
20
|
});
|
|
13
21
|
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
// Restore original environment
|
|
24
|
+
process.env = originalEnv;
|
|
25
|
+
});
|
|
26
|
+
|
|
14
27
|
describe("log method", () => {
|
|
15
28
|
it("should emit a log event with correct data", async () => {
|
|
16
29
|
const testData = "Test log message";
|
|
@@ -318,7 +331,7 @@ describe("Logger", () => {
|
|
|
318
331
|
it("should use bound context in log calls", async () => {
|
|
319
332
|
const boundContext = { source: "testSource", userId: "123" };
|
|
320
333
|
const loggerWithContext = new Logger(mockEventManager, boundContext);
|
|
321
|
-
|
|
334
|
+
|
|
322
335
|
mockEventManager.emit = jest.fn().mockResolvedValue(undefined);
|
|
323
336
|
mockEventManager.hasListeners = jest.fn().mockReturnValue(true);
|
|
324
337
|
|
|
@@ -388,6 +401,70 @@ describe("Logger", () => {
|
|
|
388
401
|
});
|
|
389
402
|
});
|
|
390
403
|
|
|
404
|
+
describe("default print threshold", () => {
|
|
405
|
+
it("should default to 'info' level when no environment variables are set", () => {
|
|
406
|
+
const logger = new Logger(mockEventManager);
|
|
407
|
+
expect(logger.printThreshold).toBe("info");
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("should respect RUNNER_DISABLE_LOGS=true to disable logging", () => {
|
|
411
|
+
process.env.RUNNER_DISABLE_LOGS = "true";
|
|
412
|
+
const logger = new Logger(mockEventManager);
|
|
413
|
+
expect(logger.printThreshold).toBeNull();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("should respect RUNNER_DISABLE_LOGS=1 to disable logging", () => {
|
|
417
|
+
process.env.RUNNER_DISABLE_LOGS = "1";
|
|
418
|
+
const logger = new Logger(mockEventManager);
|
|
419
|
+
expect(logger.printThreshold).toBeNull();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it("should respect RUNNER_LOG_LEVEL environment variable", () => {
|
|
423
|
+
process.env.RUNNER_LOG_LEVEL = "error";
|
|
424
|
+
const logger = new Logger(mockEventManager);
|
|
425
|
+
expect(logger.printThreshold).toBe("error");
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("should ignore invalid RUNNER_LOG_LEVEL and use default", () => {
|
|
429
|
+
process.env.RUNNER_LOG_LEVEL = "invalid_level";
|
|
430
|
+
const logger = new Logger(mockEventManager);
|
|
431
|
+
expect(logger.printThreshold).toBe("info");
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("should prioritize RUNNER_DISABLE_LOGS over RUNNER_LOG_LEVEL", () => {
|
|
435
|
+
process.env.RUNNER_DISABLE_LOGS = "true";
|
|
436
|
+
process.env.RUNNER_LOG_LEVEL = "debug";
|
|
437
|
+
const logger = new Logger(mockEventManager);
|
|
438
|
+
expect(logger.printThreshold).toBeNull();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("should print info level logs by default", () => {
|
|
442
|
+
const consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
|
|
443
|
+
|
|
444
|
+
const logger = new Logger(mockEventManager);
|
|
445
|
+
logger.log("info", "This should be printed by default");
|
|
446
|
+
|
|
447
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
448
|
+
expect.stringContaining("This should be printed by default")
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
consoleLogSpy.mockRestore();
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("should not print debug level logs by default", () => {
|
|
455
|
+
const consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
|
|
456
|
+
|
|
457
|
+
const logger = new Logger(mockEventManager);
|
|
458
|
+
logger.log("debug", "This should not be printed by default");
|
|
459
|
+
|
|
460
|
+
expect(consoleLogSpy).not.toHaveBeenCalledWith(
|
|
461
|
+
expect.stringContaining("This should not be printed by default")
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
consoleLogSpy.mockRestore();
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
391
468
|
it("should auto-print logs based on autoPrintLogsAfter option", () => {
|
|
392
469
|
const autoPrintLevel: LogLevels = "warn";
|
|
393
470
|
logger.setPrintThreshold(autoPrintLevel);
|
|
@@ -429,14 +506,14 @@ describe("Logger", () => {
|
|
|
429
506
|
|
|
430
507
|
it("should disable auto-printing when setPrintThreshold is set to null", () => {
|
|
431
508
|
const consoleLogSpy = jest.spyOn(console, "log").mockImplementation();
|
|
432
|
-
|
|
509
|
+
|
|
433
510
|
// Set to null to disable auto-printing
|
|
434
511
|
logger.setPrintThreshold(null);
|
|
435
|
-
|
|
512
|
+
|
|
436
513
|
logger.log("error", "Should not be printed");
|
|
437
|
-
|
|
514
|
+
|
|
438
515
|
expect(consoleLogSpy).not.toHaveBeenCalled();
|
|
439
|
-
|
|
516
|
+
|
|
440
517
|
consoleLogSpy.mockRestore();
|
|
441
518
|
});
|
|
442
519
|
});
|
|
@@ -15,4 +15,4 @@ export const cResource = defineResource({
|
|
|
15
15
|
const result: string = await aTask(); // Still benefits of autocompletion
|
|
16
16
|
return `C depends on ${result}`;
|
|
17
17
|
},
|
|
18
|
-
}) as IResource<void, string
|
|
18
|
+
}) as IResource<void, Promise<string>>; // This is the key change.
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
defineMiddleware,
|
|
7
7
|
defineOverride,
|
|
8
8
|
} from "../define";
|
|
9
|
-
import {
|
|
9
|
+
import { DependencyNotFoundError } from "../errors";
|
|
10
10
|
import { run } from "../run";
|
|
11
11
|
|
|
12
12
|
describe("run.overrides", () => {
|
|
@@ -230,7 +230,7 @@ describe("run.overrides", () => {
|
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
await expect(run(app)).rejects.toThrowError(
|
|
233
|
-
|
|
233
|
+
new DependencyNotFoundError("task2").message
|
|
234
234
|
);
|
|
235
235
|
});
|
|
236
236
|
|
|
@@ -256,7 +256,7 @@ describe("run.overrides", () => {
|
|
|
256
256
|
});
|
|
257
257
|
|
|
258
258
|
await expect(run(app)).rejects.toThrowError(
|
|
259
|
-
|
|
259
|
+
new DependencyNotFoundError("override2").message
|
|
260
260
|
);
|
|
261
261
|
});
|
|
262
262
|
|
|
@@ -6,15 +6,12 @@ import {
|
|
|
6
6
|
defineOverride,
|
|
7
7
|
defineTag,
|
|
8
8
|
} from "../define";
|
|
9
|
+
import { IMeta } from "../defs";
|
|
9
10
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ITaskDefinition,
|
|
15
|
-
RegisterableItems,
|
|
16
|
-
} from "../defs";
|
|
17
|
-
import { createTestResource } from "..";
|
|
11
|
+
EnsureResponseSatisfiesContracts,
|
|
12
|
+
HasContracts,
|
|
13
|
+
} from "../defs.returnTag";
|
|
14
|
+
import { createTestResource, run } from "..";
|
|
18
15
|
|
|
19
16
|
// This is skipped because we mostly check typesafety.
|
|
20
17
|
describe.skip("typesafety", () => {
|
|
@@ -244,7 +241,7 @@ describe.skip("typesafety", () => {
|
|
|
244
241
|
const harness = createTestResource(app);
|
|
245
242
|
|
|
246
243
|
// Types: input must match, override deps must match, output is awaited number
|
|
247
|
-
const { value: t } = await
|
|
244
|
+
const { value: t } = await run(harness);
|
|
248
245
|
const r1: number | undefined = await t.runTask(add, { x: 1 });
|
|
249
246
|
// @ts-expect-error wrong input type
|
|
250
247
|
await t.runTask(add, { z: 1 });
|
|
@@ -313,8 +310,114 @@ describe.skip("typesafety", () => {
|
|
|
313
310
|
tag3,
|
|
314
311
|
],
|
|
315
312
|
},
|
|
313
|
+
run: async (input) => {
|
|
314
|
+
return input;
|
|
315
|
+
},
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
expect(true).toBe(true);
|
|
319
319
|
});
|
|
320
|
+
|
|
321
|
+
it("should enforce contracts on tasks", async () => {
|
|
322
|
+
interface IUser {
|
|
323
|
+
name: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
interface IOther {
|
|
327
|
+
age: number;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const tag = defineTag<{ value: number }, IUser>({ id: "tag" });
|
|
331
|
+
const tag2 = defineTag<void, IOther>({ id: "tag2" });
|
|
332
|
+
|
|
333
|
+
const meta = {
|
|
334
|
+
tags: [tag.with({ value: 123 }), tag2, "string"],
|
|
335
|
+
} satisfies IMeta;
|
|
336
|
+
|
|
337
|
+
const response = {
|
|
338
|
+
age: 123,
|
|
339
|
+
name: "123", // intentional
|
|
340
|
+
};
|
|
341
|
+
type TEST = HasContracts<typeof meta>;
|
|
342
|
+
type TEST2 = EnsureResponseSatisfiesContracts<typeof meta, typeof response>;
|
|
343
|
+
|
|
344
|
+
const task = defineTask({
|
|
345
|
+
id: "task",
|
|
346
|
+
meta,
|
|
347
|
+
run: async (input: { name: string }) => {
|
|
348
|
+
return {
|
|
349
|
+
age: 123,
|
|
350
|
+
name: "123",
|
|
351
|
+
};
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
const task2 = defineTask({
|
|
355
|
+
id: "task",
|
|
356
|
+
meta,
|
|
357
|
+
// @ts-expect-error
|
|
358
|
+
run: async (input: { name: string }) => {
|
|
359
|
+
return {
|
|
360
|
+
age: "123",
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const task3 = defineTask({
|
|
366
|
+
id: "task",
|
|
367
|
+
meta,
|
|
368
|
+
// @ts-expect-error
|
|
369
|
+
run: async (input: { name: string }) => {
|
|
370
|
+
return {};
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("should enforce contracts on resources", async () => {
|
|
376
|
+
interface IUser {
|
|
377
|
+
name: string;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
interface IOther {
|
|
381
|
+
age: number;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const tag = defineTag<{ value: number }, IUser>({ id: "tag" });
|
|
385
|
+
const tag2 = defineTag<void, IOther>({ id: "tag2" });
|
|
386
|
+
|
|
387
|
+
const meta = {
|
|
388
|
+
tags: [tag.with({ value: 123 }), tag2, "string"],
|
|
389
|
+
} satisfies IMeta;
|
|
390
|
+
|
|
391
|
+
const resourceOk = defineResource({
|
|
392
|
+
id: "resource.ok",
|
|
393
|
+
meta,
|
|
394
|
+
init: async () => {
|
|
395
|
+
return {
|
|
396
|
+
age: 123,
|
|
397
|
+
name: "123",
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const resourceBad1 = defineResource({
|
|
403
|
+
id: "resource.bad1",
|
|
404
|
+
meta,
|
|
405
|
+
// @ts-expect-error
|
|
406
|
+
init: async () => {
|
|
407
|
+
return {
|
|
408
|
+
age: "123",
|
|
409
|
+
name: "123",
|
|
410
|
+
};
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const resourceBad2 = defineResource({
|
|
415
|
+
id: "resource.bad2",
|
|
416
|
+
meta,
|
|
417
|
+
// @ts-expect-error
|
|
418
|
+
init: async () => {
|
|
419
|
+
return {};
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
});
|
|
320
423
|
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { defineTask, defineResource, defineEvent, defineMiddleware } from "../define";
|
|
2
|
+
import { run } from "../run";
|
|
3
|
+
import { ValidationError } from "../errors";
|
|
4
|
+
import { IValidationSchema } from "../defs";
|
|
5
|
+
|
|
6
|
+
// Mock validation schema similar to the existing pattern
|
|
7
|
+
class MockValidationSchema<T> implements IValidationSchema<T> {
|
|
8
|
+
constructor(
|
|
9
|
+
private validator: (input: unknown) => T,
|
|
10
|
+
) {}
|
|
11
|
+
|
|
12
|
+
parse(input: unknown): T {
|
|
13
|
+
return this.validator(input);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("Validation Edge Cases", () => {
|
|
18
|
+
it("should handle non-Error thrown from task input validation", async () => {
|
|
19
|
+
const taskSchema = new MockValidationSchema<string>((input: unknown) => {
|
|
20
|
+
// Throw a non-Error object to trigger the instanceof Error === false branch
|
|
21
|
+
throw "Non-error string thrown";
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const task = defineTask({
|
|
25
|
+
id: "task.nonErrorValidation",
|
|
26
|
+
inputSchema: taskSchema,
|
|
27
|
+
run: async (input: string) => "success",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const app = defineResource({
|
|
31
|
+
id: "app",
|
|
32
|
+
register: [task],
|
|
33
|
+
dependencies: { task },
|
|
34
|
+
init: async (_, { task }) => {
|
|
35
|
+
await task("invalid");
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await expect(run(app)).rejects.toThrow(ValidationError);
|
|
40
|
+
await expect(run(app)).rejects.toThrow("Task input validation failed for task.nonErrorValidation: Non-error string thrown");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should handle non-Error thrown from resource config validation", async () => {
|
|
44
|
+
const configSchema = new MockValidationSchema<any>((input: unknown) => {
|
|
45
|
+
throw "Resource config error string";
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const resource = defineResource({
|
|
49
|
+
id: "resource.nonErrorValidation",
|
|
50
|
+
configSchema: configSchema,
|
|
51
|
+
init: async (config: any) => "success",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(() => {
|
|
55
|
+
resource.with({ invalid: "config" } as any);
|
|
56
|
+
}).toThrow(ValidationError);
|
|
57
|
+
expect(() => {
|
|
58
|
+
resource.with({ invalid: "config" } as any);
|
|
59
|
+
}).toThrow("Resource config validation failed for resource.nonErrorValidation: Resource config error string");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle non-Error thrown from middleware config validation", async () => {
|
|
63
|
+
const configSchema = new MockValidationSchema<any>((input: unknown) => {
|
|
64
|
+
throw "Middleware config error string";
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const middleware = defineMiddleware({
|
|
68
|
+
id: "middleware.nonErrorValidation",
|
|
69
|
+
configSchema: configSchema,
|
|
70
|
+
run: async ({ next }) => next(),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(() => {
|
|
74
|
+
middleware.with({ invalid: "config" } as any);
|
|
75
|
+
}).toThrow(ValidationError);
|
|
76
|
+
expect(() => {
|
|
77
|
+
middleware.with({ invalid: "config" } as any);
|
|
78
|
+
}).toThrow("Middleware config validation failed for middleware.nonErrorValidation: Middleware config error string");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should handle non-Error thrown from event payload validation", async () => {
|
|
82
|
+
const payloadSchema = new MockValidationSchema<any>((input: unknown) => {
|
|
83
|
+
throw "Event payload error string";
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const event = defineEvent({
|
|
87
|
+
id: "event.nonErrorValidation",
|
|
88
|
+
payloadSchema: payloadSchema,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const listenerTask = defineTask({
|
|
92
|
+
id: "task.listener",
|
|
93
|
+
on: event,
|
|
94
|
+
run: async (event) => {
|
|
95
|
+
// This won't be called because validation will fail
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const app = defineResource({
|
|
100
|
+
id: "app",
|
|
101
|
+
register: [event, listenerTask],
|
|
102
|
+
dependencies: { event },
|
|
103
|
+
init: async (_, { event }) => {
|
|
104
|
+
await event({ invalid: "payload" });
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
await expect(run(app)).rejects.toThrow(ValidationError);
|
|
109
|
+
await expect(run(app)).rejects.toThrow("Event payload validation failed for event.nonErrorValidation: Event payload error string");
|
|
110
|
+
});
|
|
111
|
+
});
|