@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.
- package/dist/verify-coverage.d.ts +2 -1
- package/dist/verify-coverage.d.ts.map +1 -1
- package/dist/verify-coverage.js +59 -2
- package/dist/verify-coverage.js.map +1 -1
- package/dist/verify.d.ts +3 -2
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +19 -8
- package/dist/verify.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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"}
|
package/dist/verify-coverage.js
CHANGED
|
@@ -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,
|
|
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 ${
|
|
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;
|