@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.
Files changed (67) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +113 -0
  3. package/dist/actorName.d.ts +2 -0
  4. package/dist/actorName.d.ts.map +1 -0
  5. package/dist/actorName.js +24 -0
  6. package/dist/actorName.js.map +1 -0
  7. package/dist/buildActions.d.ts +11 -0
  8. package/dist/buildActions.d.ts.map +1 -0
  9. package/dist/buildActions.js +23 -0
  10. package/dist/buildActions.js.map +1 -0
  11. package/dist/buildActiveState.d.ts +43 -0
  12. package/dist/buildActiveState.d.ts.map +1 -0
  13. package/dist/buildActiveState.js +194 -0
  14. package/dist/buildActiveState.js.map +1 -0
  15. package/dist/buildActors.d.ts +4 -0
  16. package/dist/buildActors.d.ts.map +1 -0
  17. package/dist/buildActors.js +40 -0
  18. package/dist/buildActors.js.map +1 -0
  19. package/dist/buildPassiveState.d.ts +19 -0
  20. package/dist/buildPassiveState.d.ts.map +1 -0
  21. package/dist/buildPassiveState.js +59 -0
  22. package/dist/buildPassiveState.js.map +1 -0
  23. package/dist/compile.d.ts +6 -0
  24. package/dist/compile.d.ts.map +1 -0
  25. package/dist/compile.js +164 -0
  26. package/dist/compile.js.map +1 -0
  27. package/dist/contextLift.d.ts +35 -0
  28. package/dist/contextLift.d.ts.map +1 -0
  29. package/dist/contextLift.js +172 -0
  30. package/dist/contextLift.js.map +1 -0
  31. package/dist/defineAgent.d.ts +60 -0
  32. package/dist/defineAgent.d.ts.map +1 -0
  33. package/dist/defineAgent.js +79 -0
  34. package/dist/defineAgent.js.map +1 -0
  35. package/dist/defineCompoundMode.d.ts +77 -0
  36. package/dist/defineCompoundMode.d.ts.map +1 -0
  37. package/dist/defineCompoundMode.js +79 -0
  38. package/dist/defineCompoundMode.js.map +1 -0
  39. package/dist/defineMode.d.ts +89 -0
  40. package/dist/defineMode.d.ts.map +1 -0
  41. package/dist/defineMode.js +95 -0
  42. package/dist/defineMode.js.map +1 -0
  43. package/dist/index.d.ts +6 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +10 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/injectEnd.d.ts +10 -0
  48. package/dist/injectEnd.d.ts.map +1 -0
  49. package/dist/injectEnd.js +106 -0
  50. package/dist/injectEnd.js.map +1 -0
  51. package/dist/types.d.ts +539 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +51 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/validateRoutes.d.ts +2 -0
  56. package/dist/validateRoutes.d.ts.map +1 -0
  57. package/dist/validateRoutes.js +107 -0
  58. package/dist/validateRoutes.js.map +1 -0
  59. package/dist/validateTargets.d.ts +2 -0
  60. package/dist/validateTargets.d.ts.map +1 -0
  61. package/dist/validateTargets.js +132 -0
  62. package/dist/validateTargets.js.map +1 -0
  63. package/dist/walk.d.ts +20 -0
  64. package/dist/walk.d.ts.map +1 -0
  65. package/dist/walk.js +35 -0
  66. package/dist/walk.js.map +1 -0
  67. package/package.json +63 -0
@@ -0,0 +1,4 @@
1
+ import type { AnyActorLogic } from "xstate";
2
+ import type { Slot } from "./walk.js";
3
+ export declare function buildActors(slots: readonly Slot[], deps: Readonly<Record<string, unknown>>): Record<string, AnyActorLogic>;
4
+ //# sourceMappingURL=buildActors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildActors.d.ts","sourceRoot":"","sources":["../src/buildActors.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAG5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,wBAAgB,WAAW,CACvB,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CA6B/B"}
@@ -0,0 +1,40 @@
1
+ // Build the `actors` map for XState's `setup({ actors })`. Spec:
2
+ // docs/specs/004-tasks.md Phase 5.3 + docs/specs/004-xstate-agent-wrapper.md
3
+ // §Mapping. Spec 005: the wrapper closes over the agent's frozen `deps`
4
+ // reference and threads it into every active leaf's `behavior` callback.
5
+ //
6
+ // One entry per active `Mode` (leaf) slot, keyed by `actorName(path)`. Each
7
+ // entry wraps the user's `behavior` in
8
+ // `fromPromise(async ({ input }) => behavior({ input, deps }))` — the only
9
+ // place the wrapper bridges user code to XState's actor runtime.
10
+ // Passive leaves do not produce an actor; they are atomic states with `on`
11
+ // handlers (handled in slice 5.4).
12
+ import { fromPromise } from "xstate";
13
+ import { actorName } from "./actorName.js";
14
+ export function buildActors(slots, deps) {
15
+ const actors = {};
16
+ for (const slot of slots) {
17
+ if (slot.kind !== "leaf")
18
+ continue;
19
+ const config = slot.config;
20
+ if (!("behavior" in config))
21
+ continue; // passive leaf — no actor
22
+ const name = actorName(slot.path);
23
+ if (name in actors) {
24
+ // Two leaves sharing an actor name would silently overwrite. Per
25
+ // DD-008 the name is derived from path, so a collision here means
26
+ // two slots share a path — which `walk()` already prevents — or
27
+ // future renames broke the invariant. Fail loudly.
28
+ throw new Error(`atlas/buildActors: duplicate actor name "${name}" at path "${slot.path}"`);
29
+ }
30
+ // The user's `behavior` was typed against its own `TDeps` at the
31
+ // `defineMode` call site; the internal carrier here erases that
32
+ // generic to the default `Record<string, never>`. Cast through the
33
+ // user-facing envelope shape — by-identity threading is the same
34
+ // frozen reference all callbacks receive.
35
+ const userBehavior = config.behavior;
36
+ actors[name] = fromPromise(async ({ input }) => userBehavior({ input, deps }));
37
+ }
38
+ return actors;
39
+ }
40
+ //# sourceMappingURL=buildActors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildActors.js","sourceRoot":"","sources":["../src/buildActors.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,6EAA6E;AAC7E,wEAAwE;AACxE,yEAAyE;AACzE,EAAE;AACF,4EAA4E;AAC5E,uCAAuC;AACvC,2EAA2E;AAC3E,iEAAiE;AACjE,2EAA2E;AAC3E,mCAAmC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,MAAM,UAAU,WAAW,CACvB,KAAsB,EACtB,IAAuC;IAEvC,MAAM,MAAM,GAAkC,EAAE,CAAC;IACjD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC;YAAE,SAAS,CAAC,0BAA0B;QAEjE,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YACjB,iEAAiE;YACjE,kEAAkE;YAClE,gEAAgE;YAChE,mDAAmD;YACnD,MAAM,IAAI,KAAK,CACX,4CAA4C,IAAI,cAAc,IAAI,CAAC,IAAI,GAAG,CAC7E,CAAC;QACN,CAAC;QACD,iEAAiE;QACjE,gEAAgE;QAChE,mEAAmE;QACnE,iEAAiE;QACjE,0CAA0C;QAC1C,MAAM,YAAY,GAAG,MAAM,CAAC,QAGN,CAAC;QACvB,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type LiftContext } from "./contextLift.js";
2
+ import type { JsonObject, PassiveModeConfig, RouteTarget } from "./types.js";
3
+ type InternalCtx = JsonObject;
4
+ export type LoweredTransition = {
5
+ target?: RouteTarget;
6
+ actions?: string | readonly string[];
7
+ guard?: (args: {
8
+ context: unknown;
9
+ event: unknown;
10
+ }) => boolean;
11
+ };
12
+ export type LoweredAtomicState = {
13
+ on: Record<string, LoweredTransition | readonly LoweredTransition[]>;
14
+ };
15
+ export declare function buildPassiveState(config: PassiveModeConfig<InternalCtx, {
16
+ type: string;
17
+ }>, lift: LiftContext | undefined, deps: Readonly<Record<string, unknown>>): LoweredAtomicState;
18
+ export {};
19
+ //# sourceMappingURL=buildPassiveState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildPassiveState.d.ts","sourceRoot":"","sources":["../src/buildPassiveState.ts"],"names":[],"mappings":"AAeA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,KAAK,EAER,UAAU,EACV,iBAAiB,EACjB,WAAW,EACd,MAAM,YAAY,CAAC;AAIpB,KAAK,WAAW,GAAG,UAAU,CAAC;AAK9B,MAAM,MAAM,iBAAiB,GAAG;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACrC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC7B,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,iBAAiB,EAAE,CAAC,CAAC;CACxE,CAAC;AAyCF,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,iBAAiB,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,kBAAkB,CASpB"}
@@ -0,0 +1,59 @@
1
+ // Lower a passive `ModeConfig` to an XState atomic-state config.
2
+ // Spec: docs/specs/004-tasks.md Phase 5.4 + docs/specs/004-xstate-agent-wrapper.md §Mapping.
3
+ // Spec 005: `on[*].guard` gains a `deps` parameter; the wrapper captures the
4
+ // agent's frozen `deps` reference in the generated guard's closure.
5
+ //
6
+ // Passive leaves carry only `on` handlers (no `behavior`, no actor). The
7
+ // mapping is structurally identity for `target` / `actions`:
8
+ // { on: { EVENT: { target, actions, guard } } } → same shape on XState.
9
+ // `actions` strings reference entries in `defineAgent.actions` (registered
10
+ // in `setup({ actions })` — those already close over `deps`).
11
+ //
12
+ // `END` targets stay as the `END` symbol in the output; the compound-level
13
+ // `$end` substate injection + END→`$end` rewrite happens in slice 5.11
14
+ // before the final XState `createMachine` call.
15
+ import { liftGuard } from "./contextLift.js";
16
+ // `Array.isArray` narrows to `any[]`, which fails to subtract
17
+ // `readonly T[]` from a union like `T | readonly T[]`. This typed predicate
18
+ // gives TypeScript the narrowing it needs without leaking `any`.
19
+ function isReadonlyArray(value) {
20
+ return Array.isArray(value);
21
+ }
22
+ function mapTransition(t, lift, deps) {
23
+ const out = {};
24
+ if (t.target !== undefined)
25
+ out.target = t.target;
26
+ if (t.actions !== undefined)
27
+ out.actions = t.actions;
28
+ if (t.guard !== undefined) {
29
+ // The user typed `guard` as `({ context, event, deps }) => boolean`
30
+ // with concrete C/E types. Widening to `unknown` is safe — XState
31
+ // calls the function with the matching shape at runtime, and the
32
+ // user code's narrowing is preserved as it was written.
33
+ //
34
+ // Under a compound-local context lift, route the call through
35
+ // `liftGuard` so the user sees the virtual merged view (inherited
36
+ // + local) — same view the type system promised. `deps` is
37
+ // forwarded verbatim regardless of lift.
38
+ const userGuard = t.guard;
39
+ if (lift !== undefined) {
40
+ out.guard = liftGuard(userGuard, lift, deps);
41
+ }
42
+ else {
43
+ out.guard = ({ context, event }) => userGuard({ context, event, deps });
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+ export function buildPassiveState(config, lift, deps) {
49
+ const on = {};
50
+ for (const [eventType, transitions] of Object.entries(config.on)) {
51
+ if (transitions === undefined)
52
+ continue;
53
+ on[eventType] = isReadonlyArray(transitions)
54
+ ? transitions.map((t) => mapTransition(t, lift, deps))
55
+ : mapTransition(transitions, lift, deps);
56
+ }
57
+ return { on };
58
+ }
59
+ //# sourceMappingURL=buildPassiveState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildPassiveState.js","sourceRoot":"","sources":["../src/buildPassiveState.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,6FAA6F;AAC7F,6EAA6E;AAC7E,oEAAoE;AACpE,EAAE;AACF,yEAAyE;AACzE,6DAA6D;AAC7D,0EAA0E;AAC1E,2EAA2E;AAC3E,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,uEAAuE;AACvE,gDAAgD;AAEhD,OAAO,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAC;AAyB/D,8DAA8D;AAC9D,4EAA4E;AAC5E,iEAAiE;AACjE,SAAS,eAAe,CAAI,KAAuB;IAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CAClB,CAAiD,EACjD,IAA6B,EAC7B,IAAuC;IAEvC,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;QAAE,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC;IACrD,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,oEAAoE;QACpE,kEAAkE;QAClE,iEAAiE;QACjE,wDAAwD;QACxD,EAAE;QACF,8DAA8D;QAC9D,kEAAkE;QAClE,2DAA2D;QAC3D,yCAAyC;QACzC,MAAM,SAAS,GAAG,CAAC,CAAC,KAIP,CAAC;QACd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACJ,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC7B,MAAwD,EACxD,IAA6B,EAC7B,IAAuC;IAEvC,MAAM,EAAE,GAA6B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QAC/D,IAAI,WAAW,KAAK,SAAS;YAAE,SAAS;QACxC,EAAE,CAAC,SAAS,CAAC,GAAG,eAAe,CAAC,WAAW,CAAC;YACxC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;YACtD,CAAC,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,CAAC;AAClB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type AnyStateMachine } from "xstate";
2
+ import type { AgentConfig, ModesMap } from "./types.js";
3
+ export declare function compile<TContext, TEvents extends {
4
+ type: string;
5
+ }, TModes extends ModesMap<TContext, TEvents, TDeps>, TDeps extends Record<string, unknown> = Record<string, never>>(config: AgentConfig<TContext, TEvents, TModes, TDeps>, frozenDeps: Readonly<TDeps>): AnyStateMachine;
6
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAoBA,OAAO,EAA6B,KAAK,eAAe,EAA0B,MAAM,QAAQ,CAAC;AAmBjG,OAAO,KAAK,EACR,WAAW,EAGX,QAAQ,EAGX,MAAM,YAAY,CAAC;AAkLpB,wBAAgB,OAAO,CACnB,QAAQ,EACR,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,MAAM,SAAS,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,EACjD,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAE7D,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EACrD,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,GAC5B,eAAe,CA6CjB"}
@@ -0,0 +1,164 @@
1
+ // `compile` — lowers an `AgentConfig` tree into an XState machine.
2
+ //
3
+ // Spec: docs/specs/004-xstate-agent-wrapper.md §"Wrapper internals (compile.ts)"
4
+ // + §Mapping. Tasks: docs/specs/004-tasks.md Phase 5.16 (final emit).
5
+ // Spec 005: closes over a frozen `deps` reference and threads it to every
6
+ // build* helper. `deps` is NOT placed in XState context — it lives
7
+ // in callback closures, so `JSON.stringify(actor.getSnapshot().context)`
8
+ // returns only TContext (+ synthetic compound-local slots).
9
+ //
10
+ // This is the only file in `atlas` that calls `setup().createMachine`. It
11
+ // composes the toolkit slices built in 5.1–5.15:
12
+ // - validateTargets, validateRoutes (fail-fast at machine creation)
13
+ // - walk + actorName + buildActors (active-leaf actor map)
14
+ // - buildActions (named-actions map)
15
+ // - buildActiveState / buildPassiveState (per-leaf lowering, lift-aware)
16
+ // - contextLift (LiftContext + entry/exit on compounds that declare context)
17
+ // - injectEnd (per-level `$end` substate + END → name rewrite)
18
+ //
19
+ // `defineAgent` returns this value verbatim.
20
+ import { setup } from "xstate";
21
+ import { buildActions } from "./buildActions.js";
22
+ import { buildActiveState } from "./buildActiveState.js";
23
+ import { buildActors } from "./buildActors.js";
24
+ import { buildPassiveState } from "./buildPassiveState.js";
25
+ import { compoundLocalKey, makeCompoundEntry, makeCompoundExit, } from "./contextLift.js";
26
+ import { END_SUBSTATE, hasEndReference, pickEndName, rewriteEndTargets, } from "./injectEnd.js";
27
+ import { END } from "./types.js";
28
+ import { validateRoutes } from "./validateRoutes.js";
29
+ import { validateTargets } from "./validateTargets.js";
30
+ import { walk } from "./walk.js";
31
+ function asCarrier(value) {
32
+ return value;
33
+ }
34
+ // ── END detection / rewriting at a level ─────────────────────────────
35
+ function isCompound(node) {
36
+ return "initial" in node && "states" in node;
37
+ }
38
+ function isFinal(node) {
39
+ return "type" in node && node.type === "final";
40
+ }
41
+ function nodeExitsViaEnd(node) {
42
+ if (isFinal(node))
43
+ return false;
44
+ if (isCompound(node)) {
45
+ return node.onDone !== undefined && node.onDone.target === END;
46
+ }
47
+ return hasEndReference(node);
48
+ }
49
+ function rewriteNodeEnd(node, endName) {
50
+ if (isFinal(node))
51
+ return node;
52
+ if (isCompound(node)) {
53
+ if (node.onDone !== undefined && node.onDone.target === END) {
54
+ return { ...node, onDone: { target: endName } };
55
+ }
56
+ return node;
57
+ }
58
+ return rewriteEndTargets(node, endName);
59
+ }
60
+ function injectEndAtLevel(states) {
61
+ let anyEnd = false;
62
+ for (const node of Object.values(states)) {
63
+ if (nodeExitsViaEnd(node)) {
64
+ anyEnd = true;
65
+ break;
66
+ }
67
+ }
68
+ if (!anyEnd)
69
+ return states;
70
+ const endName = pickEndName(Object.keys(states));
71
+ const rewritten = {};
72
+ for (const [name, node] of Object.entries(states)) {
73
+ rewritten[name] = rewriteNodeEnd(node, endName);
74
+ }
75
+ rewritten[endName] = END_SUBSTATE;
76
+ return rewritten;
77
+ }
78
+ // ── Recursive mode-map lowering ──────────────────────────────────────
79
+ function joinPath(parent, name) {
80
+ return parent === "" ? name : `${parent}.${name}`;
81
+ }
82
+ function buildStatesMap(modes, parentLift, parentPath, deps) {
83
+ const out = {};
84
+ for (const [name, value] of Object.entries(modes)) {
85
+ const path = joinPath(parentPath, name);
86
+ const carrier = asCarrier(value);
87
+ if (carrier.__kind === "leaf") {
88
+ const config = carrier.config;
89
+ if ("behavior" in config && config.behavior !== undefined) {
90
+ const slot = { kind: "leaf", path, config };
91
+ out[name] = buildActiveState(slot, parentLift, deps);
92
+ }
93
+ else {
94
+ out[name] = buildPassiveState(config, parentLift, deps);
95
+ }
96
+ continue;
97
+ }
98
+ // Compound.
99
+ const cfg = carrier.config;
100
+ let childLift = parentLift;
101
+ let ownEntry;
102
+ let ownExit;
103
+ if (cfg.context !== undefined) {
104
+ const ctx = cfg.context;
105
+ const newLift = {
106
+ key: compoundLocalKey(path),
107
+ inherit: ctx.inherit,
108
+ initialLocal: ctx.local,
109
+ parent: parentLift,
110
+ };
111
+ childLift = newLift;
112
+ ownEntry = makeCompoundEntry(newLift);
113
+ ownExit = makeCompoundExit(newLift);
114
+ }
115
+ const childStatesRaw = buildStatesMap(cfg.modes, childLift, path, deps);
116
+ const childStates = injectEndAtLevel(childStatesRaw);
117
+ const compound = {
118
+ initial: cfg.initial,
119
+ states: childStates,
120
+ onDone: { target: cfg.onDone },
121
+ };
122
+ if (ownEntry !== undefined)
123
+ compound.entry = ownEntry;
124
+ if (ownExit !== undefined)
125
+ compound.exit = ownExit;
126
+ out[name] = compound;
127
+ }
128
+ return out;
129
+ }
130
+ // ── Public entry ─────────────────────────────────────────────────────
131
+ export function compile(config, frozenDeps) {
132
+ const rawModes = config.modes;
133
+ // Erase TDeps for the loose internal contract — every build* helper takes
134
+ // `Readonly<Record<string, unknown>>` and the user's concrete type has
135
+ // already been enforced at the call site.
136
+ const deps = frozenDeps;
137
+ // Fail-fast at machine creation — spec verification lines 821 + 824.
138
+ validateTargets(rawModes);
139
+ validateRoutes(rawModes);
140
+ const slots = walk(rawModes);
141
+ const actors = buildActors(slots, deps);
142
+ const actions = buildActions(config.actions, deps);
143
+ const lowered = buildStatesMap(rawModes, undefined, "", deps);
144
+ const finalStates = injectEndAtLevel(lowered);
145
+ // The wrapper's type contract was discharged at the user's call site
146
+ // (defineMode / defineCompoundMode / defineAgent). At this internal layer
147
+ // every shape is `unknown`-typed by construction. XState's `setup` types
148
+ // are too strict to satisfy generically — its `MachineContext` constraint
149
+ // collides with `TContext` being arbitrary — so we hand it the already-
150
+ // shaped values through `unknown`. The output is `AnyStateMachine`, which
151
+ // is what `defineAgent` returns.
152
+ const looseSetup = setup;
153
+ const machine = looseSetup({
154
+ actors,
155
+ actions,
156
+ }).createMachine({
157
+ id: config.id,
158
+ initial: config.initial,
159
+ context: config.context,
160
+ states: finalStates,
161
+ });
162
+ return machine;
163
+ }
164
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { assign } from "xstate";
2
+ export type LiftContext = {
3
+ readonly key: string;
4
+ readonly inherit: readonly string[];
5
+ readonly initialLocal: Readonly<Record<string, unknown>>;
6
+ readonly parent?: LiftContext;
7
+ };
8
+ export declare function compoundLocalKey(path: string): string;
9
+ export declare function liftInput(userInput: (args: {
10
+ context: unknown;
11
+ deps: Readonly<Record<string, unknown>>;
12
+ }) => unknown, lift: LiftContext, deps: Readonly<Record<string, unknown>>): (args: {
13
+ context: unknown;
14
+ }) => unknown;
15
+ export declare function liftExitAssign(userAssign: (args: {
16
+ context: unknown;
17
+ payload: unknown;
18
+ deps: Readonly<Record<string, unknown>>;
19
+ }) => object, lift: LiftContext, deps: Readonly<Record<string, unknown>>): ReturnType<typeof assign>;
20
+ export declare function liftErrorAssign(userAssign: (args: {
21
+ context: unknown;
22
+ error: unknown;
23
+ deps: Readonly<Record<string, unknown>>;
24
+ }) => object, lift: LiftContext, deps: Readonly<Record<string, unknown>>): ReturnType<typeof assign>;
25
+ export declare function liftGuard(userGuard: (args: {
26
+ context: unknown;
27
+ event: unknown;
28
+ deps: Readonly<Record<string, unknown>>;
29
+ }) => boolean, lift: LiftContext, deps: Readonly<Record<string, unknown>>): (args: {
30
+ context: unknown;
31
+ event: unknown;
32
+ }) => boolean;
33
+ export declare function makeCompoundEntry(lift: LiftContext): ReturnType<typeof assign>;
34
+ export declare function makeCompoundExit(lift: LiftContext): ReturnType<typeof assign>;
35
+ //# sourceMappingURL=contextLift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextLift.d.ts","sourceRoot":"","sources":["../src/contextLift.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,MAAM,WAAW,GAAG;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CACjC,CAAC;AAOF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMrD;AAmGD,wBAAgB,SAAS,CACrB,SAAS,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,KAAK,OAAO,EAC3F,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAKzC;AAKD,wBAAgB,cAAc,CAC1B,UAAU,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,KAAK,MAAM,EAC7G,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,UAAU,CAAC,OAAO,MAAM,CAAC,CAQ3B;AAGD,wBAAgB,eAAe,CAC3B,UAAU,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,KAAK,MAAM,EAC3G,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,UAAU,CAAC,OAAO,MAAM,CAAC,CAQ3B;AAGD,wBAAgB,SAAS,CACrB,SAAS,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAAE,KAAK,OAAO,EAC3G,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACxC,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAKzD;AAOD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAI9E;AAKD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAI7E"}
@@ -0,0 +1,172 @@
1
+ // Phase 5.13 helpers: compound-local context lift.
2
+ //
3
+ // Spec: docs/specs/004-tasks.md Phase 5.13,
4
+ // docs/specs/004-xstate-agent-wrapper.md §"Lexical scoping of context"
5
+ // (lines 104-111) and §Mapping line 626.
6
+ //
7
+ // A `CompoundMode` with `context: { inherit, local }` exposes a narrowed view
8
+ // to its children: `Pick<TParent, inherit[number]> & typeof local`. At runtime the
9
+ // wrapper materializes this view by:
10
+ // - allocating a slot under a generated root-context key (`__<path>_local`)
11
+ // initialized to `local` on every entry, cleared on every exit
12
+ // - wrapping each child callback (`input`, `assign`, passive `guard`) so
13
+ // the `context` they see is the virtual merged view, and any `Partial`
14
+ // they return is split back to the correct destination
15
+ //
16
+ // Inherit keys are read live from the parent (no copy on entry, no
17
+ // project-back on exit) — writes propagate through to the owning slot in
18
+ // the same step. Local keys live in this compound's own slot and reset
19
+ // automatically on re-entry. Nested compounds chain: a nested `LiftContext`
20
+ // carries a `parent` reference that the helpers walk to resolve inherit
21
+ // reads/writes.
22
+ //
23
+ // The actual emission of `entry`/`exit` actions onto a lowered compound
24
+ // shape, plus the threading of `LiftContext` through the walk, lands in
25
+ // slice 5.16; 5.13 ships the toolkit and integrates it with
26
+ // `buildActiveState` / `buildPassiveState` via an optional `lift` argument.
27
+ import { assign } from "xstate";
28
+ // Slot-name generator. Path is the compound's dotted slot
29
+ // (e.g. `socratic.evaluating` → `__socratic_evaluating_local`). The `__`
30
+ // prefix + `_local` suffix make collisions with user-declared agent-context
31
+ // keys vanishingly rare; dots are replaced with `_` so the key is a valid
32
+ // JS identifier.
33
+ export function compoundLocalKey(path) {
34
+ if (path === "") {
35
+ throw new Error("atlas/contextLift: compoundLocalKey called with empty path");
36
+ }
37
+ const safe = path.replace(/\./g, "_");
38
+ return `__${safe}_local`;
39
+ }
40
+ function localKeys(lift) {
41
+ return Object.keys(lift.initialLocal);
42
+ }
43
+ // Build the parent's effective view from the root context. For a top-level
44
+ // compound (no parent), the parent view IS the root context. For a nested
45
+ // compound, recurse into the enclosing lift to materialize ITS subContext.
46
+ function buildParentView(rootContext, parent) {
47
+ if (parent === undefined)
48
+ return rootContext;
49
+ return buildSubContext(rootContext, parent);
50
+ }
51
+ // Build the virtual subContext a child callback sees: inherit keys read
52
+ // from the parent view (live), local keys read from this compound's slot.
53
+ function buildSubContext(rootContext, lift) {
54
+ const parentView = buildParentView(rootContext, lift.parent);
55
+ const sub = {};
56
+ for (const k of lift.inherit) {
57
+ sub[k] = parentView[k];
58
+ }
59
+ const slot = rootContext[lift.key];
60
+ if (slot !== undefined && typeof slot === "object" && slot !== null) {
61
+ const slotRec = slot;
62
+ for (const k of localKeys(lift)) {
63
+ sub[k] = slotRec[k];
64
+ }
65
+ }
66
+ return sub;
67
+ }
68
+ // Walk up the parent chain to find which ancestor declared `key` as a
69
+ // local. If no ancestor owns it, the key lives in the agent's root context.
70
+ function findInheritOwner(key, parent) {
71
+ if (parent === undefined)
72
+ return { kind: "root" };
73
+ if (localKeys(parent).includes(key))
74
+ return { kind: "slot", slotKey: parent.key };
75
+ return findInheritOwner(key, parent.parent);
76
+ }
77
+ // Split the user's `Partial<combined>` return into a root-context patch
78
+ // XState's `assign` can apply. Local writes update this compound's slot;
79
+ // inherit writes update either the agent root or an ancestor's slot,
80
+ // depending on where the key was declared as local.
81
+ function splitUserUpdate(update, rootContext, lift) {
82
+ const ownLocals = localKeys(lift);
83
+ const rootPatch = {};
84
+ const slotPatches = {};
85
+ function touchSlot(slotKey, k, v) {
86
+ const existing = slotPatches[slotKey] ?? {};
87
+ existing[k] = v;
88
+ slotPatches[slotKey] = existing;
89
+ }
90
+ for (const [k, v] of Object.entries(update)) {
91
+ if (ownLocals.includes(k)) {
92
+ touchSlot(lift.key, k, v);
93
+ continue;
94
+ }
95
+ if (lift.inherit.includes(k)) {
96
+ const owner = findInheritOwner(k, lift.parent);
97
+ if (owner.kind === "root") {
98
+ rootPatch[k] = v;
99
+ }
100
+ else {
101
+ touchSlot(owner.slotKey, k, v);
102
+ }
103
+ continue;
104
+ }
105
+ // Out-of-scope key: the type system already rejected it. A bypass
106
+ // via `as` reaches here — drop silently rather than leak into root.
107
+ }
108
+ // XState `assign` is shallow at the root level — we must hand it the
109
+ // full new slot object, not a delta.
110
+ for (const [slotKey, patch] of Object.entries(slotPatches)) {
111
+ const current = (rootContext[slotKey] ?? {});
112
+ rootPatch[slotKey] = { ...current, ...patch };
113
+ }
114
+ return rootPatch;
115
+ }
116
+ // Wrap a user `input({ context, deps })` callback so it sees the virtual
117
+ // view. `deps` is captured verbatim from the closure that `compile.ts`
118
+ // threaded down — the lift only transforms `context`.
119
+ export function liftInput(userInput, lift, deps) {
120
+ return ({ context }) => {
121
+ const sub = buildSubContext(context, lift);
122
+ return userInput({ context: sub, deps });
123
+ };
124
+ }
125
+ // Wrap a user `assign({ context, payload, deps }) => Partial<combined>`
126
+ // callback, returning an XState `assign(...)` action that applies the split
127
+ // update. `deps` is forwarded by identity from the wrapper's closure.
128
+ export function liftExitAssign(userAssign, lift, deps) {
129
+ return assign(({ context, event }) => {
130
+ const root = context;
131
+ const sub = buildSubContext(root, lift);
132
+ const payload = event.output.payload;
133
+ const update = userAssign({ context: sub, payload, deps });
134
+ return splitUserUpdate(update, root, lift);
135
+ });
136
+ }
137
+ // Wrap a user `assign({ context, error, deps })` callback (error routes).
138
+ export function liftErrorAssign(userAssign, lift, deps) {
139
+ return assign(({ context, event }) => {
140
+ const root = context;
141
+ const sub = buildSubContext(root, lift);
142
+ const error = event.error;
143
+ const update = userAssign({ context: sub, error, deps });
144
+ return splitUserUpdate(update, root, lift);
145
+ });
146
+ }
147
+ // Wrap a user `guard({ context, event, deps })` (passive `on` transitions).
148
+ export function liftGuard(userGuard, lift, deps) {
149
+ return ({ context, event }) => {
150
+ const sub = buildSubContext(context, lift);
151
+ return userGuard({ context: sub, event, deps });
152
+ };
153
+ }
154
+ // XState `entry` action: initialize this compound's local slot. A fresh
155
+ // shallow copy of `initialLocal` per entry so subsequent mutations stay
156
+ // scoped to this activation. Deep cloning is not promised — the spec's
157
+ // canonical local is primitive-valued (`{ attempts: 0 }`); nested object
158
+ // values are shared by reference.
159
+ export function makeCompoundEntry(lift) {
160
+ return assign({
161
+ [lift.key]: () => ({ ...lift.initialLocal }),
162
+ });
163
+ }
164
+ // XState `exit` action: clear this compound's local slot. The next entry
165
+ // (if any) re-initializes via `makeCompoundEntry`, satisfying the
166
+ // reset-on-re-entry invariant from spec line 109.
167
+ export function makeCompoundExit(lift) {
168
+ return assign({
169
+ [lift.key]: () => undefined,
170
+ });
171
+ }
172
+ //# sourceMappingURL=contextLift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contextLift.js","sourceRoot":"","sources":["../src/contextLift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import type { AnyStateMachine } from "xstate";
2
+ import type { AgentConfig, ModesMap } from "./types.js";
3
+ /**
4
+ * Construct the **root agent** — compiles the declarative `AgentConfig` into
5
+ * a runnable XState machine. This is the single boundary where the wrapper
6
+ * touches `xstate`: the return type is `AnyStateMachine`, so callers feed it
7
+ * straight into `createActor`, the inspector, and existing tests.
8
+ *
9
+ * The agent's `modes` map can mix `Mode`s (leaves) and nested `CompoundMode`s
10
+ * freely. The compile step lowers them, validates sibling-target references,
11
+ * injects the `END` synthetic state per-compound when referenced, and wires
12
+ * the payload-driven `routes` into XState transitions.
13
+ *
14
+ * @template TContext The agent's root context shape. Constrained to
15
+ * `JsonCompatible<TContext>` so the snapshot round-trips
16
+ * through arbitrary storage without custom encoding.
17
+ * @template TEvents The agent's full event union. Each variant must have a
18
+ * `type: string` discriminant. Pass `{} as Ev` to the
19
+ * `events` field — only its type matters; it's a phantom.
20
+ * @template TModes The root `modes` map. `initial` is keyed against this
21
+ * type so a typo is a compile error.
22
+ * @template TDeps Frozen container of external resources. Defaults to
23
+ * `Record<string, never>` when `deps` is omitted —
24
+ * callbacks still receive `deps`, typed as the empty
25
+ * object, so the envelope shape stays uniform.
26
+ *
27
+ * @param config `{ id, initial, context, events, deps?, actions?, modes }`.
28
+ * `actions` registers reusable, deps-aware callbacks (each
29
+ * returning `Partial<TContext>`) referenced by name from
30
+ * passive `on[event].actions`. The wrapper wraps them in
31
+ * `assign(...)` at compile time, so user code never imports
32
+ * from `xstate`.
33
+ *
34
+ * @returns An `AnyStateMachine` ready to pass to `createActor`.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const agent = defineAgent<Ctx, Ev, Modes, { db: Driver; logger: Logger }>({
39
+ * id: "zoe",
40
+ * initial: "listening",
41
+ * context: { messages: [], attempts: 0 },
42
+ * events: {} as Ev,
43
+ * deps: { db: realDb, logger: pino() },
44
+ * actions: {
45
+ * appendUserMsg: ({ context, event, deps }) =>
46
+ * event.type === "USER_MSG"
47
+ * ? { messages: [...context.messages, { role: "user", content: event.text }] }
48
+ * : {},
49
+ * },
50
+ * modes: { listening, classifying, greetings, socratic },
51
+ * });
52
+ *
53
+ * const actor = createActor(agent).start();
54
+ * actor.send({ type: "USER_MSG", text: "hi" });
55
+ * ```
56
+ */
57
+ export declare function defineAgent<TContext, TEvents extends {
58
+ type: string;
59
+ }, TModes extends ModesMap<TContext, TEvents, TDeps>, TDeps extends Record<string, unknown> = Record<string, never>>(config: AgentConfig<TContext, TEvents, TModes, TDeps>): AnyStateMachine;
60
+ //# sourceMappingURL=defineAgent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defineAgent.d.ts","sourceRoot":"","sources":["../src/defineAgent.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,WAAW,CACvB,QAAQ,EACR,OAAO,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAChC,MAAM,SAAS,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,EACjD,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAC/D,MAAM,EAAE,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,eAAe,CAOxE"}