@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.cjs CHANGED
@@ -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: wodWikiCore.MetricType.Hint, value: hint, image: hint, origin };
77
+ function hintMetric(hint, origin = "dialect", icon) {
78
+ return { type: wodWikiCore.MetricType.Hint, value: hint, image: hint, origin, ...icon ? { icon } : {} };
79
79
  }
80
80
  function hintsToContainer(hints, existing, origin = "dialect") {
81
81
  const container = existing ?? wodWikiCore.MetricContainer.empty("dialect");
@@ -496,6 +496,19 @@ var ChoiceGroupMetric = class {
496
496
  value;
497
497
  image;
498
498
  };
499
+ var IntensityMetric = class {
500
+ constructor(value, unit = "%") {
501
+ this.unit = unit;
502
+ this.value = { amount: value, unit };
503
+ this.image = value !== void 0 ? `${value}${unit}` : `?${unit}`;
504
+ this.origin = value === void 0 ? "hinted" : "parser";
505
+ }
506
+ unit;
507
+ value;
508
+ image;
509
+ origin;
510
+ type = wodWikiCore.MetricType.Intensity;
511
+ };
499
512
 
500
513
  // src/dialects/units/fuseUnits.ts
501
514
  function isBareNumber(m) {
@@ -654,6 +667,17 @@ function fusePairs(pairs, units) {
654
667
  }
655
668
  }
656
669
  }
670
+ if (next && isBareNumber(cur.metric)) {
671
+ const text = effortText(next.metric);
672
+ if (text !== null && text.trim() === "%") {
673
+ out.push({
674
+ metric: new IntensityMetric(cur.metric.value),
675
+ meta: fusedMeta(cur.meta, next.meta, "%")
676
+ });
677
+ i++;
678
+ continue;
679
+ }
680
+ }
657
681
  if (next) {
658
682
  const fusable = isBareNumber(cur.metric) || hasEmptyUnit(cur.metric);
659
683
  if (fusable) {
@@ -1168,6 +1192,11 @@ var ClimbDialect = class {
1168
1192
  }
1169
1193
  };
1170
1194
 
1195
+ // src/logger.ts
1196
+ function langWarn(message) {
1197
+ console.warn(message);
1198
+ }
1199
+
1171
1200
  // src/dialects/DialectStack.ts
1172
1201
  var UNITS_DIALECT_ID = "units";
1173
1202
  var SPORT_ALIASES = {
@@ -1210,7 +1239,7 @@ var DialectStack = class {
1210
1239
  if (!all.some((d) => d.id === id)) {
1211
1240
  if (!warnedSports.has(sport)) {
1212
1241
  warnedSports.add(sport);
1213
- console.warn(
1242
+ langWarn(
1214
1243
  `[DialectStack] Unknown :sport suffix "${sport}" \u2014 falling back to the full dialect stack. Known dialect ids: ${all.map((d) => d.id).join(", ")}.`
1215
1244
  );
1216
1245
  }
@@ -1392,8 +1421,20 @@ var PropertyMetric = class {
1392
1421
  this.image = options.image ?? `${key}: ${formatPropertyValue(value)}`;
1393
1422
  this.origin = options.origin ?? "parser";
1394
1423
  this.type = options.type ?? wodWikiCore.MetricType.Custom;
1424
+ const kind = wodWikiCore.valueKindOf(value);
1425
+ this.metadata = {
1426
+ originalKey: key,
1427
+ ...kind !== "object" && kind !== "undefined" ? {
1428
+ fieldRef: {
1429
+ path: wodWikiCore.normalizeFieldPath(key),
1430
+ kind,
1431
+ ...options.dimension ? { dimension: options.dimension } : {}
1432
+ }
1433
+ } : {}
1434
+ };
1395
1435
  }
1396
1436
  type;
1437
+ metadata;
1397
1438
  };
1398
1439
  var RepMetric = class {
1399
1440
  constructor(reps) {
@@ -1496,6 +1537,12 @@ function classifyPrimitive(primitive) {
1496
1537
  }
1497
1538
  return primitive.label ? [{ metrics: new RoundsMetric(primitive.label), meta: primitive.meta }] : [];
1498
1539
  }
1540
+ case "round_scheme": {
1541
+ return [
1542
+ { metrics: new RoundsMetric(primitive.rounds), meta: primitive.meta },
1543
+ { metrics: new RepMetric(primitive.reps), meta: primitive.meta }
1544
+ ];
1545
+ }
1499
1546
  case "action": {
1500
1547
  const actionText = primitive.hasColonPrefix ? primitive.raw.substring(2, primitive.raw.length - 1).trim() : primitive.raw.substring(1, primitive.raw.length - 1).trim();
1501
1548
  return [{ metrics: new ActionMetric(actionText, { raw: actionText }), meta: primitive.meta }];
@@ -1607,17 +1654,17 @@ function mergeFragments(pairs) {
1607
1654
  }
1608
1655
  var parser = lr.LRParser.deserialize({
1609
1656
  version: 14,
1610
- 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+$]",
1611
- 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~",
1612
- 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",
1613
- 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",
1614
- maxTerm: 57,
1657
+ 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+$^",
1658
+ 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~",
1659
+ 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",
1660
+ 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",
1661
+ maxTerm: 60,
1615
1662
  skippedNodes: [0],
1616
1663
  repeatNodeCount: 6,
1617
- 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~",
1664
+ 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!]~",
1618
1665
  tokenizers: [0],
1619
1666
  topRules: { "Program": [0, 1] },
1620
- tokenPrec: 400
1667
+ tokenPrec: 424
1621
1668
  });
1622
1669
 
1623
1670
  // src/grammar/parser.terms.ts
@@ -1625,14 +1672,15 @@ var Lap = 7;
1625
1672
  var Fragment = 8;
1626
1673
  var Duration = 9;
1627
1674
  var Rounds = 13;
1628
- var Action = 15;
1629
- var Text = 16;
1630
- var Heading = 17;
1631
- var Quantity = 18;
1632
- var Slash = 21;
1633
- var Pipe = 22;
1634
- var Effort = 23;
1635
- var MetricObject = 24;
1675
+ var RoundScheme = 15;
1676
+ var Action = 16;
1677
+ var Text = 17;
1678
+ var Heading = 18;
1679
+ var Quantity = 19;
1680
+ var Slash = 22;
1681
+ var Pipe = 23;
1682
+ var Effort = 24;
1683
+ var MetricObject = 25;
1636
1684
 
1637
1685
  // src/parser/syntax-parser.ts
1638
1686
  function createLineLocator(source) {
@@ -1681,7 +1729,7 @@ function extractSyntaxFacts(sourceOrState, treeArg) {
1681
1729
  if (primitive) {
1682
1730
  const meta2 = primitive.meta;
1683
1731
  statements.push({
1684
- id: meta2.line,
1732
+ id: statements.length,
1685
1733
  line: meta2.line,
1686
1734
  meta: meta2,
1687
1735
  primitives: [primitive],
@@ -1696,7 +1744,7 @@ function extractSyntaxFacts(sourceOrState, treeArg) {
1696
1744
  lastProperty = null;
1697
1745
  const statementMeta = createMeta(locator, node.from, node.to, source.slice(node.from, node.to));
1698
1746
  const statement = {
1699
- id: statementMeta.line,
1747
+ id: statements.length,
1700
1748
  line: statementMeta.line,
1701
1749
  meta: statementMeta,
1702
1750
  primitives: [],
@@ -1789,6 +1837,18 @@ function mapFragmentToPrimitive(locator, source, node) {
1789
1837
  };
1790
1838
  return primitive;
1791
1839
  }
1840
+ case RoundScheme: {
1841
+ const match = raw.match(/^(\d+)\s*[xX]\s*(\d+)$/);
1842
+ if (!match) return null;
1843
+ const primitive = {
1844
+ kind: "round_scheme",
1845
+ raw,
1846
+ meta: meta2,
1847
+ rounds: parseInt(match[1], 10),
1848
+ reps: parseInt(match[2], 10)
1849
+ };
1850
+ return primitive;
1851
+ }
1792
1852
  case Action: {
1793
1853
  const primitive = {
1794
1854
  kind: "action",
@@ -1923,23 +1983,22 @@ function createMeta(locator, from, to, raw) {
1923
1983
  raw
1924
1984
  };
1925
1985
  }
1926
-
1927
- // src/parser/lezer-mapper.ts
1928
- function extractStatementsRaw(sourceOrTree, treeArg) {
1929
- let source;
1930
- let tree = treeArg;
1986
+ function resolveSourceInput(sourceOrTree, treeArg) {
1931
1987
  if (typeof sourceOrTree === "string") {
1932
- source = sourceOrTree;
1933
- } else if ("source" in sourceOrTree) {
1934
- source = sourceOrTree.source;
1935
- tree = tree ?? sourceOrTree.tree;
1936
- } else if ("doc" in sourceOrTree) {
1937
- source = sourceOrTree.doc.toString();
1938
- } else {
1939
- source = "";
1988
+ return { source: sourceOrTree, tree: treeArg };
1989
+ }
1990
+ if ("source" in sourceOrTree) {
1991
+ return { source: sourceOrTree.source, tree: treeArg ?? sourceOrTree.tree };
1940
1992
  }
1993
+ if ("doc" in sourceOrTree) {
1994
+ return { source: sourceOrTree.doc.toString(), tree: treeArg };
1995
+ }
1996
+ return { source: "" };
1997
+ }
1998
+ function extractStatementsRaw(sourceOrTree, treeArg) {
1999
+ const { source, tree } = resolveSourceInput(sourceOrTree, treeArg);
1941
2000
  const facts = extractSyntaxFacts(source, tree);
1942
- return classifyStatements(facts);
2001
+ return normalizeInlineRounds(classifyStatements(facts));
1943
2002
  }
1944
2003
  function extractStatements(sourceOrTree, sportOrTree, sportArg) {
1945
2004
  let sport;
@@ -1950,9 +2009,70 @@ function extractStatements(sourceOrTree, sportOrTree, sportArg) {
1950
2009
  tree = sportOrTree;
1951
2010
  sport = sportArg;
1952
2011
  }
1953
- const statements = extractStatementsRaw(sourceOrTree, tree);
2012
+ const resolved = resolveSourceInput(sourceOrTree, tree);
2013
+ const facts = extractSyntaxFacts(resolved.source, resolved.tree);
2014
+ const statements = classifyStatements(facts);
1954
2015
  dialectStack.processAll(statements, sport);
1955
- return statements;
2016
+ return normalizeInlineRounds(statements);
2017
+ }
2018
+ function isInlineRoundsPrescription(stmt) {
2019
+ if (stmt.children.length > 0) return false;
2020
+ return stmt.metrics.some((m) => m.type === wodWikiCore.MetricType.Rounds) && stmt.metrics.some((m) => m.type === wodWikiCore.MetricType.Rep) && !stmt.metrics.some((m) => m.type === wodWikiCore.MetricType.Duration);
2021
+ }
2022
+ function cloneStatement(stmt) {
2023
+ const clone = Object.create(Object.getPrototypeOf(stmt));
2024
+ Object.assign(clone, stmt);
2025
+ return clone;
2026
+ }
2027
+ function normalizeInlineRounds(statements) {
2028
+ if (!statements.some(isInlineRoundsPrescription)) return statements;
2029
+ const out = [];
2030
+ const oldToNew = /* @__PURE__ */ new Map();
2031
+ const directParentOf = /* @__PURE__ */ new Map();
2032
+ const childCloneOf = /* @__PURE__ */ new Map();
2033
+ const splitOriginOf = /* @__PURE__ */ new Map();
2034
+ for (const stmt of statements) {
2035
+ if (!isInlineRoundsPrescription(stmt)) {
2036
+ oldToNew.set(stmt.id, out.length);
2037
+ out.push(stmt);
2038
+ continue;
2039
+ }
2040
+ if (stmt.parent !== void 0) directParentOf.set(stmt.id, stmt.parent);
2041
+ const parent = cloneStatement(stmt);
2042
+ const child = cloneStatement(stmt);
2043
+ const isScheme = (m) => m.type === wodWikiCore.MetricType.Rounds || m.type === wodWikiCore.MetricType.Rep;
2044
+ parent.metrics = wodWikiCore.MetricContainer.from(stmt.metrics.filter(isScheme), parent.id);
2045
+ child.metrics = wodWikiCore.MetricContainer.from(stmt.metrics.filter((m) => !isScheme(m)), child.id);
2046
+ parent.metricMeta = new Map([...stmt.metricMeta].filter(([m]) => isScheme(m)));
2047
+ child.metricMeta = new Map([...stmt.metricMeta].filter(([m]) => !isScheme(m)));
2048
+ parent.isLeaf = false;
2049
+ child.isLeaf = true;
2050
+ parent.children = [];
2051
+ child.children = [];
2052
+ childCloneOf.set(child, parent);
2053
+ splitOriginOf.set(parent, stmt.id);
2054
+ oldToNew.set(stmt.id, out.length);
2055
+ out.push(parent, child);
2056
+ }
2057
+ return out.map((stmt, id) => {
2058
+ const oldId = stmt.id;
2059
+ stmt.id = id;
2060
+ stmt.metrics = wodWikiCore.MetricContainer.from(stmt.metrics, stmt.id);
2061
+ const parentClone = childCloneOf.get(stmt);
2062
+ if (parentClone) {
2063
+ stmt.parent = id - 1;
2064
+ return stmt;
2065
+ }
2066
+ if (splitOriginOf.has(stmt)) {
2067
+ stmt.children = [[id + 1]];
2068
+ const oldParent = directParentOf.get(splitOriginOf.get(stmt));
2069
+ stmt.parent = oldParent === void 0 ? void 0 : oldToNew.get(oldParent);
2070
+ return stmt;
2071
+ }
2072
+ stmt.parent = stmt.parent === void 0 ? void 0 : oldToNew.get(stmt.parent);
2073
+ 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);
2074
+ return stmt;
2075
+ });
1956
2076
  }
1957
2077
 
1958
2078
  // src/parser/WhiteboardScript.ts
@@ -2026,7 +2146,7 @@ function parseScript(inputText, options = {}) {
2026
2146
  const statements = options.withoutDialects ? extractStatementsRaw(inputText, tree) : extractStatements(inputText, tree, sport);
2027
2147
  return new WhiteboardScript(inputText, statements, []);
2028
2148
  } catch (error) {
2029
- const msg = error?.message || "Unknown parse error";
2149
+ const msg = error instanceof Error ? error.message : "Unknown parse error";
2030
2150
  return new WhiteboardScript(inputText, [], [{ message: msg }]);
2031
2151
  }
2032
2152
  }
@@ -2687,6 +2807,7 @@ var whiteboardScriptLanguage = language.LRLanguage.define({
2687
2807
  Identifier: tags.variableName,
2688
2808
  String: tags.string,
2689
2809
  Number: tags.number,
2810
+ "schemeNumber schemeSpaced": tags.number,
2690
2811
  Timer: tags.keyword,
2691
2812
  collectibleTimer: tags.keyword,
2692
2813
  textComment: tags.lineComment,
@@ -3059,6 +3180,7 @@ var BUNDLED_EFFORT_COUNT = bundledEfforts.length;
3059
3180
  var InMemoryEffortRegistry = class {
3060
3181
  efforts = /* @__PURE__ */ new Map();
3061
3182
  sourceFilter = null;
3183
+ initialized = false;
3062
3184
  constructor(sourceFilter) {
3063
3185
  this.sourceFilter = sourceFilter ?? null;
3064
3186
  }
@@ -3069,6 +3191,10 @@ var InMemoryEffortRegistry = class {
3069
3191
  this.efforts.set(effort.slug, effort);
3070
3192
  }
3071
3193
  }
3194
+ this.initialized = true;
3195
+ }
3196
+ isInitialized() {
3197
+ return this.initialized;
3072
3198
  }
3073
3199
  /** Seed with arbitrary efforts (useful for tests) */
3074
3200
  seed(efforts) {
@@ -3079,10 +3205,21 @@ var InMemoryEffortRegistry = class {
3079
3205
  /** Clear all efforts */
3080
3206
  clear() {
3081
3207
  this.efforts.clear();
3208
+ this.initialized = false;
3082
3209
  }
3083
3210
  resolve(slug) {
3084
3211
  return this.efforts.get(slug) ?? null;
3085
3212
  }
3213
+ resolveByAlias(label) {
3214
+ const normalized = normalizeForFuzzy(label);
3215
+ for (const effort of this.efforts.values()) {
3216
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3217
+ for (const alias of effort.aliases) {
3218
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3219
+ }
3220
+ }
3221
+ return null;
3222
+ }
3086
3223
  list() {
3087
3224
  return Array.from(this.efforts.values());
3088
3225
  }
@@ -3108,6 +3245,159 @@ var InMemoryEffortRegistry = class {
3108
3245
  }
3109
3246
  };
3110
3247
 
3248
+ // src/effort-registry/CompositeEffortRegistry.ts
3249
+ var CompositeEffortRegistry = class {
3250
+ userEfforts = /* @__PURE__ */ new Map();
3251
+ bundledEfforts = /* @__PURE__ */ new Map();
3252
+ initialized = false;
3253
+ initialBundled;
3254
+ storage;
3255
+ constructor(options) {
3256
+ if (options) {
3257
+ this.initialBundled = options.bundled;
3258
+ this.storage = options.storage;
3259
+ }
3260
+ }
3261
+ /**
3262
+ * Initialize the registry:
3263
+ * 1. Load bundled efforts into memory
3264
+ * 2. Load user efforts from storage adapter into memory
3265
+ */
3266
+ async loadBundled(customBundled) {
3267
+ const seed = customBundled ?? this.initialBundled ?? bundledEfforts;
3268
+ for (const effort of seed) {
3269
+ this.bundledEfforts.set(effort.slug, effort);
3270
+ }
3271
+ if (this.storage) {
3272
+ try {
3273
+ const userEfforts = await this.storage.load();
3274
+ for (const effort of userEfforts) {
3275
+ if (effort.registrySource === "user") {
3276
+ this.userEfforts.set(effort.slug, effort);
3277
+ }
3278
+ }
3279
+ } catch (err) {
3280
+ console.warn("[CompositeEffortRegistry] Failed to load user efforts from storage adapter:", err);
3281
+ }
3282
+ }
3283
+ this.initialized = true;
3284
+ }
3285
+ /** True after loadBundled() has resolved */
3286
+ isInitialized() {
3287
+ return this.initialized;
3288
+ }
3289
+ resolve(slug) {
3290
+ return this.userEfforts.get(slug) ?? this.bundledEfforts.get(slug) ?? null;
3291
+ }
3292
+ resolveByAlias(label) {
3293
+ const normalized = normalizeForFuzzy(label);
3294
+ for (const effort of this.userEfforts.values()) {
3295
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3296
+ for (const alias of effort.aliases) {
3297
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3298
+ }
3299
+ }
3300
+ for (const effort of this.bundledEfforts.values()) {
3301
+ if (this.userEfforts.has(effort.slug)) continue;
3302
+ if (normalizeForFuzzy(effort.label) === normalized) return effort;
3303
+ for (const alias of effort.aliases) {
3304
+ if (normalizeForFuzzy(alias) === normalized) return effort;
3305
+ }
3306
+ }
3307
+ return null;
3308
+ }
3309
+ list() {
3310
+ const merged = new Map(this.bundledEfforts);
3311
+ for (const [slug, effort] of this.userEfforts) {
3312
+ merged.set(slug, effort);
3313
+ }
3314
+ return Array.from(merged.values());
3315
+ }
3316
+ listByOrigin(origin) {
3317
+ if (origin === "user") {
3318
+ return Array.from(this.userEfforts.values());
3319
+ }
3320
+ if (origin === "bundled") {
3321
+ return Array.from(this.bundledEfforts.values());
3322
+ }
3323
+ return this.list().filter((e) => e.registrySource === origin);
3324
+ }
3325
+ async upsert(effort) {
3326
+ if (effort.registrySource !== "user") {
3327
+ throw new Error(
3328
+ `CompositeEffortRegistry.upsert: only user efforts can be written. Received registrySource="${effort.registrySource}"`
3329
+ );
3330
+ }
3331
+ this.userEfforts.set(effort.slug, effort);
3332
+ if (this.storage) {
3333
+ try {
3334
+ await this.storage.save(effort);
3335
+ } catch (err) {
3336
+ console.warn("[CompositeEffortRegistry] Failed to persist effort to storage adapter:", err);
3337
+ }
3338
+ }
3339
+ }
3340
+ async delete(slug) {
3341
+ const existing = this.resolve(slug);
3342
+ if (existing && existing.registrySource !== "user") {
3343
+ throw new Error(
3344
+ `CompositeEffortRegistry.delete: cannot delete non-user effort "${slug}"`
3345
+ );
3346
+ }
3347
+ this.userEfforts.delete(slug);
3348
+ if (this.storage) {
3349
+ try {
3350
+ await this.storage.delete(slug);
3351
+ } catch (err) {
3352
+ console.warn("[CompositeEffortRegistry] Failed to delete effort from storage adapter:", err);
3353
+ }
3354
+ }
3355
+ }
3356
+ };
3357
+
3358
+ // src/effort-registry/aliasManagement.ts
3359
+ async function addAliasToEffort(registry, targetSlug, newAlias) {
3360
+ const trimmed = newAlias.trim();
3361
+ if (!trimmed) {
3362
+ throw new Error("Alias cannot be empty");
3363
+ }
3364
+ const existing = registry.resolve(targetSlug);
3365
+ if (!existing) {
3366
+ throw new Error(`Effort "${targetSlug}" not found in registry`);
3367
+ }
3368
+ const normalizedNew = normalizeForFuzzy(trimmed);
3369
+ const alreadyHas = normalizeForFuzzy(existing.label) === normalizedNew || existing.aliases.some((a) => normalizeForFuzzy(a) === normalizedNew);
3370
+ if (alreadyHas) {
3371
+ return existing;
3372
+ }
3373
+ const updatedAliases = [...existing.aliases, trimmed];
3374
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3375
+ if (existing.registrySource === "bundled") {
3376
+ const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? `effort-user-${crypto.randomUUID()}` : `effort-user-${Date.now()}`;
3377
+ const userEffort = {
3378
+ ...existing,
3379
+ id,
3380
+ registrySource: "user",
3381
+ aliases: updatedAliases,
3382
+ updatedAt: now,
3383
+ derivation: existing.derivation ?? {
3384
+ parentSlug: existing.slug,
3385
+ coefficients: {},
3386
+ hardOverrides: {}
3387
+ }
3388
+ };
3389
+ await registry.upsert(userEffort);
3390
+ return userEffort;
3391
+ }
3392
+ const updatedUserEffort = {
3393
+ ...existing,
3394
+ aliases: updatedAliases,
3395
+ updatedAt: now
3396
+ };
3397
+ await registry.upsert(updatedUserEffort);
3398
+ return updatedUserEffort;
3399
+ }
3400
+
3111
3401
  // src/runtime/contracts/IRuntimeOptions.ts
3112
3402
  var DEFAULT_RUNTIME_OPTIONS = {
3113
3403
  debugMode: false,
@@ -3671,9 +3961,7 @@ var ExecutionContext = class {
3671
3961
  this._workList.push(initialAction);
3672
3962
  while (this._workList.length > 0) {
3673
3963
  if (this._iteration >= this._maxIterations) {
3674
- const errorMsg = `[ExecutionContext] Max iterations reached (${this._maxIterations}). Aborting to prevent infinite loop.`;
3675
- console.error(errorMsg);
3676
- throw new Error(errorMsg);
3964
+ throw new Error(`[ExecutionContext] Max iterations reached (${this._maxIterations}). Aborting to prevent infinite loop.`);
3677
3965
  }
3678
3966
  const action = this._workList.shift();
3679
3967
  this._iteration++;
@@ -3736,6 +4024,7 @@ var OutputEmitter = class {
3736
4024
  return;
3737
4025
  }
3738
4026
  const processed = this._analyticsEngine ? this._analyticsEngine.run(output) : output;
4027
+ this._propagateSourceLine(processed);
3739
4028
  this._outputStatements.push(processed);
3740
4029
  for (const listener of this._outputListeners) {
3741
4030
  try {
@@ -3994,11 +4283,25 @@ var OutputEmitter = class {
3994
4283
  // =========================================================================
3995
4284
  // Private helpers
3996
4285
  // =========================================================================
4286
+ /** Options-built outputs carry only sourceStatementId — copy the source
4287
+ * statement's line/text/dialect so persisted logs stay joinable by line. */
4288
+ _propagateSourceLine(output) {
4289
+ if (output.sourceStatementId === void 0 || output.line !== void 0) return;
4290
+ const src = this._script?.getId(output.sourceStatementId);
4291
+ if (!src) return;
4292
+ output.line = src.line;
4293
+ output.text ??= src.text;
4294
+ output.dialect ??= src.dialect;
4295
+ }
3997
4296
  };
3998
4297
 
3999
4298
  // src/runtime/RuntimeObservers.ts
4000
4299
  var RuntimeObservers = class {
4001
4300
  _stackObservers = /* @__PURE__ */ new Set();
4301
+ _logger;
4302
+ constructor(logger) {
4303
+ this._logger = logger;
4304
+ }
4002
4305
  /**
4003
4306
  * Subscribe to stack snapshots. The collaborator does NOT emit an
4004
4307
  * `'initial'` snapshot automatically — the calling adapter is
@@ -4024,7 +4327,7 @@ var RuntimeObservers = class {
4024
4327
  try {
4025
4328
  observer(snapshot);
4026
4329
  } catch (err) {
4027
- console.error("[RuntimeObservers] Stack observer error:", err);
4330
+ this._logger?.error?.("[RuntimeObservers] Stack observer error:", err);
4028
4331
  }
4029
4332
  }
4030
4333
  }
@@ -4069,6 +4372,10 @@ var RuntimeObservers = class {
4069
4372
 
4070
4373
  // src/runtime/RuntimeLogger.ts
4071
4374
  var RuntimeLoggerImpl = class {
4375
+ // Console is this logger's designated output sink; all writes funnel
4376
+ // through this single reference so the no-console rule is waived once.
4377
+ // eslint-disable-next-line no-console
4378
+ _console = console;
4072
4379
  _enabled = false;
4073
4380
  _history = [];
4074
4381
  _maxHistory = 500;
@@ -4091,7 +4398,8 @@ var RuntimeLoggerImpl = class {
4091
4398
  };
4092
4399
  constructor() {
4093
4400
  if (typeof window !== "undefined") {
4094
- this._enabled = !!window.__WOD_DEBUG__;
4401
+ const debugWindow = window;
4402
+ this._enabled = !!debugWindow.__WOD_DEBUG__;
4095
4403
  }
4096
4404
  }
4097
4405
  get enabled() {
@@ -4099,11 +4407,11 @@ var RuntimeLoggerImpl = class {
4099
4407
  }
4100
4408
  enable() {
4101
4409
  this._enabled = true;
4102
- console.log("%c[RT] \u{1F527} Runtime logging ENABLED", "color: #22c55e; font-weight: bold");
4410
+ this._console.log("%c[RT] \u{1F527} Runtime logging ENABLED", "color: #22c55e; font-weight: bold");
4103
4411
  }
4104
4412
  disable() {
4105
4413
  this._enabled = false;
4106
- console.log("%c[RT] Runtime logging disabled", "color: #64748b");
4414
+ this._console.log("%c[RT] Runtime logging disabled", "color: #64748b");
4107
4415
  }
4108
4416
  getHistory() {
4109
4417
  return [...this._history];
@@ -4121,18 +4429,18 @@ var RuntimeLoggerImpl = class {
4121
4429
  if (!this._enabled) return;
4122
4430
  const behaviors = this.getBehaviorNames(block);
4123
4431
  const memoryTypes = block.getAllMemory?.().map((loc) => loc.tag) ?? [];
4124
- console.groupCollapsed(
4432
+ this._console.groupCollapsed(
4125
4433
  `%c[RT] \u25B6 PUSH %c${block.label || block.blockType || "Block"}%c [${block.key.toString().slice(0, 8)}]`,
4126
4434
  this.STYLES.push,
4127
4435
  "color: inherit; font-weight: bold",
4128
4436
  "color: #64748b; font-weight: normal"
4129
4437
  );
4130
- console.log("Key:", block.key.toString());
4131
- if (parentKey) console.log("Parent:", parentKey);
4132
- console.log("Source IDs:", block.sourceIds);
4133
- console.log("Behaviors:", behaviors);
4134
- if (memoryTypes.length > 0) console.log("Memory Types:", memoryTypes);
4135
- console.groupEnd();
4438
+ this._console.log("Key:", block.key.toString());
4439
+ if (parentKey) this._console.log("Parent:", parentKey);
4440
+ this._console.log("Source IDs:", block.sourceIds);
4441
+ this._console.log("Behaviors:", behaviors);
4442
+ if (memoryTypes.length > 0) this._console.log("Memory Types:", memoryTypes);
4443
+ this._console.groupEnd();
4136
4444
  this.addToHistory("info", "push", `PUSH ${block.label}`, {
4137
4445
  key: block.key.toString(),
4138
4446
  parentKey,
@@ -4145,7 +4453,7 @@ var RuntimeLoggerImpl = class {
4145
4453
  */
4146
4454
  logPop(block, reason) {
4147
4455
  if (!this._enabled) return;
4148
- console.log(
4456
+ this._console.log(
4149
4457
  `%c[RT] \u25C0 POP %c${block.label || block.blockType || "Block"}%c [${block.key.toString().slice(0, 8)}]${reason ? ` (${reason})` : ""}`,
4150
4458
  this.STYLES.pop,
4151
4459
  "color: inherit; font-weight: bold",
@@ -4165,15 +4473,15 @@ var RuntimeLoggerImpl = class {
4165
4473
  if (actions.length === 0) {
4166
4474
  return;
4167
4475
  }
4168
- console.groupCollapsed(
4476
+ this._console.groupCollapsed(
4169
4477
  `%c[RT] \u2192 NEXT %c${block.label || "Block"}%c \u2192 ${actions.length} action(s)`,
4170
4478
  this.STYLES.next,
4171
4479
  "color: inherit; font-weight: bold",
4172
4480
  "color: #64748b; font-weight: normal"
4173
4481
  );
4174
- console.log("Block:", block.key.toString());
4175
- console.log("Actions:", actionNames);
4176
- console.groupEnd();
4482
+ this._console.log("Block:", block.key.toString());
4483
+ this._console.log("Actions:", actionNames);
4484
+ this._console.groupEnd();
4177
4485
  this.addToHistory("info", "next", `NEXT ${block.label}`, {
4178
4486
  key: block.key.toString(),
4179
4487
  actions: actionNames
@@ -4186,15 +4494,15 @@ var RuntimeLoggerImpl = class {
4186
4494
  if (!this._enabled) return;
4187
4495
  if (actions.length === 0) return;
4188
4496
  const actionNames = actions.map((a) => a.type || a.constructor?.name || "Action");
4189
- console.groupCollapsed(
4497
+ this._console.groupCollapsed(
4190
4498
  `%c[RT] \u26A1 EVENT %c${event.name}%c \u2192 ${actions.length} action(s)`,
4191
4499
  this.STYLES.event,
4192
4500
  "color: inherit; font-weight: bold",
4193
4501
  "color: #64748b; font-weight: normal"
4194
4502
  );
4195
- console.log("Event Data:", event.data);
4196
- console.log("Actions:", actionNames);
4197
- console.groupEnd();
4503
+ this._console.log("Event Data:", event.data);
4504
+ this._console.log("Actions:", actionNames);
4505
+ this._console.groupEnd();
4198
4506
  this.addToHistory("info", "event", `EVENT ${event.name}`, {
4199
4507
  eventName: event.name,
4200
4508
  eventData: event.data,
@@ -4210,7 +4518,7 @@ var RuntimeLoggerImpl = class {
4210
4518
  if (typeof value === "object" && value !== null) {
4211
4519
  displayValue = this.summarizeObject(value);
4212
4520
  }
4213
- console.log(
4521
+ this._console.log(
4214
4522
  `%c[RT] \u{1F4DD} MEMORY %c${memoryType}%c on [${blockKey.slice(0, 8)}]:`,
4215
4523
  this.STYLES.memory,
4216
4524
  "color: inherit; font-weight: bold",
@@ -4230,17 +4538,35 @@ var RuntimeLoggerImpl = class {
4230
4538
  if (!this._enabled) return;
4231
4539
  const indent = " ".repeat(depth);
4232
4540
  const actionName = action.type || action.constructor?.name || "Action";
4233
- console.log(
4541
+ this._console.log(
4234
4542
  `%c[RT] ${indent}\u2699 %c${actionName}`,
4235
4543
  this.STYLES.action,
4236
4544
  "color: inherit"
4237
4545
  );
4238
4546
  }
4547
+ /**
4548
+ * General-purpose message log for runtime consumers.
4549
+ * Gates on _enabled; writes to the console sink and history.
4550
+ */
4551
+ logMessage(level, category, message, data) {
4552
+ if (!this._enabled) return;
4553
+ this._console.log(`%c[RT] ${category} %c${message}`, this.styleFor(level), "color: inherit");
4554
+ this.addToHistory(level, category, message, data);
4555
+ }
4556
+ /**
4557
+ * Always-on variant of `logMessage`. Use sparingly for errors/warnings that
4558
+ * must be visible even when debug logging is disabled.
4559
+ */
4560
+ logAlways(level, category, message, data) {
4561
+ this._console.log(`%c[RT] ${category} %c${message}`, this.styleFor(level), "color: inherit");
4562
+ this.addToHistory(level, category, message, data);
4563
+ }
4239
4564
  // ============================================================================
4240
4565
  // Helpers
4241
4566
  // ============================================================================
4242
4567
  getBehaviorNames(block) {
4243
- const behaviors = block.behaviors;
4568
+ const blockWithBehaviors = block;
4569
+ const behaviors = blockWithBehaviors.behaviors;
4244
4570
  if (!behaviors || !Array.isArray(behaviors)) return [];
4245
4571
  return behaviors.map((b) => b.constructor?.name || "Behavior");
4246
4572
  }
@@ -4257,6 +4583,18 @@ var RuntimeLoggerImpl = class {
4257
4583
  }
4258
4584
  return summary;
4259
4585
  }
4586
+ styleFor(level) {
4587
+ switch (level) {
4588
+ case "error":
4589
+ return "color: #ef4444; font-weight: bold";
4590
+ case "warn":
4591
+ return "color: #f59e0b; font-weight: bold";
4592
+ case "info":
4593
+ return "color: #3b82f6; font-weight: bold";
4594
+ default:
4595
+ return "color: #64748b";
4596
+ }
4597
+ }
4260
4598
  addToHistory(level, category, message, data) {
4261
4599
  this._history.push({
4262
4600
  timestamp: /* @__PURE__ */ new Date(),
@@ -4361,6 +4699,7 @@ var ScriptRuntime = class {
4361
4699
  constructor(script, compiler, dependencies, options = {}, now = wallClockNow) {
4362
4700
  this.script = script;
4363
4701
  this.options = { ...DEFAULT_RUNTIME_OPTIONS, ...options };
4702
+ this._observers = new RuntimeObservers(this.options.logger);
4364
4703
  this.stack = dependencies.stack;
4365
4704
  this.clock = dependencies.clock;
4366
4705
  this.eventBus = dependencies.eventBus;
@@ -4401,7 +4740,7 @@ var ScriptRuntime = class {
4401
4740
  _output = new OutputEmitter();
4402
4741
  // Shared observer collaborator — owns the stack subscriber Set and the
4403
4742
  // post-mount snapshot contract. See `RuntimeObservers` for the seam.
4404
- _observers = new RuntimeObservers();
4743
+ _observers;
4405
4744
  // The current execution context for the "turn"
4406
4745
  _activeContext = null;
4407
4746
  _now;
@@ -4548,12 +4887,12 @@ var ScriptRuntime = class {
4548
4887
  if (block) {
4549
4888
  try {
4550
4889
  block.unmount(this);
4551
- } catch (_e) {
4890
+ } catch {
4552
4891
  }
4553
4892
  this.stack.pop();
4554
4893
  try {
4555
4894
  block.dispose(this);
4556
- } catch (_e) {
4895
+ } catch {
4557
4896
  }
4558
4897
  } else {
4559
4898
  this.stack.pop();
@@ -4620,16 +4959,18 @@ var ScriptRuntime = class {
4620
4959
  }
4621
4960
  };
4622
4961
  var TypedMemoryReference = class {
4623
- constructor(_memory, ownerId, type, visibility = "private") {
4962
+ constructor(_memory, ownerId, type, visibility = "private", _logger) {
4624
4963
  this._memory = _memory;
4625
4964
  this.ownerId = ownerId;
4626
4965
  this.type = type;
4627
4966
  this.visibility = visibility;
4967
+ this._logger = _logger;
4628
4968
  }
4629
4969
  _memory;
4630
4970
  ownerId;
4631
4971
  type;
4632
4972
  visibility;
4973
+ _logger;
4633
4974
  id = uuid.v7();
4634
4975
  _subscriptions = [];
4635
4976
  /**
@@ -4700,7 +5041,7 @@ var TypedMemoryReference = class {
4700
5041
  try {
4701
5042
  subscription.callback(newValue, oldValue);
4702
5043
  } catch (error) {
4703
- console.error(`Error in memory subscription callback:`, error);
5044
+ this._logger?.error?.("Error in memory subscription callback:", error);
4704
5045
  }
4705
5046
  }
4706
5047
  }
@@ -5122,7 +5463,7 @@ var BehaviorContext = class {
5122
5463
  try {
5123
5464
  unsubscribe();
5124
5465
  } catch (err) {
5125
- console.error("[BehaviorContext] Unsubscribe error:", err);
5466
+ this.runtime.options.logger?.error?.("[BehaviorContext] Unsubscribe error:", err);
5126
5467
  }
5127
5468
  }
5128
5469
  this.subscriptions = [];
@@ -5398,7 +5739,7 @@ var RuntimeBlock = class {
5398
5739
  */
5399
5740
  next(runtime, options) {
5400
5741
  if (!this._behaviorContext) {
5401
- console.warn("[RuntimeBlock] next() called before mount()");
5742
+ RuntimeLogger.logAlways("warn", "RuntimeBlock", "next() called before mount()");
5402
5743
  return [];
5403
5744
  }
5404
5745
  const clock = options?.clock ?? this._behaviorContext.clock;
@@ -5448,7 +5789,7 @@ var RuntimeBlock = class {
5448
5789
  try {
5449
5790
  unsub();
5450
5791
  } catch (err) {
5451
- console.error("[RuntimeBlock] Error unsubscribing event handler:", err);
5792
+ RuntimeLogger.logAlways("error", "RuntimeBlock", "Error unsubscribing event handler", { error: err });
5452
5793
  }
5453
5794
  }
5454
5795
  this._eventUnsubscribers = [];
@@ -5626,8 +5967,7 @@ var CompletionTimestampBehavior = class {
5626
5967
  return [];
5627
5968
  }
5628
5969
  onNext(ctx) {
5629
- const blockState = ctx.block.state;
5630
- if (blockState && blockState.isComplete && !this.wasComplete && !this.completionTime) {
5970
+ if (ctx.block.isComplete && !this.wasComplete && !this.completionTime) {
5631
5971
  this.completionTime = new Date(ctx.clock.currentDate);
5632
5972
  this.wasComplete = true;
5633
5973
  const completionFragment = {
@@ -5810,7 +6150,10 @@ var ClearChildrenAction = class {
5810
6150
  iterations++;
5811
6151
  }
5812
6152
  if (iterations >= MAX_ITERATIONS) {
5813
- console.error(`[ClearChildrenAction] Max iterations reached, possible infinite loop`);
6153
+ runtime.options.logger?.error?.(
6154
+ "[ClearChildrenAction] Max iterations reached, possible infinite loop",
6155
+ new Error(`Exceeded ${MAX_ITERATIONS} pop iterations for target ${this.targetBlockKey}`)
6156
+ );
5814
6157
  }
5815
6158
  if (runtime.stack.current?.key.toString() === this.targetBlockKey) {
5816
6159
  actions.push(new NextAction(void 0, runtime.nowProvider));
@@ -5990,7 +6333,7 @@ var CountdownTimerBehavior = class {
5990
6333
  if (childLoc?.metrics[0]) {
5991
6334
  ctx.updateMemory("children:status", [{ ...childLoc.metrics[0], value: newChildStatus }]);
5992
6335
  } else {
5993
- ctx.pushMemory("children:status", [{ type: 0, image: "", origin: "runtime", value: newChildStatus }]);
6336
+ ctx.pushMemory("children:status", [{ type: "status", image: "", origin: "runtime", value: newChildStatus }]);
5994
6337
  }
5995
6338
  }
5996
6339
  }
@@ -6394,6 +6737,7 @@ function resolveDominantOrigin(origins) {
6394
6737
  }
6395
6738
 
6396
6739
  // src/runtime/compiler/EffortEnrichmentPass.ts
6740
+ var logTrace = (message) => console.log(message);
6397
6741
  function applyEffortEnrichment(block, options) {
6398
6742
  const { resolver, overwrite = false, debug = false } = options;
6399
6743
  tryEnrichBlock(block, resolver, overwrite, debug);
@@ -6405,14 +6749,14 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
6405
6749
  }
6406
6750
  const label = block.label;
6407
6751
  if (!label) {
6408
- if (debug) console.log(`[EffortEnrichment] Skipping block with no label: ${block.key}`);
6752
+ if (debug) logTrace(`[EffortEnrichment] Skipping block with no label: ${block.key}`);
6409
6753
  return;
6410
6754
  }
6411
6755
  const existingEffortData = block.getAllMemory().find(
6412
6756
  (loc) => loc.metrics.toArray().some((m) => m.type === EFFORT_DATA_METRIC_TYPE)
6413
6757
  );
6414
6758
  if (existingEffortData && !overwrite) {
6415
- if (debug) console.log(`[EffortEnrichment] Skipping block already enriched: ${label}`);
6759
+ if (debug) logTrace(`[EffortEnrichment] Skipping block already enriched: ${label}`);
6416
6760
  return;
6417
6761
  }
6418
6762
  try {
@@ -6422,7 +6766,7 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
6422
6766
  block.mergeHints(effortHints);
6423
6767
  }
6424
6768
  if (debug) {
6425
- console.log(`[EffortEnrichment] Resolved effort: ${label} \u2192 ${resolved.label} (MET: ${resolved.met})`);
6769
+ logTrace(`[EffortEnrichment] Resolved effort: ${label} \u2192 ${resolved.label} (MET: ${resolved.met})`);
6426
6770
  }
6427
6771
  const effortDataMetric = {
6428
6772
  type: EFFORT_DATA_METRIC_TYPE,
@@ -6478,7 +6822,7 @@ var CompileAndPushBlockAction = class {
6478
6822
  }
6479
6823
  return [new PushBlockAction(compiledBlock, this.options)];
6480
6824
  } catch (error) {
6481
- console.error(`CompileAndPushBlockAction: Compilation failed`, error);
6825
+ runtime.options.logger?.error?.("CompileAndPushBlockAction: Compilation failed", error);
6482
6826
  return [];
6483
6827
  }
6484
6828
  }
@@ -6697,7 +7041,7 @@ var ChildSelectionBehavior = class _ChildSelectionBehavior {
6697
7041
  if (childLoc?.metrics[0]) {
6698
7042
  ctx.updateMemory("children:status", [{ ...childLoc.metrics[0], value: status }]);
6699
7043
  } else {
6700
- ctx.pushMemory("children:status", [{ type: 0, image: "", origin: "runtime", value: status }]);
7044
+ ctx.pushMemory("children:status", [{ type: "status", image: "", origin: "runtime", value: status }]);
6701
7045
  }
6702
7046
  }
6703
7047
  /**
@@ -7245,7 +7589,7 @@ var WaitingToStartBlock = class _WaitingToStartBlock extends RuntimeBlock {
7245
7589
  */
7246
7590
  static buildBehaviors() {
7247
7591
  const behaviors = [];
7248
- behaviors.push(new ReportOutputBehavior({ label: "Ready to Start" }));
7592
+ behaviors.push(new ReportOutputBehavior({ label: "Ready to Start", emitSegmentOnMount: true }));
7249
7593
  behaviors.push(new LabelingBehavior({
7250
7594
  mode: "clock",
7251
7595
  label: "Ready to Start"
@@ -7681,7 +8025,7 @@ var JitCompiler = class {
7681
8025
  const promotion = this._promotionResolver.resolvePromotions(parentBlock, runtime);
7682
8026
  if (promotion.hasPromotions) {
7683
8027
  const promotedFragments = promotion.promotedFragments;
7684
- effectiveNodes = nodes.map((node) => {
8028
+ effectiveNodes = effectiveNodes.map((node) => {
7685
8029
  const clone = Object.create(Object.getPrototypeOf(node));
7686
8030
  Object.assign(clone, node);
7687
8031
  clone.metrics = node.metrics.clone(node.id).add(...promotedFragments);
@@ -7698,8 +8042,7 @@ var JitCompiler = class {
7698
8042
  }
7699
8043
  try {
7700
8044
  return builder.build();
7701
- } catch (e) {
7702
- console.error("Failed to build block from composition:", e);
8045
+ } catch {
7703
8046
  return void 0;
7704
8047
  }
7705
8048
  }
@@ -11637,7 +11980,7 @@ var RuntimeAdapter = class {
11637
11980
  blocks: stackBlocks,
11638
11981
  activeIndex: activeIndex >= 0 ? activeIndex : 0,
11639
11982
  depth: stackBlocks.length,
11640
- rootBlockKey: stackBlocks.length > 0 ? stackBlocks[0].key : void 0
11983
+ rootBlockKey: stackBlocks.at(-1)?.key
11641
11984
  },
11642
11985
  memory: {
11643
11986
  entries: memoryEntries,
@@ -11664,7 +12007,7 @@ var RuntimeAdapter = class {
11664
12007
  const blocks = runtime.stack.blocks;
11665
12008
  return blocks.map((block, index) => {
11666
12009
  const blockType = this.mapBlockType(block);
11667
- const isActive = index === blocks.length - 1;
12010
+ const isActive = index === 0;
11668
12011
  const status = this.determineBlockStatus(isActive);
11669
12012
  const metrics = this.extractBlockFragments(runtime, block);
11670
12013
  const displayLocs = block.getMemoryByTag("metric:display");
@@ -11686,7 +12029,7 @@ var RuntimeAdapter = class {
11686
12029
  metrics,
11687
12030
  metricGroups,
11688
12031
  sourceIds: block.sourceIds,
11689
- lineNumber: block.sourceIds.length > 0 ? block.sourceIds[0] : void 0,
12032
+ lineNumber: block.sourceIds.length > 0 ? runtime.script.getId(block.sourceIds[0])?.line : void 0,
11690
12033
  metadata: {
11691
12034
  mountTime: void 0,
11692
12035
  executionTime: void 0,
@@ -11713,9 +12056,8 @@ var RuntimeAdapter = class {
11713
12056
  const ownerLabelMap = /* @__PURE__ */ new Map();
11714
12057
  runtime.stack.blocks.forEach((block) => {
11715
12058
  const key = block.key.toString();
11716
- if (block.sourceIds && block.sourceIds.length > 0) {
11717
- ownerLineMap.set(key, block.sourceIds[0]);
11718
- }
12059
+ const line = block.sourceIds.length > 0 ? runtime.script.getId(block.sourceIds[0])?.line : void 0;
12060
+ if (line !== void 0) ownerLineMap.set(key, line);
11719
12061
  ownerLabelMap.set(key, block.label);
11720
12062
  });
11721
12063
  for (const block of runtime.stack.blocks) {
@@ -11853,8 +12195,7 @@ var RuntimeAdapter = class {
11853
12195
  return "pending";
11854
12196
  }
11855
12197
  findParentKey(blocks, currentIndex) {
11856
- if (currentIndex === 0) return void 0;
11857
- return blocks[currentIndex - 1].key.toString();
12198
+ return blocks[currentIndex + 1]?.key.toString();
11858
12199
  }
11859
12200
  findChildrenKeys(_blocks, _parentKey) {
11860
12201
  return [];
@@ -12456,6 +12797,8 @@ function evaluateNode(node, context, row) {
12456
12797
  return right === 0 ? void 0 : left / right;
12457
12798
  case "^":
12458
12799
  return Math.pow(left, right);
12800
+ default:
12801
+ return void 0;
12459
12802
  }
12460
12803
  }
12461
12804
  case "call":
@@ -12796,8 +13139,6 @@ function compileLineForm(src, defaultScope) {
12796
13139
  const sc = SCOPE_RE.exec(line);
12797
13140
  if (sc && /^(on\s|when\s|:)/.test(line.slice(sc[0].length).trim())) {
12798
13141
  scope = sc[1];
12799
- fences = void 0;
12800
- defWhen = void 0;
12801
13142
  const header = parseSectionHeader(line.slice(sc[0].length).trim());
12802
13143
  fences = header.fences;
12803
13144
  defWhen = header.when;
@@ -13194,7 +13535,7 @@ function computeWorkloadRollups(loads, throughDay) {
13194
13535
  return rollups;
13195
13536
  }
13196
13537
  function toStoredOutputStatement(output) {
13197
- const metrics = output.metrics instanceof wodWikiCore.MetricContainer ? output.metrics.toArray() : Array.isArray(output.metrics) ? output.metrics : output.metrics?.toArray ? output.metrics.toArray() : [];
13538
+ const metrics = output.metrics instanceof wodWikiCore.MetricContainer ? output.metrics.toArray() : Array.isArray(output.metrics) ? output.metrics : typeof output.metrics.toArray === "function" ? output.metrics.toArray() : [];
13198
13539
  const hints = getHints(output);
13199
13540
  return {
13200
13541
  id: output.id,
@@ -13213,15 +13554,194 @@ function toStoredOutputStatement(output) {
13213
13554
  };
13214
13555
  }
13215
13556
 
13216
- // src/version.ts
13217
- var VERSION = "0.10.42";
13218
- var GIT_SHA = "79c6d18";
13219
- var BUILD_TIME = "2026-08-26T04:44:54.356Z";
13220
- var SEMVER = {
13221
- version: "0.10.42",
13222
- gitSha: "79c6d18",
13223
- buildTime: "2026-08-26T04:44:54.356Z"
13224
- };
13557
+ // src/parser-fixture/metricLine.ts
13558
+ var TAIL_TOKEN_RE = /[\w-]+:(?:"(?:[^"\\]|\\.)*"|[^ ]+)/g;
13559
+ var FixtureSyntaxError = class extends Error {
13560
+ constructor(message) {
13561
+ super(message);
13562
+ this.name = "FixtureSyntaxError";
13563
+ }
13564
+ };
13565
+ function canonicalizeType(token) {
13566
+ const kebab = token.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
13567
+ return kebab;
13568
+ }
13569
+ var TIME_RE = /^\d{1,2}(:\d{2}){1,2}$/;
13570
+ var NUMBER_RE = /^\d+(\.\d+)?$/;
13571
+ var TAIL_RE = /^[\w-]+:([^ ]+)$/;
13572
+ function parseTimeToMs(token) {
13573
+ const parts = token.split(":").map(Number);
13574
+ return parts.reduce((acc, p) => acc * 60 + p, 0) * 1e3;
13575
+ }
13576
+ function parseTails(sequence, at) {
13577
+ const fields = {};
13578
+ TAIL_TOKEN_RE.lastIndex = 0;
13579
+ let cursor = 0;
13580
+ let m;
13581
+ while ((m = TAIL_TOKEN_RE.exec(sequence)) !== null) {
13582
+ if (sequence.slice(cursor, m.index).trim() !== "") {
13583
+ at(`invalid object tail ${JSON.stringify(sequence.slice(cursor, m.index).trim())}`);
13584
+ }
13585
+ const sep = m[0].indexOf(":");
13586
+ const k = m[0].slice(0, sep);
13587
+ const raw = m[0].slice(sep + 1);
13588
+ fields[k] = raw.startsWith('"') ? JSON.parse(raw) : NUMBER_RE.test(raw) ? Number(raw) : raw;
13589
+ cursor = m.index + m[0].length;
13590
+ }
13591
+ if (sequence.slice(cursor).trim() !== "") {
13592
+ at(`invalid object tail ${JSON.stringify(sequence.slice(cursor).trim())}`);
13593
+ }
13594
+ return fields;
13595
+ }
13596
+ function parseMetricLine(line, file, lineNo) {
13597
+ const src = line.trim();
13598
+ function at(detail) {
13599
+ throw new FixtureSyntaxError(`${file} [line ${lineNo}]: ${detail}`);
13600
+ }
13601
+ if (!line.startsWith("- ")) {
13602
+ at(`metric line must start with "- " (got: ${JSON.stringify(line)})`);
13603
+ }
13604
+ const body = line.slice(2).trim();
13605
+ const tokens = body.split(/\s+/).filter(Boolean);
13606
+ if (tokens.length === 0) {
13607
+ at("metric line is empty");
13608
+ }
13609
+ const [typeToken, ...rest] = tokens;
13610
+ const type = canonicalizeType(typeToken);
13611
+ if (rest.length === 0) {
13612
+ at(`metric "${typeToken}" is missing a value`);
13613
+ }
13614
+ let origin;
13615
+ const lastToken = rest[rest.length - 1];
13616
+ const beforeLast = rest.slice(0, -1).join(" ");
13617
+ const quotesBeforeLast = (beforeLast.match(/"/g) ?? []).length;
13618
+ if (lastToken.startsWith("@") && quotesBeforeLast % 2 === 0) {
13619
+ origin = lastToken.slice(1);
13620
+ rest.pop();
13621
+ if (rest.length === 0) {
13622
+ at(`metric "${typeToken}" is missing a value before @${origin}`);
13623
+ }
13624
+ }
13625
+ const first = rest[0];
13626
+ if (first.startsWith('"')) {
13627
+ const joined = rest.join(" ");
13628
+ const m = joined.match(/^"(?:[^"\\]|\\.)*"/);
13629
+ if (!m) {
13630
+ at(`unterminated quoted string in: ${joined}`);
13631
+ }
13632
+ let value;
13633
+ try {
13634
+ value = JSON.parse(m[0]);
13635
+ } catch {
13636
+ at(`invalid quoted string ${m[0]} (JSON escapes only)`);
13637
+ }
13638
+ const tailPart = joined.slice(m[0].length).trim();
13639
+ if (tailPart) {
13640
+ at(`quoted string cannot combine with object tails (${tailPart}) \u2014 assert fields on the object-valued form instead`);
13641
+ }
13642
+ return { type, kind: "string", value, source: src, ...origin ? { origin } : {} };
13643
+ }
13644
+ if (TIME_RE.test(first)) {
13645
+ if (rest.length > 1) at(`unexpected tokens after time literal: ${rest.slice(1).join(" ")}`);
13646
+ return { type, kind: "number", value: parseTimeToMs(first), source: src, ...origin ? { origin } : {} };
13647
+ }
13648
+ if (NUMBER_RE.test(first)) {
13649
+ if (rest.length === 1) {
13650
+ return { type, kind: "number", value: Number(first), source: src, ...origin ? { origin } : {} };
13651
+ }
13652
+ const second = rest[1];
13653
+ if (!TAIL_RE.test(second)) {
13654
+ if (rest.length > 2) at(`unexpected tokens after amount+unit: ${rest.slice(2).join(" ")}`);
13655
+ return { type, kind: "amountUnit", amount: Number(first), unit: second, source: src, ...origin ? { origin } : {} };
13656
+ }
13657
+ at(`number literal followed by object tail is ambiguous: ${rest.join(" ")}`);
13658
+ }
13659
+ if (TAIL_RE.test(first)) {
13660
+ return { type, kind: "object", fields: parseTails(rest.join(" "), at), source: src, ...origin ? { origin } : {} };
13661
+ }
13662
+ if (first === "?" && rest.length === 1) {
13663
+ return { type, kind: "undefined", source: src, ...origin ? { origin } : {} };
13664
+ }
13665
+ if (rest.length === 1) {
13666
+ return { type, kind: "string", value: first, source: src, ...origin ? { origin } : {} };
13667
+ }
13668
+ at(`unquoted string value with spaces \u2014 use quotes: ${rest.join(" ")}`);
13669
+ }
13670
+
13671
+ // src/parser-fixture/compare.ts
13672
+ function renderMetric(m) {
13673
+ const type = canonicalizeType(String(m.type));
13674
+ const cap = type.charAt(0).toUpperCase() + type.slice(1);
13675
+ const origin = m.origin ? ` @${m.origin}` : "";
13676
+ if (m.value !== null && typeof m.value === "object") {
13677
+ const inner = Object.entries(m.value).map(([k, v]) => `${k}:${String(v)}`).join(" ");
13678
+ return `${cap} ${inner}${origin}`;
13679
+ }
13680
+ if (typeof m.value === "string") {
13681
+ return m.value.includes(" ") ? `${cap} "${m.value}"${origin}` : `${cap} ${m.value}${origin}`;
13682
+ }
13683
+ return `${cap} ${String(m.value)}${origin}`;
13684
+ }
13685
+ function literalMatches(line, metric) {
13686
+ const value = metric.value;
13687
+ switch (line.kind) {
13688
+ case "number":
13689
+ return typeof value === "number" && value === line.value;
13690
+ case "undefined":
13691
+ return metric.value === void 0;
13692
+ case "string":
13693
+ return value === line.value;
13694
+ case "amountUnit": {
13695
+ if (value === null || typeof value !== "object") return false;
13696
+ const v = value;
13697
+ return v.amount === line.amount && v.unit === line.unit && metric.unit === line.unit;
13698
+ }
13699
+ case "object": {
13700
+ if (value === null || typeof value !== "object") return false;
13701
+ const obj = value;
13702
+ return Object.entries(line.fields ?? {}).every(([k, expected]) => {
13703
+ const actual = obj[k];
13704
+ if (typeof actual === "number" && typeof expected === "number") {
13705
+ return actual === expected;
13706
+ }
13707
+ return String(actual) === String(expected);
13708
+ });
13709
+ }
13710
+ }
13711
+ }
13712
+ function lineMatches(line, metric) {
13713
+ if (canonicalizeType(String(metric.type)) !== line.type) return false;
13714
+ if (line.origin !== void 0 && metric.origin !== line.origin) return false;
13715
+ return literalMatches(line, metric);
13716
+ }
13717
+ function diffStatement(expected, actual) {
13718
+ const remaining = [...actual];
13719
+ const matched = [];
13720
+ const missingExpected = [];
13721
+ for (const line of expected) {
13722
+ const hit = remaining.findIndex((m) => lineMatches(line, m));
13723
+ if (hit === -1) {
13724
+ missingExpected.push(line);
13725
+ } else {
13726
+ matched.push({ expected: line, actual: remaining.splice(hit, 1)[0] });
13727
+ }
13728
+ }
13729
+ return { matched, missingExpected, extraActual: remaining };
13730
+ }
13731
+ function compareStatement(expectations, actual, mode) {
13732
+ const { missingExpected, extraActual } = diffStatement(expectations, actual);
13733
+ const diffs = [];
13734
+ const present = actual.map(renderMetric).join("; ") || "nothing";
13735
+ for (const line of missingExpected) {
13736
+ diffs.push(`unmatched expectation ${line.source} \u2014 statement carries: ${present}`);
13737
+ }
13738
+ if (mode === "closed" && missingExpected.length === 0) {
13739
+ for (const m of extraActual) {
13740
+ diffs.push(`unexpected metric ${renderMetric(m)} \u2014 not in the Expected block`);
13741
+ }
13742
+ }
13743
+ return diffs;
13744
+ }
13225
13745
 
13226
13746
  exports.ABSENT = ABSENT;
13227
13747
  exports.AGGREGATE_BUILTINS = AGGREGATE_BUILTINS;
@@ -13230,7 +13750,6 @@ exports.AbortSessionAction = AbortSessionAction;
13230
13750
  exports.AmrapLogicStrategy = AmrapLogicStrategy;
13231
13751
  exports.AnalyticsEngine = AnalyticsEngine;
13232
13752
  exports.AnalyticsTransformer = AnalyticsTransformer;
13233
- exports.BUILD_TIME = BUILD_TIME;
13234
13753
  exports.BUILTIN_CALCS = BUILTIN_CALCS;
13235
13754
  exports.BUNDLED_EFFORT_COUNT = BUNDLED_EFFORT_COUNT;
13236
13755
  exports.BehaviorContext = BehaviorContext;
@@ -13256,6 +13775,7 @@ exports.ClimbDialect = ClimbDialect;
13256
13775
  exports.ClimbMetricType = ClimbMetricType;
13257
13776
  exports.CompileAndPushBlockAction = CompileAndPushBlockAction;
13258
13777
  exports.CompletionTimestampBehavior = CompletionTimestampBehavior;
13778
+ exports.CompositeEffortRegistry = CompositeEffortRegistry;
13259
13779
  exports.CountdownTimerBehavior = CountdownTimerBehavior;
13260
13780
  exports.CountupTimerBehavior = CountupTimerBehavior;
13261
13781
  exports.CrossFitDialect = CrossFitDialect;
@@ -13283,7 +13803,7 @@ exports.ErrorAction = ErrorAction;
13283
13803
  exports.EventBus = EventBus;
13284
13804
  exports.ExecutionContext = ExecutionContext;
13285
13805
  exports.ExitBehavior = ExitBehavior;
13286
- exports.GIT_SHA = GIT_SHA;
13806
+ exports.FixtureSyntaxError = FixtureSyntaxError;
13287
13807
  exports.GenericGroupStrategy = GenericGroupStrategy;
13288
13808
  exports.GenericLoopStrategy = GenericLoopStrategy;
13289
13809
  exports.GenericTimerStrategy = GenericTimerStrategy;
@@ -13328,7 +13848,6 @@ exports.RuntimeLogger = RuntimeLogger;
13328
13848
  exports.RuntimeMemory = RuntimeMemory;
13329
13849
  exports.RuntimeObservers = RuntimeObservers;
13330
13850
  exports.RuntimeStack = RuntimeStack;
13331
- exports.SEMVER = SEMVER;
13332
13851
  exports.STANDARD_UNITS = STANDARD_UNITS;
13333
13852
  exports.STORE_CALCS = STORE_CALCS;
13334
13853
  exports.STREAM_ATOMS = STREAM_ATOMS;
@@ -13354,7 +13873,6 @@ exports.UnitSet = UnitSet;
13354
13873
  exports.UnitsDialect = UnitsDialect;
13355
13874
  exports.UnregisterEventHandlerAction = UnregisterEventHandlerAction;
13356
13875
  exports.UpdateNextPreviewAction = UpdateNextPreviewAction;
13357
- exports.VERSION = VERSION;
13358
13876
  exports.VISIBILITY_ICONS = VISIBILITY_ICONS;
13359
13877
  exports.VISIBILITY_LABELS = VISIBILITY_LABELS;
13360
13878
  exports.WaitingToStartBlock = WaitingToStartBlock;
@@ -13362,16 +13880,19 @@ exports.WaitingToStartInjectorBehavior = WaitingToStartInjectorBehavior;
13362
13880
  exports.WhiteboardScript = WhiteboardScript;
13363
13881
  exports.WodDialect = WodDialect;
13364
13882
  exports.YogaDialect = YogaDialect;
13883
+ exports.addAliasToEffort = addAliasToEffort;
13365
13884
  exports.addDim = addDim;
13366
13885
  exports.buildStaticLookups = buildStaticLookups;
13367
13886
  exports.buildTooltip = buildTooltip;
13368
13887
  exports.bundledEfforts = bundledEfforts;
13369
13888
  exports.calculateElapsed = calculateElapsed;
13889
+ exports.canonicalizeType = canonicalizeType;
13370
13890
  exports.checkCalcLine = checkCalcLine;
13371
13891
  exports.classifyStatements = classifyStatements;
13372
13892
  exports.closeCurrentSpan = closeCurrentSpan;
13373
13893
  exports.collapseUnresolvedChoices = collapseUnresolvedChoices;
13374
13894
  exports.commonFixtureSet = commonFixtureSet;
13895
+ exports.compareStatement = compareStatement;
13375
13896
  exports.compileLineForm = compileLineForm;
13376
13897
  exports.composeDivUnit = composeDivUnit;
13377
13898
  exports.composeMulUnit = composeMulUnit;
@@ -13398,6 +13919,7 @@ exports.dailySessionLoads = dailySessionLoads;
13398
13919
  exports.dayBucket = dayBucket;
13399
13920
  exports.dialectRegistry = dialectRegistry;
13400
13921
  exports.dialectStack = dialectStack;
13922
+ exports.diffStatement = diffStatement;
13401
13923
  exports.dimEquals = dimEquals;
13402
13924
  exports.disciplineFactorFor = disciplineFactorFor;
13403
13925
  exports.evaluate = evaluate;
@@ -13452,10 +13974,12 @@ exports.outputNodeIds = outputNodeIds;
13452
13974
  exports.parseCalcLine = parseCalcLine;
13453
13975
  exports.parseCalculateBlock = parseCalculateBlock;
13454
13976
  exports.parseExpression = parseExpression;
13977
+ exports.parseMetricLine = parseMetricLine;
13455
13978
  exports.parseScript = parseScript;
13456
13979
  exports.printCalculation = printCalculation;
13457
13980
  exports.readTimer = readTimer;
13458
13981
  exports.realtimeProcessorRegistry = realtimeProcessorRegistry;
13982
+ exports.renderMetric = renderMetric;
13459
13983
  exports.resolveDominantOrigin = resolveDominantOrigin;
13460
13984
  exports.sharedParser = sharedParser;
13461
13985
  exports.startSpan = startSpan;