@bitcobblers/wod-wiki-lang 0.10.42 → 0.35.2115

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/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { wallClockNow, formatDurationSmart } from './chunk-PFQDVQQO.mjs';
2
2
  export { createBrowserRuntimeNow, frozenNow, getRuntimeNowMs, wallClockNow } from './chunk-PFQDVQQO.mjs';
3
- import { MetricType, MetricContainer, BlockKey, ParsedCodeStatement, OutputStatement } from '@bitcobblers/wod-wiki-core';
3
+ import { MetricType, MetricContainer, BlockKey, ParsedCodeStatement, OutputStatement, valueKindOf, normalizeFieldPath } from '@bitcobblers/wod-wiki-core';
4
4
  import { LRParser } from '@lezer/lr';
5
5
  import { LRLanguage, LanguageSupport } from '@codemirror/language';
6
6
  import { NodeProp } from '@lezer/common';
@@ -74,8 +74,8 @@ function metricsOf(source) {
74
74
  if (Array.isArray(m)) return m;
75
75
  return m.rawMetrics ?? [];
76
76
  }
77
- function hintMetric(hint, origin = "dialect") {
78
- return { type: MetricType.Hint, value: hint, image: hint, origin };
77
+ function hintMetric(hint, origin = "dialect", icon) {
78
+ return { type: MetricType.Hint, value: hint, image: hint, origin, ...icon ? { icon } : {} };
79
79
  }
80
80
  function hintsToContainer(hints, existing, origin = "dialect") {
81
81
  const container = existing ?? MetricContainer.empty("dialect");
@@ -456,6 +456,19 @@ var ChoiceGroupMetric = class {
456
456
  value;
457
457
  image;
458
458
  };
459
+ var IntensityMetric = class {
460
+ constructor(value, unit = "%") {
461
+ this.unit = unit;
462
+ this.value = { amount: value, unit };
463
+ this.image = value !== void 0 ? `${value}${unit}` : `?${unit}`;
464
+ this.origin = value === void 0 ? "hinted" : "parser";
465
+ }
466
+ unit;
467
+ value;
468
+ image;
469
+ origin;
470
+ type = MetricType.Intensity;
471
+ };
459
472
 
460
473
  // src/dialects/units/fuseUnits.ts
461
474
  function isBareNumber(m) {
@@ -614,6 +627,17 @@ function fusePairs(pairs, units) {
614
627
  }
615
628
  }
616
629
  }
630
+ if (next && isBareNumber(cur.metric)) {
631
+ const text = effortText(next.metric);
632
+ if (text !== null && text.trim() === "%") {
633
+ out.push({
634
+ metric: new IntensityMetric(cur.metric.value),
635
+ meta: fusedMeta(cur.meta, next.meta, "%")
636
+ });
637
+ i++;
638
+ continue;
639
+ }
640
+ }
617
641
  if (next) {
618
642
  const fusable = isBareNumber(cur.metric) || hasEmptyUnit(cur.metric);
619
643
  if (fusable) {
@@ -1128,6 +1152,11 @@ var ClimbDialect = class {
1128
1152
  }
1129
1153
  };
1130
1154
 
1155
+ // src/logger.ts
1156
+ function langWarn(message) {
1157
+ console.warn(message);
1158
+ }
1159
+
1131
1160
  // src/dialects/DialectStack.ts
1132
1161
  var UNITS_DIALECT_ID = "units";
1133
1162
  var SPORT_ALIASES = {
@@ -1170,7 +1199,7 @@ var DialectStack = class {
1170
1199
  if (!all.some((d) => d.id === id)) {
1171
1200
  if (!warnedSports.has(sport)) {
1172
1201
  warnedSports.add(sport);
1173
- console.warn(
1202
+ langWarn(
1174
1203
  `[DialectStack] Unknown :sport suffix "${sport}" \u2014 falling back to the full dialect stack. Known dialect ids: ${all.map((d) => d.id).join(", ")}.`
1175
1204
  );
1176
1205
  }
@@ -1352,8 +1381,20 @@ var PropertyMetric = class {
1352
1381
  this.image = options.image ?? `${key}: ${formatPropertyValue(value)}`;
1353
1382
  this.origin = options.origin ?? "parser";
1354
1383
  this.type = options.type ?? MetricType.Custom;
1384
+ const kind = valueKindOf(value);
1385
+ this.metadata = {
1386
+ originalKey: key,
1387
+ ...kind !== "object" && kind !== "undefined" ? {
1388
+ fieldRef: {
1389
+ path: normalizeFieldPath(key),
1390
+ kind,
1391
+ ...options.dimension ? { dimension: options.dimension } : {}
1392
+ }
1393
+ } : {}
1394
+ };
1355
1395
  }
1356
1396
  type;
1397
+ metadata;
1357
1398
  };
1358
1399
  var RepMetric = class {
1359
1400
  constructor(reps) {
@@ -1456,6 +1497,12 @@ function classifyPrimitive(primitive) {
1456
1497
  }
1457
1498
  return primitive.label ? [{ metrics: new RoundsMetric(primitive.label), meta: primitive.meta }] : [];
1458
1499
  }
1500
+ case "round_scheme": {
1501
+ return [
1502
+ { metrics: new RoundsMetric(primitive.rounds), meta: primitive.meta },
1503
+ { metrics: new RepMetric(primitive.reps), meta: primitive.meta }
1504
+ ];
1505
+ }
1459
1506
  case "action": {
1460
1507
  const actionText = primitive.hasColonPrefix ? primitive.raw.substring(2, primitive.raw.length - 1).trim() : primitive.raw.substring(1, primitive.raw.length - 1).trim();
1461
1508
  return [{ metrics: new ActionMetric(actionText, { raw: actionText }), meta: primitive.meta }];
@@ -1567,17 +1614,17 @@ function mergeFragments(pairs) {
1567
1614
  }
1568
1615
  var parser = LRParser.deserialize({
1569
1616
  version: 14,
1570
- states: "(QQVQPOOO!dQPO'#C|O#qQPO'#CcOOQO'#Cc'#CcOOQO'#Cf'#CfOOQO'#Cg'#CgOOQO'#Ce'#CeO${QPO'#CeO%TQPO'#CiOOQO'#C|'#C|OOQO'#Cs'#CsO%]QPO'#CkOOQO'#Cl'#ClOOQO'#Cm'#CmOOQO'#Co'#CoOOQO'#Cp'#CpOOQO'#Cn'#CnO%bQPO'#CnOOQO'#Cq'#CqOOQO'#Cr'#CrO&XQPO'#CtOOQO'#Cd'#CdOOQO'#Cy'#CyO'gQPO'#CbO&`QPO'#CbOOQO'#Cx'#CxQVQPOOO'nQPO,58xOOQO,59P,59PO'yQPO'#CjOOQO'#Cz'#CzO(XQPO,59TOOQO-E6z-E6zO(dQPO,59VOOQO,59Y,59YOOQO'#C}'#C}O(oQPO,59`OOQO,59`,59`OOQO-E6w-E6wOOQO,58|,58|O(vQPO,58|OOQO-E6v-E6vOOQO1G.d1G.dO(}QPO'#C{O)SQPO,59UOOQO-E6x-E6xOOQO1G.o1G.oO)bQPO1G.qOOQO-E6{-E6{OOQO1G.z1G.zOOQO1G.h1G.hOOQO,59g,59gOOQO-E6y-E6yOOQO7+$]7+$]",
1571
- stateData: "*Z~OtOS~ORPOT`O[UOvQOwROxSOyVOzTO{WO}ZO!OXO!Q[O!R]O!S^O!T_O!UbO!VcO!WdO!ZiO~OukORpXTpX[pXvpXxpXypXzpX{pX}pX!OpX!QpX!RpX!SpX!TpX!UpX!VpX!WpX!ZpX~ORVXTVX[VXvVXxVXyVXzVX{VX}VX!OVX!QVX!RVX!SVX!TVX!UVX!VVX!WVX!ZpX~O[lOzTO~ORnOTmO~OuqO~OTrO!T_O~ORsOSsOTsOisOjsOksOusO!OsO!XsO~O!YuO~P%jORXOT`O[UOvXOxSOyVOzTO{WO}ZO!OXO!Q[O!R]O!S^O!T_O!UbO!VcO!WdO~O!ZwO~P&`ORzOSzOTzO~Ov{OR^XT^X|^X~ORnOTmO|!OO~ORXOvXO!OXO~O!Y!RO~P%jO!Z!SO~P&`OT!TO~Ov{OR^aT^a|^a~ORXOvXO!OXO!P!VO~O!Q!Rz[!Sxyu!U!V!O!W!Y!XijkRvwT!Zt!W~",
1572
- goto: "$TrPPsPPPsw{!S!Z!bP!S!l!S!S!S!S!p!w!S!S!S!SPPP#R#X#d#j#p#}TiOjThOjZfOghjxZeOghjxZVOghjxYUOghjxRlVTnWoZaOghjxY`OghjxRraQjORyjSgOjSvgxRxhQoWR}oQ|mR!U|YYOghjxQp!PR!PqQtdR!Qt",
1573
- nodeNames: "\u26A0 Program Property Identifier String Number Block Lap Fragment Duration Trend CollectibleTimer Timer Rounds Sequence Action Text Heading Quantity AtSign Question Slash Pipe Effort MetricObject True False Null",
1574
- maxTerm: 57,
1617
+ states: "(WQVQPOOO!jQPO'#C}O#}QPO'#CcOOQO'#Cc'#CcOOQO'#Cf'#CfOOQO'#Cg'#CgOOQO'#Ce'#CeO%_QPO'#CeO%gQPO'#CiOOQO'#Ck'#CkOOQO'#C}'#C}OOQO'#Ct'#CtO%oQPO'#ClOOQO'#Cm'#CmOOQO'#Cn'#CnOOQO'#Cp'#CpOOQO'#Cq'#CqOOQO'#Co'#CoO%tQPO'#CoOOQO'#Cr'#CrOOQO'#Cs'#CsO&kQPO'#CuOOQO'#Cd'#CdOOQO'#Cz'#CzO(PQPO'#CbO&rQPO'#CbOOQO'#Cy'#CyQVQPOOO(WQPO,58xOOQO,59P,59PO(cQPO'#CjOOQO'#C{'#C{O(qQPO,59TOOQO-E6{-E6{O(|QPO,59WOOQO,59Z,59ZOOQO'#DO'#DOO)XQPO,59aOOQO,59a,59aOOQO-E6x-E6xOOQO,58|,58|O)`QPO,58|OOQO-E6w-E6wOOQO1G.d1G.dO)gQPO'#C|O)lQPO,59UOOQO-E6y-E6yOOQO1G.o1G.oO)zQPO1G.rOOQO-E6|-E6|OOQO1G.{1G.{OOQO1G.h1G.hOOQO,59h,59hOOQO-E6z-E6zOOQO7+$^7+$^",
1618
+ stateData: "*u~OuOS~ORPOTaO[UOwQOxROySOzVO{TO|WO!OXO!PXO!Q[O!RYO!T]O!U^O!V_O!W`O!XcO!YdO!ZeO!^jO~OvlORqXTqX[qXwqXyqXzqX{qX|qX!OqX!PqX!QqX!RqX!TqX!UqX!VqX!WqX!XqX!YqX!ZqX!^qX~ORVXTVX[VXwVXyVXzVX{VX|VX!OVX!PVX!QVX!RVX!TVX!UVX!VVX!WVX!XVX!YVX!ZVX!^qX~O[mO{TO~ORoOTnO~OvrO~OTsO!W`O~ORtOStOTtOjtOktOltOvtO!RtO![tO~O!]vO~P%|ORYOTaO[UOwYOySOzVO{TO|WO!OXO!PXO!Q[O!RYO!T]O!U^O!V_O!W`O!XcO!YdO!ZeO~O!^xO~P&rOR{OS{OT{O~Ow|OR^XT^X}^X~ORoOTnO}!PO~ORYOwYO!RYO~O!]!SO~P%|O!^!TO~P&rOT!UO~Ow|OR^aT^a}^a~ORYOwYO!RYO!S!WO~O!T!U!O!P{[!Vyzv!X!Y!R!Z!]![jklRwxT!^u!R~",
1619
+ goto: "$UsPPtPPPtx|!T![!cP!T!m!T!T!T!T!T!q!x!T!T!T!TPPP#S#Y#e#k#q$OTjOkTiOkZgOhikyZfOhikyZVOhikyYUOhikyRmVToWpZbOhikyYaOhikyRsbQkORzkShOkSwhyRyiQpWR!OpQ}nR!V}YZOhikyQq!QR!QrQueR!Ru",
1620
+ nodeNames: "\u26A0 Program Property Identifier String Number Block Lap Fragment Duration Trend CollectibleTimer Timer Rounds Sequence RoundScheme Action Text Heading Quantity AtSign Question Slash Pipe Effort MetricObject True False Null",
1621
+ maxTerm: 60,
1575
1622
  skippedNodes: [0],
1576
1623
  repeatNodeCount: 6,
1577
- tokenData: "5_~RvXY#iYZ#z]^$Ppq#iqr$Vrs$}st'dtu$Vuv$Vvw$Vxy(Oyz(Tz{(Y{|)S|})X}!O*R!O!P*W!P!Q+Z!Q![+}![!],t!_!`$V!a!b-^!b!c-c!c!}.]!}#O.k#O#P$V#P#Q.p#Q#R.u#T#Y.]#Y#Z/o#Z#b.]#b#c1p#c#h.]#h#i3`#i#o.]#o#p5O#p#q5T#q#r5Y~#nSt~XY#iYZ#z]^$Ppq#i~$PO!Z~~$SPYZ#z~$[Z!O~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~%QXOY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^<%lO$}~%rXS~OY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^<%lO$}~&bRO;'S$};'S;=`&k;=`O$}~&nYOY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^;=`<%l$}<%lO$}~'aP;=`<%l$}~'iT!R~OY'dZ]'d^;'S'd;'S;=`'x<%lO'd~'{P;=`<%l'd~(TO{~~(YO|~~(aZy~!O~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~)XOw~~)`Z!O~!X~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~*WOv~~*][!O~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!Q![+R!_!`$V!b!c$V#O#P$V#Q#R$V~+WPT~!Q![+R~+`P!U~!P!Q+c~+hT!Q~OY+cZ]+c^;'S+c;'S;=`+w<%lO+c~+zP;=`<%l+c~,SRT~!O!P,]!Q![+}![!],c~,`P!Q![+R~,fP!Q![,i~,nQ[~!Q![,i![!],c~,yQu~!Q![-P!a!b-X~-UP[~!Q![-P~-^Oz~~-cO!T~~-jZ!S~!O~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~.bRR~!Q![.]!c!}.]#T#o.]~.pO}~~.uO!P~~.|Zx~!O~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~/tSR~!Q![.]!c!}.]#T#U0Q#U#o.]~0VTR~!Q![.]!c!}.]#T#`.]#`#a0f#a#o.]~0kTR~!Q![.]!c!}.]#T#g.]#g#h0z#h#o.]~1PTR~!Q![.]!c!}.]#T#X.]#X#Y1`#Y#o.]~1gRj~R~!Q![.]!c!}.]#T#o.]~1uTR~!Q![.]!c!}.]#T#i.]#i#j2U#j#o.]~2ZTR~!Q![.]!c!}.]#T#`.]#`#a2j#a#o.]~2oTR~!Q![.]!c!}.]#T#`.]#`#a3O#a#o.]~3VRk~R~!Q![.]!c!}.]#T#o.]~3eTR~!Q![.]!c!}.]#T#f.]#f#g3t#g#o.]~3yTR~!Q![.]!c!}.]#T#i.]#i#j4Y#j#o.]~4_TR~!Q![.]!c!}.]#T#X.]#X#Y4n#Y#o.]~4uRi~R~!Q![.]!c!}.]#T#o.]~5TO!W~~5YO!V~~5_O!Y~",
1624
+ tokenData: "6k~RvXY#iYZ#z]^$Ppq#iqr$Vrs$}st'dtu$Vuv$Vvw$Vxy(Oyz(Tz{(Y{|)S|})X}!O*R!O!P*W!P!Q+Z!Q![+}![!].Q!_!`$V!a!b.j!b!c.o!c!}/i!}#O/w#O#P$V#P#Q/|#Q#R0R#T#Y/i#Y#Z0{#Z#b/i#b#c2|#c#h/i#h#i4l#i#o/i#o#p6[#p#q6a#q#r6f~#nSu~XY#iYZ#z]^$Ppq#i~$PO!^~~$SPYZ#z~$[Z!R~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~%QXOY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^<%lO$}~%rXS~OY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^<%lO$}~&bRO;'S$};'S;=`&k;=`O$}~&nYOY$}Z]$}^r$}rs%ms#O$}#O#P&_#P;'S$};'S;=`'^;=`<%l$}<%lO$}~'aP;=`<%l$}~'iT!U~OY'dZ]'d^;'S'd;'S;=`'x<%lO'd~'{P;=`<%l'd~(TO|~~(YO}~~(aZz~!R~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~)XOx~~)`Z!R~![~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~*WOw~~*][!R~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!Q![+R!_!`$V!b!c$V#O#P$V#Q#R$V~+WPT~!Q![+R~+`P!X~!P!Q+c~+hT!T~OY+cZ]+c^;'S+c;'S;=`+w<%lO+c~+zP;=`<%l+c~,SUT~pq,f!O!P-Z!Q![+}![!]-a!z!{-r#l#m-r~,iRpq,f!z!{,r#l#m,r~,uPpq,x~,{Qpq,x!Q![-R~-WP!P~!Q![-R~-^P!Q![+R~-dP!Q![-g~-lQ[~!Q![-g![!]-a~-uP!Q![-x~-}P!O~!Q![-x~.VQv~!Q![.]!a!b.e~.bP[~!Q![.]~.jO{~~.oO!W~~.vZ!V~!R~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~/nRR~!Q![/i!c!}/i#T#o/i~/|O!Q~~0RO!S~~0YZy~!R~qr$Vtu$Vuv$Vvw$Vz{$V|}$V!O!P$V!_!`$V!b!c$V#O#P$V#Q#R$V~1QSR~!Q![/i!c!}/i#T#U1^#U#o/i~1cTR~!Q![/i!c!}/i#T#`/i#`#a1r#a#o/i~1wTR~!Q![/i!c!}/i#T#g/i#g#h2W#h#o/i~2]TR~!Q![/i!c!}/i#T#X/i#X#Y2l#Y#o/i~2sRk~R~!Q![/i!c!}/i#T#o/i~3RTR~!Q![/i!c!}/i#T#i/i#i#j3b#j#o/i~3gTR~!Q![/i!c!}/i#T#`/i#`#a3v#a#o/i~3{TR~!Q![/i!c!}/i#T#`/i#`#a4[#a#o/i~4cRl~R~!Q![/i!c!}/i#T#o/i~4qTR~!Q![/i!c!}/i#T#f/i#f#g5Q#g#o/i~5VTR~!Q![/i!c!}/i#T#i/i#i#j5f#j#o/i~5kTR~!Q![/i!c!}/i#T#X/i#X#Y5z#Y#o/i~6RRj~R~!Q![/i!c!}/i#T#o/i~6aO!Z~~6fO!Y~~6kO!]~",
1578
1625
  tokenizers: [0],
1579
1626
  topRules: { "Program": [0, 1] },
1580
- tokenPrec: 400
1627
+ tokenPrec: 424
1581
1628
  });
1582
1629
 
1583
1630
  // src/grammar/parser.terms.ts
@@ -1585,14 +1632,15 @@ var Lap = 7;
1585
1632
  var Fragment = 8;
1586
1633
  var Duration = 9;
1587
1634
  var Rounds = 13;
1588
- var Action = 15;
1589
- var Text = 16;
1590
- var Heading = 17;
1591
- var Quantity = 18;
1592
- var Slash = 21;
1593
- var Pipe = 22;
1594
- var Effort = 23;
1595
- var MetricObject = 24;
1635
+ var RoundScheme = 15;
1636
+ var Action = 16;
1637
+ var Text = 17;
1638
+ var Heading = 18;
1639
+ var Quantity = 19;
1640
+ var Slash = 22;
1641
+ var Pipe = 23;
1642
+ var Effort = 24;
1643
+ var MetricObject = 25;
1596
1644
 
1597
1645
  // src/parser/syntax-parser.ts
1598
1646
  function createLineLocator(source) {
@@ -1641,7 +1689,7 @@ function extractSyntaxFacts(sourceOrState, treeArg) {
1641
1689
  if (primitive) {
1642
1690
  const meta2 = primitive.meta;
1643
1691
  statements.push({
1644
- id: meta2.line,
1692
+ id: statements.length,
1645
1693
  line: meta2.line,
1646
1694
  meta: meta2,
1647
1695
  primitives: [primitive],
@@ -1656,7 +1704,7 @@ function extractSyntaxFacts(sourceOrState, treeArg) {
1656
1704
  lastProperty = null;
1657
1705
  const statementMeta = createMeta(locator, node.from, node.to, source.slice(node.from, node.to));
1658
1706
  const statement = {
1659
- id: statementMeta.line,
1707
+ id: statements.length,
1660
1708
  line: statementMeta.line,
1661
1709
  meta: statementMeta,
1662
1710
  primitives: [],
@@ -1749,6 +1797,18 @@ function mapFragmentToPrimitive(locator, source, node) {
1749
1797
  };
1750
1798
  return primitive;
1751
1799
  }
1800
+ case RoundScheme: {
1801
+ const match = raw.match(/^(\d+)\s*[xX]\s*(\d+)$/);
1802
+ if (!match) return null;
1803
+ const primitive = {
1804
+ kind: "round_scheme",
1805
+ raw,
1806
+ meta: meta2,
1807
+ rounds: parseInt(match[1], 10),
1808
+ reps: parseInt(match[2], 10)
1809
+ };
1810
+ return primitive;
1811
+ }
1752
1812
  case Action: {
1753
1813
  const primitive = {
1754
1814
  kind: "action",
@@ -1883,23 +1943,22 @@ function createMeta(locator, from, to, raw) {
1883
1943
  raw
1884
1944
  };
1885
1945
  }
1886
-
1887
- // src/parser/lezer-mapper.ts
1888
- function extractStatementsRaw(sourceOrTree, treeArg) {
1889
- let source;
1890
- let tree = treeArg;
1946
+ function resolveSourceInput(sourceOrTree, treeArg) {
1891
1947
  if (typeof sourceOrTree === "string") {
1892
- source = sourceOrTree;
1893
- } else if ("source" in sourceOrTree) {
1894
- source = sourceOrTree.source;
1895
- tree = tree ?? sourceOrTree.tree;
1896
- } else if ("doc" in sourceOrTree) {
1897
- source = sourceOrTree.doc.toString();
1898
- } else {
1899
- source = "";
1948
+ return { source: sourceOrTree, tree: treeArg };
1949
+ }
1950
+ if ("source" in sourceOrTree) {
1951
+ return { source: sourceOrTree.source, tree: treeArg ?? sourceOrTree.tree };
1900
1952
  }
1953
+ if ("doc" in sourceOrTree) {
1954
+ return { source: sourceOrTree.doc.toString(), tree: treeArg };
1955
+ }
1956
+ return { source: "" };
1957
+ }
1958
+ function extractStatementsRaw(sourceOrTree, treeArg) {
1959
+ const { source, tree } = resolveSourceInput(sourceOrTree, treeArg);
1901
1960
  const facts = extractSyntaxFacts(source, tree);
1902
- return classifyStatements(facts);
1961
+ return normalizeInlineRounds(classifyStatements(facts));
1903
1962
  }
1904
1963
  function extractStatements(sourceOrTree, sportOrTree, sportArg) {
1905
1964
  let sport;
@@ -1910,9 +1969,70 @@ function extractStatements(sourceOrTree, sportOrTree, sportArg) {
1910
1969
  tree = sportOrTree;
1911
1970
  sport = sportArg;
1912
1971
  }
1913
- const statements = extractStatementsRaw(sourceOrTree, tree);
1972
+ const resolved = resolveSourceInput(sourceOrTree, tree);
1973
+ const facts = extractSyntaxFacts(resolved.source, resolved.tree);
1974
+ const statements = classifyStatements(facts);
1914
1975
  dialectStack.processAll(statements, sport);
1915
- return statements;
1976
+ return normalizeInlineRounds(statements);
1977
+ }
1978
+ function isInlineRoundsPrescription(stmt) {
1979
+ if (stmt.children.length > 0) return false;
1980
+ return stmt.metrics.some((m) => m.type === MetricType.Rounds) && stmt.metrics.some((m) => m.type === MetricType.Rep) && !stmt.metrics.some((m) => m.type === MetricType.Duration);
1981
+ }
1982
+ function cloneStatement(stmt) {
1983
+ const clone = Object.create(Object.getPrototypeOf(stmt));
1984
+ Object.assign(clone, stmt);
1985
+ return clone;
1986
+ }
1987
+ function normalizeInlineRounds(statements) {
1988
+ if (!statements.some(isInlineRoundsPrescription)) return statements;
1989
+ const out = [];
1990
+ const oldToNew = /* @__PURE__ */ new Map();
1991
+ const directParentOf = /* @__PURE__ */ new Map();
1992
+ const childCloneOf = /* @__PURE__ */ new Map();
1993
+ const splitOriginOf = /* @__PURE__ */ new Map();
1994
+ for (const stmt of statements) {
1995
+ if (!isInlineRoundsPrescription(stmt)) {
1996
+ oldToNew.set(stmt.id, out.length);
1997
+ out.push(stmt);
1998
+ continue;
1999
+ }
2000
+ if (stmt.parent !== void 0) directParentOf.set(stmt.id, stmt.parent);
2001
+ const parent = cloneStatement(stmt);
2002
+ const child = cloneStatement(stmt);
2003
+ const isScheme = (m) => m.type === MetricType.Rounds || m.type === MetricType.Rep;
2004
+ parent.metrics = MetricContainer.from(stmt.metrics.filter(isScheme), parent.id);
2005
+ child.metrics = MetricContainer.from(stmt.metrics.filter((m) => !isScheme(m)), child.id);
2006
+ parent.metricMeta = new Map([...stmt.metricMeta].filter(([m]) => isScheme(m)));
2007
+ child.metricMeta = new Map([...stmt.metricMeta].filter(([m]) => !isScheme(m)));
2008
+ parent.isLeaf = false;
2009
+ child.isLeaf = true;
2010
+ parent.children = [];
2011
+ child.children = [];
2012
+ childCloneOf.set(child, parent);
2013
+ splitOriginOf.set(parent, stmt.id);
2014
+ oldToNew.set(stmt.id, out.length);
2015
+ out.push(parent, child);
2016
+ }
2017
+ return out.map((stmt, id) => {
2018
+ const oldId = stmt.id;
2019
+ stmt.id = id;
2020
+ stmt.metrics = MetricContainer.from(stmt.metrics, stmt.id);
2021
+ const parentClone = childCloneOf.get(stmt);
2022
+ if (parentClone) {
2023
+ stmt.parent = id - 1;
2024
+ return stmt;
2025
+ }
2026
+ if (splitOriginOf.has(stmt)) {
2027
+ stmt.children = [[id + 1]];
2028
+ const oldParent = directParentOf.get(splitOriginOf.get(stmt));
2029
+ stmt.parent = oldParent === void 0 ? void 0 : oldToNew.get(oldParent);
2030
+ return stmt;
2031
+ }
2032
+ stmt.parent = stmt.parent === void 0 ? void 0 : oldToNew.get(stmt.parent);
2033
+ stmt.children = stmt.children.map((group) => group.filter((childId) => !directParentOf.has(childId) || directParentOf.get(childId) === oldId).map((childId) => oldToNew.get(childId) ?? childId)).filter((group) => group.length > 0);
2034
+ return stmt;
2035
+ });
1916
2036
  }
1917
2037
 
1918
2038
  // src/parser/WhiteboardScript.ts
@@ -1986,7 +2106,7 @@ function parseScript(inputText, options = {}) {
1986
2106
  const statements = options.withoutDialects ? extractStatementsRaw(inputText, tree) : extractStatements(inputText, tree, sport);
1987
2107
  return new WhiteboardScript(inputText, statements, []);
1988
2108
  } catch (error) {
1989
- const msg = error?.message || "Unknown parse error";
2109
+ const msg = error instanceof Error ? error.message : "Unknown parse error";
1990
2110
  return new WhiteboardScript(inputText, [], [{ message: msg }]);
1991
2111
  }
1992
2112
  }
@@ -2647,6 +2767,7 @@ var whiteboardScriptLanguage = LRLanguage.define({
2647
2767
  Identifier: tags.variableName,
2648
2768
  String: tags.string,
2649
2769
  Number: tags.number,
2770
+ "schemeNumber schemeSpaced": tags.number,
2650
2771
  Timer: tags.keyword,
2651
2772
  collectibleTimer: tags.keyword,
2652
2773
  textComment: tags.lineComment,
@@ -3019,6 +3140,7 @@ var BUNDLED_EFFORT_COUNT = bundledEfforts.length;
3019
3140
  var InMemoryEffortRegistry = class {
3020
3141
  efforts = /* @__PURE__ */ new Map();
3021
3142
  sourceFilter = null;
3143
+ initialized = false;
3022
3144
  constructor(sourceFilter) {
3023
3145
  this.sourceFilter = sourceFilter ?? null;
3024
3146
  }
@@ -3029,6 +3151,10 @@ var InMemoryEffortRegistry = class {
3029
3151
  this.efforts.set(effort.slug, effort);
3030
3152
  }
3031
3153
  }
3154
+ this.initialized = true;
3155
+ }
3156
+ isInitialized() {
3157
+ return this.initialized;
3032
3158
  }
3033
3159
  /** Seed with arbitrary efforts (useful for tests) */
3034
3160
  seed(efforts) {
@@ -3039,10 +3165,21 @@ var InMemoryEffortRegistry = class {
3039
3165
  /** Clear all efforts */
3040
3166
  clear() {
3041
3167
  this.efforts.clear();
3168
+ this.initialized = false;
3042
3169
  }
3043
3170
  resolve(slug) {
3044
3171
  return this.efforts.get(slug) ?? null;
3045
3172
  }
3173
+ resolveByAlias(label) {
3174
+ const normalized = normalizeForFuzzy(label);
3175
+ for (const effort of this.efforts.values()) {
3176
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3177
+ for (const alias of effort.aliases) {
3178
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3179
+ }
3180
+ }
3181
+ return null;
3182
+ }
3046
3183
  list() {
3047
3184
  return Array.from(this.efforts.values());
3048
3185
  }
@@ -3068,6 +3205,159 @@ var InMemoryEffortRegistry = class {
3068
3205
  }
3069
3206
  };
3070
3207
 
3208
+ // src/effort-registry/CompositeEffortRegistry.ts
3209
+ var CompositeEffortRegistry = class {
3210
+ userEfforts = /* @__PURE__ */ new Map();
3211
+ bundledEfforts = /* @__PURE__ */ new Map();
3212
+ initialized = false;
3213
+ initialBundled;
3214
+ storage;
3215
+ constructor(options) {
3216
+ if (options) {
3217
+ this.initialBundled = options.bundled;
3218
+ this.storage = options.storage;
3219
+ }
3220
+ }
3221
+ /**
3222
+ * Initialize the registry:
3223
+ * 1. Load bundled efforts into memory
3224
+ * 2. Load user efforts from storage adapter into memory
3225
+ */
3226
+ async loadBundled(customBundled) {
3227
+ const seed = customBundled ?? this.initialBundled ?? bundledEfforts;
3228
+ for (const effort of seed) {
3229
+ this.bundledEfforts.set(effort.slug, effort);
3230
+ }
3231
+ if (this.storage) {
3232
+ try {
3233
+ const userEfforts = await this.storage.load();
3234
+ for (const effort of userEfforts) {
3235
+ if (effort.registrySource === "user") {
3236
+ this.userEfforts.set(effort.slug, effort);
3237
+ }
3238
+ }
3239
+ } catch (err) {
3240
+ console.warn("[CompositeEffortRegistry] Failed to load user efforts from storage adapter:", err);
3241
+ }
3242
+ }
3243
+ this.initialized = true;
3244
+ }
3245
+ /** True after loadBundled() has resolved */
3246
+ isInitialized() {
3247
+ return this.initialized;
3248
+ }
3249
+ resolve(slug) {
3250
+ return this.userEfforts.get(slug) ?? this.bundledEfforts.get(slug) ?? null;
3251
+ }
3252
+ resolveByAlias(label) {
3253
+ const normalized = normalizeForFuzzy(label);
3254
+ for (const effort of this.userEfforts.values()) {
3255
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3256
+ for (const alias of effort.aliases) {
3257
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3258
+ }
3259
+ }
3260
+ for (const effort of this.bundledEfforts.values()) {
3261
+ if (this.userEfforts.has(effort.slug)) continue;
3262
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3263
+ for (const alias of effort.aliases) {
3264
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3265
+ }
3266
+ }
3267
+ return null;
3268
+ }
3269
+ list() {
3270
+ const merged = new Map(this.bundledEfforts);
3271
+ for (const [slug, effort] of this.userEfforts) {
3272
+ merged.set(slug, effort);
3273
+ }
3274
+ return Array.from(merged.values());
3275
+ }
3276
+ listByOrigin(origin) {
3277
+ if (origin === "user") {
3278
+ return Array.from(this.userEfforts.values());
3279
+ }
3280
+ if (origin === "bundled") {
3281
+ return Array.from(this.bundledEfforts.values());
3282
+ }
3283
+ return this.list().filter((e) => e.registrySource === origin);
3284
+ }
3285
+ async upsert(effort) {
3286
+ if (effort.registrySource !== "user") {
3287
+ throw new Error(
3288
+ `CompositeEffortRegistry.upsert: only user efforts can be written. Received registrySource="${effort.registrySource}"`
3289
+ );
3290
+ }
3291
+ this.userEfforts.set(effort.slug, effort);
3292
+ if (this.storage) {
3293
+ try {
3294
+ await this.storage.save(effort);
3295
+ } catch (err) {
3296
+ console.warn("[CompositeEffortRegistry] Failed to persist effort to storage adapter:", err);
3297
+ }
3298
+ }
3299
+ }
3300
+ async delete(slug) {
3301
+ const existing = this.resolve(slug);
3302
+ if (existing && existing.registrySource !== "user") {
3303
+ throw new Error(
3304
+ `CompositeEffortRegistry.delete: cannot delete non-user effort "${slug}"`
3305
+ );
3306
+ }
3307
+ this.userEfforts.delete(slug);
3308
+ if (this.storage) {
3309
+ try {
3310
+ await this.storage.delete(slug);
3311
+ } catch (err) {
3312
+ console.warn("[CompositeEffortRegistry] Failed to delete effort from storage adapter:", err);
3313
+ }
3314
+ }
3315
+ }
3316
+ };
3317
+
3318
+ // src/effort-registry/aliasManagement.ts
3319
+ async function addAliasToEffort(registry, targetSlug, newAlias) {
3320
+ const trimmed = newAlias.trim();
3321
+ if (!trimmed) {
3322
+ throw new Error("Alias cannot be empty");
3323
+ }
3324
+ const existing = registry.resolve(targetSlug);
3325
+ if (!existing) {
3326
+ throw new Error(`Effort "${targetSlug}" not found in registry`);
3327
+ }
3328
+ const normalizedNew = normalizeForFuzzy(trimmed);
3329
+ const alreadyHas = normalizeForFuzzy(existing.label) === normalizedNew || existing.aliases.some((a) => normalizeForFuzzy(a) === normalizedNew);
3330
+ if (alreadyHas) {
3331
+ return existing;
3332
+ }
3333
+ const updatedAliases = [...existing.aliases, trimmed];
3334
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3335
+ if (existing.registrySource === "bundled") {
3336
+ const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? `effort-user-${crypto.randomUUID()}` : `effort-user-${Date.now()}`;
3337
+ const userEffort = {
3338
+ ...existing,
3339
+ id,
3340
+ registrySource: "user",
3341
+ aliases: updatedAliases,
3342
+ updatedAt: now,
3343
+ derivation: existing.derivation ?? {
3344
+ parentSlug: existing.slug,
3345
+ coefficients: {},
3346
+ hardOverrides: {}
3347
+ }
3348
+ };
3349
+ await registry.upsert(userEffort);
3350
+ return userEffort;
3351
+ }
3352
+ const updatedUserEffort = {
3353
+ ...existing,
3354
+ aliases: updatedAliases,
3355
+ updatedAt: now
3356
+ };
3357
+ await registry.upsert(updatedUserEffort);
3358
+ return updatedUserEffort;
3359
+ }
3360
+
3071
3361
  // src/runtime/contracts/IRuntimeOptions.ts
3072
3362
  var DEFAULT_RUNTIME_OPTIONS = {
3073
3363
  debugMode: false,
@@ -3622,9 +3912,7 @@ var ExecutionContext = class {
3622
3912
  this._workList.push(initialAction);
3623
3913
  while (this._workList.length > 0) {
3624
3914
  if (this._iteration >= this._maxIterations) {
3625
- const errorMsg = `[ExecutionContext] Max iterations reached (${this._maxIterations}). Aborting to prevent infinite loop.`;
3626
- console.error(errorMsg);
3627
- throw new Error(errorMsg);
3915
+ throw new Error(`[ExecutionContext] Max iterations reached (${this._maxIterations}). Aborting to prevent infinite loop.`);
3628
3916
  }
3629
3917
  const action = this._workList.shift();
3630
3918
  this._iteration++;
@@ -3687,6 +3975,7 @@ var OutputEmitter = class {
3687
3975
  return;
3688
3976
  }
3689
3977
  const processed = this._analyticsEngine ? this._analyticsEngine.run(output) : output;
3978
+ this._propagateSourceLine(processed);
3690
3979
  this._outputStatements.push(processed);
3691
3980
  for (const listener of this._outputListeners) {
3692
3981
  try {
@@ -3945,11 +4234,25 @@ var OutputEmitter = class {
3945
4234
  // =========================================================================
3946
4235
  // Private helpers
3947
4236
  // =========================================================================
4237
+ /** Options-built outputs carry only sourceStatementId — copy the source
4238
+ * statement's line/text/dialect so persisted logs stay joinable by line. */
4239
+ _propagateSourceLine(output) {
4240
+ if (output.sourceStatementId === void 0 || output.line !== void 0) return;
4241
+ const src = this._script?.getId(output.sourceStatementId);
4242
+ if (!src) return;
4243
+ output.line = src.line;
4244
+ output.text ??= src.text;
4245
+ output.dialect ??= src.dialect;
4246
+ }
3948
4247
  };
3949
4248
 
3950
4249
  // src/runtime/RuntimeObservers.ts
3951
4250
  var RuntimeObservers = class {
3952
4251
  _stackObservers = /* @__PURE__ */ new Set();
4252
+ _logger;
4253
+ constructor(logger) {
4254
+ this._logger = logger;
4255
+ }
3953
4256
  /**
3954
4257
  * Subscribe to stack snapshots. The collaborator does NOT emit an
3955
4258
  * `'initial'` snapshot automatically — the calling adapter is
@@ -3975,7 +4278,7 @@ var RuntimeObservers = class {
3975
4278
  try {
3976
4279
  observer(snapshot);
3977
4280
  } catch (err) {
3978
- console.error("[RuntimeObservers] Stack observer error:", err);
4281
+ this._logger?.error?.("[RuntimeObservers] Stack observer error:", err);
3979
4282
  }
3980
4283
  }
3981
4284
  }
@@ -4020,6 +4323,10 @@ var RuntimeObservers = class {
4020
4323
 
4021
4324
  // src/runtime/RuntimeLogger.ts
4022
4325
  var RuntimeLoggerImpl = class {
4326
+ // Console is this logger's designated output sink; all writes funnel
4327
+ // through this single reference so the no-console rule is waived once.
4328
+ // eslint-disable-next-line no-console
4329
+ _console = console;
4023
4330
  _enabled = false;
4024
4331
  _history = [];
4025
4332
  _maxHistory = 500;
@@ -4042,7 +4349,8 @@ var RuntimeLoggerImpl = class {
4042
4349
  };
4043
4350
  constructor() {
4044
4351
  if (typeof window !== "undefined") {
4045
- this._enabled = !!window.__WOD_DEBUG__;
4352
+ const debugWindow = window;
4353
+ this._enabled = !!debugWindow.__WOD_DEBUG__;
4046
4354
  }
4047
4355
  }
4048
4356
  get enabled() {
@@ -4050,11 +4358,11 @@ var RuntimeLoggerImpl = class {
4050
4358
  }
4051
4359
  enable() {
4052
4360
  this._enabled = true;
4053
- console.log("%c[RT] \u{1F527} Runtime logging ENABLED", "color: #22c55e; font-weight: bold");
4361
+ this._console.log("%c[RT] \u{1F527} Runtime logging ENABLED", "color: #22c55e; font-weight: bold");
4054
4362
  }
4055
4363
  disable() {
4056
4364
  this._enabled = false;
4057
- console.log("%c[RT] Runtime logging disabled", "color: #64748b");
4365
+ this._console.log("%c[RT] Runtime logging disabled", "color: #64748b");
4058
4366
  }
4059
4367
  getHistory() {
4060
4368
  return [...this._history];
@@ -4072,18 +4380,18 @@ var RuntimeLoggerImpl = class {
4072
4380
  if (!this._enabled) return;
4073
4381
  const behaviors = this.getBehaviorNames(block);
4074
4382
  const memoryTypes = block.getAllMemory?.().map((loc) => loc.tag) ?? [];
4075
- console.groupCollapsed(
4383
+ this._console.groupCollapsed(
4076
4384
  `%c[RT] \u25B6 PUSH %c${block.label || block.blockType || "Block"}%c [${block.key.toString().slice(0, 8)}]`,
4077
4385
  this.STYLES.push,
4078
4386
  "color: inherit; font-weight: bold",
4079
4387
  "color: #64748b; font-weight: normal"
4080
4388
  );
4081
- console.log("Key:", block.key.toString());
4082
- if (parentKey) console.log("Parent:", parentKey);
4083
- console.log("Source IDs:", block.sourceIds);
4084
- console.log("Behaviors:", behaviors);
4085
- if (memoryTypes.length > 0) console.log("Memory Types:", memoryTypes);
4086
- console.groupEnd();
4389
+ this._console.log("Key:", block.key.toString());
4390
+ if (parentKey) this._console.log("Parent:", parentKey);
4391
+ this._console.log("Source IDs:", block.sourceIds);
4392
+ this._console.log("Behaviors:", behaviors);
4393
+ if (memoryTypes.length > 0) this._console.log("Memory Types:", memoryTypes);
4394
+ this._console.groupEnd();
4087
4395
  this.addToHistory("info", "push", `PUSH ${block.label}`, {
4088
4396
  key: block.key.toString(),
4089
4397
  parentKey,
@@ -4096,7 +4404,7 @@ var RuntimeLoggerImpl = class {
4096
4404
  */
4097
4405
  logPop(block, reason) {
4098
4406
  if (!this._enabled) return;
4099
- console.log(
4407
+ this._console.log(
4100
4408
  `%c[RT] \u25C0 POP %c${block.label || block.blockType || "Block"}%c [${block.key.toString().slice(0, 8)}]${reason ? ` (${reason})` : ""}`,
4101
4409
  this.STYLES.pop,
4102
4410
  "color: inherit; font-weight: bold",
@@ -4116,15 +4424,15 @@ var RuntimeLoggerImpl = class {
4116
4424
  if (actions.length === 0) {
4117
4425
  return;
4118
4426
  }
4119
- console.groupCollapsed(
4427
+ this._console.groupCollapsed(
4120
4428
  `%c[RT] \u2192 NEXT %c${block.label || "Block"}%c \u2192 ${actions.length} action(s)`,
4121
4429
  this.STYLES.next,
4122
4430
  "color: inherit; font-weight: bold",
4123
4431
  "color: #64748b; font-weight: normal"
4124
4432
  );
4125
- console.log("Block:", block.key.toString());
4126
- console.log("Actions:", actionNames);
4127
- console.groupEnd();
4433
+ this._console.log("Block:", block.key.toString());
4434
+ this._console.log("Actions:", actionNames);
4435
+ this._console.groupEnd();
4128
4436
  this.addToHistory("info", "next", `NEXT ${block.label}`, {
4129
4437
  key: block.key.toString(),
4130
4438
  actions: actionNames
@@ -4137,15 +4445,15 @@ var RuntimeLoggerImpl = class {
4137
4445
  if (!this._enabled) return;
4138
4446
  if (actions.length === 0) return;
4139
4447
  const actionNames = actions.map((a) => a.type || a.constructor?.name || "Action");
4140
- console.groupCollapsed(
4448
+ this._console.groupCollapsed(
4141
4449
  `%c[RT] \u26A1 EVENT %c${event.name}%c \u2192 ${actions.length} action(s)`,
4142
4450
  this.STYLES.event,
4143
4451
  "color: inherit; font-weight: bold",
4144
4452
  "color: #64748b; font-weight: normal"
4145
4453
  );
4146
- console.log("Event Data:", event.data);
4147
- console.log("Actions:", actionNames);
4148
- console.groupEnd();
4454
+ this._console.log("Event Data:", event.data);
4455
+ this._console.log("Actions:", actionNames);
4456
+ this._console.groupEnd();
4149
4457
  this.addToHistory("info", "event", `EVENT ${event.name}`, {
4150
4458
  eventName: event.name,
4151
4459
  eventData: event.data,
@@ -4161,7 +4469,7 @@ var RuntimeLoggerImpl = class {
4161
4469
  if (typeof value === "object" && value !== null) {
4162
4470
  displayValue = this.summarizeObject(value);
4163
4471
  }
4164
- console.log(
4472
+ this._console.log(
4165
4473
  `%c[RT] \u{1F4DD} MEMORY %c${memoryType}%c on [${blockKey.slice(0, 8)}]:`,
4166
4474
  this.STYLES.memory,
4167
4475
  "color: inherit; font-weight: bold",
@@ -4181,17 +4489,35 @@ var RuntimeLoggerImpl = class {
4181
4489
  if (!this._enabled) return;
4182
4490
  const indent = " ".repeat(depth);
4183
4491
  const actionName = action.type || action.constructor?.name || "Action";
4184
- console.log(
4492
+ this._console.log(
4185
4493
  `%c[RT] ${indent}\u2699 %c${actionName}`,
4186
4494
  this.STYLES.action,
4187
4495
  "color: inherit"
4188
4496
  );
4189
4497
  }
4498
+ /**
4499
+ * General-purpose message log for runtime consumers.
4500
+ * Gates on _enabled; writes to the console sink and history.
4501
+ */
4502
+ logMessage(level, category, message, data) {
4503
+ if (!this._enabled) return;
4504
+ this._console.log(`%c[RT] ${category} %c${message}`, this.styleFor(level), "color: inherit");
4505
+ this.addToHistory(level, category, message, data);
4506
+ }
4507
+ /**
4508
+ * Always-on variant of `logMessage`. Use sparingly for errors/warnings that
4509
+ * must be visible even when debug logging is disabled.
4510
+ */
4511
+ logAlways(level, category, message, data) {
4512
+ this._console.log(`%c[RT] ${category} %c${message}`, this.styleFor(level), "color: inherit");
4513
+ this.addToHistory(level, category, message, data);
4514
+ }
4190
4515
  // ============================================================================
4191
4516
  // Helpers
4192
4517
  // ============================================================================
4193
4518
  getBehaviorNames(block) {
4194
- const behaviors = block.behaviors;
4519
+ const blockWithBehaviors = block;
4520
+ const behaviors = blockWithBehaviors.behaviors;
4195
4521
  if (!behaviors || !Array.isArray(behaviors)) return [];
4196
4522
  return behaviors.map((b) => b.constructor?.name || "Behavior");
4197
4523
  }
@@ -4208,6 +4534,18 @@ var RuntimeLoggerImpl = class {
4208
4534
  }
4209
4535
  return summary;
4210
4536
  }
4537
+ styleFor(level) {
4538
+ switch (level) {
4539
+ case "error":
4540
+ return "color: #ef4444; font-weight: bold";
4541
+ case "warn":
4542
+ return "color: #f59e0b; font-weight: bold";
4543
+ case "info":
4544
+ return "color: #3b82f6; font-weight: bold";
4545
+ default:
4546
+ return "color: #64748b";
4547
+ }
4548
+ }
4211
4549
  addToHistory(level, category, message, data) {
4212
4550
  this._history.push({
4213
4551
  timestamp: /* @__PURE__ */ new Date(),
@@ -4312,6 +4650,7 @@ var ScriptRuntime = class {
4312
4650
  constructor(script, compiler, dependencies, options = {}, now = wallClockNow) {
4313
4651
  this.script = script;
4314
4652
  this.options = { ...DEFAULT_RUNTIME_OPTIONS, ...options };
4653
+ this._observers = new RuntimeObservers(this.options.logger);
4315
4654
  this.stack = dependencies.stack;
4316
4655
  this.clock = dependencies.clock;
4317
4656
  this.eventBus = dependencies.eventBus;
@@ -4352,7 +4691,7 @@ var ScriptRuntime = class {
4352
4691
  _output = new OutputEmitter();
4353
4692
  // Shared observer collaborator — owns the stack subscriber Set and the
4354
4693
  // post-mount snapshot contract. See `RuntimeObservers` for the seam.
4355
- _observers = new RuntimeObservers();
4694
+ _observers;
4356
4695
  // The current execution context for the "turn"
4357
4696
  _activeContext = null;
4358
4697
  _now;
@@ -4499,12 +4838,12 @@ var ScriptRuntime = class {
4499
4838
  if (block) {
4500
4839
  try {
4501
4840
  block.unmount(this);
4502
- } catch (_e) {
4841
+ } catch {
4503
4842
  }
4504
4843
  this.stack.pop();
4505
4844
  try {
4506
4845
  block.dispose(this);
4507
- } catch (_e) {
4846
+ } catch {
4508
4847
  }
4509
4848
  } else {
4510
4849
  this.stack.pop();
@@ -4571,16 +4910,18 @@ var ScriptRuntime = class {
4571
4910
  }
4572
4911
  };
4573
4912
  var TypedMemoryReference = class {
4574
- constructor(_memory, ownerId, type, visibility = "private") {
4913
+ constructor(_memory, ownerId, type, visibility = "private", _logger) {
4575
4914
  this._memory = _memory;
4576
4915
  this.ownerId = ownerId;
4577
4916
  this.type = type;
4578
4917
  this.visibility = visibility;
4918
+ this._logger = _logger;
4579
4919
  }
4580
4920
  _memory;
4581
4921
  ownerId;
4582
4922
  type;
4583
4923
  visibility;
4924
+ _logger;
4584
4925
  id = v7();
4585
4926
  _subscriptions = [];
4586
4927
  /**
@@ -4651,7 +4992,7 @@ var TypedMemoryReference = class {
4651
4992
  try {
4652
4993
  subscription.callback(newValue, oldValue);
4653
4994
  } catch (error) {
4654
- console.error(`Error in memory subscription callback:`, error);
4995
+ this._logger?.error?.("Error in memory subscription callback:", error);
4655
4996
  }
4656
4997
  }
4657
4998
  }
@@ -5073,7 +5414,7 @@ var BehaviorContext = class {
5073
5414
  try {
5074
5415
  unsubscribe();
5075
5416
  } catch (err) {
5076
- console.error("[BehaviorContext] Unsubscribe error:", err);
5417
+ this.runtime.options.logger?.error?.("[BehaviorContext] Unsubscribe error:", err);
5077
5418
  }
5078
5419
  }
5079
5420
  this.subscriptions = [];
@@ -5349,7 +5690,7 @@ var RuntimeBlock = class {
5349
5690
  */
5350
5691
  next(runtime, options) {
5351
5692
  if (!this._behaviorContext) {
5352
- console.warn("[RuntimeBlock] next() called before mount()");
5693
+ RuntimeLogger.logAlways("warn", "RuntimeBlock", "next() called before mount()");
5353
5694
  return [];
5354
5695
  }
5355
5696
  const clock = options?.clock ?? this._behaviorContext.clock;
@@ -5399,7 +5740,7 @@ var RuntimeBlock = class {
5399
5740
  try {
5400
5741
  unsub();
5401
5742
  } catch (err) {
5402
- console.error("[RuntimeBlock] Error unsubscribing event handler:", err);
5743
+ RuntimeLogger.logAlways("error", "RuntimeBlock", "Error unsubscribing event handler", { error: err });
5403
5744
  }
5404
5745
  }
5405
5746
  this._eventUnsubscribers = [];
@@ -5554,8 +5895,7 @@ var CompletionTimestampBehavior = class {
5554
5895
  return [];
5555
5896
  }
5556
5897
  onNext(ctx) {
5557
- const blockState = ctx.block.state;
5558
- if (blockState && blockState.isComplete && !this.wasComplete && !this.completionTime) {
5898
+ if (ctx.block.isComplete && !this.wasComplete && !this.completionTime) {
5559
5899
  this.completionTime = new Date(ctx.clock.currentDate);
5560
5900
  this.wasComplete = true;
5561
5901
  const completionFragment = {
@@ -5738,7 +6078,10 @@ var ClearChildrenAction = class {
5738
6078
  iterations++;
5739
6079
  }
5740
6080
  if (iterations >= MAX_ITERATIONS) {
5741
- console.error(`[ClearChildrenAction] Max iterations reached, possible infinite loop`);
6081
+ runtime.options.logger?.error?.(
6082
+ "[ClearChildrenAction] Max iterations reached, possible infinite loop",
6083
+ new Error(`Exceeded ${MAX_ITERATIONS} pop iterations for target ${this.targetBlockKey}`)
6084
+ );
5742
6085
  }
5743
6086
  if (runtime.stack.current?.key.toString() === this.targetBlockKey) {
5744
6087
  actions.push(new NextAction(void 0, runtime.nowProvider));
@@ -5918,7 +6261,7 @@ var CountdownTimerBehavior = class {
5918
6261
  if (childLoc?.metrics[0]) {
5919
6262
  ctx.updateMemory("children:status", [{ ...childLoc.metrics[0], value: newChildStatus }]);
5920
6263
  } else {
5921
- ctx.pushMemory("children:status", [{ type: 0, image: "", origin: "runtime", value: newChildStatus }]);
6264
+ ctx.pushMemory("children:status", [{ type: "status", image: "", origin: "runtime", value: newChildStatus }]);
5922
6265
  }
5923
6266
  }
5924
6267
  }
@@ -6322,6 +6665,7 @@ function resolveDominantOrigin(origins) {
6322
6665
  }
6323
6666
 
6324
6667
  // src/runtime/compiler/EffortEnrichmentPass.ts
6668
+ var logTrace = (message) => console.log(message);
6325
6669
  function applyEffortEnrichment(block, options) {
6326
6670
  const { resolver, overwrite = false, debug = false } = options;
6327
6671
  tryEnrichBlock(block, resolver, overwrite, debug);
@@ -6333,14 +6677,14 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
6333
6677
  }
6334
6678
  const label = block.label;
6335
6679
  if (!label) {
6336
- if (debug) console.log(`[EffortEnrichment] Skipping block with no label: ${block.key}`);
6680
+ if (debug) logTrace(`[EffortEnrichment] Skipping block with no label: ${block.key}`);
6337
6681
  return;
6338
6682
  }
6339
6683
  const existingEffortData = block.getAllMemory().find(
6340
6684
  (loc) => loc.metrics.toArray().some((m) => m.type === EFFORT_DATA_METRIC_TYPE)
6341
6685
  );
6342
6686
  if (existingEffortData && !overwrite) {
6343
- if (debug) console.log(`[EffortEnrichment] Skipping block already enriched: ${label}`);
6687
+ if (debug) logTrace(`[EffortEnrichment] Skipping block already enriched: ${label}`);
6344
6688
  return;
6345
6689
  }
6346
6690
  try {
@@ -6350,7 +6694,7 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
6350
6694
  block.mergeHints(effortHints);
6351
6695
  }
6352
6696
  if (debug) {
6353
- console.log(`[EffortEnrichment] Resolved effort: ${label} \u2192 ${resolved.label} (MET: ${resolved.met})`);
6697
+ logTrace(`[EffortEnrichment] Resolved effort: ${label} \u2192 ${resolved.label} (MET: ${resolved.met})`);
6354
6698
  }
6355
6699
  const effortDataMetric = {
6356
6700
  type: EFFORT_DATA_METRIC_TYPE,
@@ -6406,7 +6750,7 @@ var CompileAndPushBlockAction = class {
6406
6750
  }
6407
6751
  return [new PushBlockAction(compiledBlock, this.options)];
6408
6752
  } catch (error) {
6409
- console.error(`CompileAndPushBlockAction: Compilation failed`, error);
6753
+ runtime.options.logger?.error?.("CompileAndPushBlockAction: Compilation failed", error);
6410
6754
  return [];
6411
6755
  }
6412
6756
  }
@@ -6625,7 +6969,7 @@ var ChildSelectionBehavior = class _ChildSelectionBehavior {
6625
6969
  if (childLoc?.metrics[0]) {
6626
6970
  ctx.updateMemory("children:status", [{ ...childLoc.metrics[0], value: status }]);
6627
6971
  } else {
6628
- ctx.pushMemory("children:status", [{ type: 0, image: "", origin: "runtime", value: status }]);
6972
+ ctx.pushMemory("children:status", [{ type: "status", image: "", origin: "runtime", value: status }]);
6629
6973
  }
6630
6974
  }
6631
6975
  /**
@@ -7173,7 +7517,7 @@ var WaitingToStartBlock = class _WaitingToStartBlock extends RuntimeBlock {
7173
7517
  */
7174
7518
  static buildBehaviors() {
7175
7519
  const behaviors = [];
7176
- behaviors.push(new ReportOutputBehavior({ label: "Ready to Start" }));
7520
+ behaviors.push(new ReportOutputBehavior({ label: "Ready to Start", emitSegmentOnMount: true }));
7177
7521
  behaviors.push(new LabelingBehavior({
7178
7522
  mode: "clock",
7179
7523
  label: "Ready to Start"
@@ -7609,7 +7953,7 @@ var JitCompiler = class {
7609
7953
  const promotion = this._promotionResolver.resolvePromotions(parentBlock, runtime);
7610
7954
  if (promotion.hasPromotions) {
7611
7955
  const promotedFragments = promotion.promotedFragments;
7612
- effectiveNodes = nodes.map((node) => {
7956
+ effectiveNodes = effectiveNodes.map((node) => {
7613
7957
  const clone = Object.create(Object.getPrototypeOf(node));
7614
7958
  Object.assign(clone, node);
7615
7959
  clone.metrics = node.metrics.clone(node.id).add(...promotedFragments);
@@ -7626,8 +7970,7 @@ var JitCompiler = class {
7626
7970
  }
7627
7971
  try {
7628
7972
  return builder.build();
7629
- } catch (e) {
7630
- console.error("Failed to build block from composition:", e);
7973
+ } catch {
7631
7974
  return void 0;
7632
7975
  }
7633
7976
  }
@@ -11565,7 +11908,7 @@ var RuntimeAdapter = class {
11565
11908
  blocks: stackBlocks,
11566
11909
  activeIndex: activeIndex >= 0 ? activeIndex : 0,
11567
11910
  depth: stackBlocks.length,
11568
- rootBlockKey: stackBlocks.length > 0 ? stackBlocks[0].key : void 0
11911
+ rootBlockKey: stackBlocks.at(-1)?.key
11569
11912
  },
11570
11913
  memory: {
11571
11914
  entries: memoryEntries,
@@ -11592,7 +11935,7 @@ var RuntimeAdapter = class {
11592
11935
  const blocks = runtime.stack.blocks;
11593
11936
  return blocks.map((block, index) => {
11594
11937
  const blockType = this.mapBlockType(block);
11595
- const isActive = index === blocks.length - 1;
11938
+ const isActive = index === 0;
11596
11939
  const status = this.determineBlockStatus(isActive);
11597
11940
  const metrics = this.extractBlockFragments(runtime, block);
11598
11941
  const displayLocs = block.getMemoryByTag("metric:display");
@@ -11614,7 +11957,7 @@ var RuntimeAdapter = class {
11614
11957
  metrics,
11615
11958
  metricGroups,
11616
11959
  sourceIds: block.sourceIds,
11617
- lineNumber: block.sourceIds.length > 0 ? block.sourceIds[0] : void 0,
11960
+ lineNumber: block.sourceIds.length > 0 ? runtime.script.getId(block.sourceIds[0])?.line : void 0,
11618
11961
  metadata: {
11619
11962
  mountTime: void 0,
11620
11963
  executionTime: void 0,
@@ -11641,9 +11984,8 @@ var RuntimeAdapter = class {
11641
11984
  const ownerLabelMap = /* @__PURE__ */ new Map();
11642
11985
  runtime.stack.blocks.forEach((block) => {
11643
11986
  const key = block.key.toString();
11644
- if (block.sourceIds && block.sourceIds.length > 0) {
11645
- ownerLineMap.set(key, block.sourceIds[0]);
11646
- }
11987
+ const line = block.sourceIds.length > 0 ? runtime.script.getId(block.sourceIds[0])?.line : void 0;
11988
+ if (line !== void 0) ownerLineMap.set(key, line);
11647
11989
  ownerLabelMap.set(key, block.label);
11648
11990
  });
11649
11991
  for (const block of runtime.stack.blocks) {
@@ -11781,8 +12123,7 @@ var RuntimeAdapter = class {
11781
12123
  return "pending";
11782
12124
  }
11783
12125
  findParentKey(blocks, currentIndex) {
11784
- if (currentIndex === 0) return void 0;
11785
- return blocks[currentIndex - 1].key.toString();
12126
+ return blocks[currentIndex + 1]?.key.toString();
11786
12127
  }
11787
12128
  findChildrenKeys(_blocks, _parentKey) {
11788
12129
  return [];
@@ -12384,6 +12725,8 @@ function evaluateNode(node, context, row) {
12384
12725
  return right === 0 ? void 0 : left / right;
12385
12726
  case "^":
12386
12727
  return Math.pow(left, right);
12728
+ default:
12729
+ return void 0;
12387
12730
  }
12388
12731
  }
12389
12732
  case "call":
@@ -12724,8 +13067,6 @@ function compileLineForm(src, defaultScope) {
12724
13067
  const sc = SCOPE_RE.exec(line);
12725
13068
  if (sc && /^(on\s|when\s|:)/.test(line.slice(sc[0].length).trim())) {
12726
13069
  scope = sc[1];
12727
- fences = void 0;
12728
- defWhen = void 0;
12729
13070
  const header = parseSectionHeader(line.slice(sc[0].length).trim());
12730
13071
  fences = header.fences;
12731
13072
  defWhen = header.when;
@@ -13122,7 +13463,7 @@ function computeWorkloadRollups(loads, throughDay) {
13122
13463
  return rollups;
13123
13464
  }
13124
13465
  function toStoredOutputStatement(output) {
13125
- const metrics = output.metrics instanceof MetricContainer ? output.metrics.toArray() : Array.isArray(output.metrics) ? output.metrics : output.metrics?.toArray ? output.metrics.toArray() : [];
13466
+ const metrics = output.metrics instanceof MetricContainer ? output.metrics.toArray() : Array.isArray(output.metrics) ? output.metrics : typeof output.metrics.toArray === "function" ? output.metrics.toArray() : [];
13126
13467
  const hints = getHints(output);
13127
13468
  return {
13128
13469
  id: output.id,
@@ -13141,16 +13482,195 @@ function toStoredOutputStatement(output) {
13141
13482
  };
13142
13483
  }
13143
13484
 
13144
- // src/version.ts
13145
- var VERSION = "0.10.42";
13146
- var GIT_SHA = "79c6d18";
13147
- var BUILD_TIME = "2026-08-26T04:44:54.356Z";
13148
- var SEMVER = {
13149
- version: "0.10.42",
13150
- gitSha: "79c6d18",
13151
- buildTime: "2026-08-26T04:44:54.356Z"
13152
- };
13485
+ // src/parser-fixture/metricLine.ts
13486
+ var TAIL_TOKEN_RE = /[\w-]+:(?:"(?:[^"\\]|\\.)*"|[^ ]+)/g;
13487
+ var FixtureSyntaxError = class extends Error {
13488
+ constructor(message) {
13489
+ super(message);
13490
+ this.name = "FixtureSyntaxError";
13491
+ }
13492
+ };
13493
+ function canonicalizeType(token) {
13494
+ const kebab = token.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
13495
+ return kebab;
13496
+ }
13497
+ var TIME_RE = /^\d{1,2}(:\d{2}){1,2}$/;
13498
+ var NUMBER_RE = /^\d+(\.\d+)?$/;
13499
+ var TAIL_RE = /^[\w-]+:([^ ]+)$/;
13500
+ function parseTimeToMs(token) {
13501
+ const parts = token.split(":").map(Number);
13502
+ return parts.reduce((acc, p) => acc * 60 + p, 0) * 1e3;
13503
+ }
13504
+ function parseTails(sequence, at) {
13505
+ const fields = {};
13506
+ TAIL_TOKEN_RE.lastIndex = 0;
13507
+ let cursor = 0;
13508
+ let m;
13509
+ while ((m = TAIL_TOKEN_RE.exec(sequence)) !== null) {
13510
+ if (sequence.slice(cursor, m.index).trim() !== "") {
13511
+ at(`invalid object tail ${JSON.stringify(sequence.slice(cursor, m.index).trim())}`);
13512
+ }
13513
+ const sep = m[0].indexOf(":");
13514
+ const k = m[0].slice(0, sep);
13515
+ const raw = m[0].slice(sep + 1);
13516
+ fields[k] = raw.startsWith('"') ? JSON.parse(raw) : NUMBER_RE.test(raw) ? Number(raw) : raw;
13517
+ cursor = m.index + m[0].length;
13518
+ }
13519
+ if (sequence.slice(cursor).trim() !== "") {
13520
+ at(`invalid object tail ${JSON.stringify(sequence.slice(cursor).trim())}`);
13521
+ }
13522
+ return fields;
13523
+ }
13524
+ function parseMetricLine(line, file, lineNo) {
13525
+ const src = line.trim();
13526
+ function at(detail) {
13527
+ throw new FixtureSyntaxError(`${file} [line ${lineNo}]: ${detail}`);
13528
+ }
13529
+ if (!line.startsWith("- ")) {
13530
+ at(`metric line must start with "- " (got: ${JSON.stringify(line)})`);
13531
+ }
13532
+ const body = line.slice(2).trim();
13533
+ const tokens = body.split(/\s+/).filter(Boolean);
13534
+ if (tokens.length === 0) {
13535
+ at("metric line is empty");
13536
+ }
13537
+ const [typeToken, ...rest] = tokens;
13538
+ const type = canonicalizeType(typeToken);
13539
+ if (rest.length === 0) {
13540
+ at(`metric "${typeToken}" is missing a value`);
13541
+ }
13542
+ let origin;
13543
+ const lastToken = rest[rest.length - 1];
13544
+ const beforeLast = rest.slice(0, -1).join(" ");
13545
+ const quotesBeforeLast = (beforeLast.match(/"/g) ?? []).length;
13546
+ if (lastToken.startsWith("@") && quotesBeforeLast % 2 === 0) {
13547
+ origin = lastToken.slice(1);
13548
+ rest.pop();
13549
+ if (rest.length === 0) {
13550
+ at(`metric "${typeToken}" is missing a value before @${origin}`);
13551
+ }
13552
+ }
13553
+ const first = rest[0];
13554
+ if (first.startsWith('"')) {
13555
+ const joined = rest.join(" ");
13556
+ const m = joined.match(/^"(?:[^"\\]|\\.)*"/);
13557
+ if (!m) {
13558
+ at(`unterminated quoted string in: ${joined}`);
13559
+ }
13560
+ let value;
13561
+ try {
13562
+ value = JSON.parse(m[0]);
13563
+ } catch {
13564
+ at(`invalid quoted string ${m[0]} (JSON escapes only)`);
13565
+ }
13566
+ const tailPart = joined.slice(m[0].length).trim();
13567
+ if (tailPart) {
13568
+ at(`quoted string cannot combine with object tails (${tailPart}) \u2014 assert fields on the object-valued form instead`);
13569
+ }
13570
+ return { type, kind: "string", value, source: src, ...origin ? { origin } : {} };
13571
+ }
13572
+ if (TIME_RE.test(first)) {
13573
+ if (rest.length > 1) at(`unexpected tokens after time literal: ${rest.slice(1).join(" ")}`);
13574
+ return { type, kind: "number", value: parseTimeToMs(first), source: src, ...origin ? { origin } : {} };
13575
+ }
13576
+ if (NUMBER_RE.test(first)) {
13577
+ if (rest.length === 1) {
13578
+ return { type, kind: "number", value: Number(first), source: src, ...origin ? { origin } : {} };
13579
+ }
13580
+ const second = rest[1];
13581
+ if (!TAIL_RE.test(second)) {
13582
+ if (rest.length > 2) at(`unexpected tokens after amount+unit: ${rest.slice(2).join(" ")}`);
13583
+ return { type, kind: "amountUnit", amount: Number(first), unit: second, source: src, ...origin ? { origin } : {} };
13584
+ }
13585
+ at(`number literal followed by object tail is ambiguous: ${rest.join(" ")}`);
13586
+ }
13587
+ if (TAIL_RE.test(first)) {
13588
+ return { type, kind: "object", fields: parseTails(rest.join(" "), at), source: src, ...origin ? { origin } : {} };
13589
+ }
13590
+ if (first === "?" && rest.length === 1) {
13591
+ return { type, kind: "undefined", source: src, ...origin ? { origin } : {} };
13592
+ }
13593
+ if (rest.length === 1) {
13594
+ return { type, kind: "string", value: first, source: src, ...origin ? { origin } : {} };
13595
+ }
13596
+ at(`unquoted string value with spaces \u2014 use quotes: ${rest.join(" ")}`);
13597
+ }
13598
+
13599
+ // src/parser-fixture/compare.ts
13600
+ function renderMetric(m) {
13601
+ const type = canonicalizeType(String(m.type));
13602
+ const cap = type.charAt(0).toUpperCase() + type.slice(1);
13603
+ const origin = m.origin ? ` @${m.origin}` : "";
13604
+ if (m.value !== null && typeof m.value === "object") {
13605
+ const inner = Object.entries(m.value).map(([k, v]) => `${k}:${String(v)}`).join(" ");
13606
+ return `${cap} ${inner}${origin}`;
13607
+ }
13608
+ if (typeof m.value === "string") {
13609
+ return m.value.includes(" ") ? `${cap} "${m.value}"${origin}` : `${cap} ${m.value}${origin}`;
13610
+ }
13611
+ return `${cap} ${String(m.value)}${origin}`;
13612
+ }
13613
+ function literalMatches(line, metric) {
13614
+ const value = metric.value;
13615
+ switch (line.kind) {
13616
+ case "number":
13617
+ return typeof value === "number" && value === line.value;
13618
+ case "undefined":
13619
+ return metric.value === void 0;
13620
+ case "string":
13621
+ return value === line.value;
13622
+ case "amountUnit": {
13623
+ if (value === null || typeof value !== "object") return false;
13624
+ const v = value;
13625
+ return v.amount === line.amount && v.unit === line.unit && metric.unit === line.unit;
13626
+ }
13627
+ case "object": {
13628
+ if (value === null || typeof value !== "object") return false;
13629
+ const obj = value;
13630
+ return Object.entries(line.fields ?? {}).every(([k, expected]) => {
13631
+ const actual = obj[k];
13632
+ if (typeof actual === "number" && typeof expected === "number") {
13633
+ return actual === expected;
13634
+ }
13635
+ return String(actual) === String(expected);
13636
+ });
13637
+ }
13638
+ }
13639
+ }
13640
+ function lineMatches(line, metric) {
13641
+ if (canonicalizeType(String(metric.type)) !== line.type) return false;
13642
+ if (line.origin !== void 0 && metric.origin !== line.origin) return false;
13643
+ return literalMatches(line, metric);
13644
+ }
13645
+ function diffStatement(expected, actual) {
13646
+ const remaining = [...actual];
13647
+ const matched = [];
13648
+ const missingExpected = [];
13649
+ for (const line of expected) {
13650
+ const hit = remaining.findIndex((m) => lineMatches(line, m));
13651
+ if (hit === -1) {
13652
+ missingExpected.push(line);
13653
+ } else {
13654
+ matched.push({ expected: line, actual: remaining.splice(hit, 1)[0] });
13655
+ }
13656
+ }
13657
+ return { matched, missingExpected, extraActual: remaining };
13658
+ }
13659
+ function compareStatement(expectations, actual, mode) {
13660
+ const { missingExpected, extraActual } = diffStatement(expectations, actual);
13661
+ const diffs = [];
13662
+ const present = actual.map(renderMetric).join("; ") || "nothing";
13663
+ for (const line of missingExpected) {
13664
+ diffs.push(`unmatched expectation ${line.source} \u2014 statement carries: ${present}`);
13665
+ }
13666
+ if (mode === "closed" && missingExpected.length === 0) {
13667
+ for (const m of extraActual) {
13668
+ diffs.push(`unexpected metric ${renderMetric(m)} \u2014 not in the Expected block`);
13669
+ }
13670
+ }
13671
+ return diffs;
13672
+ }
13153
13673
 
13154
- export { ABSENT, AGGREGATE_BUILTINS, AUTHORITATIVE_CASTS, AbortSessionAction, AmrapLogicStrategy, AnalyticsEngine, AnalyticsTransformer, BUILD_TIME, BUILTIN_CALCS, BUNDLED_EFFORT_COUNT, BehaviorContext, BlockBuilder, BlockContext, ButtonBehavior, CONSUMED_HINTS, CONSUMED_HINT_KEYS, CONTEXT_ATOMS, CalcEngine, CalcEvalError, CalcParseError, CalcRegistrationError, CalcUnitError, CalculateBlockProcessor, CalculationRegistry, CardioDialect, ChildSelectionBehavior, ChildrenStrategy, ChoiceGroupMetric, ClearChildrenAction, ClimbDialect, ClimbMetricType, CompileAndPushBlockAction, CompletionTimestampBehavior, CountdownTimerBehavior, CountupTimerBehavior, CrossFitDialect, DAY, DEFAULT_RESOLVER_OPTIONS, DEFAULT_RUNTIME_OPTIONS, DEFAULT_UNRESOLVED_EFFORT_MET, DIM_COUNT, DIM_ENERGY, DIM_LENGTH, DIM_MASS, DIM_TIME, DIM_ZERO, DISCIPLINE_FACTORS, DialectStack, EFFORT_DATA_METRIC_TYPE, EFFORT_DISCIPLINES, EFFORT_REGISTRY_ORIGINS, EffortBlock, EffortFallbackStrategy, EffortResolver, EmitEventAction, EmitSystemOutputAction, ErrorAction, EventBus, ExecutionContext, ExitBehavior, GIT_SHA, GenericGroupStrategy, GenericLoopStrategy, GenericTimerStrategy, HabitsDialect, INTENSITY_TIERS, InMemoryEffortRegistry, IntervalLogicStrategy, JitCompiler, LabelingBehavior, LocalEventProvider, LocalRuntimeSubscription, LookupRegistry, MdTimerRuntime, MemoryLocation, MemoryTypeEnum, MetricPromotionBehavior, NAMED_COMPOUNDS, NextAction, NextEvent, NextEventHandler, OutputEmitter, PREDEFINED_SOUNDS, PRODUCTION_STRATEGIES, PlaySoundAction, PopBlockAction, PushBlockAction, PushRestBlockAction, REST_EXCLUSION, RPE_LABELS, RegisterEventHandlerAction, Registry, ReportOutputBehavior, ReportOutputStrategy, RestBlock, RoundsMetric, RuntimeAdapter, RuntimeBlock, RuntimeBuilder, RuntimeClock, RuntimeFactory, RuntimeLogger, RuntimeMemory, RuntimeObservers, RuntimeStack, SEMVER, STANDARD_UNITS, STORE_CALCS, STREAM_ATOMS, ScriptRuntime, SessionRootBlock, SessionRootStrategy, SnapshotClock, SoundCueBehavior, SoundStrategy, SpanTrackingBehavior, StandardAnalyticsProfile, StartSessionAction, StartWorkoutAction, SubscriptionManager, ThrowError, ThrowErrorAction, TimeSpan, TwoPassEffortResolutionProcess, TypedMemoryReference, UNITS, UnitRegistry, UnitSet, UnitsDialect, UnregisterEventHandlerAction, UpdateNextPreviewAction, VERSION, VISIBILITY_ICONS, VISIBILITY_LABELS, WaitingToStartBlock, WaitingToStartInjectorBehavior, WhiteboardScript, WodDialect, YogaDialect, addDim, buildStaticLookups, buildTooltip, bundledEfforts, calculateElapsed, checkCalcLine, classifyStatements, closeCurrentSpan, collapseUnresolvedChoices, commonFixtureSet, compileLineForm, composeDivUnit, composeMulUnit, compoundName, computeColumnLabel, computeLabel, computeWorkloadRollups, convertScalar, createAnalyticsEngineForBlock, createCalcEngine, createCalcStack, createCompiler, createDialectStack, createDisciplinesTable, createEffortTable, createLineLocator, createPolicy as createMetricPresentationPolicy, createMockClock, createParser, createProfileTable, createRpeLabelsTable, dailySessionLoads, dayBucket, dialectRegistry, dialectStack, dimEquals, disciplineFactorFor, evaluate, evaluateCalculateDefinitions, extractEffortData, extractMetrics, extractStatements, extractStatementsRaw, extractSyntaxFacts, filterTagsByVisibility, findBestFuzzyMatch, findUnresolvedChoices, fixtureBackSquat, fixtureBurpee, fixturePlank, fixtureRowing, fixtureRunning, fixtureUserCustom, fixtureUserOverride, formatDim, formatDuration, metricsToLabel as fragmentsToLabel, fullFixtureSet, fuseUnits, fuseUnitsInMetrics, getAnalyticsFromLogs, getAnalyticsFromRuntime, getHints, getMetricVisibility, getUnit, hasHint, hintMetric, hintsToContainer, inferDim, isCastSubscription, isChoiceResolved, isDimensionless, isEffortDiscipline, isFragmentTag, isTimeLikeMetric, isWithinThreshold, levenshteinDistance, metricPresentation, mutateTimerSpans, normalizeForFuzzy, num, openSpan, outputNodeId, outputNodeIds, parseCalcLine, parseCalculateBlock, parseExpression, parseScript, printCalculation, readTimer, realtimeProcessorRegistry, resolveDominantOrigin, sharedParser, startSpan, str, strategyRegistry, subDim, summaryProcessorRegistry, toStoredOutputStatement, truthy, validateOutputUnit, parser as whiteboardParser, whiteboardScript, whiteboardScriptLanguage, writeChoiceSelection };
13674
+ export { ABSENT, AGGREGATE_BUILTINS, AUTHORITATIVE_CASTS, AbortSessionAction, AmrapLogicStrategy, AnalyticsEngine, AnalyticsTransformer, BUILTIN_CALCS, BUNDLED_EFFORT_COUNT, BehaviorContext, BlockBuilder, BlockContext, ButtonBehavior, CONSUMED_HINTS, CONSUMED_HINT_KEYS, CONTEXT_ATOMS, CalcEngine, CalcEvalError, CalcParseError, CalcRegistrationError, CalcUnitError, CalculateBlockProcessor, CalculationRegistry, CardioDialect, ChildSelectionBehavior, ChildrenStrategy, ChoiceGroupMetric, ClearChildrenAction, ClimbDialect, ClimbMetricType, CompileAndPushBlockAction, CompletionTimestampBehavior, CompositeEffortRegistry, CountdownTimerBehavior, CountupTimerBehavior, CrossFitDialect, DAY, DEFAULT_RESOLVER_OPTIONS, DEFAULT_RUNTIME_OPTIONS, DEFAULT_UNRESOLVED_EFFORT_MET, DIM_COUNT, DIM_ENERGY, DIM_LENGTH, DIM_MASS, DIM_TIME, DIM_ZERO, DISCIPLINE_FACTORS, DialectStack, EFFORT_DATA_METRIC_TYPE, EFFORT_DISCIPLINES, EFFORT_REGISTRY_ORIGINS, EffortBlock, EffortFallbackStrategy, EffortResolver, EmitEventAction, EmitSystemOutputAction, ErrorAction, EventBus, ExecutionContext, ExitBehavior, FixtureSyntaxError, GenericGroupStrategy, GenericLoopStrategy, GenericTimerStrategy, HabitsDialect, INTENSITY_TIERS, InMemoryEffortRegistry, IntervalLogicStrategy, JitCompiler, LabelingBehavior, LocalEventProvider, LocalRuntimeSubscription, LookupRegistry, MdTimerRuntime, MemoryLocation, MemoryTypeEnum, MetricPromotionBehavior, NAMED_COMPOUNDS, NextAction, NextEvent, NextEventHandler, OutputEmitter, PREDEFINED_SOUNDS, PRODUCTION_STRATEGIES, PlaySoundAction, PopBlockAction, PushBlockAction, PushRestBlockAction, REST_EXCLUSION, RPE_LABELS, RegisterEventHandlerAction, Registry, ReportOutputBehavior, ReportOutputStrategy, RestBlock, RoundsMetric, RuntimeAdapter, RuntimeBlock, RuntimeBuilder, RuntimeClock, RuntimeFactory, RuntimeLogger, RuntimeMemory, RuntimeObservers, RuntimeStack, STANDARD_UNITS, STORE_CALCS, STREAM_ATOMS, ScriptRuntime, SessionRootBlock, SessionRootStrategy, SnapshotClock, SoundCueBehavior, SoundStrategy, SpanTrackingBehavior, StandardAnalyticsProfile, StartSessionAction, StartWorkoutAction, SubscriptionManager, ThrowError, ThrowErrorAction, TimeSpan, TwoPassEffortResolutionProcess, TypedMemoryReference, UNITS, UnitRegistry, UnitSet, UnitsDialect, UnregisterEventHandlerAction, UpdateNextPreviewAction, VISIBILITY_ICONS, VISIBILITY_LABELS, WaitingToStartBlock, WaitingToStartInjectorBehavior, WhiteboardScript, WodDialect, YogaDialect, addAliasToEffort, addDim, buildStaticLookups, buildTooltip, bundledEfforts, calculateElapsed, canonicalizeType, checkCalcLine, classifyStatements, closeCurrentSpan, collapseUnresolvedChoices, commonFixtureSet, compareStatement, compileLineForm, composeDivUnit, composeMulUnit, compoundName, computeColumnLabel, computeLabel, computeWorkloadRollups, convertScalar, createAnalyticsEngineForBlock, createCalcEngine, createCalcStack, createCompiler, createDialectStack, createDisciplinesTable, createEffortTable, createLineLocator, createPolicy as createMetricPresentationPolicy, createMockClock, createParser, createProfileTable, createRpeLabelsTable, dailySessionLoads, dayBucket, dialectRegistry, dialectStack, diffStatement, dimEquals, disciplineFactorFor, evaluate, evaluateCalculateDefinitions, extractEffortData, extractMetrics, extractStatements, extractStatementsRaw, extractSyntaxFacts, filterTagsByVisibility, findBestFuzzyMatch, findUnresolvedChoices, fixtureBackSquat, fixtureBurpee, fixturePlank, fixtureRowing, fixtureRunning, fixtureUserCustom, fixtureUserOverride, formatDim, formatDuration, metricsToLabel as fragmentsToLabel, fullFixtureSet, fuseUnits, fuseUnitsInMetrics, getAnalyticsFromLogs, getAnalyticsFromRuntime, getHints, getMetricVisibility, getUnit, hasHint, hintMetric, hintsToContainer, inferDim, isCastSubscription, isChoiceResolved, isDimensionless, isEffortDiscipline, isFragmentTag, isTimeLikeMetric, isWithinThreshold, levenshteinDistance, metricPresentation, mutateTimerSpans, normalizeForFuzzy, num, openSpan, outputNodeId, outputNodeIds, parseCalcLine, parseCalculateBlock, parseExpression, parseMetricLine, parseScript, printCalculation, readTimer, realtimeProcessorRegistry, renderMetric, resolveDominantOrigin, sharedParser, startSpan, str, strategyRegistry, subDim, summaryProcessorRegistry, toStoredOutputStatement, truthy, validateOutputUnit, parser as whiteboardParser, whiteboardScript, whiteboardScriptLanguage, writeChoiceSelection };
13155
13675
  //# sourceMappingURL=index.mjs.map
13156
13676
  //# sourceMappingURL=index.mjs.map