@sublang/slc 0.15.1 → 0.16.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.
@@ -1 +1 @@
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"}
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;AAirCD,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,CAqLjE;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;AAo0BD;;;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;IACtB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAoBnD"}
@@ -285,18 +285,20 @@ function initializedCoverageContext(machine, dynamic) {
285
285
  };
286
286
  }
287
287
  /** The `Output shall include` clause a result description declares. */
288
- const OUTPUT_CLAUSE = /Output shall include\s+(.+)$/;
289
- /** Every backticked `field:` property named inside that clause. */
290
- const REQUIRED_FIELD = /`([A-Za-z_$][A-Za-z0-9_$]*):/g;
288
+ const OUTPUT_CLAUSE = /(?:Output shall include|输出应包含)\s*(.*)$/s;
289
+ /** Every plain or annotated backticked identifier field inside that clause. */
290
+ const REQUIRED_FIELD = /`([A-Za-z_$][A-Za-z0-9_$]*)(?::[^`]*)?`/g;
291
291
  /** Structured fields named by the generic Captain result contracts. */
292
292
  const STRUCTURED_RESULT_FIELD = /\b(response|question|remainingPlan|nextPlaybookId|nextPlaybookInput)\b/g;
293
293
  function requiredFields(description) {
294
- const clause = OUTPUT_CLAUSE.exec(description)?.[1] ?? '';
294
+ const clause = OUTPUT_CLAUSE.exec(description)?.[1];
295
+ // An explicit clause owns the payload schema. Generic Captain words in the
296
+ // preceding outcome prose must not invent extra fields in a closed record.
297
+ // Preserve older Captain descriptions that predate explicit output clauses.
295
298
  return [
296
299
  ...new Set([
297
- ...[...clause.matchAll(REQUIRED_FIELD)].map((match) => match[1]),
298
- ...[...description.matchAll(STRUCTURED_RESULT_FIELD)].map((match) => match[1]),
299
- ]),
300
+ ...(clause ?? description).matchAll(clause === undefined ? STRUCTURED_RESULT_FIELD : REQUIRED_FIELD),
301
+ ].map((match) => match[1])),
300
302
  ];
301
303
  }
302
304
  function synthesizedFieldValue(field) {
@@ -623,6 +625,7 @@ function persistedSnapshotWithContext(provided, context) {
623
625
  function makeActor(machine, script, playbookScript = () => null, restoredContext, observeTransition) {
624
626
  coverageCheckpoint();
625
627
  const coverageErrors = [];
628
+ const coverageTransitions = [];
626
629
  const workActor = fromPromise(async ({ input, self, }) => {
627
630
  const output = await script(input ?? {}, self.id);
628
631
  if (output === null)
@@ -671,7 +674,18 @@ function makeActor(machine, script, playbookScript = () => null, restoredContext
671
674
  }
672
675
  const source = transition.source;
673
676
  const target = transition.target;
674
- observeTransition?.({
677
+ const event = inspection.event;
678
+ const arms = source?.transitions?.get(typeof event === 'object' && event !== null
679
+ ? String(event.type)
680
+ : '');
681
+ const observed = {
682
+ root: source === provided.root,
683
+ ...(typeof event === 'object' && event !== null
684
+ ? { event: event }
685
+ : {}),
686
+ ...(arms === undefined
687
+ ? {}
688
+ : { armIndex: arms.indexOf(transition) }),
675
689
  ...(typeof source === 'object' &&
676
690
  source !== null &&
677
691
  typeof source.id === 'string'
@@ -684,7 +698,9 @@ function makeActor(machine, script, playbookScript = () => null, restoredContext
684
698
  ? [candidate.id]
685
699
  : [])
686
700
  : [],
687
- });
701
+ };
702
+ coverageTransitions.push(observed);
703
+ observeTransition?.(observed);
688
704
  }
689
705
  }
690
706
  const event = inspection.event;
@@ -703,6 +719,9 @@ function makeActor(machine, script, playbookScript = () => null, restoredContext
703
719
  Object.defineProperty(actor, 'coverageErrors', {
704
720
  value: coverageErrors,
705
721
  });
722
+ Object.defineProperty(actor, 'coverageTransitions', {
723
+ value: coverageTransitions,
724
+ });
706
725
  actor.subscribe({ error: () => { } });
707
726
  actor.start();
708
727
  coverageCheckpoint();
@@ -2330,11 +2349,27 @@ export function fsmCoverageTestTimeout(fsmModule) {
2330
2349
  : playbooks.reduce((total, playbook) => total +
2331
2350
  transitionArms(playbook.invocation.onDone).length +
2332
2351
  transitionArms(playbook.invocation.onError).length, 0);
2352
+ if (!controller) {
2353
+ for (const [index, arm] of transitionArms((config.on ?? {})[INTERRUPT_EVENT]).entries()) {
2354
+ const targetName = rawArmTarget(arm);
2355
+ const target = targetName === undefined
2356
+ ? undefined
2357
+ : stateRefForTarget(refs, targetName);
2358
+ const captain = captains.find((candidate) => target !== undefined && sameStateRef(candidate.ref, target));
2359
+ if (captain !== undefined &&
2360
+ captain.binding.actor !== 'script' &&
2361
+ !interruptDriveForRef(machine, refs, captain.ref, captainPublicStateId(captain), [], index).satisfiable)
2362
+ pathSearches += 1;
2363
+ }
2364
+ }
2333
2365
  for (const captain of captains) {
2334
2366
  const resultKeys = Object.keys(captain.binding.result);
2335
2367
  const guardedDoneArms = transitionArms(captain.invocation.onDone).filter((arm) => armGuard(arm) !== undefined).length;
2336
2368
  const errorArms = transitionArms(captain.invocation.onError).length;
2337
- if (!controller && rootInterruptProbes === 0) {
2369
+ if (!controller &&
2370
+ (rootInterruptProbes === 0 ||
2371
+ (captain.binding.actor !== 'script' &&
2372
+ !interruptDriveForRef(machine, refs, captain.ref, captainInterruptTarget(captain)).satisfiable))) {
2338
2373
  pathSearches +=
2339
2374
  resultKeys.length +
2340
2375
  (resultKeys.includes(NEEDS_BOSS_REPLY) ? 1 : 0) +
@@ -2559,6 +2594,45 @@ async function runFsmCoverage(fsmModule, sourceText) {
2559
2594
  }
2560
2595
  const drive = interruptDriveForRef(machine, refs, target, targetId, sourceCandidates, armIndex);
2561
2596
  if (!drive.satisfiable) {
2597
+ if (!isController &&
2598
+ targetCaptain !== undefined &&
2599
+ targetCaptain.binding.actor !== 'script') {
2600
+ const guard = orderedArmPredicate(machine, transitionArms((machine.config.on ?? {})[INTERRUPT_EVENT]), armIndex);
2601
+ const reached = await searchCoverageEntry(machine, { kind: 'acting', value: targetCaptain }, refs, captains, playbooks, sourceCandidates, async (actor) => {
2602
+ try {
2603
+ if (guard === undefined ||
2604
+ !guard.run({
2605
+ context: actor.getSnapshot().context,
2606
+ event: drive.event,
2607
+ }))
2608
+ return false;
2609
+ }
2610
+ catch {
2611
+ coverageCheckpoint();
2612
+ return false;
2613
+ }
2614
+ const before = actor.coverageTransitions.length;
2615
+ actor.send(drive.event);
2616
+ const completed = await settle(actor, atState(target));
2617
+ return (completed &&
2618
+ actor.coverageTransitions
2619
+ .slice(before)
2620
+ .some((transition) => transition.root &&
2621
+ transition.armIndex === armIndex &&
2622
+ transition.event === drive.event &&
2623
+ transition.targetIds.includes(resolvedStateNode(machine, target)?.id ?? '')));
2624
+ });
2625
+ if (reached.covered)
2626
+ continue;
2627
+ if (reached.inputFailure !== undefined) {
2628
+ findings.push(`${INTERRUPT_EVENT} target ${arm.target}: ${reached.inputFailure}`);
2629
+ continue;
2630
+ }
2631
+ if (reached.limitation !== undefined) {
2632
+ findings.push(`${INTERRUPT_EVENT} target ${arm.target}: ${reached.limitation}`);
2633
+ continue;
2634
+ }
2635
+ }
2562
2636
  findings.push(`${INTERRUPT_EVENT} target ${arm.target} is unsatisfiable under context/event probing`);
2563
2637
  continue;
2564
2638
  }
@@ -2608,7 +2682,10 @@ async function runFsmCoverage(fsmModule, sourceText) {
2608
2682
  return findings;
2609
2683
  for (const captain of captains) {
2610
2684
  await coverageYield();
2611
- if (!canJump && !isController) {
2685
+ if (!isController &&
2686
+ (!canJump ||
2687
+ (captain.binding.actor !== 'script' &&
2688
+ !interruptDriveForRef(machine, refs, captain.ref, captainInterruptTarget(captain), sourceCandidates).satisfiable))) {
2612
2689
  findings.push(...(await probeNonPreemptiveActor(machine, captain, refs, captains, playbooks, [...stateCandidates, ...sourceCandidates])));
2613
2690
  continue;
2614
2691
  }
@@ -3021,9 +3098,10 @@ export async function emitFsmCoverageTest(opts) {
3021
3098
  fsmSourceFile: `./${opts.basename}.fsm.ts`,
3022
3099
  verifyModule: opts.verifyModule ?? VERIFY_MODULE,
3023
3100
  });
3024
- await mkdir(opts.artifactDir, { recursive: true });
3101
+ if (opts.write === undefined)
3102
+ await mkdir(opts.artifactDir, { recursive: true });
3025
3103
  const path = join(opts.artifactDir, `${opts.basename}.fsm.coverage.test.ts`);
3026
- await writeFile(path, content);
3104
+ await (opts.write ?? writeFile)(path, content);
3027
3105
  return {
3028
3106
  path,
3029
3107
  diagnostics: findings.map((finding) => `fsm coverage: ${finding}`),