@sublang/slc 0.11.0 → 0.12.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.
@@ -13,9 +13,10 @@ interface MachineLike {
13
13
  provide(implementations: {
14
14
  actors: Record<string, unknown>;
15
15
  }): MachineLike;
16
- /** XState exposes `setup()`-registered guards here. */
16
+ /** XState exposes `setup()`-registered guards and actions here. */
17
17
  implementations?: {
18
18
  guards?: Record<string, unknown>;
19
+ actions?: Record<string, unknown>;
19
20
  };
20
21
  /** XState's resolved state nodes expose the actual invocation actor ids. */
21
22
  root?: ResolvedStateNodeLike;
@@ -1 +1 @@
1
- {"version":3,"file":"verify-coverage.d.ts","sourceRoot":"","sources":["../src/verify-coverage.ts"],"names":[],"mappings":"AA8BA,OAAO,EAgBL,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,sEAAsE;AACtE,eAAO,MAAM,aAAa,YAAY,CAAC;AAavC,iFAAiF;AACjF,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,cAGC;CACF;AAyDD,6DAA6D;AAC7D,UAAU,WAAW;IACnB,MAAM,EAAE,iBAAiB,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,OAAO,CAAC,eAAe,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,WAAW,CAAC;IAC3E,uDAAuD;IACvD,eAAe,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IACvD,4EAA4E;IAC5E,IAAI,CAAC,EAAE,qBAAqB,CAAC;CAC9B;AAsBD,UAAU,qBAAqB;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjC;AAqJD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,WAAW,CAwB3D;AAgnCD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAU/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,EAC7D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,GAAE,SAAS,OAAO,EAAO,GACnC,OAAO,CAST;AAg6DD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CA+IjE;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,OAAO,EAClB,IAAI,GAAE;IACJ,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,MAAM,EAAE,CAAC,CAkCnB;AAktBD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CA8BT;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAmBnD"}
1
+ {"version":3,"file":"verify-coverage.d.ts","sourceRoot":"","sources":["../src/verify-coverage.ts"],"names":[],"mappings":"AA8BA,OAAO,EAeL,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,sEAAsE;AACtE,eAAO,MAAM,aAAa,YAAY,CAAC;AAavC,iFAAiF;AACjF,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,cAGC;CACF;AAyDD,6DAA6D;AAC7D,UAAU,WAAW;IACnB,MAAM,EAAE,iBAAiB,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,OAAO,CAAC,eAAe,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,WAAW,CAAC;IAC3E,mEAAmE;IACnE,eAAe,CAAC,EAAE;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC,CAAC;IACF,4EAA4E;IAC5E,IAAI,CAAC,EAAE,qBAAqB,CAAC;CAC9B;AAyBD,UAAU,qBAAqB;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC/C,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjC;AAqJD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,WAAW,CAwB3D;AAopCD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAU/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,CAAC,GAAG,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,EAC7D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,GAAE,SAAS,OAAO,EAAO,GACnC,OAAO,CAST;AAg6DD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CA+IjE;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,OAAO,EAClB,IAAI,GAAE;IACJ,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACf,GACL,OAAO,CAAC,MAAM,EAAE,CAAC,CAkCnB;AAyvBD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CA8BT;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAmBnD"}
@@ -24,7 +24,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
24
24
  import { join } from 'node:path';
25
25
  import { setTimeout as delay } from 'node:timers/promises';
26
26
  import { createActor, fromPromise } from 'xstate';
27
- import { AWAIT_BOSS_REPLY_STATE, BOSS_REPLY_EVENT, CONTROLLER_ACTION_GUARDS, INTERRUPT_EVENT, NEEDS_BOSS_REPLY, VERIFY_MODULE, controllerDecisionNearMiss, enumerateCaptainStates, enumerateScriptStates, initialStateTarget, isControllerDecisionResult, isControllerMachine, loadFsmModule, normalizeArms, } from './verify.js';
27
+ import { AWAIT_BOSS_REPLY_STATE, BOSS_REPLY_EVENT, INTERRUPT_EVENT, NEEDS_BOSS_REPLY, VERIFY_MODULE, controllerDecisionNearMiss, enumerateCaptainStates, enumerateScriptStates, initialStateTarget, isControllerDecisionResult, isControllerMachine, loadFsmModule, normalizeArms, } from './verify.js';
28
28
  /** The `gears2fsm`-mandated captain actor name a machine declares. */
29
29
  export const CAPTAIN_ACTOR = 'captain';
30
30
  // Each check owns its resources, including concurrent checks and async actor
@@ -539,6 +539,40 @@ function tagsOf(state) {
539
539
  return [state.tags];
540
540
  return Array.isArray(state.tags) ? state.tags : [];
541
541
  }
542
+ /** XState's built-in actions that send the machine an event by themselves. */
543
+ const EVENT_RAISING_ACTION_TYPES = new Set([
544
+ 'xstate.raise',
545
+ 'xstate.sendTo',
546
+ 'xstate.enqueueActions',
547
+ ]);
548
+ /**
549
+ * Whether an `entry` or `exit` action list can move the machine without an
550
+ * external event: a built-in raise, send, or enqueue, written inline or behind
551
+ * a `setup()`-registered name. An action that only assigns keeps a parked leaf
552
+ * inert (DR-053).
553
+ */
554
+ function raisesEvent(machine, actions) {
555
+ const typeOf = (action) => {
556
+ if (typeof action === 'string')
557
+ return action;
558
+ if ((typeof action === 'object' && action !== null) ||
559
+ typeof action === 'function') {
560
+ const type = action.type;
561
+ return typeof type === 'string' ? type : undefined;
562
+ }
563
+ return undefined;
564
+ };
565
+ const list = actions === undefined ? [] : Array.isArray(actions) ? actions : [actions];
566
+ return list.some((action) => {
567
+ const type = typeOf(action);
568
+ if (type === undefined)
569
+ return false;
570
+ if (EVENT_RAISING_ACTION_TYPES.has(type))
571
+ return true;
572
+ const named = typeOf(machine.implementations?.actions?.[type]);
573
+ return named !== undefined && EVENT_RAISING_ACTION_TYPES.has(named);
574
+ });
575
+ }
542
576
  function stateRefForTarget(refs, target, source) {
543
577
  const absolute = target.startsWith('#');
544
578
  const normalized = absolute
@@ -2441,7 +2475,7 @@ async function runFsmCoverage(fsmModule, sourceText) {
2441
2475
  const detail = nearMiss.missing.length > 0
2442
2476
  ? `missing ${JSON.stringify(nearMiss.missing[0])}`
2443
2477
  : `extra ${JSON.stringify(nearMiss.extra[0])}`;
2444
- findings.push(`state ${captain.binding.stateId}: controller decision contract near-miss (${detail}); the controller domain requires exactly ${CONTROLLER_ACTION_GUARDS.join(', ')}`);
2478
+ findings.push(`state ${captain.binding.stateId}: controller decision contract near-miss (${detail}); the controller domain requires exactly ${nearMiss.domain.join(', ')}`);
2445
2479
  }
2446
2480
  for (const ref of parallelRefs) {
2447
2481
  await coverageYield();
@@ -2604,6 +2638,29 @@ async function runFsmCoverage(fsmModule, sourceText) {
2604
2638
  resolveGuard(machine, rawGuard) === undefined)) {
2605
2639
  continue;
2606
2640
  }
2641
+ // A controller's final defensive fallback rejects malformed actor
2642
+ // output. It is not a second business action for a valid result,
2643
+ // provided the parked leaf it enters cannot leave by itself.
2644
+ const targetRef = stateRefForTarget(refs, target, captain.ref);
2645
+ if (isControllerDecisionResult(state.result) &&
2646
+ rawGuard === undefined &&
2647
+ index === rawDoneArms.length - 1 &&
2648
+ targetRef !== undefined &&
2649
+ tagsOf(targetRef.state).includes('playbook.parked') &&
2650
+ targetRef.state.invoke === undefined &&
2651
+ targetRef.state.states === undefined &&
2652
+ !Object.hasOwn(targetRef.state, 'always') &&
2653
+ !Object.hasOwn(targetRef.state, 'after') &&
2654
+ !raisesEvent(machine, targetRef.state.entry) &&
2655
+ !raisesEvent(machine, targetRef.state.exit) &&
2656
+ !rawDoneArms
2657
+ .slice(0, index)
2658
+ .some((otherArm) => stateRefForTarget(refs, rawArmTarget(otherArm) ?? '', captain.ref) === targetRef) &&
2659
+ transitionArms(captain.invocation.onError).some((errorArm, errorIndex, errorArms) => errorIndex === errorArms.length - 1 &&
2660
+ armGuard(errorArm) === undefined &&
2661
+ stateRefForTarget(refs, rawArmTarget(errorArm) ?? '', captain.ref) === targetRef)) {
2662
+ continue;
2663
+ }
2607
2664
  const guard = orderedArmPredicate(machine, rawDoneArms, index);
2608
2665
  if (guard === undefined)
2609
2666
  continue;