@bluelibs/runner 3.3.2 → 3.4.1

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 (76) hide show
  1. package/README.md +491 -74
  2. package/dist/define.d.ts +5 -5
  3. package/dist/define.js +22 -2
  4. package/dist/define.js.map +1 -1
  5. package/dist/defs.d.ts +55 -21
  6. package/dist/defs.js.map +1 -1
  7. package/dist/defs.returnTag.d.ts +36 -0
  8. package/dist/defs.returnTag.js +4 -0
  9. package/dist/defs.returnTag.js.map +1 -0
  10. package/dist/errors.d.ts +60 -10
  11. package/dist/errors.js +103 -12
  12. package/dist/errors.js.map +1 -1
  13. package/dist/globals/globalMiddleware.d.ts +4 -4
  14. package/dist/globals/globalResources.d.ts +28 -10
  15. package/dist/globals/middleware/cache.middleware.d.ts +9 -9
  16. package/dist/globals/resources/queue.resource.d.ts +5 -2
  17. package/dist/index.d.ts +33 -14
  18. package/dist/index.js +2 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/models/DependencyProcessor.js +4 -4
  21. package/dist/models/DependencyProcessor.js.map +1 -1
  22. package/dist/models/EventManager.js +10 -1
  23. package/dist/models/EventManager.js.map +1 -1
  24. package/dist/models/Logger.d.ts +8 -0
  25. package/dist/models/Logger.js +24 -0
  26. package/dist/models/Logger.js.map +1 -1
  27. package/dist/models/OverrideManager.js +1 -1
  28. package/dist/models/OverrideManager.js.map +1 -1
  29. package/dist/models/ResourceInitializer.d.ts +2 -2
  30. package/dist/models/ResourceInitializer.js.map +1 -1
  31. package/dist/models/Store.d.ts +5 -3
  32. package/dist/models/Store.js +7 -1
  33. package/dist/models/Store.js.map +1 -1
  34. package/dist/models/StoreConstants.d.ts +6 -3
  35. package/dist/models/StoreRegistry.d.ts +5 -3
  36. package/dist/models/StoreRegistry.js +17 -1
  37. package/dist/models/StoreRegistry.js.map +1 -1
  38. package/dist/models/StoreTypes.d.ts +1 -1
  39. package/dist/models/StoreValidator.js +5 -5
  40. package/dist/models/StoreValidator.js.map +1 -1
  41. package/dist/models/TaskRunner.js +10 -0
  42. package/dist/models/TaskRunner.js.map +1 -1
  43. package/dist/run.d.ts +3 -3
  44. package/dist/run.js +1 -1
  45. package/dist/run.js.map +1 -1
  46. package/dist/t1.d.ts +1 -0
  47. package/dist/t1.js +13 -0
  48. package/dist/t1.js.map +1 -0
  49. package/dist/testing.d.ts +1 -1
  50. package/package.json +2 -2
  51. package/src/__tests__/errors.test.ts +92 -11
  52. package/src/__tests__/models/EventManager.test.ts +0 -1
  53. package/src/__tests__/models/Logger.test.ts +82 -5
  54. package/src/__tests__/models/Store.test.ts +57 -0
  55. package/src/__tests__/recursion/c.resource.ts +1 -1
  56. package/src/__tests__/run.overrides.test.ts +3 -3
  57. package/src/__tests__/typesafety.test.ts +112 -9
  58. package/src/__tests__/validation-edge-cases.test.ts +111 -0
  59. package/src/__tests__/validation-interface.test.ts +428 -0
  60. package/src/define.ts +47 -15
  61. package/src/defs.returnTag.ts +91 -0
  62. package/src/defs.ts +84 -27
  63. package/src/errors.ts +95 -23
  64. package/src/index.ts +1 -0
  65. package/src/models/DependencyProcessor.ts +9 -5
  66. package/src/models/EventManager.ts +12 -3
  67. package/src/models/Logger.ts +28 -0
  68. package/src/models/OverrideManager.ts +2 -7
  69. package/src/models/ResourceInitializer.ts +8 -3
  70. package/src/models/Store.ts +12 -3
  71. package/src/models/StoreRegistry.ts +27 -2
  72. package/src/models/StoreTypes.ts +1 -1
  73. package/src/models/StoreValidator.ts +6 -6
  74. package/src/models/TaskRunner.ts +10 -1
  75. package/src/run.ts +8 -5
  76. package/src/testing.ts +1 -1
@@ -6,15 +6,12 @@ import {
6
6
  defineOverride,
7
7
  defineTag,
8
8
  } from "../define";
9
+ import { IMeta } from "../defs";
9
10
  import {
10
- IEventDefinition,
11
- IMiddlewareDefinition,
12
- IResource,
13
- IResourceWithConfig,
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 (await import("../run")).run(harness);
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
+ });
@@ -0,0 +1,428 @@
1
+ import { defineTask, defineResource, defineEvent, defineMiddleware } from "../define";
2
+ import { run } from "../run";
3
+ import { IValidationSchema } from "../defs";
4
+
5
+ // Simple mock validation schemas for testing the interface
6
+ class MockValidationSchema<T> implements IValidationSchema<T> {
7
+ constructor(
8
+ private validator: (input: unknown) => T,
9
+ private errorMessage?: string
10
+ ) {}
11
+
12
+ parse(input: unknown): T {
13
+ try {
14
+ return this.validator(input);
15
+ } catch (error) {
16
+ throw new Error(this.errorMessage || "Validation failed");
17
+ }
18
+ }
19
+ }
20
+
21
+ // Helper functions to create mock schemas similar to Zod
22
+ const mockSchema = {
23
+ object: <T extends Record<string, any>>(shape: Record<keyof T, string>): IValidationSchema<T> => {
24
+ return new MockValidationSchema((input: unknown) => {
25
+ if (typeof input !== "object" || input === null) {
26
+ throw new Error("Expected object");
27
+ }
28
+ const obj = input as any;
29
+ for (const [key, expectedType] of Object.entries(shape)) {
30
+ if (expectedType === "string" && typeof obj[key] !== "string") {
31
+ throw new Error(`${key} must be string`);
32
+ }
33
+ if (expectedType === "number" && typeof obj[key] !== "number") {
34
+ throw new Error(`${key} must be number`);
35
+ }
36
+ if (expectedType === "boolean" && typeof obj[key] !== "boolean") {
37
+ throw new Error(`${key} must be boolean`);
38
+ }
39
+ }
40
+ return obj as T;
41
+ });
42
+ },
43
+
44
+ string: (): IValidationSchema<string> => {
45
+ return new MockValidationSchema((input: unknown) => {
46
+ if (typeof input !== "string") {
47
+ throw new Error("Expected string");
48
+ }
49
+ return input;
50
+ });
51
+ },
52
+
53
+ number: (): IValidationSchema<number> => {
54
+ return new MockValidationSchema((input: unknown) => {
55
+ if (typeof input !== "number") {
56
+ throw new Error("Expected number");
57
+ }
58
+ return input;
59
+ });
60
+ },
61
+
62
+ boolean: (): IValidationSchema<boolean> => {
63
+ return new MockValidationSchema((input: unknown) => {
64
+ if (typeof input !== "boolean") {
65
+ throw new Error("Expected boolean");
66
+ }
67
+ return input;
68
+ });
69
+ },
70
+
71
+ transform: <T, U>(schema: IValidationSchema<T>, transformer: (value: T) => U): IValidationSchema<U> => {
72
+ return new MockValidationSchema((input: unknown) => {
73
+ const validated = schema.parse(input);
74
+ return transformer(validated);
75
+ });
76
+ },
77
+
78
+ withDefaults: <T>(defaultValue: T): IValidationSchema<T> => {
79
+ return new MockValidationSchema((input: unknown) => {
80
+ return input === undefined ? defaultValue : (input as T);
81
+ });
82
+ },
83
+ };
84
+
85
+ describe("Generic Validation Interface", () => {
86
+ describe("Task Input Validation", () => {
87
+ it("should validate task input successfully with valid data", async () => {
88
+ const userSchema = mockSchema.object<{
89
+ name: string;
90
+ age: number;
91
+ }>({
92
+ name: "string",
93
+ age: "number",
94
+ });
95
+
96
+ const createUserTask = defineTask({
97
+ id: "task.createUser",
98
+ inputSchema: userSchema,
99
+ run: async (input) => {
100
+ return `Created user ${(input as any).name}`;
101
+ },
102
+ });
103
+
104
+ const app = defineResource({
105
+ id: "app",
106
+ register: [createUserTask],
107
+ dependencies: { createUserTask },
108
+ init: async (_, { createUserTask }) => {
109
+ const result = await createUserTask({
110
+ name: "John Doe",
111
+ age: 30,
112
+ });
113
+ expect(result).toBe("Created user John Doe");
114
+ return result;
115
+ },
116
+ });
117
+
118
+ await run(app);
119
+ });
120
+
121
+ it("should throw validation error for invalid task input", async () => {
122
+ const userSchema = new MockValidationSchema((input: unknown) => {
123
+ if (typeof input !== "object" || input === null) {
124
+ throw new Error("Expected object");
125
+ }
126
+ const obj = input as any;
127
+ if (typeof obj.name !== "string") {
128
+ throw new Error("name must be string");
129
+ }
130
+ if (typeof obj.age !== "number" || obj.age < 0) {
131
+ throw new Error("age must be positive number");
132
+ }
133
+ return obj;
134
+ });
135
+
136
+ const createUserTask = defineTask({
137
+ id: "task.createUser.invalid",
138
+ inputSchema: userSchema,
139
+ run: async (input) => {
140
+ return `Created user ${(input as any).name}`;
141
+ },
142
+ });
143
+
144
+ const app = defineResource({
145
+ id: "app",
146
+ register: [createUserTask],
147
+ dependencies: { createUserTask },
148
+ init: async (_, { createUserTask }) => {
149
+ await createUserTask({
150
+ name: "John Doe",
151
+ age: -5, // Invalid: negative age
152
+ });
153
+ },
154
+ });
155
+
156
+ await expect(run(app)).rejects.toThrow(/Task input validation failed/);
157
+ });
158
+
159
+ it("should transform input data when schema supports it", async () => {
160
+ const stringToNumberSchema = mockSchema.transform(
161
+ mockSchema.string(),
162
+ (val: string) => parseInt(val, 10)
163
+ );
164
+
165
+ const mathTask = defineTask({
166
+ id: "task.math",
167
+ inputSchema: stringToNumberSchema,
168
+ run: async (input: number) => {
169
+ expect(typeof input).toBe("number");
170
+ return input * 2;
171
+ },
172
+ });
173
+
174
+ const app = defineResource({
175
+ id: "app",
176
+ register: [mathTask],
177
+ dependencies: { mathTask },
178
+ init: async (_, { mathTask }) => {
179
+ const result = await (mathTask as any)("42"); // String input should be transformed to number
180
+ expect(result).toBe(84);
181
+ return result;
182
+ },
183
+ });
184
+
185
+ await run(app);
186
+ });
187
+ });
188
+
189
+ describe("Resource Config Validation", () => {
190
+ it("should validate resource config when .with() is called (fail fast)", async () => {
191
+ const configSchema = new MockValidationSchema((input: unknown) => {
192
+ if (typeof input !== "object" || input === null) {
193
+ throw new Error("Expected object");
194
+ }
195
+ const obj = input as any;
196
+ if (typeof obj.host !== "string") {
197
+ throw new Error("host must be string");
198
+ }
199
+ if (typeof obj.port !== "number" || obj.port < 1 || obj.port > 65535) {
200
+ throw new Error("port must be number between 1-65535");
201
+ }
202
+ return obj;
203
+ });
204
+
205
+ const databaseResource = defineResource({
206
+ id: "resource.database",
207
+ configSchema: configSchema,
208
+ init: async (config) => {
209
+ return {
210
+ connect: () => `Connected to ${(config as any).host}:${(config as any).port}`,
211
+ };
212
+ },
213
+ });
214
+
215
+ // This should throw immediately when .with() is called, not during init
216
+ expect(() => {
217
+ databaseResource.with({
218
+ host: "localhost",
219
+ port: 99999, // Invalid: port too high
220
+ });
221
+ }).toThrow(/Resource config validation failed/);
222
+ });
223
+
224
+ it("should work with valid resource config", async () => {
225
+ const configSchema = mockSchema.object<{
226
+ host: string;
227
+ port: number;
228
+ }>({
229
+ host: "string",
230
+ port: "number",
231
+ });
232
+
233
+ const databaseResource = defineResource({
234
+ id: "resource.database.valid",
235
+ configSchema: configSchema,
236
+ init: async (config) => {
237
+ return {
238
+ connect: () => `Connected to ${(config as any).host}:${(config as any).port}`,
239
+ };
240
+ },
241
+ });
242
+
243
+ const app = defineResource({
244
+ id: "app",
245
+ register: [
246
+ databaseResource.with({
247
+ host: "localhost",
248
+ port: 5432,
249
+ }),
250
+ ],
251
+ dependencies: { database: databaseResource },
252
+ init: async (_, { database }) => {
253
+ const result = database.connect();
254
+ expect(result).toBe("Connected to localhost:5432");
255
+ return result;
256
+ },
257
+ });
258
+
259
+ await run(app);
260
+ });
261
+ });
262
+
263
+ describe("Event Payload Validation", () => {
264
+ it("should validate event payload when emitted", async () => {
265
+ const payloadSchema = new MockValidationSchema((input: unknown) => {
266
+ if (typeof input !== "object" || input === null) {
267
+ throw new Error("Expected object");
268
+ }
269
+ const obj = input as any;
270
+ if (typeof obj.message !== "string") {
271
+ throw new Error("message must be string");
272
+ }
273
+ return obj;
274
+ });
275
+
276
+ const testEvent = defineEvent({
277
+ id: "event.test",
278
+ payloadSchema: payloadSchema,
279
+ });
280
+
281
+ let receivedMessage = "";
282
+ const listenerTask = defineTask({
283
+ id: "task.listener",
284
+ on: testEvent,
285
+ run: async (event) => {
286
+ receivedMessage = (event.data as any).message;
287
+ },
288
+ });
289
+
290
+ const app = defineResource({
291
+ id: "app",
292
+ register: [testEvent, listenerTask],
293
+ dependencies: { testEvent },
294
+ init: async (_, { testEvent }) => {
295
+ // This should work with valid payload
296
+ await testEvent({ message: "Hello World" });
297
+ expect(receivedMessage).toBe("Hello World");
298
+
299
+ // This should throw with invalid payload
300
+ await expect(testEvent({ invalidField: 123 } as any)).rejects.toThrow(/Event payload validation failed/);
301
+ },
302
+ });
303
+
304
+ await run(app);
305
+ });
306
+ });
307
+
308
+ describe("Middleware Config Validation", () => {
309
+ it("should validate middleware config when .with() is called (fail fast)", async () => {
310
+ const configSchema = new MockValidationSchema((input: unknown) => {
311
+ if (typeof input !== "object" || input === null) {
312
+ throw new Error("Expected object");
313
+ }
314
+ const obj = input as any;
315
+ if (typeof obj.timeout !== "number" || obj.timeout <= 0) {
316
+ throw new Error("timeout must be positive number");
317
+ }
318
+ return obj;
319
+ });
320
+
321
+ const timingMiddleware = defineMiddleware({
322
+ id: "middleware.timing",
323
+ configSchema: configSchema,
324
+ run: async ({ next }, _, config) => {
325
+ return next();
326
+ },
327
+ });
328
+
329
+ // This should throw immediately when .with() is called
330
+ expect(() => {
331
+ timingMiddleware.with({
332
+ timeout: -5, // Invalid: negative timeout
333
+ });
334
+ }).toThrow(/Middleware config validation failed/);
335
+ });
336
+
337
+ it("should work with valid middleware config", async () => {
338
+ const configSchema = mockSchema.object<{
339
+ timeout: number;
340
+ }>({
341
+ timeout: "number",
342
+ });
343
+
344
+ const timingMiddleware = defineMiddleware({
345
+ id: "middleware.timing.valid",
346
+ configSchema: configSchema,
347
+ run: async ({ next }, _, config) => {
348
+ const start = Date.now();
349
+ const result = await next();
350
+ const duration = Date.now() - start;
351
+ expect(typeof (config as any).timeout).toBe("number");
352
+ return result;
353
+ },
354
+ });
355
+
356
+ const testTask = defineTask({
357
+ id: "task.test",
358
+ middleware: [timingMiddleware.with({ timeout: 5000 })],
359
+ run: async () => {
360
+ return "success";
361
+ },
362
+ });
363
+
364
+ const app = defineResource({
365
+ id: "app",
366
+ register: [timingMiddleware, testTask],
367
+ dependencies: { testTask },
368
+ init: async (_, { testTask }) => {
369
+ const result = await testTask();
370
+ expect(result).toBe("success");
371
+ return result;
372
+ },
373
+ });
374
+
375
+ await run(app);
376
+ });
377
+ });
378
+
379
+ describe("No Validation (Backward Compatibility)", () => {
380
+ it("should work without any validation schemas", async () => {
381
+ const task = defineTask({
382
+ id: "task.noValidation",
383
+ run: async (input: any) => {
384
+ return `Received: ${JSON.stringify(input)}`;
385
+ },
386
+ });
387
+
388
+ const resource = defineResource({
389
+ id: "resource.noValidation",
390
+ init: async (config: any) => {
391
+ return { config };
392
+ },
393
+ });
394
+
395
+ const event = defineEvent<any>({
396
+ id: "event.noValidation",
397
+ });
398
+
399
+ const middleware = defineMiddleware({
400
+ id: "middleware.noValidation",
401
+ run: async ({ next }) => next(),
402
+ });
403
+
404
+ const app = defineResource({
405
+ id: "app",
406
+ register: [
407
+ task,
408
+ resource.with({ anything: "goes" }),
409
+ event,
410
+ middleware,
411
+ ],
412
+ dependencies: { task, resource, event },
413
+ init: async (_, { task, resource, event }) => {
414
+ const taskResult = await task({ any: "data" });
415
+ expect(taskResult).toBe('Received: {"any":"data"}');
416
+
417
+ expect(resource.config.anything).toBe("goes");
418
+
419
+ await event({ any: "payload" }); // Should work without validation
420
+
421
+ return "success";
422
+ },
423
+ });
424
+
425
+ await run(app);
426
+ });
427
+ });
428
+ });