@bitcobblers/wod-wiki-lang 0.36.2136 → 0.37.2139
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/{browserRuntimeNow-CoR7dCNU.d.cts → browserRuntimeNow-Xsaghuya.d.cts} +181 -47
- package/dist/{browserRuntimeNow-CoR7dCNU.d.ts → browserRuntimeNow-Xsaghuya.d.ts} +181 -47
- package/dist/index.cjs +1033 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -10
- package/dist/index.d.ts +120 -10
- package/dist/index.mjs +1025 -32
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -418,15 +418,19 @@ function createPolicy() {
|
|
|
418
418
|
}
|
|
419
419
|
var metricPresentation = createPolicy();
|
|
420
420
|
var EffortMetric = class {
|
|
421
|
-
constructor(effort) {
|
|
421
|
+
constructor(effort, options) {
|
|
422
422
|
this.effort = effort;
|
|
423
|
-
this.value = effort;
|
|
423
|
+
this.value = options?.resolvedSlug ?? effort;
|
|
424
424
|
this.image = effort;
|
|
425
|
+
if (options?.references) {
|
|
426
|
+
this.metadata = { [wodWikiCore.REFERENCE_METADATA_KEY]: options.references };
|
|
427
|
+
}
|
|
425
428
|
}
|
|
426
429
|
effort;
|
|
427
430
|
value;
|
|
428
431
|
image;
|
|
429
432
|
origin = "parser";
|
|
433
|
+
metadata;
|
|
430
434
|
type = wodWikiCore.MetricType.Effort;
|
|
431
435
|
};
|
|
432
436
|
var DistanceMetric = class {
|
|
@@ -1394,6 +1398,27 @@ var DurationMetric = class {
|
|
|
1394
1398
|
return this.value > 0 ? "down" : "up";
|
|
1395
1399
|
}
|
|
1396
1400
|
};
|
|
1401
|
+
var EquipmentMetric = class {
|
|
1402
|
+
type = wodWikiCore.MetricType.Equipment;
|
|
1403
|
+
origin;
|
|
1404
|
+
/** Canonical slugs, ordered and deduplicated. */
|
|
1405
|
+
value;
|
|
1406
|
+
image;
|
|
1407
|
+
name = "equipment";
|
|
1408
|
+
metadata;
|
|
1409
|
+
constructor(slugs, options) {
|
|
1410
|
+
this.value = [...new Set(slugs)];
|
|
1411
|
+
this.image = options?.source ?? this.value.join(" ");
|
|
1412
|
+
this.origin = options?.origin ?? "parser";
|
|
1413
|
+
const reference = {
|
|
1414
|
+
matched: options?.matched ?? "whole",
|
|
1415
|
+
source: options?.source ?? this.image,
|
|
1416
|
+
...options?.refs ? { refs: options.refs } : {},
|
|
1417
|
+
...options?.diagnostics?.length ? { diagnostics: options.diagnostics } : {}
|
|
1418
|
+
};
|
|
1419
|
+
this.metadata = { [wodWikiCore.REFERENCE_METADATA_KEY]: reference };
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1397
1422
|
var GroupMetric = class {
|
|
1398
1423
|
constructor(group, image) {
|
|
1399
1424
|
this.group = group;
|
|
@@ -1490,16 +1515,29 @@ var PROPERTY_KEY_TO_METRIC_TYPE = {
|
|
|
1490
1515
|
volume: wodWikiCore.MetricType.Volume,
|
|
1491
1516
|
work: wodWikiCore.MetricType.Work
|
|
1492
1517
|
};
|
|
1493
|
-
|
|
1518
|
+
var STRUCTURAL_EFFORT_WORDS = /* @__PURE__ */ new Set(["rest", "amrap", "emom", "tabata", "for time"]);
|
|
1519
|
+
var STANDARD_UNITS2 = UnitRegistry.standard();
|
|
1520
|
+
var REFERENCE_PAIR_KEYS = /* @__PURE__ */ new Set(["effort", "equipment"]);
|
|
1521
|
+
function referenceMetadata(resolution) {
|
|
1522
|
+
return {
|
|
1523
|
+
matched: resolution.matched,
|
|
1524
|
+
source: resolution.label,
|
|
1525
|
+
...resolution.effortRef || resolution.equipment.length > 0 ? { refs: [...resolution.effortRef ? [resolution.effortRef] : [], ...resolution.equipment] } : {},
|
|
1526
|
+
...resolution.remaining ? { remaining: resolution.remaining } : {},
|
|
1527
|
+
...resolution.diagnostics.length > 0 ? { diagnostics: resolution.diagnostics } : {}
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function classifyStatements(facts, references) {
|
|
1494
1531
|
return facts.statements.map((fact) => {
|
|
1495
1532
|
const statement = new wodWikiCore.ParsedCodeStatement();
|
|
1496
|
-
|
|
1533
|
+
let metricPairs = [];
|
|
1497
1534
|
for (const primitive of fact.primitives) {
|
|
1498
1535
|
metricPairs.push(...classifyPrimitive(primitive));
|
|
1499
1536
|
}
|
|
1500
1537
|
const mergedPairs = mergeFragments(metricPairs);
|
|
1501
|
-
|
|
1502
|
-
statement.
|
|
1538
|
+
const finalPairs = references ? applyReferenceResolution(mergedPairs, fact, references) : mergedPairs;
|
|
1539
|
+
statement.metrics = finalPairs.map((pair) => pair.metrics);
|
|
1540
|
+
statement.metricMeta = new Map(finalPairs.map((pair) => [pair.metrics, pair.meta]));
|
|
1503
1541
|
statement.id = fact.id;
|
|
1504
1542
|
statement.line = fact.line;
|
|
1505
1543
|
statement.text = fact.meta.raw;
|
|
@@ -1511,6 +1549,105 @@ function classifyStatements(facts) {
|
|
|
1511
1549
|
return statement;
|
|
1512
1550
|
});
|
|
1513
1551
|
}
|
|
1552
|
+
function applyReferenceResolution(pairs, fact, references) {
|
|
1553
|
+
const { index, defaults } = references;
|
|
1554
|
+
const result = [];
|
|
1555
|
+
const emitResolution = (resolution, authored, meta2) => {
|
|
1556
|
+
if (resolution.effortRef || resolution.matched !== "none" || resolution.diagnostics.length > 0) {
|
|
1557
|
+
result.push({
|
|
1558
|
+
metrics: new EffortMetric(authored, {
|
|
1559
|
+
resolvedSlug: resolution.effortRef?.slug,
|
|
1560
|
+
references: referenceMetadata(resolution)
|
|
1561
|
+
}),
|
|
1562
|
+
meta: meta2
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
if (resolution.equipment.length > 0) {
|
|
1566
|
+
result.push({
|
|
1567
|
+
metrics: new EquipmentMetric(
|
|
1568
|
+
resolution.equipment.map((ref) => ref.slug),
|
|
1569
|
+
{
|
|
1570
|
+
refs: resolution.equipment,
|
|
1571
|
+
source: authored,
|
|
1572
|
+
matched: resolution.matched,
|
|
1573
|
+
diagnostics: resolution.diagnostics
|
|
1574
|
+
}
|
|
1575
|
+
),
|
|
1576
|
+
meta: meta2
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
const handledRawObjects = /* @__PURE__ */ new Set();
|
|
1581
|
+
const malformedHints = [];
|
|
1582
|
+
for (const primitive of fact.primitives) {
|
|
1583
|
+
if (primitive.kind !== "metric_object") continue;
|
|
1584
|
+
if (handledRawObjects.has(primitive.raw)) continue;
|
|
1585
|
+
handledRawObjects.add(primitive.raw);
|
|
1586
|
+
if (primitive.pairs.length === 0) {
|
|
1587
|
+
malformedHints.push({
|
|
1588
|
+
metrics: {
|
|
1589
|
+
...hintMetric("reference.malformed_json", "parser"),
|
|
1590
|
+
metadata: referenceMetadata(index.resolve(primitive.raw))
|
|
1591
|
+
},
|
|
1592
|
+
meta: primitive.meta
|
|
1593
|
+
});
|
|
1594
|
+
continue;
|
|
1595
|
+
}
|
|
1596
|
+
const hasReferenceKeys = primitive.pairs.some((pair) => REFERENCE_PAIR_KEYS.has(pair.key.toLowerCase()));
|
|
1597
|
+
if (!hasReferenceKeys) continue;
|
|
1598
|
+
const resolution = index.resolve(primitive.raw);
|
|
1599
|
+
if (resolution.matched === "explicit") {
|
|
1600
|
+
emitResolution(resolution, primitive.raw, primitive.meta);
|
|
1601
|
+
}
|
|
1602
|
+
for (const pair of primitive.pairs) {
|
|
1603
|
+
if (REFERENCE_PAIR_KEYS.has(pair.key.toLowerCase())) continue;
|
|
1604
|
+
const metricType = PROPERTY_KEY_TO_METRIC_TYPE[pair.key.toLowerCase()] ?? wodWikiCore.MetricType.Custom;
|
|
1605
|
+
const rawValue = Array.isArray(pair.value) ? pair.value.join(", ") : pair.value;
|
|
1606
|
+
result.push({
|
|
1607
|
+
metrics: new PropertyMetric(pair.key, rawValue, { type: metricType, image: `${pair.key}: ${rawValue}` }),
|
|
1608
|
+
meta: primitive.meta
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
let sawEffortMetric = false;
|
|
1613
|
+
let sawDurationMetric = false;
|
|
1614
|
+
let sawMovementQuantity = false;
|
|
1615
|
+
let statementMeta;
|
|
1616
|
+
for (const pair of pairs) {
|
|
1617
|
+
const metric = pair.metrics;
|
|
1618
|
+
if (metric.type === wodWikiCore.MetricType.Custom && handledRawObjects.has(pair.meta.raw)) continue;
|
|
1619
|
+
if (metric.type === wodWikiCore.MetricType.Effort) {
|
|
1620
|
+
const authored = metric instanceof EffortMetric ? metric.effort : String(metric.value ?? "");
|
|
1621
|
+
const normalized = authored.trim().toLowerCase();
|
|
1622
|
+
if (STRUCTURAL_EFFORT_WORDS.has(normalized) || STANDARD_UNITS2.has(normalized)) {
|
|
1623
|
+
result.push(pair);
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
sawEffortMetric = true;
|
|
1627
|
+
const resolution = index.resolve(authored, defaults);
|
|
1628
|
+
if (resolution.matched === "none" && resolution.diagnostics.length === 0) {
|
|
1629
|
+
result.push(pair);
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
emitResolution(resolution, authored, pair.meta);
|
|
1633
|
+
continue;
|
|
1634
|
+
}
|
|
1635
|
+
if (metric.type === wodWikiCore.MetricType.Duration) sawDurationMetric = true;
|
|
1636
|
+
if (metric.type === wodWikiCore.MetricType.Rep || metric.type === wodWikiCore.MetricType.Rounds || metric.type === wodWikiCore.MetricType.Resistance || metric.type === wodWikiCore.MetricType.Distance) {
|
|
1637
|
+
sawMovementQuantity = true;
|
|
1638
|
+
}
|
|
1639
|
+
result.push(pair);
|
|
1640
|
+
statementMeta ??= pair.meta;
|
|
1641
|
+
}
|
|
1642
|
+
if (defaults && !sawEffortMetric && fact.isLeaf && sawMovementQuantity && !sawDurationMetric) {
|
|
1643
|
+
const resolution = index.resolve("", defaults);
|
|
1644
|
+
if (resolution.effortRef) {
|
|
1645
|
+
emitResolution(resolution, resolution.effortRef.label, statementMeta ?? fact.meta);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
result.push(...malformedHints);
|
|
1649
|
+
return result;
|
|
1650
|
+
}
|
|
1514
1651
|
function classifyPrimitive(primitive) {
|
|
1515
1652
|
switch (primitive.kind) {
|
|
1516
1653
|
case "lap":
|
|
@@ -1579,10 +1716,11 @@ function classifyPrimitive(primitive) {
|
|
|
1579
1716
|
const pairs = primitive.pairs;
|
|
1580
1717
|
return pairs.map((pair) => {
|
|
1581
1718
|
const metricType = PROPERTY_KEY_TO_METRIC_TYPE[pair.key.toLowerCase()] ?? wodWikiCore.MetricType.Custom;
|
|
1719
|
+
const rawValue = Array.isArray(pair.value) ? pair.value.join(", ") : pair.value;
|
|
1582
1720
|
return {
|
|
1583
|
-
metrics: new PropertyMetric(pair.key,
|
|
1721
|
+
metrics: new PropertyMetric(pair.key, rawValue, {
|
|
1584
1722
|
type: metricType,
|
|
1585
|
-
image: `${pair.key}: ${
|
|
1723
|
+
image: `${pair.key}: ${rawValue}`
|
|
1586
1724
|
}),
|
|
1587
1725
|
meta: primitive.meta
|
|
1588
1726
|
};
|
|
@@ -1620,6 +1758,23 @@ function mergeFragments(pairs) {
|
|
|
1620
1758
|
continue;
|
|
1621
1759
|
}
|
|
1622
1760
|
}
|
|
1761
|
+
if (current.metrics instanceof RepMetric && next.metrics instanceof EffortMetric && /^(reps?)$/i.test(next.metrics.effort)) {
|
|
1762
|
+
const gap = next.meta.startOffset - current.meta.endOffset;
|
|
1763
|
+
if (gap <= 1) {
|
|
1764
|
+
const mergedMeta = {
|
|
1765
|
+
...current.meta,
|
|
1766
|
+
endOffset: next.meta.endOffset,
|
|
1767
|
+
columnEnd: next.meta.columnEnd,
|
|
1768
|
+
length: next.meta.endOffset - current.meta.startOffset,
|
|
1769
|
+
raw: current.meta.raw + (gap === 1 ? " " : "") + next.meta.raw
|
|
1770
|
+
};
|
|
1771
|
+
current = {
|
|
1772
|
+
metrics: current.metrics,
|
|
1773
|
+
meta: mergedMeta
|
|
1774
|
+
};
|
|
1775
|
+
continue;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1623
1778
|
if (current.metrics instanceof RepMetric && (next.metrics instanceof ResistanceMetric || next.metrics instanceof DistanceMetric)) {
|
|
1624
1779
|
const gap = next.meta.startOffset - current.meta.endOffset;
|
|
1625
1780
|
if (gap <= 1) {
|
|
@@ -1995,12 +2150,12 @@ function resolveSourceInput(sourceOrTree, treeArg) {
|
|
|
1995
2150
|
}
|
|
1996
2151
|
return { source: "" };
|
|
1997
2152
|
}
|
|
1998
|
-
function extractStatementsRaw(sourceOrTree, treeArg) {
|
|
2153
|
+
function extractStatementsRaw(sourceOrTree, treeArg, references) {
|
|
1999
2154
|
const { source, tree } = resolveSourceInput(sourceOrTree, treeArg);
|
|
2000
2155
|
const facts = extractSyntaxFacts(source, tree);
|
|
2001
|
-
return normalizeInlineRounds(classifyStatements(facts));
|
|
2156
|
+
return normalizeInlineRounds(classifyStatements(facts, references));
|
|
2002
2157
|
}
|
|
2003
|
-
function extractStatements(sourceOrTree, sportOrTree, sportArg) {
|
|
2158
|
+
function extractStatements(sourceOrTree, sportOrTree, sportArg, references) {
|
|
2004
2159
|
let sport;
|
|
2005
2160
|
let tree;
|
|
2006
2161
|
if (typeof sportOrTree === "string") {
|
|
@@ -2011,7 +2166,7 @@ function extractStatements(sourceOrTree, sportOrTree, sportArg) {
|
|
|
2011
2166
|
}
|
|
2012
2167
|
const resolved = resolveSourceInput(sourceOrTree, tree);
|
|
2013
2168
|
const facts = extractSyntaxFacts(resolved.source, resolved.tree);
|
|
2014
|
-
const statements = classifyStatements(facts);
|
|
2169
|
+
const statements = classifyStatements(facts, references);
|
|
2015
2170
|
dialectStack.processAll(statements, sport);
|
|
2016
2171
|
return normalizeInlineRounds(statements);
|
|
2017
2172
|
}
|
|
@@ -2143,7 +2298,8 @@ function parseScript(inputText, options = {}) {
|
|
|
2143
2298
|
const doc = inputText.endsWith("\n") ? inputText : inputText + "\n";
|
|
2144
2299
|
const tree = parser.parse(doc);
|
|
2145
2300
|
const sport = options.sport ?? options.dialect;
|
|
2146
|
-
const
|
|
2301
|
+
const references = options.references ? { index: options.references, defaults: options.defaults } : void 0;
|
|
2302
|
+
const statements = options.withoutDialects ? extractStatementsRaw(inputText, tree, references) : extractStatements(inputText, tree, sport, references);
|
|
2147
2303
|
return new WhiteboardScript(inputText, statements, []);
|
|
2148
2304
|
} catch (error) {
|
|
2149
2305
|
const msg = error instanceof Error ? error.message : "Unknown parse error";
|
|
@@ -2153,6 +2309,10 @@ function parseScript(inputText, options = {}) {
|
|
|
2153
2309
|
|
|
2154
2310
|
// src/parser/md-timer.ts
|
|
2155
2311
|
var MdTimerRuntime = class {
|
|
2312
|
+
config;
|
|
2313
|
+
constructor(config) {
|
|
2314
|
+
this.config = config;
|
|
2315
|
+
}
|
|
2156
2316
|
/**
|
|
2157
2317
|
* Parse WhiteboardScript source into a {@link WhiteboardScript}, running the
|
|
2158
2318
|
* Dialect Stack on every statement.
|
|
@@ -2160,22 +2320,21 @@ var MdTimerRuntime = class {
|
|
|
2160
2320
|
* @param sport - The block's `:sport` fence suffix (` ```log:climbing `).
|
|
2161
2321
|
*/
|
|
2162
2322
|
read(inputText, sport) {
|
|
2163
|
-
return parseScript(inputText, { sport });
|
|
2323
|
+
return parseScript(inputText, { sport, ...this.config });
|
|
2164
2324
|
}
|
|
2165
2325
|
/**
|
|
2166
2326
|
* Parse without running the Dialect Stack. Used by the parser test harness
|
|
2167
2327
|
* which applies its own Dialect set. Production consumers should use {@link read}.
|
|
2168
2328
|
*/
|
|
2169
2329
|
readWithoutDialects(inputText) {
|
|
2170
|
-
return parseScript(inputText, { withoutDialects: true });
|
|
2330
|
+
return parseScript(inputText, { withoutDialects: true, ...this.config });
|
|
2171
2331
|
}
|
|
2172
2332
|
};
|
|
2173
2333
|
|
|
2174
2334
|
// src/parser/parserInstance.ts
|
|
2175
|
-
function createParser() {
|
|
2176
|
-
return new MdTimerRuntime();
|
|
2335
|
+
function createParser(config) {
|
|
2336
|
+
return new MdTimerRuntime(config);
|
|
2177
2337
|
}
|
|
2178
|
-
var sharedParser = /* @__PURE__ */ createParser();
|
|
2179
2338
|
var nextTagID = 0;
|
|
2180
2339
|
var Tag = class _Tag {
|
|
2181
2340
|
/**
|
|
@@ -2878,6 +3037,426 @@ function disciplineFactorFor(discipline) {
|
|
|
2878
3037
|
const key = discipline.toLowerCase();
|
|
2879
3038
|
return isEffortDiscipline(key) ? DISCIPLINE_FACTORS[key] : 1;
|
|
2880
3039
|
}
|
|
3040
|
+
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
3041
|
+
var SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
3042
|
+
function unquote(value) {
|
|
3043
|
+
return value.replace(
|
|
3044
|
+
/^(['"])(.*)\1$/,
|
|
3045
|
+
(_match, quote, inner) => quote === '"' ? inner.replace(
|
|
3046
|
+
/\\(["\\nrt])/g,
|
|
3047
|
+
(_all, c) => c === "n" ? "\n" : c === "r" ? "\r" : c === "t" ? " " : c
|
|
3048
|
+
) : inner
|
|
3049
|
+
);
|
|
3050
|
+
}
|
|
3051
|
+
function quoteYaml(val) {
|
|
3052
|
+
if (!val) return '""';
|
|
3053
|
+
if (/[:"'\n#{}[\],&*?|\-<>=%!@`]/.test(val) || val !== val.trim()) {
|
|
3054
|
+
return `"${val.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
|
|
3055
|
+
}
|
|
3056
|
+
return val;
|
|
3057
|
+
}
|
|
3058
|
+
function parseFrontmatter(raw) {
|
|
3059
|
+
const match = raw.match(FRONTMATTER_RE);
|
|
3060
|
+
if (!match) return { meta: {}, body: raw };
|
|
3061
|
+
const lines = match[1].split(/\r?\n/);
|
|
3062
|
+
const meta2 = {};
|
|
3063
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3064
|
+
const keyMatch = lines[i].match(/^([A-Za-z][\w.-]*)\s*:(.*)$/);
|
|
3065
|
+
if (!keyMatch) continue;
|
|
3066
|
+
const key = keyMatch[1];
|
|
3067
|
+
const rawVal = keyMatch[2].trim();
|
|
3068
|
+
if (rawVal === "") {
|
|
3069
|
+
const items = [];
|
|
3070
|
+
while (i + 1 < lines.length) {
|
|
3071
|
+
const itemMatch = lines[i + 1].match(/^\s+-\s+(.+)$/);
|
|
3072
|
+
if (!itemMatch) break;
|
|
3073
|
+
items.push(unquote(itemMatch[1].trim()));
|
|
3074
|
+
i++;
|
|
3075
|
+
}
|
|
3076
|
+
meta2[key] = items.length > 0 ? items : "";
|
|
3077
|
+
} else {
|
|
3078
|
+
const wasQuoted = /^(['"])(.*)\1$/.test(rawVal);
|
|
3079
|
+
const value = unquote(rawVal);
|
|
3080
|
+
const num2 = Number(value);
|
|
3081
|
+
meta2[key] = !wasQuoted && value !== "" && !isNaN(num2) ? num2 : value;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
return { meta: meta2, body: raw.slice(match[0].length) };
|
|
3085
|
+
}
|
|
3086
|
+
function getScalar(meta2, key) {
|
|
3087
|
+
const value = meta2[key];
|
|
3088
|
+
return typeof value === "string" || typeof value === "number" ? value : void 0;
|
|
3089
|
+
}
|
|
3090
|
+
function getListStrict(meta2, key) {
|
|
3091
|
+
const value = meta2[key];
|
|
3092
|
+
if (value === void 0) return [];
|
|
3093
|
+
if (typeof value === "string" && value === "[]") return [];
|
|
3094
|
+
return Array.isArray(value) ? value : void 0;
|
|
3095
|
+
}
|
|
3096
|
+
function coerceNestedScalar(raw) {
|
|
3097
|
+
if (raw === "true") return true;
|
|
3098
|
+
if (raw === "false") return false;
|
|
3099
|
+
if (raw !== "" && !Number.isNaN(Number(raw))) return Number(raw);
|
|
3100
|
+
return unquote(raw);
|
|
3101
|
+
}
|
|
3102
|
+
function readNestedSection(lines, key) {
|
|
3103
|
+
const start = lines.findIndex((l) => new RegExp(`^${key}:\\s*$`).test(l));
|
|
3104
|
+
if (start === -1) return void 0;
|
|
3105
|
+
const result = {};
|
|
3106
|
+
let subKey = null;
|
|
3107
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
3108
|
+
const line = lines[i];
|
|
3109
|
+
if (/^\S/.test(line)) break;
|
|
3110
|
+
const trimmed = line.trim();
|
|
3111
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3112
|
+
const indent = line.length - line.trimStart().length;
|
|
3113
|
+
const m = trimmed.match(/^([\w.-]+):\s*(.*)$/);
|
|
3114
|
+
if (!m) continue;
|
|
3115
|
+
const [, k, rawVal] = m;
|
|
3116
|
+
if (indent === 2) {
|
|
3117
|
+
if (rawVal === "") {
|
|
3118
|
+
subKey = k;
|
|
3119
|
+
result[k] = {};
|
|
3120
|
+
} else {
|
|
3121
|
+
subKey = null;
|
|
3122
|
+
result[k] = coerceNestedScalar(rawVal);
|
|
3123
|
+
}
|
|
3124
|
+
} else if (indent >= 4 && subKey !== null) {
|
|
3125
|
+
result[subKey][k] = coerceNestedScalar(rawVal);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
3129
|
+
}
|
|
3130
|
+
function parseDiscipline(val) {
|
|
3131
|
+
if (!val) return void 0;
|
|
3132
|
+
const normalized = unquote(val.trim()).toLowerCase();
|
|
3133
|
+
if (isEffortDiscipline(normalized)) return normalized;
|
|
3134
|
+
console.warn(`[definition-markdown] unknown discipline '${val}' \u2014 expected one of the canonical values; treating as unset`);
|
|
3135
|
+
return void 0;
|
|
3136
|
+
}
|
|
3137
|
+
function identityProblems(slug, label) {
|
|
3138
|
+
const problems = [];
|
|
3139
|
+
const slugText = slug === void 0 ? "" : String(slug);
|
|
3140
|
+
if (!slugText) problems.push("Missing required field: slug");
|
|
3141
|
+
else if (!SLUG_RE.test(slugText)) problems.push("Invalid slug: must be lowercase letters, numbers, and hyphens only");
|
|
3142
|
+
if (label === void 0 || !String(label).trim()) problems.push("Missing required field: label");
|
|
3143
|
+
return problems;
|
|
3144
|
+
}
|
|
3145
|
+
function stringListProblems(values, kind) {
|
|
3146
|
+
const problems = [];
|
|
3147
|
+
for (const value of values) {
|
|
3148
|
+
if (!value.trim()) problems.push(`Invalid ${kind}: blank entries are not allowed`);
|
|
3149
|
+
else if (kind === "equipment" && !SLUG_RE.test(value)) {
|
|
3150
|
+
problems.push(`Invalid ${kind}: '${value}' must be a canonical slug (lowercase letters, numbers, hyphens)`);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
const duplicate = values.find((v, i) => values.indexOf(v) !== i);
|
|
3154
|
+
if (duplicate !== void 0) problems.push(`Invalid ${kind}: duplicate entry '${duplicate}'`);
|
|
3155
|
+
return problems;
|
|
3156
|
+
}
|
|
3157
|
+
function metProblem(value) {
|
|
3158
|
+
if (value === void 0) return "Missing required field: baseAttributes.met";
|
|
3159
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
3160
|
+
return "Invalid baseAttributes.met: must be a positive number";
|
|
3161
|
+
}
|
|
3162
|
+
return null;
|
|
3163
|
+
}
|
|
3164
|
+
function parseEffortFile(raw) {
|
|
3165
|
+
const blockMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
3166
|
+
if (!blockMatch) return null;
|
|
3167
|
+
const innerLines = blockMatch[1].split(/\r?\n/);
|
|
3168
|
+
const { meta: meta2, body } = parseFrontmatter(raw);
|
|
3169
|
+
const type = getScalar(meta2, "type");
|
|
3170
|
+
if (type !== void 0 && type !== "effort") return null;
|
|
3171
|
+
const id = getScalar(meta2, "id");
|
|
3172
|
+
const slug = getScalar(meta2, "slug");
|
|
3173
|
+
const label = getScalar(meta2, "label");
|
|
3174
|
+
const aliases = getListStrict(meta2, "aliases");
|
|
3175
|
+
const equipment = getListStrict(meta2, "equipment");
|
|
3176
|
+
if (aliases === void 0 || equipment === void 0) return null;
|
|
3177
|
+
if (identityProblems(slug, label).length > 0) return null;
|
|
3178
|
+
if (stringListProblems(aliases, "aliases").length > 0) return null;
|
|
3179
|
+
if (equipment.length > 0 && stringListProblems(equipment, "equipment").length > 0) return null;
|
|
3180
|
+
const baseAttrs = readNestedSection(innerLines, "baseAttributes") ?? {};
|
|
3181
|
+
if (metProblem(baseAttrs.met) !== null) return null;
|
|
3182
|
+
const met = baseAttrs.met;
|
|
3183
|
+
const discipline = parseDiscipline(
|
|
3184
|
+
typeof baseAttrs.discipline === "string" ? baseAttrs.discipline : void 0
|
|
3185
|
+
);
|
|
3186
|
+
const intensityTier = baseAttrs.intensityTier;
|
|
3187
|
+
const disciplineFactor = typeof baseAttrs.disciplineFactor === "number" && Number.isFinite(baseAttrs.disciplineFactor) ? baseAttrs.disciplineFactor : void 0;
|
|
3188
|
+
const hints = readNestedSection(innerLines, "hints");
|
|
3189
|
+
const derivation = readNestedSection(innerLines, "derivation");
|
|
3190
|
+
const trimmedBody = body.trim();
|
|
3191
|
+
return {
|
|
3192
|
+
id: String(id ?? ""),
|
|
3193
|
+
slug: String(slug),
|
|
3194
|
+
label: String(label),
|
|
3195
|
+
aliases,
|
|
3196
|
+
baseAttributes: {
|
|
3197
|
+
met,
|
|
3198
|
+
...discipline ? { discipline } : {},
|
|
3199
|
+
...disciplineFactor !== void 0 ? { disciplineFactor } : {},
|
|
3200
|
+
...intensityTier ? { intensityTier } : {}
|
|
3201
|
+
},
|
|
3202
|
+
registrySource: "bundled",
|
|
3203
|
+
...equipment.length > 0 ? { equipment } : {},
|
|
3204
|
+
...trimmedBody ? { body: trimmedBody } : {},
|
|
3205
|
+
...hints ? { hints } : {},
|
|
3206
|
+
...derivation ? { derivation } : {}
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
function parseEquipmentFile(raw) {
|
|
3210
|
+
const { meta: meta2, body } = parseFrontmatter(raw);
|
|
3211
|
+
if (getScalar(meta2, "type") !== "equipment") return null;
|
|
3212
|
+
const id = getScalar(meta2, "id");
|
|
3213
|
+
const slug = getScalar(meta2, "slug");
|
|
3214
|
+
const label = getScalar(meta2, "label");
|
|
3215
|
+
const aliases = getListStrict(meta2, "aliases");
|
|
3216
|
+
if (id === void 0 || aliases === void 0) return null;
|
|
3217
|
+
if (identityProblems(slug, label).length > 0) return null;
|
|
3218
|
+
if (stringListProblems(aliases, "aliases").length > 0) return null;
|
|
3219
|
+
const trimmedBody = body.trim();
|
|
3220
|
+
return {
|
|
3221
|
+
id: String(id),
|
|
3222
|
+
slug: String(slug),
|
|
3223
|
+
label: String(label),
|
|
3224
|
+
aliases,
|
|
3225
|
+
registrySource: "bundled",
|
|
3226
|
+
...trimmedBody ? { body: trimmedBody } : {}
|
|
3227
|
+
};
|
|
3228
|
+
}
|
|
3229
|
+
function createDefaultEffort() {
|
|
3230
|
+
return {
|
|
3231
|
+
id: `effort-user-${uuid.v7()}`,
|
|
3232
|
+
slug: "",
|
|
3233
|
+
label: "",
|
|
3234
|
+
aliases: [],
|
|
3235
|
+
baseAttributes: { met: 5 },
|
|
3236
|
+
registrySource: "user"
|
|
3237
|
+
};
|
|
3238
|
+
}
|
|
3239
|
+
function documentToEffort(doc, baseEffort) {
|
|
3240
|
+
const errors = [];
|
|
3241
|
+
const blockMatch = doc.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
3242
|
+
if (!blockMatch) {
|
|
3243
|
+
errors.push("Missing YAML frontmatter delimiters (---)");
|
|
3244
|
+
return { effort: baseEffort ?? createDefaultEffort(), errors };
|
|
3245
|
+
}
|
|
3246
|
+
const innerLines = blockMatch[1].split(/\r?\n/);
|
|
3247
|
+
const { meta: meta2, body } = parseFrontmatter(doc);
|
|
3248
|
+
const bodyText = body.replace(/^\r?\n+/, "") || void 0;
|
|
3249
|
+
const id = getScalar(meta2, "id");
|
|
3250
|
+
const slug = getScalar(meta2, "slug");
|
|
3251
|
+
const label = getScalar(meta2, "label");
|
|
3252
|
+
const registrySource = getScalar(meta2, "registrySource");
|
|
3253
|
+
const createdAt = getScalar(meta2, "createdAt");
|
|
3254
|
+
const updatedAt = getScalar(meta2, "updatedAt");
|
|
3255
|
+
const aliases = getListStrict(meta2, "aliases");
|
|
3256
|
+
const equipmentRaw = meta2["equipment"];
|
|
3257
|
+
const equipment = getListStrict(meta2, "equipment");
|
|
3258
|
+
if (aliases === void 0) {
|
|
3259
|
+
errors.push("Invalid aliases: must be a list (`aliases:` followed by `- item` lines)");
|
|
3260
|
+
}
|
|
3261
|
+
if (equipment === void 0) {
|
|
3262
|
+
errors.push("Invalid equipment: must be a list (`equipment:` followed by `- slug` lines)");
|
|
3263
|
+
}
|
|
3264
|
+
const baseAttrs = readNestedSection(innerLines, "baseAttributes") ?? {};
|
|
3265
|
+
const baseAttributes = {};
|
|
3266
|
+
const metRaw = baseAttrs.met;
|
|
3267
|
+
if (metRaw === void 0) {
|
|
3268
|
+
const fallbackMet = baseEffort?.baseAttributes.met;
|
|
3269
|
+
const fallbackProblem = fallbackMet === void 0 ? null : metProblem(fallbackMet);
|
|
3270
|
+
if (fallbackProblem) errors.push(fallbackProblem);
|
|
3271
|
+
else if (fallbackMet !== void 0) baseAttributes.met = fallbackMet;
|
|
3272
|
+
else errors.push("Missing required field: baseAttributes.met");
|
|
3273
|
+
} else {
|
|
3274
|
+
const problem = metProblem(metRaw);
|
|
3275
|
+
if (problem) errors.push(problem);
|
|
3276
|
+
else baseAttributes.met = Number(metRaw);
|
|
3277
|
+
}
|
|
3278
|
+
if (getScalar(meta2, "met") !== void 0) {
|
|
3279
|
+
errors.push(
|
|
3280
|
+
"Invalid met placement: 'met' must be nested under baseAttributes (missing baseAttributes.met)"
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
const discipline = parseDiscipline(
|
|
3284
|
+
typeof baseAttrs.discipline === "string" ? baseAttrs.discipline : void 0
|
|
3285
|
+
);
|
|
3286
|
+
if (discipline) baseAttributes.discipline = discipline;
|
|
3287
|
+
if (typeof baseAttrs.disciplineFactor === "number" && Number.isFinite(baseAttrs.disciplineFactor)) {
|
|
3288
|
+
baseAttributes.disciplineFactor = baseAttrs.disciplineFactor;
|
|
3289
|
+
}
|
|
3290
|
+
if (baseAttrs.intensityTier) baseAttributes.intensityTier = baseAttrs.intensityTier;
|
|
3291
|
+
const hints = readNestedSection(innerLines, "hints") ?? baseEffort?.hints;
|
|
3292
|
+
const derivation = readNestedSection(innerLines, "derivation");
|
|
3293
|
+
errors.push(...identityProblems(slug, label));
|
|
3294
|
+
if (aliases !== void 0) errors.push(...stringListProblems(aliases, "aliases"));
|
|
3295
|
+
if (equipment !== void 0 && equipment.length > 0) {
|
|
3296
|
+
errors.push(...stringListProblems(equipment, "equipment"));
|
|
3297
|
+
}
|
|
3298
|
+
const resolvedEquipment = equipmentRaw !== void 0 ? equipment ?? [] : baseEffort?.equipment?.length ? baseEffort.equipment : void 0;
|
|
3299
|
+
const effort = {
|
|
3300
|
+
id: String(id ?? "") || baseEffort?.id || `effort-user-${uuid.v7()}`,
|
|
3301
|
+
slug: String(slug ?? ""),
|
|
3302
|
+
label: String(label ?? ""),
|
|
3303
|
+
aliases: aliases ?? [],
|
|
3304
|
+
baseAttributes,
|
|
3305
|
+
registrySource: registrySource || baseEffort?.registrySource || "user",
|
|
3306
|
+
...resolvedEquipment ? { equipment: resolvedEquipment } : {},
|
|
3307
|
+
...createdAt ? { createdAt: String(createdAt) } : {},
|
|
3308
|
+
...updatedAt ? { updatedAt: String(updatedAt) } : {},
|
|
3309
|
+
...bodyText ? { body: bodyText } : {},
|
|
3310
|
+
...hints && Object.keys(hints).length > 0 ? { hints } : {},
|
|
3311
|
+
...derivation ? { derivation } : {}
|
|
3312
|
+
};
|
|
3313
|
+
return { effort, errors };
|
|
3314
|
+
}
|
|
3315
|
+
function effortToDocument(effort) {
|
|
3316
|
+
const lines = ["---"];
|
|
3317
|
+
lines.push(`id: ${effort.id}`);
|
|
3318
|
+
lines.push(`slug: ${effort.slug}`);
|
|
3319
|
+
lines.push(`label: ${quoteYaml(effort.label)}`);
|
|
3320
|
+
if (effort.aliases.length > 0) {
|
|
3321
|
+
lines.push("aliases:");
|
|
3322
|
+
for (const alias of effort.aliases) {
|
|
3323
|
+
lines.push(` - ${quoteYaml(alias)}`);
|
|
3324
|
+
}
|
|
3325
|
+
} else {
|
|
3326
|
+
lines.push("aliases: []");
|
|
3327
|
+
}
|
|
3328
|
+
lines.push("baseAttributes:");
|
|
3329
|
+
lines.push(` met: ${effort.baseAttributes.met}`);
|
|
3330
|
+
if (effort.baseAttributes.discipline) {
|
|
3331
|
+
lines.push(` discipline: ${effort.baseAttributes.discipline}`);
|
|
3332
|
+
}
|
|
3333
|
+
if (effort.baseAttributes.disciplineFactor !== void 0) {
|
|
3334
|
+
lines.push(` disciplineFactor: ${effort.baseAttributes.disciplineFactor}`);
|
|
3335
|
+
}
|
|
3336
|
+
if (effort.baseAttributes.intensityTier) {
|
|
3337
|
+
lines.push(` intensityTier: ${effort.baseAttributes.intensityTier}`);
|
|
3338
|
+
}
|
|
3339
|
+
lines.push(`registrySource: ${effort.registrySource}`);
|
|
3340
|
+
if (effort.equipment && effort.equipment.length > 0) {
|
|
3341
|
+
lines.push("equipment:");
|
|
3342
|
+
for (const slug of effort.equipment) {
|
|
3343
|
+
lines.push(` - ${quoteYaml(slug)}`);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
if (effort.derivation) {
|
|
3347
|
+
lines.push("derivation:");
|
|
3348
|
+
if (effort.derivation.parentSlug) {
|
|
3349
|
+
lines.push(` parentSlug: ${effort.derivation.parentSlug}`);
|
|
3350
|
+
}
|
|
3351
|
+
if (effort.derivation.coefficients && Object.keys(effort.derivation.coefficients).length > 0) {
|
|
3352
|
+
lines.push(" coefficients:");
|
|
3353
|
+
for (const [k, v] of Object.entries(effort.derivation.coefficients)) {
|
|
3354
|
+
lines.push(` ${k}: ${v}`);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (effort.derivation.hardOverrides && Object.keys(effort.derivation.hardOverrides).length > 0) {
|
|
3358
|
+
lines.push(" hardOverrides:");
|
|
3359
|
+
for (const [k, v] of Object.entries(effort.derivation.hardOverrides)) {
|
|
3360
|
+
lines.push(` ${k}: ${JSON.stringify(v)}`);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
if (effort.createdAt) {
|
|
3365
|
+
lines.push(`createdAt: ${effort.createdAt}`);
|
|
3366
|
+
}
|
|
3367
|
+
if (effort.updatedAt) {
|
|
3368
|
+
lines.push(`updatedAt: ${effort.updatedAt}`);
|
|
3369
|
+
}
|
|
3370
|
+
if (effort.hints && Object.keys(effort.hints).length > 0) {
|
|
3371
|
+
lines.push("hints:");
|
|
3372
|
+
for (const [k, v] of Object.entries(effort.hints)) {
|
|
3373
|
+
lines.push(` ${k}: ${v !== null && typeof v === "object" ? JSON.stringify(v) : String(v)}`);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
lines.push("---");
|
|
3377
|
+
if (effort.body) {
|
|
3378
|
+
lines.push("");
|
|
3379
|
+
lines.push(effort.body);
|
|
3380
|
+
}
|
|
3381
|
+
return lines.join("\n");
|
|
3382
|
+
}
|
|
3383
|
+
function createDefaultEquipment() {
|
|
3384
|
+
return {
|
|
3385
|
+
id: `equipment-user-${uuid.v7()}`,
|
|
3386
|
+
slug: "",
|
|
3387
|
+
label: "",
|
|
3388
|
+
aliases: [],
|
|
3389
|
+
registrySource: "user"
|
|
3390
|
+
};
|
|
3391
|
+
}
|
|
3392
|
+
function documentToEquipment(doc, baseEquipment) {
|
|
3393
|
+
const errors = [];
|
|
3394
|
+
const blockMatch = doc.match(FRONTMATTER_RE);
|
|
3395
|
+
if (!blockMatch) {
|
|
3396
|
+
errors.push("Missing YAML frontmatter delimiters (---)");
|
|
3397
|
+
return { equipment: baseEquipment ?? createDefaultEquipment(), errors };
|
|
3398
|
+
}
|
|
3399
|
+
const { meta: meta2, body } = parseFrontmatter(doc);
|
|
3400
|
+
const bodyText = body.replace(/^\r?\n+/, "") || void 0;
|
|
3401
|
+
const type = getScalar(meta2, "type");
|
|
3402
|
+
const id = getScalar(meta2, "id");
|
|
3403
|
+
const slug = getScalar(meta2, "slug");
|
|
3404
|
+
const label = getScalar(meta2, "label");
|
|
3405
|
+
const registrySource = getScalar(meta2, "registrySource");
|
|
3406
|
+
const createdAt = getScalar(meta2, "createdAt");
|
|
3407
|
+
const updatedAt = getScalar(meta2, "updatedAt");
|
|
3408
|
+
const aliases = getListStrict(meta2, "aliases");
|
|
3409
|
+
if (type !== "equipment") errors.push("Missing or invalid discriminator: frontmatter must declare `type: equipment`");
|
|
3410
|
+
if (!slug || !SLUG_RE.test(String(slug))) errors.push("Invalid slug: must be a canonical slug (lowercase letters, numbers, hyphens)");
|
|
3411
|
+
if (!label) errors.push("Missing required field: label");
|
|
3412
|
+
if (aliases === void 0) {
|
|
3413
|
+
errors.push("Invalid aliases: must be a list (`aliases:` followed by `- item` lines)");
|
|
3414
|
+
} else {
|
|
3415
|
+
errors.push(...stringListProblems(aliases, "aliases"));
|
|
3416
|
+
}
|
|
3417
|
+
if (registrySource !== void 0 && registrySource !== "bundled" && registrySource !== "user") {
|
|
3418
|
+
errors.push("Invalid registrySource: must be 'bundled' or 'user'");
|
|
3419
|
+
}
|
|
3420
|
+
const equipment = {
|
|
3421
|
+
id: String(id ?? "") || baseEquipment?.id || `equipment-user-${uuid.v7()}`,
|
|
3422
|
+
slug: String(slug ?? ""),
|
|
3423
|
+
label: String(label ?? ""),
|
|
3424
|
+
aliases: aliases ?? [],
|
|
3425
|
+
registrySource: registrySource || baseEquipment?.registrySource || "user",
|
|
3426
|
+
...createdAt ? { createdAt: String(createdAt) } : {},
|
|
3427
|
+
...updatedAt ? { updatedAt: String(updatedAt) } : {},
|
|
3428
|
+
...bodyText ? { body: bodyText } : {}
|
|
3429
|
+
};
|
|
3430
|
+
return { equipment, errors };
|
|
3431
|
+
}
|
|
3432
|
+
function equipmentToDocument(equipment) {
|
|
3433
|
+
const lines = ["---"];
|
|
3434
|
+
lines.push("type: equipment");
|
|
3435
|
+
lines.push(`id: ${equipment.id}`);
|
|
3436
|
+
lines.push(`slug: ${equipment.slug}`);
|
|
3437
|
+
lines.push(`label: ${quoteYaml(equipment.label)}`);
|
|
3438
|
+
if (equipment.aliases.length > 0) {
|
|
3439
|
+
lines.push("aliases:");
|
|
3440
|
+
for (const alias of equipment.aliases) {
|
|
3441
|
+
lines.push(` - ${quoteYaml(alias)}`);
|
|
3442
|
+
}
|
|
3443
|
+
} else {
|
|
3444
|
+
lines.push("aliases: []");
|
|
3445
|
+
}
|
|
3446
|
+
lines.push(`registrySource: ${equipment.registrySource}`);
|
|
3447
|
+
if (equipment.createdAt) {
|
|
3448
|
+
lines.push(`createdAt: ${equipment.createdAt}`);
|
|
3449
|
+
}
|
|
3450
|
+
if (equipment.updatedAt) {
|
|
3451
|
+
lines.push(`updatedAt: ${equipment.updatedAt}`);
|
|
3452
|
+
}
|
|
3453
|
+
lines.push("---");
|
|
3454
|
+
if (equipment.body) {
|
|
3455
|
+
lines.push("");
|
|
3456
|
+
lines.push(equipment.body);
|
|
3457
|
+
}
|
|
3458
|
+
return lines.join("\n");
|
|
3459
|
+
}
|
|
2881
3460
|
|
|
2882
3461
|
// src/effort-registry/fuzzyMatch.ts
|
|
2883
3462
|
function levenshteinDistance(a, b) {
|
|
@@ -3262,12 +3841,27 @@ var CompositeEffortRegistry = class {
|
|
|
3262
3841
|
* Initialize the registry:
|
|
3263
3842
|
* 1. Load bundled efforts into memory
|
|
3264
3843
|
* 2. Load user efforts from storage adapter into memory
|
|
3844
|
+
*
|
|
3845
|
+
* An explicit `customBundled` REPLACES the bundled tier — passing an empty
|
|
3846
|
+
* set clears it — so a seed source removal never leaves stale bundled
|
|
3847
|
+
* efforts cached. The no-argument call keeps the constructor/default set.
|
|
3848
|
+
* Both tiers rebuild only AFTER a successful adapter load: a failed load
|
|
3849
|
+
* propagates (readiness must never report over stale data), and deleted
|
|
3850
|
+
* custom rows disappear because the user map is cleared too.
|
|
3265
3851
|
*/
|
|
3266
3852
|
async loadBundled(customBundled) {
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3853
|
+
this.bundledEfforts.clear();
|
|
3854
|
+
if (customBundled) {
|
|
3855
|
+
for (const effort of customBundled) {
|
|
3856
|
+
this.bundledEfforts.set(effort.slug, effort);
|
|
3857
|
+
}
|
|
3858
|
+
} else {
|
|
3859
|
+
const seed = this.initialBundled ?? bundledEfforts;
|
|
3860
|
+
for (const effort of seed) {
|
|
3861
|
+
this.bundledEfforts.set(effort.slug, effort);
|
|
3862
|
+
}
|
|
3270
3863
|
}
|
|
3864
|
+
this.userEfforts.clear();
|
|
3271
3865
|
if (this.storage) {
|
|
3272
3866
|
try {
|
|
3273
3867
|
const userEfforts = await this.storage.load();
|
|
@@ -3397,6 +3991,358 @@ async function addAliasToEffort(registry, targetSlug, newAlias) {
|
|
|
3397
3991
|
await registry.upsert(updatedUserEffort);
|
|
3398
3992
|
return updatedUserEffort;
|
|
3399
3993
|
}
|
|
3994
|
+
function buildExactIndex(entities) {
|
|
3995
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3996
|
+
const add = (key, entity) => {
|
|
3997
|
+
if (!key) return;
|
|
3998
|
+
const existing = byKey.get(key);
|
|
3999
|
+
if (existing) {
|
|
4000
|
+
if (!existing.includes(entity)) existing.push(entity);
|
|
4001
|
+
} else {
|
|
4002
|
+
byKey.set(key, [entity]);
|
|
4003
|
+
}
|
|
4004
|
+
};
|
|
4005
|
+
for (const entity of entities) {
|
|
4006
|
+
add(entity.slug.toLowerCase(), entity);
|
|
4007
|
+
add(normalizeForFuzzy(entity.label), entity);
|
|
4008
|
+
for (const alias of entity.aliases) add(normalizeForFuzzy(alias), entity);
|
|
4009
|
+
}
|
|
4010
|
+
return {
|
|
4011
|
+
byKey,
|
|
4012
|
+
lookup(key) {
|
|
4013
|
+
const direct = byKey.get(key) ?? byKey.get(normalizeForFuzzy(key));
|
|
4014
|
+
if (!direct || direct.length === 0) return { entity: null, ambiguous: [] };
|
|
4015
|
+
if (direct.length === 1) return { entity: direct[0], ambiguous: [] };
|
|
4016
|
+
return { entity: null, ambiguous: direct };
|
|
4017
|
+
}
|
|
4018
|
+
};
|
|
4019
|
+
}
|
|
4020
|
+
function equipmentRef(entity) {
|
|
4021
|
+
return { kind: "equipment", slug: entity.slug, label: entity.label };
|
|
4022
|
+
}
|
|
4023
|
+
function dedupeRefs(refs) {
|
|
4024
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4025
|
+
const result = [];
|
|
4026
|
+
for (const ref of refs) {
|
|
4027
|
+
const key = `${ref.kind}:${ref.slug}`;
|
|
4028
|
+
if (seen.has(key)) continue;
|
|
4029
|
+
seen.add(key);
|
|
4030
|
+
result.push(ref);
|
|
4031
|
+
}
|
|
4032
|
+
return result;
|
|
4033
|
+
}
|
|
4034
|
+
function createReferenceIndex(catalog, materialize) {
|
|
4035
|
+
const efforts = buildExactIndex(catalog.efforts ?? []);
|
|
4036
|
+
const equipment = buildExactIndex(catalog.equipment ?? []);
|
|
4037
|
+
const prefixCandidates = (catalog.equipment ?? []).flatMap((entity) => [entity.label, ...entity.aliases].filter((name2) => !!name2 && !!name2.trim()).map((name2) => ({ lower: name2.trim().toLowerCase(), entity }))).sort((a, b) => b.lower.length - a.lower.length);
|
|
4038
|
+
const materializer = materialize ?? ((effort) => ({
|
|
4039
|
+
effort,
|
|
4040
|
+
definition: effort,
|
|
4041
|
+
slug: effort.slug,
|
|
4042
|
+
label: effort.label,
|
|
4043
|
+
met: effort.baseAttributes.met,
|
|
4044
|
+
baseAttributes: effort.baseAttributes,
|
|
4045
|
+
discipline: effort.baseAttributes.discipline,
|
|
4046
|
+
disciplineFactor: effort.baseAttributes.disciplineFactor ?? 1,
|
|
4047
|
+
intensityTier: effort.baseAttributes.intensityTier,
|
|
4048
|
+
modifiers: {},
|
|
4049
|
+
registrySource: effort.registrySource,
|
|
4050
|
+
resolvedFrom: effort.registrySource === "user" ? "user" : effort.registrySource === "bundled" ? "bundled" : "default",
|
|
4051
|
+
isEstimated: false
|
|
4052
|
+
}));
|
|
4053
|
+
const resolveEffortExact = (key) => {
|
|
4054
|
+
const found = efforts.lookup(key.trim());
|
|
4055
|
+
if (found.entity) return { entity: found.entity };
|
|
4056
|
+
if (found.ambiguous.length > 0) {
|
|
4057
|
+
return {
|
|
4058
|
+
entity: null,
|
|
4059
|
+
diagnostic: {
|
|
4060
|
+
reason: "ambiguous-effort",
|
|
4061
|
+
message: `"${key}" matches multiple efforts.`,
|
|
4062
|
+
token: key,
|
|
4063
|
+
candidates: found.ambiguous.map((e) => e.slug)
|
|
4064
|
+
}
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
return {
|
|
4068
|
+
entity: null,
|
|
4069
|
+
diagnostic: { reason: "unknown-effort", message: `Unknown effort "${key}".`, token: key }
|
|
4070
|
+
};
|
|
4071
|
+
};
|
|
4072
|
+
const resolveEquipmentValues = (values) => {
|
|
4073
|
+
const refs = [];
|
|
4074
|
+
const diagnostics = [];
|
|
4075
|
+
for (const value of values) {
|
|
4076
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
4077
|
+
diagnostics.push({
|
|
4078
|
+
reason: "invalid-explicit",
|
|
4079
|
+
message: `Equipment reference must be a slug string, got ${JSON.stringify(value) ?? "null"}.`,
|
|
4080
|
+
token: String(value)
|
|
4081
|
+
});
|
|
4082
|
+
continue;
|
|
4083
|
+
}
|
|
4084
|
+
const found = equipment.lookup(value.trim());
|
|
4085
|
+
if (found.entity) {
|
|
4086
|
+
refs.push(equipmentRef(found.entity));
|
|
4087
|
+
} else if (found.ambiguous.length > 0) {
|
|
4088
|
+
diagnostics.push({
|
|
4089
|
+
reason: "ambiguous-equipment",
|
|
4090
|
+
message: `"${value}" matches multiple equipment entries.`,
|
|
4091
|
+
token: value,
|
|
4092
|
+
candidates: found.ambiguous.map((e) => e.slug)
|
|
4093
|
+
});
|
|
4094
|
+
} else {
|
|
4095
|
+
diagnostics.push({
|
|
4096
|
+
reason: "invalid-explicit",
|
|
4097
|
+
message: `Unknown equipment "${value}".`,
|
|
4098
|
+
token: value
|
|
4099
|
+
});
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
return { refs: dedupeRefs(refs), diagnostics };
|
|
4103
|
+
};
|
|
4104
|
+
const index = {
|
|
4105
|
+
resolveEffort(slugOrAlias) {
|
|
4106
|
+
return efforts.lookup(slugOrAlias).entity;
|
|
4107
|
+
},
|
|
4108
|
+
resolveEquipment(slugOrAlias) {
|
|
4109
|
+
return equipment.lookup(slugOrAlias).entity;
|
|
4110
|
+
},
|
|
4111
|
+
materialize(effort) {
|
|
4112
|
+
return materializer(effort);
|
|
4113
|
+
},
|
|
4114
|
+
resolve(label, defaults) {
|
|
4115
|
+
const source = (label ?? "").trim();
|
|
4116
|
+
const diagnostics = [];
|
|
4117
|
+
const push = (diagnostic) => {
|
|
4118
|
+
if (diagnostic) diagnostics.push(diagnostic);
|
|
4119
|
+
};
|
|
4120
|
+
let matched = "none";
|
|
4121
|
+
let effortEntity = null;
|
|
4122
|
+
let remaining = "";
|
|
4123
|
+
let equipmentRefs = [];
|
|
4124
|
+
let equipmentAuthored = false;
|
|
4125
|
+
const fillEquipment = () => {
|
|
4126
|
+
if (equipmentRefs.length > 0 || equipmentAuthored) return;
|
|
4127
|
+
if (effortEntity === null) return;
|
|
4128
|
+
if (defaults?.equipment?.length) {
|
|
4129
|
+
const resolvedDefaults = resolveEquipmentValues(defaults.equipment);
|
|
4130
|
+
equipmentRefs = resolvedDefaults.refs;
|
|
4131
|
+
for (const diagnostic of resolvedDefaults.diagnostics) {
|
|
4132
|
+
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
4133
|
+
}
|
|
4134
|
+
if (equipmentRefs.length > 0) return;
|
|
4135
|
+
}
|
|
4136
|
+
if (effortEntity.equipment?.length) {
|
|
4137
|
+
const resolvedDefEquipment = resolveEquipmentValues(effortEntity.equipment);
|
|
4138
|
+
equipmentRefs = resolvedDefEquipment.refs;
|
|
4139
|
+
for (const diagnostic of resolvedDefEquipment.diagnostics) {
|
|
4140
|
+
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
if (!source) {
|
|
4145
|
+
if (defaults?.effort) {
|
|
4146
|
+
const found = resolveEffortExact(defaults.effort);
|
|
4147
|
+
if (found.entity) {
|
|
4148
|
+
effortEntity = found.entity;
|
|
4149
|
+
matched = "default";
|
|
4150
|
+
} else {
|
|
4151
|
+
push({
|
|
4152
|
+
reason: found.diagnostic?.reason === "ambiguous-effort" ? "ambiguous-effort" : "unknown-effort",
|
|
4153
|
+
message: `Default effort "${defaults.effort}" is not in the catalog.`,
|
|
4154
|
+
token: defaults.effort,
|
|
4155
|
+
candidates: found.diagnostic?.candidates
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
fillEquipment();
|
|
4160
|
+
return {
|
|
4161
|
+
matched,
|
|
4162
|
+
label: source,
|
|
4163
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
4164
|
+
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
4165
|
+
equipment: equipmentRefs,
|
|
4166
|
+
remaining: "",
|
|
4167
|
+
diagnostics
|
|
4168
|
+
};
|
|
4169
|
+
}
|
|
4170
|
+
if (source.startsWith("{")) {
|
|
4171
|
+
let parsed;
|
|
4172
|
+
try {
|
|
4173
|
+
parsed = JSON.parse(source);
|
|
4174
|
+
} catch {
|
|
4175
|
+
return {
|
|
4176
|
+
matched: "none",
|
|
4177
|
+
label: source,
|
|
4178
|
+
effort: null,
|
|
4179
|
+
effortRef: null,
|
|
4180
|
+
equipment: [],
|
|
4181
|
+
remaining: "",
|
|
4182
|
+
diagnostics: [{
|
|
4183
|
+
reason: "malformed-json",
|
|
4184
|
+
message: "Reference object is not valid JSON.",
|
|
4185
|
+
token: source
|
|
4186
|
+
}]
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && ("effort" in parsed || "equipment" in parsed)) {
|
|
4190
|
+
matched = "explicit";
|
|
4191
|
+
const obj = parsed;
|
|
4192
|
+
if (obj.effort !== void 0) {
|
|
4193
|
+
if (typeof obj.effort === "string" && obj.effort.trim()) {
|
|
4194
|
+
const found = resolveEffortExact(obj.effort);
|
|
4195
|
+
effortEntity = found.entity;
|
|
4196
|
+
if (found.diagnostic) {
|
|
4197
|
+
push(found.diagnostic.reason === "ambiguous-effort" ? found.diagnostic : { ...found.diagnostic, reason: "invalid-explicit", message: `Explicit effort "${obj.effort}" is not in the catalog.` });
|
|
4198
|
+
}
|
|
4199
|
+
} else {
|
|
4200
|
+
push({
|
|
4201
|
+
reason: "invalid-explicit",
|
|
4202
|
+
message: 'Explicit "effort" must be a slug string.',
|
|
4203
|
+
token: String(obj.effort)
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
if (obj.equipment !== void 0) {
|
|
4208
|
+
equipmentAuthored = true;
|
|
4209
|
+
const values = Array.isArray(obj.equipment) ? obj.equipment : [obj.equipment];
|
|
4210
|
+
const resolvedEquipment = resolveEquipmentValues(values);
|
|
4211
|
+
equipmentRefs = resolvedEquipment.refs;
|
|
4212
|
+
diagnostics.push(...resolvedEquipment.diagnostics);
|
|
4213
|
+
if (effortEntity?.equipment?.length) {
|
|
4214
|
+
const definitionSlugs = new Set(effortEntity.equipment);
|
|
4215
|
+
const authoredSlugs = equipmentRefs.map((ref) => ref.slug);
|
|
4216
|
+
if (authoredSlugs.length > 0 && authoredSlugs.some((slug) => !definitionSlugs.has(slug))) {
|
|
4217
|
+
push({
|
|
4218
|
+
reason: "conflict",
|
|
4219
|
+
message: `Explicit equipment [${authoredSlugs.join(", ")}] contradicts effort "${effortEntity.slug}" definition equipment [${effortEntity.equipment.join(", ")}].`,
|
|
4220
|
+
token: source
|
|
4221
|
+
});
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
fillEquipment();
|
|
4226
|
+
return {
|
|
4227
|
+
matched,
|
|
4228
|
+
label: source,
|
|
4229
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
4230
|
+
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
4231
|
+
equipment: equipmentRefs,
|
|
4232
|
+
remaining: "",
|
|
4233
|
+
diagnostics
|
|
4234
|
+
};
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
const lowerSource = source.toLowerCase();
|
|
4238
|
+
for (const candidate of prefixCandidates) {
|
|
4239
|
+
if (!lowerSource.startsWith(candidate.lower)) continue;
|
|
4240
|
+
const after = source.slice(candidate.lower.length);
|
|
4241
|
+
if (after && !/^\s/.test(after)) continue;
|
|
4242
|
+
const candidateRemaining = after.trim();
|
|
4243
|
+
if (!candidateRemaining) continue;
|
|
4244
|
+
const equipmentFound = equipment.lookup(candidate.lower);
|
|
4245
|
+
if (equipmentFound.ambiguous.length > 0) {
|
|
4246
|
+
push({
|
|
4247
|
+
reason: "ambiguous-equipment",
|
|
4248
|
+
message: `"${candidate.lower}" matches multiple equipment entries.`,
|
|
4249
|
+
token: source,
|
|
4250
|
+
candidates: equipmentFound.ambiguous.map((e) => e.slug)
|
|
4251
|
+
});
|
|
4252
|
+
return {
|
|
4253
|
+
matched: "none",
|
|
4254
|
+
label: source,
|
|
4255
|
+
effort: null,
|
|
4256
|
+
effortRef: null,
|
|
4257
|
+
equipment: [],
|
|
4258
|
+
remaining: "",
|
|
4259
|
+
diagnostics
|
|
4260
|
+
};
|
|
4261
|
+
}
|
|
4262
|
+
if (!equipmentFound.entity) continue;
|
|
4263
|
+
const effortFound = resolveEffortExact(candidateRemaining);
|
|
4264
|
+
if (effortFound.entity) {
|
|
4265
|
+
matched = "split";
|
|
4266
|
+
effortEntity = effortFound.entity;
|
|
4267
|
+
equipmentRefs = [equipmentRef(equipmentFound.entity)];
|
|
4268
|
+
equipmentAuthored = true;
|
|
4269
|
+
remaining = candidateRemaining;
|
|
4270
|
+
break;
|
|
4271
|
+
}
|
|
4272
|
+
if (effortFound.diagnostic?.reason === "ambiguous-effort") {
|
|
4273
|
+
push(effortFound.diagnostic);
|
|
4274
|
+
return {
|
|
4275
|
+
matched: "none",
|
|
4276
|
+
label: source,
|
|
4277
|
+
effort: null,
|
|
4278
|
+
effortRef: null,
|
|
4279
|
+
equipment: [],
|
|
4280
|
+
remaining: "",
|
|
4281
|
+
diagnostics
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
if (matched === "none") {
|
|
4286
|
+
const found = resolveEffortExact(source);
|
|
4287
|
+
if (found.entity) {
|
|
4288
|
+
effortEntity = found.entity;
|
|
4289
|
+
matched = "whole";
|
|
4290
|
+
} else if (found.diagnostic?.reason === "ambiguous-effort") {
|
|
4291
|
+
push(found.diagnostic);
|
|
4292
|
+
return {
|
|
4293
|
+
matched: "none",
|
|
4294
|
+
label: source,
|
|
4295
|
+
effort: null,
|
|
4296
|
+
effortRef: null,
|
|
4297
|
+
equipment: [],
|
|
4298
|
+
remaining: "",
|
|
4299
|
+
diagnostics
|
|
4300
|
+
};
|
|
4301
|
+
} else if (found.diagnostic) {
|
|
4302
|
+
push(found.diagnostic);
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
fillEquipment();
|
|
4306
|
+
return {
|
|
4307
|
+
matched,
|
|
4308
|
+
label: source,
|
|
4309
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
4310
|
+
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
4311
|
+
equipment: equipmentRefs,
|
|
4312
|
+
remaining,
|
|
4313
|
+
diagnostics
|
|
4314
|
+
};
|
|
4315
|
+
}
|
|
4316
|
+
};
|
|
4317
|
+
return index;
|
|
4318
|
+
}
|
|
4319
|
+
function extractStatementRefs(statements) {
|
|
4320
|
+
const efforts = [];
|
|
4321
|
+
const equipment = [];
|
|
4322
|
+
const seenEffort = /* @__PURE__ */ new Set();
|
|
4323
|
+
const seenEquipment = /* @__PURE__ */ new Set();
|
|
4324
|
+
const pushUnique = (list, seen, value) => {
|
|
4325
|
+
if (!value) return;
|
|
4326
|
+
if (seen.has(value)) return;
|
|
4327
|
+
seen.add(value);
|
|
4328
|
+
list.push(value);
|
|
4329
|
+
};
|
|
4330
|
+
for (const statement of statements ?? []) {
|
|
4331
|
+
const metrics = Array.isArray(statement.metrics) ? statement.metrics : statement.metrics?.toArray?.() ?? [];
|
|
4332
|
+
for (const metric of metrics) {
|
|
4333
|
+
const refs = wodWikiCore.getReferenceMetadata(metric.metadata)?.refs;
|
|
4334
|
+
if (!refs) continue;
|
|
4335
|
+
for (const ref of refs) {
|
|
4336
|
+
if (ref.kind === "effort") pushUnique(efforts, seenEffort, ref.slug);
|
|
4337
|
+
if (ref.kind === "equipment") pushUnique(equipment, seenEquipment, ref.slug);
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
return { efforts, equipment };
|
|
4342
|
+
}
|
|
4343
|
+
function slugifyRefLabel(label) {
|
|
4344
|
+
return normalizeForFuzzy(label).replace(/\s+/g, "-");
|
|
4345
|
+
}
|
|
3400
4346
|
|
|
3401
4347
|
// src/runtime/contracts/IRuntimeOptions.ts
|
|
3402
4348
|
var DEFAULT_RUNTIME_OPTIONS = {
|
|
@@ -6735,14 +7681,12 @@ function resolveDominantOrigin(origins) {
|
|
|
6735
7681
|
if (origins.every((o) => o === "compiler")) return "compiler";
|
|
6736
7682
|
return "analyzed";
|
|
6737
7683
|
}
|
|
6738
|
-
|
|
6739
|
-
// src/runtime/compiler/EffortEnrichmentPass.ts
|
|
6740
7684
|
var logTrace = (message) => console.log(message);
|
|
6741
7685
|
function applyEffortEnrichment(block, options) {
|
|
6742
|
-
const { resolver, overwrite = false, debug = false } = options;
|
|
6743
|
-
tryEnrichBlock(block, resolver, overwrite, debug);
|
|
7686
|
+
const { resolver, references, overwrite = false, debug = false } = options;
|
|
7687
|
+
tryEnrichBlock(block, resolver, references, overwrite, debug);
|
|
6744
7688
|
}
|
|
6745
|
-
function tryEnrichBlock(block, resolver, overwrite, debug) {
|
|
7689
|
+
function tryEnrichBlock(block, resolver, references, overwrite, debug) {
|
|
6746
7690
|
const normalizedBlockType = block.blockType?.toLowerCase();
|
|
6747
7691
|
if (normalizedBlockType !== "effort" && normalizedBlockType !== "exercise") {
|
|
6748
7692
|
return;
|
|
@@ -6760,7 +7704,32 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
|
|
|
6760
7704
|
return;
|
|
6761
7705
|
}
|
|
6762
7706
|
try {
|
|
6763
|
-
|
|
7707
|
+
let resolved;
|
|
7708
|
+
let matchedKind = "none";
|
|
7709
|
+
let equipmentRefs = [];
|
|
7710
|
+
let diagnostics = [];
|
|
7711
|
+
if (references) {
|
|
7712
|
+
const resolution = references.resolve(label);
|
|
7713
|
+
resolved = resolution.effort ?? void 0;
|
|
7714
|
+
matchedKind = resolution.matched;
|
|
7715
|
+
equipmentRefs = resolution.equipment;
|
|
7716
|
+
diagnostics = resolution.diagnostics;
|
|
7717
|
+
} else {
|
|
7718
|
+
resolved = resolver.resolveEffort(label);
|
|
7719
|
+
}
|
|
7720
|
+
if (!resolved) {
|
|
7721
|
+
if (diagnostics.length > 0) {
|
|
7722
|
+
block.pushMemory(new MemoryLocation("metric:tracked", [{
|
|
7723
|
+
type: wodWikiCore.MetricType.Hint,
|
|
7724
|
+
value: "reference.unresolved",
|
|
7725
|
+
image: label,
|
|
7726
|
+
origin: "compiler",
|
|
7727
|
+
metadata: { [wodWikiCore.REFERENCE_METADATA_KEY]: { matched: "none", source: label, diagnostics } }
|
|
7728
|
+
}]));
|
|
7729
|
+
}
|
|
7730
|
+
if (debug) logTrace(`[EffortEnrichment] Unresolved effort: ${label}`);
|
|
7731
|
+
return;
|
|
7732
|
+
}
|
|
6764
7733
|
const effortHints = resolved.effort.hints;
|
|
6765
7734
|
if (effortHints && Object.keys(effortHints).length > 0) {
|
|
6766
7735
|
block.mergeHints(effortHints);
|
|
@@ -6773,7 +7742,8 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
|
|
|
6773
7742
|
image: resolved.label,
|
|
6774
7743
|
value: resolved,
|
|
6775
7744
|
origin: "compiler",
|
|
6776
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
7745
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
7746
|
+
...equipmentRefs.length > 0 || diagnostics.length > 0 ? { metadata: { [wodWikiCore.REFERENCE_METADATA_KEY]: { matched: matchedKind, source: label, refs: [{ kind: "effort", slug: resolved.slug, label: resolved.label }, ...equipmentRefs], ...diagnostics.length > 0 ? { diagnostics } : {} } } } : {}
|
|
6777
7747
|
};
|
|
6778
7748
|
const privateMetrics = block.getMetricMemoryByVisibility("private");
|
|
6779
7749
|
const existingLoc = privateMetrics.find(
|
|
@@ -6788,10 +7758,31 @@ function tryEnrichBlock(block, resolver, overwrite, debug) {
|
|
|
6788
7758
|
const location = new MemoryLocation("metric:tracked", [effortDataMetric]);
|
|
6789
7759
|
block.pushMemory(location);
|
|
6790
7760
|
}
|
|
7761
|
+
if (equipmentRefs.length > 0) {
|
|
7762
|
+
pushPrivateMetrics(block, wodWikiCore.MetricType.Equipment, [
|
|
7763
|
+
new EquipmentMetric(equipmentRefs.map((ref) => ref.slug), {
|
|
7764
|
+
refs: equipmentRefs,
|
|
7765
|
+
source: label,
|
|
7766
|
+
origin: "compiler"
|
|
7767
|
+
})
|
|
7768
|
+
], wodWikiCore.MetricType.Equipment, overwrite);
|
|
7769
|
+
}
|
|
6791
7770
|
} catch (err) {
|
|
6792
7771
|
console.error(`[EffortEnrichment] Failed to resolve effort "${label}":`, err);
|
|
6793
7772
|
}
|
|
6794
7773
|
}
|
|
7774
|
+
function pushPrivateMetrics(block, markerType, metrics, replaceType, overwrite) {
|
|
7775
|
+
const privateMetrics = block.getMetricMemoryByVisibility("private");
|
|
7776
|
+
const existingLoc = privateMetrics.find(
|
|
7777
|
+
(loc) => loc.metrics.toArray().some((m) => m.type === markerType)
|
|
7778
|
+
);
|
|
7779
|
+
if (existingLoc && overwrite) {
|
|
7780
|
+
const filtered = existingLoc.metrics.toArray().filter((m) => m.type !== replaceType);
|
|
7781
|
+
existingLoc.update([...filtered, ...metrics]);
|
|
7782
|
+
} else if (!existingLoc) {
|
|
7783
|
+
block.pushMemory(new MemoryLocation("metric:tracked", metrics));
|
|
7784
|
+
}
|
|
7785
|
+
}
|
|
6795
7786
|
|
|
6796
7787
|
// src/runtime/actions/stack/CompileAndPushBlockAction.ts
|
|
6797
7788
|
var CompileAndPushBlockAction = class {
|
|
@@ -6817,7 +7808,8 @@ var CompileAndPushBlockAction = class {
|
|
|
6817
7808
|
}
|
|
6818
7809
|
if (runtime.analyticsContext?.effortResolver) {
|
|
6819
7810
|
applyEffortEnrichment(compiledBlock, {
|
|
6820
|
-
resolver: runtime.analyticsContext.effortResolver
|
|
7811
|
+
resolver: runtime.analyticsContext.effortResolver,
|
|
7812
|
+
references: runtime.analyticsContext.references
|
|
6821
7813
|
});
|
|
6822
7814
|
}
|
|
6823
7815
|
return [new PushBlockAction(compiledBlock, this.options)];
|
|
@@ -10843,7 +11835,8 @@ function createAnalyticsEngineForBlock(block, options) {
|
|
|
10843
11835
|
effortResolver = new EffortResolver(registry);
|
|
10844
11836
|
}
|
|
10845
11837
|
const analyticsContext = {
|
|
10846
|
-
effortResolver
|
|
11838
|
+
effortResolver,
|
|
11839
|
+
...options?.references ? { references: options.references } : {}
|
|
10847
11840
|
};
|
|
10848
11841
|
const context = {
|
|
10849
11842
|
dialect,
|
|
@@ -13914,6 +14907,7 @@ exports.createMetricPresentationPolicy = createPolicy;
|
|
|
13914
14907
|
exports.createMockClock = createMockClock;
|
|
13915
14908
|
exports.createParser = createParser;
|
|
13916
14909
|
exports.createProfileTable = createProfileTable;
|
|
14910
|
+
exports.createReferenceIndex = createReferenceIndex;
|
|
13917
14911
|
exports.createRpeLabelsTable = createRpeLabelsTable;
|
|
13918
14912
|
exports.dailySessionLoads = dailySessionLoads;
|
|
13919
14913
|
exports.dayBucket = dayBucket;
|
|
@@ -13922,10 +14916,15 @@ exports.dialectStack = dialectStack;
|
|
|
13922
14916
|
exports.diffStatement = diffStatement;
|
|
13923
14917
|
exports.dimEquals = dimEquals;
|
|
13924
14918
|
exports.disciplineFactorFor = disciplineFactorFor;
|
|
14919
|
+
exports.documentToEffort = documentToEffort;
|
|
14920
|
+
exports.documentToEquipment = documentToEquipment;
|
|
14921
|
+
exports.effortToDocument = effortToDocument;
|
|
14922
|
+
exports.equipmentToDocument = equipmentToDocument;
|
|
13925
14923
|
exports.evaluate = evaluate;
|
|
13926
14924
|
exports.evaluateCalculateDefinitions = evaluateCalculateDefinitions;
|
|
13927
14925
|
exports.extractEffortData = extractEffortData;
|
|
13928
14926
|
exports.extractMetrics = extractMetrics;
|
|
14927
|
+
exports.extractStatementRefs = extractStatementRefs;
|
|
13929
14928
|
exports.extractStatements = extractStatements;
|
|
13930
14929
|
exports.extractStatementsRaw = extractStatementsRaw;
|
|
13931
14930
|
exports.extractSyntaxFacts = extractSyntaxFacts;
|
|
@@ -13973,15 +14972,18 @@ exports.outputNodeId = outputNodeId;
|
|
|
13973
14972
|
exports.outputNodeIds = outputNodeIds;
|
|
13974
14973
|
exports.parseCalcLine = parseCalcLine;
|
|
13975
14974
|
exports.parseCalculateBlock = parseCalculateBlock;
|
|
14975
|
+
exports.parseEffortFile = parseEffortFile;
|
|
14976
|
+
exports.parseEquipmentFile = parseEquipmentFile;
|
|
13976
14977
|
exports.parseExpression = parseExpression;
|
|
13977
14978
|
exports.parseMetricLine = parseMetricLine;
|
|
13978
14979
|
exports.parseScript = parseScript;
|
|
13979
14980
|
exports.printCalculation = printCalculation;
|
|
14981
|
+
exports.readNestedSection = readNestedSection;
|
|
13980
14982
|
exports.readTimer = readTimer;
|
|
13981
14983
|
exports.realtimeProcessorRegistry = realtimeProcessorRegistry;
|
|
13982
14984
|
exports.renderMetric = renderMetric;
|
|
13983
14985
|
exports.resolveDominantOrigin = resolveDominantOrigin;
|
|
13984
|
-
exports.
|
|
14986
|
+
exports.slugifyRefLabel = slugifyRefLabel;
|
|
13985
14987
|
exports.startSpan = startSpan;
|
|
13986
14988
|
exports.str = str;
|
|
13987
14989
|
exports.strategyRegistry = strategyRegistry;
|