@eduardorenani/atlasjs 0.1.0-alpha.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/LICENSE +201 -0
- package/README.md +113 -0
- package/dist/actorName.d.ts +2 -0
- package/dist/actorName.d.ts.map +1 -0
- package/dist/actorName.js +24 -0
- package/dist/actorName.js.map +1 -0
- package/dist/buildActions.d.ts +11 -0
- package/dist/buildActions.d.ts.map +1 -0
- package/dist/buildActions.js +23 -0
- package/dist/buildActions.js.map +1 -0
- package/dist/buildActiveState.d.ts +43 -0
- package/dist/buildActiveState.d.ts.map +1 -0
- package/dist/buildActiveState.js +194 -0
- package/dist/buildActiveState.js.map +1 -0
- package/dist/buildActors.d.ts +4 -0
- package/dist/buildActors.d.ts.map +1 -0
- package/dist/buildActors.js +40 -0
- package/dist/buildActors.js.map +1 -0
- package/dist/buildPassiveState.d.ts +19 -0
- package/dist/buildPassiveState.d.ts.map +1 -0
- package/dist/buildPassiveState.js +59 -0
- package/dist/buildPassiveState.js.map +1 -0
- package/dist/compile.d.ts +6 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +164 -0
- package/dist/compile.js.map +1 -0
- package/dist/contextLift.d.ts +35 -0
- package/dist/contextLift.d.ts.map +1 -0
- package/dist/contextLift.js +172 -0
- package/dist/contextLift.js.map +1 -0
- package/dist/defineAgent.d.ts +60 -0
- package/dist/defineAgent.d.ts.map +1 -0
- package/dist/defineAgent.js +79 -0
- package/dist/defineAgent.js.map +1 -0
- package/dist/defineCompoundMode.d.ts +77 -0
- package/dist/defineCompoundMode.d.ts.map +1 -0
- package/dist/defineCompoundMode.js +79 -0
- package/dist/defineCompoundMode.js.map +1 -0
- package/dist/defineMode.d.ts +89 -0
- package/dist/defineMode.d.ts.map +1 -0
- package/dist/defineMode.js +95 -0
- package/dist/defineMode.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/injectEnd.d.ts +10 -0
- package/dist/injectEnd.d.ts.map +1 -0
- package/dist/injectEnd.js +106 -0
- package/dist/injectEnd.js.map +1 -0
- package/dist/types.d.ts +539 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +51 -0
- package/dist/types.js.map +1 -0
- package/dist/validateRoutes.d.ts +2 -0
- package/dist/validateRoutes.d.ts.map +1 -0
- package/dist/validateRoutes.js +107 -0
- package/dist/validateRoutes.js.map +1 -0
- package/dist/validateTargets.d.ts +2 -0
- package/dist/validateTargets.d.ts.map +1 -0
- package/dist/validateTargets.js +132 -0
- package/dist/validateTargets.js.map +1 -0
- package/dist/walk.d.ts +20 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +35 -0
- package/dist/walk.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Phase 5.15: route shape runtime validator — belt-and-suspenders for the
|
|
2
|
+
// `RouteList<E>` constraint.
|
|
3
|
+
//
|
|
4
|
+
// Spec: docs/specs/004-tasks.md Phase 5.15,
|
|
5
|
+
// docs/specs/004-xstate-agent-wrapper.md verification line 821.
|
|
6
|
+
//
|
|
7
|
+
// The type system already enforces `RouteList<E>` at every call site:
|
|
8
|
+
// - `achieved` / `abandoned` / `error` reject the empty array `[]`
|
|
9
|
+
// - non-last array entries must carry `when` (otherwise they shadow later
|
|
10
|
+
// entries — first-match-wins at runtime would skip everything after)
|
|
11
|
+
// - the last array entry must NOT carry `when` (it is the unguarded
|
|
12
|
+
// default; without it no fallback fires).
|
|
13
|
+
//
|
|
14
|
+
// A user can bypass these checks with `as Routes<...>`. This validator
|
|
15
|
+
// re-checks the same invariants at machine-creation time and throws a
|
|
16
|
+
// structured error naming the leaf path, the slot (`achieved` / `retry` /
|
|
17
|
+
// `abandoned` / `error`), and the offending index — spec verification
|
|
18
|
+
// line 821.
|
|
19
|
+
//
|
|
20
|
+
// Scope is intentionally narrow:
|
|
21
|
+
// - shape only (array vs. scalar, length, presence of `when`)
|
|
22
|
+
// - target resolution stays in `validateTargets` (5.14)
|
|
23
|
+
// - field-level shape (`target` missing on an ExitEntry, `assign` not a
|
|
24
|
+
// function, etc.) is out of scope for this slice; the type system
|
|
25
|
+
// remains the primary guard for those.
|
|
26
|
+
//
|
|
27
|
+
// Like 5.14, this slice ships the validator standalone. Slice 5.16 wires
|
|
28
|
+
// it into `defineAgent(...)` so the throw fires on machine creation.
|
|
29
|
+
function joinPath(parent, name) {
|
|
30
|
+
return parent === "" ? name : `${parent}.${name}`;
|
|
31
|
+
}
|
|
32
|
+
function asCarrier(value, path) {
|
|
33
|
+
if (typeof value !== "object" || value === null || !("__kind" in value)) {
|
|
34
|
+
throw new Error(`atlas/validateRoutes: state at "${path}" is not a Mode or CompoundMode. ` +
|
|
35
|
+
`Pass values constructed via defineMode() or defineCompoundMode().`);
|
|
36
|
+
}
|
|
37
|
+
const kind = value.__kind;
|
|
38
|
+
if (kind !== "leaf" && kind !== "compound") {
|
|
39
|
+
throw new Error(`atlas/validateRoutes: state at "${path}" has unknown kind ${String(kind)}`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
function fail(leafPath, slot, index, reason) {
|
|
44
|
+
const where = index === null ? `routes.${slot}` : `routes.${slot}[${index}]`;
|
|
45
|
+
throw new Error(`atlas/validateRoutes: ${where} at "${leafPath}": ${reason}`);
|
|
46
|
+
}
|
|
47
|
+
function validateRouteListShape(slot, value, leafPath) {
|
|
48
|
+
if (value === undefined) {
|
|
49
|
+
if (slot === "error")
|
|
50
|
+
return; // optional
|
|
51
|
+
fail(leafPath, slot, null, `required outcome is missing (declare an entry with \`target\` or, for retry, supply \`[]\`)`);
|
|
52
|
+
}
|
|
53
|
+
if (!Array.isArray(value)) {
|
|
54
|
+
// Scalar form — single-default. Field-level shape (target, when,
|
|
55
|
+
// assign) is the type system's responsibility; we only verify it
|
|
56
|
+
// is at least an object.
|
|
57
|
+
if (typeof value !== "object" || value === null) {
|
|
58
|
+
fail(leafPath, slot, null, `must be an object or array (got ${typeof value})`);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const arr = value;
|
|
63
|
+
if (arr.length === 0) {
|
|
64
|
+
if (slot === "retry")
|
|
65
|
+
return; // explicit empty retry = no-op default
|
|
66
|
+
fail(leafPath, slot, null, `must not be the empty array []. Provide a single default entry, or guarded entries followed by a default.`);
|
|
67
|
+
}
|
|
68
|
+
// Non-last entries MUST carry `when` — without it they shadow every
|
|
69
|
+
// entry after them at first-match-wins runtime.
|
|
70
|
+
for (let i = 0; i < arr.length - 1; i += 1) {
|
|
71
|
+
if (arr[i].when === undefined) {
|
|
72
|
+
fail(leafPath, slot, i, `non-last entry is missing \`when\` — it would shadow later entries at runtime (first-match-wins)`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Last entry MUST NOT carry `when` — the last entry is the unguarded
|
|
76
|
+
// default; otherwise no fallback exists when every preceding `when`
|
|
77
|
+
// returns false.
|
|
78
|
+
const lastIndex = arr.length - 1;
|
|
79
|
+
const last = arr[lastIndex];
|
|
80
|
+
if (last !== undefined && last.when !== undefined) {
|
|
81
|
+
fail(leafPath, slot, lastIndex, `last entry carries \`when\` — the last entry must be the unguarded default`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function validateActiveLeafRoutes(config, leafPath) {
|
|
85
|
+
const routes = (config.routes ?? {});
|
|
86
|
+
validateRouteListShape("achieved", routes.achieved, leafPath);
|
|
87
|
+
validateRouteListShape("retry", routes.retry, leafPath);
|
|
88
|
+
validateRouteListShape("abandoned", routes.abandoned, leafPath);
|
|
89
|
+
validateRouteListShape("error", routes.error, leafPath);
|
|
90
|
+
}
|
|
91
|
+
// Public entry point. Recursively validates `routes` shape on every active
|
|
92
|
+
// leaf in the tree. Passive leaves are skipped (no `routes` field).
|
|
93
|
+
export function validateRoutes(modes, parentPath = "") {
|
|
94
|
+
for (const [name, value] of Object.entries(modes)) {
|
|
95
|
+
const path = joinPath(parentPath, name);
|
|
96
|
+
const carrier = asCarrier(value, path);
|
|
97
|
+
if (carrier.__kind === "leaf") {
|
|
98
|
+
if ("behavior" in carrier.config && carrier.config.behavior !== undefined) {
|
|
99
|
+
validateActiveLeafRoutes(carrier.config, path);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
validateRoutes(carrier.config.modes, path);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=validateRoutes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateRoutes.js","sourceRoot":"","sources":["../src/validateRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateTargets.d.ts","sourceRoot":"","sources":["../src/validateTargets.ts"],"names":[],"mappings":"AAkLA,wBAAgB,eAAe,CAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,UAAU,GAAE,MAAW,GACxB,IAAI,CAiBN"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Phase 5.14: target resolution validator (sibling-name only).
|
|
2
|
+
//
|
|
3
|
+
// Spec: docs/specs/004-tasks.md Phase 5.14,
|
|
4
|
+
// docs/specs/004-xstate-agent-wrapper.md §Target resolution (lines 824-829).
|
|
5
|
+
//
|
|
6
|
+
// Every `target` declared by the user — `routes.achieved[i].target`,
|
|
7
|
+
// `routes.abandoned[i].target`, `routes.error[i].target`, passive
|
|
8
|
+
// `on[event][i].target`, and a compound's `onDone` — must name a key in
|
|
9
|
+
// the **immediate enclosing** `modes` map. The token symbols `END`
|
|
10
|
+
// (achieved/abandoned/error/onDone/passive-on) and `RE_THROW`
|
|
11
|
+
// (error only) pass through verbatim — they are resolved by slices 5.10
|
|
12
|
+
// (RE_THROW) and 5.11 ($end injection).
|
|
13
|
+
//
|
|
14
|
+
// String targets are additionally rejected when they:
|
|
15
|
+
// - contain `.` (dotted paths like "socratic.teaching")
|
|
16
|
+
// - start with `#` (XState absolute paths like "#agent.root")
|
|
17
|
+
// - start with `.` (descendant paths — caught implicitly by the dot rule)
|
|
18
|
+
//
|
|
19
|
+
// On failure the validator throws a structured `Error` whose message names
|
|
20
|
+
// the offending leaf (or compound) path, the slot descriptor
|
|
21
|
+
// (`routes.achieved[0]`, `on.CLICK[1]`, `onDone`), and the literal bad
|
|
22
|
+
// target string — spec verification line 824.
|
|
23
|
+
//
|
|
24
|
+
// 5.14 ships the validator as a standalone toolkit. Wiring it into
|
|
25
|
+
// `defineAgent(...)` (so the throw fires on machine creation) is part of
|
|
26
|
+
// the final assembly slice (5.16) — same composition pattern as 5.11 and
|
|
27
|
+
// 5.13.
|
|
28
|
+
import { END, RE_THROW } from "./types.js";
|
|
29
|
+
function joinPath(parent, name) {
|
|
30
|
+
return parent === "" ? name : `${parent}.${name}`;
|
|
31
|
+
}
|
|
32
|
+
function asCarrier(value, path) {
|
|
33
|
+
if (typeof value !== "object" || value === null || !("__kind" in value)) {
|
|
34
|
+
throw new Error(`atlas/validateTargets: state at "${path}" is not a Mode or CompoundMode. ` +
|
|
35
|
+
`Pass values constructed via defineMode() or defineCompoundMode().`);
|
|
36
|
+
}
|
|
37
|
+
const kind = value.__kind;
|
|
38
|
+
if (kind !== "leaf" && kind !== "compound") {
|
|
39
|
+
throw new Error(`atlas/validateTargets: state at "${path}" has unknown kind ${String(kind)}`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
// Normalize a `T | readonly T[] | undefined` slot to a flat `readonly T[]`
|
|
44
|
+
// for uniform iteration.
|
|
45
|
+
function normalize(value) {
|
|
46
|
+
if (value === undefined)
|
|
47
|
+
return [];
|
|
48
|
+
if (Array.isArray(value))
|
|
49
|
+
return value;
|
|
50
|
+
return [value];
|
|
51
|
+
}
|
|
52
|
+
// Format error consistently — spec verification line 824 requires the path,
|
|
53
|
+
// slot descriptor, and literal target.
|
|
54
|
+
function fail(target, leafPath, slotDesc, reason) {
|
|
55
|
+
const literal = typeof target === "string" ? JSON.stringify(target) : String(target);
|
|
56
|
+
throw new Error(`atlas/validateTargets: target ${literal} at "${leafPath}" in ${slotDesc}: ${reason}`);
|
|
57
|
+
}
|
|
58
|
+
function checkTarget(target, siblings, leafPath, slotDesc) {
|
|
59
|
+
if (target === END || target === RE_THROW)
|
|
60
|
+
return;
|
|
61
|
+
if (typeof target !== "string") {
|
|
62
|
+
fail(target, leafPath, slotDesc, `target must be a sibling name (string), END, or RE_THROW`);
|
|
63
|
+
}
|
|
64
|
+
if (target.includes(".")) {
|
|
65
|
+
fail(target, leafPath, slotDesc, `dotted paths are not accepted — END is the only way to exit a compound`);
|
|
66
|
+
}
|
|
67
|
+
if (target.startsWith("#")) {
|
|
68
|
+
fail(target, leafPath, slotDesc, `XState absolute paths (#-prefixed) are not accepted`);
|
|
69
|
+
}
|
|
70
|
+
if (!siblings.includes(target)) {
|
|
71
|
+
fail(target, leafPath, slotDesc, `no such sibling. Available siblings: ${JSON.stringify(siblings)}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function validateActiveLeaf(config, leafPath, siblings) {
|
|
75
|
+
const routes = (config.routes ?? {});
|
|
76
|
+
const groups = [
|
|
77
|
+
{ key: "achieved", allowReThrow: false },
|
|
78
|
+
{ key: "abandoned", allowReThrow: false },
|
|
79
|
+
{ key: "error", allowReThrow: true },
|
|
80
|
+
];
|
|
81
|
+
for (const { key } of groups) {
|
|
82
|
+
if (routes[key] === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
const entries = normalize(routes[key]);
|
|
85
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
86
|
+
const target = entries[i].target;
|
|
87
|
+
checkTarget(target, siblings, leafPath, `routes.${key}[${i}]`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// `routes.retry` has no `target` field at the type level — skipped.
|
|
91
|
+
}
|
|
92
|
+
function validatePassiveLeaf(config, leafPath, siblings) {
|
|
93
|
+
const on = (config.on ?? {});
|
|
94
|
+
for (const [event, transitions] of Object.entries(on)) {
|
|
95
|
+
const list = normalize(transitions);
|
|
96
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
97
|
+
const target = list[i].target;
|
|
98
|
+
// `on[event].target` is optional — a transition with only
|
|
99
|
+
// `actions` is an internal/no-target action and is valid.
|
|
100
|
+
if (target === undefined)
|
|
101
|
+
continue;
|
|
102
|
+
checkTarget(target, siblings, leafPath, `on.${event}[${i}]`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function validateCompound(carrier, compoundPath, siblings) {
|
|
107
|
+
checkTarget(carrier.config.onDone, siblings, compoundPath, "onDone");
|
|
108
|
+
}
|
|
109
|
+
// Public entry point. Recursively validates every `target` in the tree.
|
|
110
|
+
// Throws on the first violation — fail-fast surfaces the user's mistake at
|
|
111
|
+
// `defineAgent(...)` call time with full context.
|
|
112
|
+
export function validateTargets(modes, parentPath = "") {
|
|
113
|
+
const siblings = Object.keys(modes);
|
|
114
|
+
for (const [name, value] of Object.entries(modes)) {
|
|
115
|
+
const path = joinPath(parentPath, name);
|
|
116
|
+
const carrier = asCarrier(value, path);
|
|
117
|
+
if (carrier.__kind === "leaf") {
|
|
118
|
+
if ("behavior" in carrier.config && carrier.config.behavior !== undefined) {
|
|
119
|
+
validateActiveLeaf(carrier.config, path, siblings);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
validatePassiveLeaf(carrier.config, path, siblings);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
validateCompound(carrier, path, siblings);
|
|
127
|
+
// Inner siblings = keys of THIS compound's `modes` map.
|
|
128
|
+
validateTargets(carrier.config.modes, path);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=validateTargets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateTargets.js","sourceRoot":"","sources":["../src/validateTargets.ts"],"names":[],"mappings":"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"}
|
package/dist/walk.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { JsonObject, ModeConfig } from "./types.js";
|
|
2
|
+
type InternalCtx = JsonObject;
|
|
3
|
+
export type LeafSlot = {
|
|
4
|
+
readonly kind: "leaf";
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly config: ModeConfig<InternalCtx, {
|
|
7
|
+
type: string;
|
|
8
|
+
}, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export type CompoundSlot = {
|
|
11
|
+
readonly kind: "compound";
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly config: {
|
|
14
|
+
readonly modes: Record<string, unknown>;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type Slot = LeafSlot | CompoundSlot;
|
|
18
|
+
export declare function walk(modes: Record<string, unknown>, parentPath?: string): Slot[];
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=walk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKzD,KAAK,WAAW,GAAG,UAAU,CAAC;AAe9B,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,CAAC,CAAC;CACvE,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;CAChE,CAAC;AAEF,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,YAAY,CAAC;AAgB3C,wBAAgB,IAAI,CAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,UAAU,SAAK,GAChB,IAAI,EAAE,CAaR"}
|
package/dist/walk.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Tree walk over an agent's `modes` map.
|
|
2
|
+
//
|
|
3
|
+
// Spec: docs/specs/004-tasks.md Phase 5.1.
|
|
4
|
+
//
|
|
5
|
+
// Enumerates every `Mode` (leaf) and `CompoundMode` slot, recording a
|
|
6
|
+
// root-relative dotted path. The path is used only inside `compile.ts` (for
|
|
7
|
+
// actor naming, for target resolution, for error messages); it is never
|
|
8
|
+
// exposed to users.
|
|
9
|
+
function asCarrier(value, path) {
|
|
10
|
+
if (typeof value !== "object" || value === null || !("__kind" in value)) {
|
|
11
|
+
throw new Error(`atlas/walk: state at "${path}" is not a Mode or CompoundMode. ` +
|
|
12
|
+
`Pass values constructed via defineMode() or defineCompoundMode().`);
|
|
13
|
+
}
|
|
14
|
+
const kind = value.__kind;
|
|
15
|
+
if (kind !== "leaf" && kind !== "compound") {
|
|
16
|
+
throw new Error(`atlas/walk: state at "${path}" has unknown kind ${String(kind)}`);
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
export function walk(modes, parentPath = "") {
|
|
21
|
+
const slots = [];
|
|
22
|
+
for (const [key, value] of Object.entries(modes)) {
|
|
23
|
+
const path = parentPath === "" ? key : `${parentPath}.${key}`;
|
|
24
|
+
const carrier = asCarrier(value, path);
|
|
25
|
+
if (carrier.__kind === "leaf") {
|
|
26
|
+
slots.push({ kind: "leaf", path, config: carrier.config });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
slots.push({ kind: "compound", path, config: carrier.config });
|
|
30
|
+
slots.push(...walk(carrier.config.modes, path));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return slots;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=walk.js.map
|
package/dist/walk.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,2CAA2C;AAC3C,EAAE;AACF,sEAAsE;AACtE,4EAA4E;AAC5E,wEAAwE;AACxE,oBAAoB;AAoCpB,SAAS,SAAS,CAAC,KAAc,EAAE,IAAY;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CACX,yBAAyB,IAAI,mCAAmC;YAC5D,mEAAmE,CAC1E,CAAC;IACN,CAAC;IACD,MAAM,IAAI,GAAI,KAA6B,CAAC,MAAM,CAAC;IACnD,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,sBAAsB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,KAAsC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,IAAI,CAChB,KAA8B,EAC9B,UAAU,GAAG,EAAE;IAEf,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,IAAI,GAAG,EAAE,CAAC;QAC9D,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACJ,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;YAC/D,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eduardorenani/atlasjs",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "Mode-based agent orchestration on top of XState v5. Typed modes, four-outcome contract, goal-bound exits.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.build.json && node scripts/fix-dts-extensions.mjs",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"test": "vitest run --passWithNoTests && vitest --typecheck --run --passWithNoTests",
|
|
24
|
+
"test:types": "vitest --typecheck --run --passWithNoTests",
|
|
25
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"xstate": "^5"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.7",
|
|
32
|
+
"vitest": "^3",
|
|
33
|
+
"xstate": "^5"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"xstate",
|
|
37
|
+
"state-machine",
|
|
38
|
+
"agent",
|
|
39
|
+
"ai-agent",
|
|
40
|
+
"orchestration",
|
|
41
|
+
"llm",
|
|
42
|
+
"typescript",
|
|
43
|
+
"modes"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/EduardoRenani/atlas.git",
|
|
48
|
+
"directory": "packages/atlas"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/EduardoRenani/atlas#readme",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/EduardoRenani/atlas/issues"
|
|
53
|
+
},
|
|
54
|
+
"license": "Apache-2.0",
|
|
55
|
+
"author": "Eduardo Renani",
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"provenance": true
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=20"
|
|
62
|
+
}
|
|
63
|
+
}
|