@ontrails/core 1.0.0-beta.1 → 1.0.0-beta.11
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +121 -0
- package/README.md +54 -11
- package/dist/context.d.ts +2 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +12 -7
- package/dist/context.js.map +1 -1
- package/dist/derive.d.ts +1 -1
- package/dist/derive.d.ts.map +1 -1
- package/dist/derive.js +4 -1
- package/dist/derive.js.map +1 -1
- package/dist/dispatch.d.ts +27 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +34 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/event.d.ts +2 -2
- package/dist/event.d.ts.map +1 -1
- package/dist/event.js +1 -1
- package/dist/event.js.map +1 -1
- package/dist/execute.d.ts +33 -0
- package/dist/execute.d.ts.map +1 -0
- package/dist/execute.js +207 -0
- package/dist/execute.js.map +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/patterns/status.d.ts +1 -1
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +15 -4
- package/dist/result.js.map +1 -1
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +45 -7
- package/dist/serialization.js.map +1 -1
- package/dist/service.d.ts +69 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +56 -0
- package/dist/service.js.map +1 -0
- package/dist/topo.d.ts +11 -4
- package/dist/topo.d.ts.map +1 -1
- package/dist/topo.js +43 -18
- package/dist/topo.js.map +1 -1
- package/dist/trail.d.ts +21 -10
- package/dist/trail.d.ts.map +1 -1
- package/dist/trail.js +5 -2
- package/dist/trail.js.map +1 -1
- package/dist/type-utils.d.ts +24 -0
- package/dist/type-utils.d.ts.map +1 -0
- package/dist/type-utils.js +12 -0
- package/dist/type-utils.js.map +1 -0
- package/dist/types.d.ts +10 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-topo.d.ts +2 -2
- package/dist/validate-topo.d.ts.map +1 -1
- package/dist/validate-topo.js +75 -9
- package/dist/validate-topo.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +34 -3
- package/dist/validation.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/context.test.ts +16 -5
- package/src/__tests__/derive.test.ts +44 -0
- package/src/__tests__/dispatch.test.ts +181 -0
- package/src/__tests__/event.test.ts +5 -5
- package/src/__tests__/execute.test.ts +523 -0
- package/src/__tests__/layer.test.ts +14 -113
- package/src/__tests__/serialization.test.ts +166 -1
- package/src/__tests__/service.test.ts +197 -0
- package/src/__tests__/topo.test.ts +171 -78
- package/src/__tests__/trail.test.ts +119 -37
- package/src/__tests__/type-utils.test.ts +90 -0
- package/src/__tests__/validate-topo.test.ts +140 -19
- package/src/__tests__/validation.test.ts +53 -0
- package/src/context.ts +18 -9
- package/src/derive.ts +12 -2
- package/src/dispatch.ts +54 -0
- package/src/event.ts +3 -3
- package/src/execute.ts +345 -0
- package/src/index.ts +39 -18
- package/src/result.ts +18 -4
- package/src/serialization.ts +56 -11
- package/src/service.ts +139 -0
- package/src/topo.ts +66 -27
- package/src/trail.ts +36 -13
- package/src/type-utils.ts +45 -0
- package/src/types.ts +11 -2
- package/src/validate-topo.ts +92 -10
- package/src/validation.ts +35 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/hike.d.ts +0 -36
- package/dist/hike.d.ts.map +0 -1
- package/dist/hike.js +0 -20
- package/dist/hike.js.map +0 -1
- package/src/__tests__/hike.test.ts +0 -117
- package/src/__tests__/job.test.ts +0 -98
- package/src/adapters.ts +0 -68
- package/src/health.ts +0 -23
- package/src/hike.ts +0 -77
- package/src/job.ts +0 -20
package/dist/validation.js
CHANGED
|
@@ -54,6 +54,33 @@ export const validateOutput = (schema, data) => {
|
|
|
54
54
|
// ---------------------------------------------------------------------------
|
|
55
55
|
// Zod → JSON Schema (public API)
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
|
+
/**
|
|
58
|
+
* Sentinel indicating a dynamic default that should be omitted from schema
|
|
59
|
+
* exports. Zod v4 wraps all defaults in getters; dynamic ones (functions)
|
|
60
|
+
* produce new values on each access. We detect this by reading the getter
|
|
61
|
+
* twice and comparing with `Object.is`. If values differ, the default is
|
|
62
|
+
* dynamic and we cache this sentinel to skip it in future calls.
|
|
63
|
+
*/
|
|
64
|
+
const DYNAMIC_DEFAULT = Symbol('DYNAMIC_DEFAULT');
|
|
65
|
+
const defaultValueCache = new WeakMap();
|
|
66
|
+
/** Read a Zod v4 default getter twice and decide if it's stable.
|
|
67
|
+
* Uses Object.is for primitives and JSON.stringify for objects/arrays. */
|
|
68
|
+
const resolveDefault = (def) => {
|
|
69
|
+
try {
|
|
70
|
+
const a = def['defaultValue'];
|
|
71
|
+
const b = def['defaultValue'];
|
|
72
|
+
if (Object.is(a, b)) {
|
|
73
|
+
return a;
|
|
74
|
+
}
|
|
75
|
+
// Object/array defaults produce new references each call but may
|
|
76
|
+
// still be structurally identical (e.g. `() => ({ key: 'val' })`).
|
|
77
|
+
return JSON.stringify(a) === JSON.stringify(b) ? a : DYNAMIC_DEFAULT;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// BigInt, circular refs, or other non-serializable defaults
|
|
81
|
+
return DYNAMIC_DEFAULT;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
57
84
|
/**
|
|
58
85
|
* Convert common Zod types to a JSON Schema object.
|
|
59
86
|
*
|
|
@@ -95,9 +122,13 @@ export const zodToJsonSchema = (schema) => {
|
|
|
95
122
|
default: (value) => {
|
|
96
123
|
const inner = value._zod.def['innerType'];
|
|
97
124
|
const innerSchema = zodToJsonSchema(inner);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
125
|
+
if (!defaultValueCache.has(value._zod.def)) {
|
|
126
|
+
defaultValueCache.set(value._zod.def, resolveDefault(value._zod.def));
|
|
127
|
+
}
|
|
128
|
+
const cached = defaultValueCache.get(value._zod.def);
|
|
129
|
+
if (cached !== DYNAMIC_DEFAULT) {
|
|
130
|
+
innerSchema['default'] = cached;
|
|
131
|
+
}
|
|
101
132
|
return innerSchema;
|
|
102
133
|
},
|
|
103
134
|
enum: (value) => {
|
package/dist/validation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAkBrC,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAE9E,MAAM,cAAc,GAAG,CAAC,CAAe,EAAW,EAAE;IAClD,MAAM,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAW,CAAC;IAC7C,OAAO,OAAO,KAAK,UAAU,IAAI,OAAO,KAAK,SAAS,CAAC;AACzD,CAAC,CAAC;AAEF,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAoB,EAAY,EAAE,CAChE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;IACnB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEL,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAAoB,EACpB,IAAa,EACe,EAAE;IAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,GAAG,CACf,IAAI,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACvC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;KACzC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,kGAAkG;AAClG,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAAoB,EACpB,IAAa,EACe,EAAE;IAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,GAAG,CACf,IAAI,eAAe,CAAC,6BAA6B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE;QACtE,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE;KACzC,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,iCAAiC;AACjC,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAClD,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAmB,CAAC;AAEzD;2EAC2E;AAC3E,MAAM,cAAc,GAAG,CAAC,GAA4B,EAAW,EAAE;IAC/D,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,GAAG,CAAC,cAAc,CAAC,CAAC;QAC9B,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC;QACX,CAAC;QACD,iEAAiE;QACjE,mEAAmE;QACnE,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,4DAA4D;QAC5D,OAAO,eAAe,CAAC;IACzB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAwB,CAClD,MAAiB,EACL,EAAE;IACd,MAAM,CAAC,GAAG,MAAiC,CAAC;IAE5C,MAAM,mBAAmB,GAAG,CAAC,KAAmC,EAAE,EAAE;QAClE,MAAM,UAAU,GAAe,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,UAAU,CAAC,GAAG,CAAC,GAAG,eAAe,CAAC,KAA6B,CAAC,CAAC;YACjE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAClC,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAc,EAAE;QACxD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAEvB,CAAC;QACd,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;QACD,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAe,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC1D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,aAAa,GAAwD;QACzE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;YACf,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAyB,CAAC;YAClE,OAAO,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC5D,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACpC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAyB,CAAC;YAClE,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC3C,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACxE,CAAC;YACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrD,IAAI,MAAM,KAAK,eAAe,EAAE,CAAC;gBAC/B,WAAW,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC;YAClC,CAAC;YACD,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,IAAI,EAAE,CAAC,KAAK,EAAE,EAAE;YACd,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAA2B,CAAC;YACpE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC1D,CAAC;QACD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAc,CAAC;YACrD,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,CAAC;QACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;YAClB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAyB,CAAC;YAClE,OAAO,EAAE,KAAK,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAC/D,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAClC,MAAM,EAAE,aAAa;QACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;YAClB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAyB,CAAC;YAClE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAClC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE;YACf,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAA2B,CAAC;YACpE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACrE,CAAC;KACF,CAAC;IAEF,MAAM,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAW,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3C,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts",
|
|
@@ -16,6 +16,6 @@
|
|
|
16
16
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"zod": "
|
|
19
|
+
"zod": "^4.3.5"
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -48,14 +48,25 @@ describe('createTrailContext', () => {
|
|
|
48
48
|
expect(ctx.workspaceRoot).toBe('/tmp');
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
test('TrailContext
|
|
51
|
+
test('TrailContext supports extensions for custom fields', () => {
|
|
52
52
|
const ctx: TrailContext = createTrailContext({
|
|
53
|
-
customField: 42,
|
|
54
|
-
nested: { key: 'value' },
|
|
53
|
+
extensions: { customField: 42, nested: { key: 'value' } },
|
|
55
54
|
});
|
|
56
55
|
|
|
57
|
-
expect(ctx['customField']).toBe(42);
|
|
58
|
-
expect(ctx['nested']).toEqual({ key: 'value' });
|
|
56
|
+
expect(ctx.extensions?.['customField']).toBe(42);
|
|
57
|
+
expect(ctx.extensions?.['nested']).toEqual({ key: 'value' });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('provides a service accessor backed by extensions', () => {
|
|
61
|
+
const widget = { id: 'widget-1' };
|
|
62
|
+
const ctx = createTrailContext({
|
|
63
|
+
extensions: { 'widget.main': widget },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(ctx.service('widget.main')).toBe(widget);
|
|
67
|
+
expect(ctx.service<{ id: string }>({ id: 'widget.main' }).id).toBe(
|
|
68
|
+
'widget-1'
|
|
69
|
+
);
|
|
59
70
|
});
|
|
60
71
|
|
|
61
72
|
test('each call generates a unique requestId', () => {
|
|
@@ -77,6 +77,50 @@ describe('derive', () => {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
describe('array types', () => {
|
|
81
|
+
test('z.array(z.string()) derives string[] field', () => {
|
|
82
|
+
const schema = z.object({ names: z.array(z.string()) });
|
|
83
|
+
const fields = deriveFields(schema);
|
|
84
|
+
expect(fields[0]).toMatchObject({
|
|
85
|
+
name: 'names',
|
|
86
|
+
options: undefined,
|
|
87
|
+
required: true,
|
|
88
|
+
type: 'string[]',
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('z.array(z.number()) derives number[] field', () => {
|
|
93
|
+
const schema = z.object({ scores: z.array(z.number()) });
|
|
94
|
+
const fields = deriveFields(schema);
|
|
95
|
+
expect(fields[0]).toMatchObject({
|
|
96
|
+
name: 'scores',
|
|
97
|
+
options: undefined,
|
|
98
|
+
required: true,
|
|
99
|
+
type: 'number[]',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('optional z.array(z.string()) marks not required', () => {
|
|
104
|
+
const schema = z.object({ tags: z.array(z.string()).optional() });
|
|
105
|
+
const fields = deriveFields(schema);
|
|
106
|
+
expect(fields[0]).toMatchObject({
|
|
107
|
+
name: 'tags',
|
|
108
|
+
required: false,
|
|
109
|
+
type: 'string[]',
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('z.array(z.string()) with default sets default value', () => {
|
|
114
|
+
const schema = z.object({
|
|
115
|
+
items: z.array(z.string()).default(['a', 'b']),
|
|
116
|
+
});
|
|
117
|
+
const fields = deriveFields(schema);
|
|
118
|
+
expect(fields[0]?.required).toBe(false);
|
|
119
|
+
expect(fields[0]?.type).toBe('string[]');
|
|
120
|
+
expect(fields[0]?.default).toEqual(['a', 'b']);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
80
124
|
describe('modifiers', () => {
|
|
81
125
|
test('.describe() sets label', () => {
|
|
82
126
|
const schema = z.object({
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* oxlint-disable require-await -- trail implementations satisfy async interface without awaiting */
|
|
2
|
+
import { describe, test, expect } from 'bun:test';
|
|
3
|
+
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import { dispatch } from '../dispatch';
|
|
7
|
+
import { InternalError, NotFoundError, ValidationError } from '../errors';
|
|
8
|
+
import type { Layer } from '../layer';
|
|
9
|
+
import { Result } from '../result';
|
|
10
|
+
import { service } from '../service';
|
|
11
|
+
import { topo } from '../topo';
|
|
12
|
+
import { trail } from '../trail';
|
|
13
|
+
import type { TrailContext, TrailContextInit } from '../types';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Fixtures
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
const echoTrail = trail('echo', {
|
|
20
|
+
input: z.object({ value: z.string() }),
|
|
21
|
+
output: z.object({ value: z.string() }),
|
|
22
|
+
run: (input) => Result.ok({ value: input.value }),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const throwingTrail = trail('throws', {
|
|
26
|
+
input: z.object({}),
|
|
27
|
+
run: () => {
|
|
28
|
+
throw new Error('kaboom');
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const testTopo = topo('test', { echoTrail, throwingTrail });
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Tests
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
describe('dispatch', () => {
|
|
39
|
+
describe('happy path', () => {
|
|
40
|
+
test('dispatches by ID and returns Result.ok with expected value', async () => {
|
|
41
|
+
const result = await dispatch(testTopo, 'echo', { value: 'hello' });
|
|
42
|
+
|
|
43
|
+
expect(result.isOk()).toBe(true);
|
|
44
|
+
expect(result.unwrap()).toEqual({ value: 'hello' });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('passes service overrides through to executeTrail', async () => {
|
|
48
|
+
const id = `dispatch.service.${Bun.randomUUIDv7()}`;
|
|
49
|
+
const db = service(id, {
|
|
50
|
+
create: () => Result.ok({ source: 'factory' }),
|
|
51
|
+
});
|
|
52
|
+
const searchTrail = trail('search', {
|
|
53
|
+
input: z.object({}),
|
|
54
|
+
output: z.object({ source: z.string() }),
|
|
55
|
+
run: (_input, ctx) => Result.ok({ source: db.from(ctx).source }),
|
|
56
|
+
services: [db],
|
|
57
|
+
});
|
|
58
|
+
const searchTopo = topo('service-test', { searchTrail });
|
|
59
|
+
|
|
60
|
+
const result = await dispatch(
|
|
61
|
+
searchTopo,
|
|
62
|
+
'search',
|
|
63
|
+
{},
|
|
64
|
+
{
|
|
65
|
+
services: { [id]: { source: 'override' } },
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(result.isOk()).toBe(true);
|
|
70
|
+
expect(result.unwrap()).toEqual({ source: 'override' });
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('not found', () => {
|
|
75
|
+
test('returns NotFoundError for unknown trail ID', async () => {
|
|
76
|
+
const result = await dispatch(testTopo, 'nonexistent', {});
|
|
77
|
+
|
|
78
|
+
expect(result.isErr()).toBe(true);
|
|
79
|
+
expect(result.error).toBeInstanceOf(NotFoundError);
|
|
80
|
+
expect(result.error.message).toContain('nonexistent');
|
|
81
|
+
expect(result.error.message).toContain('test');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('validation', () => {
|
|
86
|
+
test('returns ValidationError for invalid input', async () => {
|
|
87
|
+
const result = await dispatch(testTopo, 'echo', { value: 42 });
|
|
88
|
+
|
|
89
|
+
expect(result.isErr()).toBe(true);
|
|
90
|
+
expect(result.error).toBeInstanceOf(ValidationError);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('layers', () => {
|
|
95
|
+
test('layer composition works through dispatch', async () => {
|
|
96
|
+
const log: string[] = [];
|
|
97
|
+
const layer: Layer = {
|
|
98
|
+
name: 'test-layer',
|
|
99
|
+
wrap(_trail, impl) {
|
|
100
|
+
return async (input, ctx) => {
|
|
101
|
+
log.push('before');
|
|
102
|
+
const r = await impl(input, ctx);
|
|
103
|
+
log.push('after');
|
|
104
|
+
return r;
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const result = await dispatch(
|
|
110
|
+
testTopo,
|
|
111
|
+
'echo',
|
|
112
|
+
{ value: 'x' },
|
|
113
|
+
{ layers: [layer] }
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(result.isOk()).toBe(true);
|
|
117
|
+
expect(log).toEqual(['before', 'after']);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('context', () => {
|
|
122
|
+
test('context overrides work through dispatch', async () => {
|
|
123
|
+
let capturedCtx: TrailContext | undefined;
|
|
124
|
+
const ctxTrail = trail('ctx-dispatch-test', {
|
|
125
|
+
input: z.object({}),
|
|
126
|
+
run: (_input, ctx) => {
|
|
127
|
+
capturedCtx = ctx;
|
|
128
|
+
return Result.ok(null);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const ctxTopo = topo('ctx-test', { ctxTrail });
|
|
133
|
+
await dispatch(
|
|
134
|
+
ctxTopo,
|
|
135
|
+
'ctx-dispatch-test',
|
|
136
|
+
{},
|
|
137
|
+
{ ctx: { requestId: 'override-id' } }
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
expect(capturedCtx?.requestId).toBe('override-id');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('createContext factory works through dispatch', async () => {
|
|
144
|
+
let capturedCtx: TrailContext | undefined;
|
|
145
|
+
const ctxTrail = trail('factory-dispatch-test', {
|
|
146
|
+
input: z.object({}),
|
|
147
|
+
run: (_input, ctx) => {
|
|
148
|
+
capturedCtx = ctx;
|
|
149
|
+
return Result.ok(null);
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const ctxTopo = topo('factory-test', { ctxTrail });
|
|
154
|
+
const customCtx: TrailContextInit = {
|
|
155
|
+
cwd: '/custom',
|
|
156
|
+
requestId: 'factory-id',
|
|
157
|
+
signal: new AbortController().signal,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
await dispatch(
|
|
161
|
+
ctxTopo,
|
|
162
|
+
'factory-dispatch-test',
|
|
163
|
+
{},
|
|
164
|
+
{ createContext: () => customCtx }
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
expect(capturedCtx?.requestId).toBe('factory-id');
|
|
168
|
+
expect(capturedCtx?.cwd).toBe('/custom');
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('error handling', () => {
|
|
173
|
+
test('never throws — exceptions become InternalError', async () => {
|
|
174
|
+
const result = await dispatch(testTopo, 'throws', {});
|
|
175
|
+
|
|
176
|
+
expect(result.isErr()).toBe(true);
|
|
177
|
+
expect(result.error).toBeInstanceOf(InternalError);
|
|
178
|
+
expect(result.error.message).toBe('kaboom');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
@@ -12,7 +12,7 @@ const payloadSchema = z.object({
|
|
|
12
12
|
const userAction = event('user.action', {
|
|
13
13
|
description: 'A user performed an action',
|
|
14
14
|
from: ['auth.login', 'auth.signup'],
|
|
15
|
-
|
|
15
|
+
metadata: { domain: 'auth', priority: 1 },
|
|
16
16
|
payload: payloadSchema,
|
|
17
17
|
});
|
|
18
18
|
|
|
@@ -45,7 +45,7 @@ describe('event() basics', () => {
|
|
|
45
45
|
});
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
describe('event() from and
|
|
48
|
+
describe('event() from and metadata', () => {
|
|
49
49
|
test('preserves from', () => {
|
|
50
50
|
expect(userAction.from).toEqual(['auth.login', 'auth.signup']);
|
|
51
51
|
});
|
|
@@ -54,8 +54,8 @@ describe('event() from and markers', () => {
|
|
|
54
54
|
expect(Object.isFrozen(userAction.from)).toBe(true);
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
test('preserves
|
|
58
|
-
expect(userAction.
|
|
57
|
+
test('preserves metadata', () => {
|
|
58
|
+
expect(userAction.metadata).toEqual({ domain: 'auth', priority: 1 });
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
test('optional fields default to undefined', () => {
|
|
@@ -64,7 +64,7 @@ describe('event() from and markers', () => {
|
|
|
64
64
|
});
|
|
65
65
|
expect(minimal.description).toBeUndefined();
|
|
66
66
|
expect(minimal.from).toBeUndefined();
|
|
67
|
-
expect(minimal.
|
|
67
|
+
expect(minimal.metadata).toBeUndefined();
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
|