@bitcobblers/wod-wiki-lang 0.50.2187 → 0.50.2188
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 +797 -698
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +798 -700
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { wallClockNow, formatDurationSmart } from './chunk-PFQDVQQO.mjs';
|
|
2
2
|
export { createBrowserRuntimeNow, frozenNow, getRuntimeNowMs, wallClockNow } from './chunk-PFQDVQQO.mjs';
|
|
3
|
-
import { MetricType, MetricContainer, BlockKey, REFERENCE_METADATA_KEY, ParsedCodeStatement,
|
|
3
|
+
import { MetricType, MetricContainer, BlockKey, REFERENCE_METADATA_KEY, ParsedCodeStatement, getReferenceMetadata, TAG_REFERENCE_METADATA_KEY, OutputStatement, valueKindOf, normalizeFieldPath, getTagReference } from '@bitcobblers/wod-wiki-core';
|
|
4
4
|
import { LRParser } from '@lezer/lr';
|
|
5
5
|
import { LRLanguage, LanguageSupport, syntaxTree } from '@codemirror/language';
|
|
6
6
|
import { NodeProp } from '@lezer/common';
|
|
@@ -511,6 +511,9 @@ var EffortMetric = class {
|
|
|
511
511
|
this.effort = effort;
|
|
512
512
|
this.value = options?.resolvedSlug ?? effort;
|
|
513
513
|
this.image = effort;
|
|
514
|
+
if (options?.canonicalLabel) {
|
|
515
|
+
this.name = options.canonicalLabel;
|
|
516
|
+
}
|
|
514
517
|
if (options?.references) {
|
|
515
518
|
this.metadata = { [REFERENCE_METADATA_KEY]: options.references };
|
|
516
519
|
}
|
|
@@ -518,6 +521,7 @@ var EffortMetric = class {
|
|
|
518
521
|
effort;
|
|
519
522
|
value;
|
|
520
523
|
image;
|
|
524
|
+
name;
|
|
521
525
|
origin = "parser";
|
|
522
526
|
metadata;
|
|
523
527
|
type = MetricType.Effort;
|
|
@@ -1531,11 +1535,12 @@ var EquipmentMetric = class {
|
|
|
1531
1535
|
/** Canonical slugs, ordered and deduplicated. */
|
|
1532
1536
|
value;
|
|
1533
1537
|
image;
|
|
1534
|
-
name
|
|
1538
|
+
name;
|
|
1535
1539
|
metadata;
|
|
1536
1540
|
constructor(slugs, options) {
|
|
1537
1541
|
this.value = [...new Set(slugs)];
|
|
1538
1542
|
this.image = options?.source ?? this.value.join(" ");
|
|
1543
|
+
this.name = options?.refs?.length ? options.refs.map((r) => r.label).join(", ") : "equipment";
|
|
1539
1544
|
this.origin = options?.origin ?? "parser";
|
|
1540
1545
|
const reference = {
|
|
1541
1546
|
matched: options?.matched ?? "whole",
|
|
@@ -1679,9 +1684,14 @@ function classifyStatements(facts, references) {
|
|
|
1679
1684
|
metricPairs.push(slot.pair);
|
|
1680
1685
|
}
|
|
1681
1686
|
const mergedPairs = mergeFragments(metricPairs);
|
|
1682
|
-
const finalPairs = references ? applyReferenceResolution(mergedPairs, fact, references) : mergedPairs;
|
|
1687
|
+
const finalPairs = references?.index ? applyReferenceResolution(mergedPairs, fact, references) : mergedPairs;
|
|
1683
1688
|
statement.metrics = finalPairs.map((pair) => pair.metrics);
|
|
1684
1689
|
statement.metricMeta = new Map(finalPairs.map((pair) => [pair.metrics, pair.meta]));
|
|
1690
|
+
const effortMetric = finalPairs.find((p) => p.metrics.type === MetricType.Effort)?.metrics;
|
|
1691
|
+
const effortRef = getReferenceMetadata(effortMetric?.metadata)?.refs?.find((r) => r.kind === "effort");
|
|
1692
|
+
if (effortRef?.slug) {
|
|
1693
|
+
statement.exerciseId = effortRef.slug;
|
|
1694
|
+
}
|
|
1685
1695
|
statement.id = fact.id;
|
|
1686
1696
|
statement.line = fact.line;
|
|
1687
1697
|
statement.text = fact.meta.raw;
|
|
@@ -1709,33 +1719,34 @@ function equipmentMeta(resolution, authored, meta2) {
|
|
|
1709
1719
|
};
|
|
1710
1720
|
}
|
|
1711
1721
|
function applyReferenceResolution(pairs, fact, references) {
|
|
1712
|
-
const
|
|
1722
|
+
const index = references.index;
|
|
1723
|
+
const { defaults } = references;
|
|
1713
1724
|
const result = [];
|
|
1714
1725
|
const emitResolution = (resolution, authored, meta2) => {
|
|
1715
1726
|
if (resolution.effortRef || resolution.matched !== "none" || resolution.diagnostics.length > 0) {
|
|
1716
1727
|
result.push({
|
|
1717
1728
|
metrics: new EffortMetric(authored, {
|
|
1718
1729
|
resolvedSlug: resolution.effortRef?.slug,
|
|
1730
|
+
canonicalLabel: resolution.effortRef?.label,
|
|
1719
1731
|
references: referenceMetadata(resolution)
|
|
1720
1732
|
}),
|
|
1721
1733
|
meta: meta2
|
|
1722
1734
|
});
|
|
1723
1735
|
}
|
|
1724
1736
|
if (resolution.equipment.length > 0) {
|
|
1737
|
+
const eqMeta = equipmentMeta(resolution, authored, meta2);
|
|
1738
|
+
const eqSource = resolution.matched === "split" ? eqMeta.raw : resolution.matched === "explicit" ? resolution.equipment.map((ref) => ref.label).join(", ") : authored;
|
|
1725
1739
|
result.push({
|
|
1726
1740
|
metrics: new EquipmentMetric(
|
|
1727
1741
|
resolution.equipment.map((ref) => ref.slug),
|
|
1728
1742
|
{
|
|
1729
1743
|
refs: resolution.equipment,
|
|
1730
|
-
source:
|
|
1744
|
+
source: eqSource,
|
|
1731
1745
|
matched: resolution.matched,
|
|
1732
1746
|
diagnostics: resolution.diagnostics
|
|
1733
1747
|
}
|
|
1734
1748
|
),
|
|
1735
|
-
|
|
1736
|
-
// what follows. Marking the whole phrase for both would let one mark
|
|
1737
|
-
// hide the other.
|
|
1738
|
-
meta: equipmentMeta(resolution, authored, meta2)
|
|
1749
|
+
meta: eqMeta
|
|
1739
1750
|
});
|
|
1740
1751
|
}
|
|
1741
1752
|
};
|
|
@@ -1810,6 +1821,64 @@ function applyReferenceResolution(pairs, fact, references) {
|
|
|
1810
1821
|
result.push(...malformedHints);
|
|
1811
1822
|
return result;
|
|
1812
1823
|
}
|
|
1824
|
+
function resolveFusedEffortReferences(statements, references) {
|
|
1825
|
+
if (!references?.index) return;
|
|
1826
|
+
const { index, defaults } = references;
|
|
1827
|
+
for (const statement of statements) {
|
|
1828
|
+
const current = statement.metrics.toArray();
|
|
1829
|
+
if (!current.some((m) => m.type === MetricType.Effort && !m.metadata)) continue;
|
|
1830
|
+
const nextMetrics = [];
|
|
1831
|
+
const nextMeta = new Map(statement.metricMeta);
|
|
1832
|
+
for (const metric of current) {
|
|
1833
|
+
if (metric.type !== MetricType.Effort || metric.metadata) {
|
|
1834
|
+
nextMetrics.push(metric);
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
const authored = metric instanceof EffortMetric ? metric.effort : String(metric.value ?? "");
|
|
1838
|
+
const normalized = authored.trim().toLowerCase();
|
|
1839
|
+
if (STRUCTURAL_EFFORT_WORDS.has(normalized) || STANDARD_UNITS2.has(normalized)) {
|
|
1840
|
+
nextMetrics.push(metric);
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
const meta2 = statement.metricMeta?.get(metric);
|
|
1844
|
+
const resolution = index.resolve(authored, defaults);
|
|
1845
|
+
if (resolution.matched === "none" && resolution.diagnostics.length === 0) {
|
|
1846
|
+
nextMetrics.push(metric);
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1849
|
+
nextMeta.delete(metric);
|
|
1850
|
+
if (resolution.effortRef || resolution.matched !== "none" || resolution.diagnostics.length > 0) {
|
|
1851
|
+
const resolvedEffort = new EffortMetric(authored, {
|
|
1852
|
+
resolvedSlug: resolution.effortRef?.slug,
|
|
1853
|
+
canonicalLabel: resolution.effortRef?.label,
|
|
1854
|
+
references: referenceMetadata(resolution)
|
|
1855
|
+
});
|
|
1856
|
+
nextMetrics.push(resolvedEffort);
|
|
1857
|
+
if (meta2) nextMeta.set(resolvedEffort, meta2);
|
|
1858
|
+
if (resolution.effortRef?.slug) {
|
|
1859
|
+
statement.exerciseId = resolution.effortRef.slug;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
if (resolution.equipment.length > 0) {
|
|
1863
|
+
const eqMeta = meta2 ? equipmentMeta(resolution, authored, meta2) : void 0;
|
|
1864
|
+
const eqSource = resolution.matched === "split" && eqMeta ? eqMeta.raw : authored;
|
|
1865
|
+
const eqMetric = new EquipmentMetric(
|
|
1866
|
+
resolution.equipment.map((ref) => ref.slug),
|
|
1867
|
+
{
|
|
1868
|
+
refs: resolution.equipment,
|
|
1869
|
+
source: eqSource,
|
|
1870
|
+
matched: resolution.matched,
|
|
1871
|
+
diagnostics: resolution.diagnostics
|
|
1872
|
+
}
|
|
1873
|
+
);
|
|
1874
|
+
nextMetrics.push(eqMetric);
|
|
1875
|
+
if (eqMeta) nextMeta.set(eqMetric, eqMeta);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
statement.metrics.clear().add(...nextMetrics);
|
|
1879
|
+
statement.metricMeta = nextMeta;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1813
1882
|
function classifyPrimitive(primitive) {
|
|
1814
1883
|
switch (primitive.kind) {
|
|
1815
1884
|
case "lap":
|
|
@@ -2427,6 +2496,7 @@ function extractStatements(sourceOrTree, sportOrTree, sportArg, references) {
|
|
|
2427
2496
|
const facts = extractSyntaxFacts(resolved.source, resolved.tree);
|
|
2428
2497
|
const statements = classifyStatements(facts, references);
|
|
2429
2498
|
dialectStack.processAll(statements, sport);
|
|
2499
|
+
resolveFusedEffortReferences(statements, references);
|
|
2430
2500
|
return normalizeInlineRounds(statements);
|
|
2431
2501
|
}
|
|
2432
2502
|
function isInlineRoundsPrescription(stmt) {
|
|
@@ -2548,557 +2618,87 @@ var WhiteboardScript = class {
|
|
|
2548
2618
|
}
|
|
2549
2619
|
};
|
|
2550
2620
|
|
|
2551
|
-
// src/
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
}
|
|
2571
|
-
function resolveEntityTier(entity) {
|
|
2572
|
-
if (entity.registrySource === "user") return "user";
|
|
2573
|
-
return resolveSourceTier(entity.sourceId);
|
|
2574
|
-
}
|
|
2575
|
-
function resolveEntityRank(entity) {
|
|
2576
|
-
return SEED_PRECEDENCE_RANK[resolveEntityTier(entity)];
|
|
2577
|
-
}
|
|
2578
|
-
function dedupeEntitiesByPrecedence(entities) {
|
|
2579
|
-
const bySlug = /* @__PURE__ */ new Map();
|
|
2580
|
-
for (const entity of entities) {
|
|
2581
|
-
const key = entity.slug.toLowerCase();
|
|
2582
|
-
const prior = bySlug.get(key);
|
|
2583
|
-
if (!prior || resolveEntityRank(entity) >= resolveEntityRank(prior)) {
|
|
2584
|
-
bySlug.set(key, entity);
|
|
2585
|
-
}
|
|
2621
|
+
// src/parser/parseScript.ts
|
|
2622
|
+
function parseScript(inputText, options = {}) {
|
|
2623
|
+
if (!inputText || !inputText.trim()) {
|
|
2624
|
+
return new WhiteboardScript(inputText, [], []);
|
|
2625
|
+
}
|
|
2626
|
+
try {
|
|
2627
|
+
const doc = inputText.endsWith("\n") ? inputText : inputText + "\n";
|
|
2628
|
+
const tree = parser.parse(doc);
|
|
2629
|
+
const sport = options.sport ?? options.dialect;
|
|
2630
|
+
const references = options.references || options.tags ? {
|
|
2631
|
+
...options.references ? { index: options.references } : {},
|
|
2632
|
+
defaults: options.defaults,
|
|
2633
|
+
tags: options.tags
|
|
2634
|
+
} : void 0;
|
|
2635
|
+
const statements = options.withoutDialects ? extractStatementsRaw(inputText, tree, references) : extractStatements(inputText, tree, sport, references);
|
|
2636
|
+
return new WhiteboardScript(inputText, statements, []);
|
|
2637
|
+
} catch (error) {
|
|
2638
|
+
const msg = error instanceof Error ? error.message : "Unknown parse error";
|
|
2639
|
+
return new WhiteboardScript(inputText, [], [{ message: msg }]);
|
|
2586
2640
|
}
|
|
2587
|
-
return Array.from(bySlug.values());
|
|
2588
2641
|
}
|
|
2589
|
-
var INTENSITY_TIERS = ["low", "moderate", "high"];
|
|
2590
|
-
var DEFAULT_RESOLVER_OPTIONS = {
|
|
2591
|
-
defaultMet: 5,
|
|
2592
|
-
defaultThreshold: 2
|
|
2593
|
-
};
|
|
2594
2642
|
|
|
2595
|
-
// src/
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
if (a.length > b.length) {
|
|
2601
|
-
const tmp = a;
|
|
2602
|
-
a = b;
|
|
2603
|
-
b = tmp;
|
|
2643
|
+
// src/parser/md-timer.ts
|
|
2644
|
+
var MdTimerRuntime = class {
|
|
2645
|
+
config;
|
|
2646
|
+
constructor(config) {
|
|
2647
|
+
this.config = config;
|
|
2604
2648
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2649
|
+
/**
|
|
2650
|
+
* Parse WhiteboardScript source into a {@link WhiteboardScript}, running the
|
|
2651
|
+
* Dialect Stack on every statement.
|
|
2652
|
+
*
|
|
2653
|
+
* @param sport - The block's `:sport` fence suffix (` ```log:climbing `).
|
|
2654
|
+
*/
|
|
2655
|
+
read(inputText, sport) {
|
|
2656
|
+
return parseScript(inputText, { sport, ...this.config });
|
|
2611
2657
|
}
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
prev[i] + 1,
|
|
2619
|
-
// deletion
|
|
2620
|
-
curr[i - 1] + 1,
|
|
2621
|
-
// insertion
|
|
2622
|
-
prev[i - 1] + cost
|
|
2623
|
-
// substitution
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
const swap = prev;
|
|
2627
|
-
prev = curr;
|
|
2628
|
-
curr = swap;
|
|
2658
|
+
/**
|
|
2659
|
+
* Parse without running the Dialect Stack. Used by the parser test harness
|
|
2660
|
+
* which applies its own Dialect set. Production consumers should use {@link read}.
|
|
2661
|
+
*/
|
|
2662
|
+
readWithoutDialects(inputText) {
|
|
2663
|
+
return parseScript(inputText, { withoutDialects: true, ...this.config });
|
|
2629
2664
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
function isWithinThreshold(a, b, threshold) {
|
|
2636
|
-
const na = normalizeForFuzzy(a);
|
|
2637
|
-
const nb = normalizeForFuzzy(b);
|
|
2638
|
-
if (na === nb) return true;
|
|
2639
|
-
if (Math.abs(na.length - nb.length) > threshold) return false;
|
|
2640
|
-
return levenshteinDistance(na, nb) <= threshold;
|
|
2665
|
+
};
|
|
2666
|
+
|
|
2667
|
+
// src/parser/parserInstance.ts
|
|
2668
|
+
function createParser(config) {
|
|
2669
|
+
return new MdTimerRuntime(config);
|
|
2641
2670
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
}
|
|
2671
|
+
var nextTagID = 0;
|
|
2672
|
+
var Tag = class _Tag {
|
|
2673
|
+
/**
|
|
2674
|
+
@internal
|
|
2675
|
+
*/
|
|
2676
|
+
constructor(name2, set, base, modified) {
|
|
2677
|
+
this.name = name2;
|
|
2678
|
+
this.set = set;
|
|
2679
|
+
this.base = base;
|
|
2680
|
+
this.modified = modified;
|
|
2681
|
+
this.id = nextTagID++;
|
|
2654
2682
|
}
|
|
2655
|
-
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
2662
|
-
const add = (key, entity) => {
|
|
2663
|
-
if (!key) return;
|
|
2664
|
-
const existing = byKey.get(key);
|
|
2665
|
-
if (existing) {
|
|
2666
|
-
if (!existing.includes(entity)) existing.push(entity);
|
|
2667
|
-
} else {
|
|
2668
|
-
byKey.set(key, [entity]);
|
|
2669
|
-
}
|
|
2670
|
-
};
|
|
2671
|
-
for (const entity of deduped) {
|
|
2672
|
-
add(entity.slug.toLowerCase(), entity);
|
|
2673
|
-
add(normalizeForFuzzy(entity.label), entity);
|
|
2674
|
-
for (const alias of entity.aliases) add(normalizeForFuzzy(alias), entity);
|
|
2683
|
+
toString() {
|
|
2684
|
+
let { name: name2 } = this;
|
|
2685
|
+
for (let mod of this.modified)
|
|
2686
|
+
if (mod.name)
|
|
2687
|
+
name2 = `${mod.name}(${name2})`;
|
|
2688
|
+
return name2;
|
|
2675
2689
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
if (topTier.length === 1) return { entity: topTier[0], ambiguous: [] };
|
|
2689
|
-
return { entity: null, ambiguous: topTier };
|
|
2690
|
-
}
|
|
2691
|
-
};
|
|
2692
|
-
}
|
|
2693
|
-
function equipmentRef(entity) {
|
|
2694
|
-
return { kind: "equipment", slug: entity.slug, label: entity.label };
|
|
2695
|
-
}
|
|
2696
|
-
function dedupeRefs(refs) {
|
|
2697
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2698
|
-
const result = [];
|
|
2699
|
-
for (const ref of refs) {
|
|
2700
|
-
const key = `${ref.kind}:${ref.slug}`;
|
|
2701
|
-
if (seen.has(key)) continue;
|
|
2702
|
-
seen.add(key);
|
|
2703
|
-
result.push(ref);
|
|
2704
|
-
}
|
|
2705
|
-
return result;
|
|
2706
|
-
}
|
|
2707
|
-
function createReferenceIndex(catalog, materialize) {
|
|
2708
|
-
const dedupedEquipment = dedupeEntitiesByPrecedence(catalog.equipment ?? []);
|
|
2709
|
-
const efforts = buildExactIndex(catalog.efforts ?? []);
|
|
2710
|
-
const equipment = buildExactIndex(dedupedEquipment);
|
|
2711
|
-
const prefixCandidates = dedupedEquipment.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);
|
|
2712
|
-
const materializer = materialize ?? ((effort) => ({
|
|
2713
|
-
effort,
|
|
2714
|
-
definition: effort,
|
|
2715
|
-
slug: effort.slug,
|
|
2716
|
-
label: effort.label,
|
|
2717
|
-
met: effort.baseAttributes.met,
|
|
2718
|
-
baseAttributes: effort.baseAttributes,
|
|
2719
|
-
discipline: effort.baseAttributes.discipline,
|
|
2720
|
-
disciplineFactor: effort.baseAttributes.disciplineFactor ?? 1,
|
|
2721
|
-
intensityTier: effort.baseAttributes.intensityTier,
|
|
2722
|
-
modifiers: {},
|
|
2723
|
-
registrySource: effort.registrySource,
|
|
2724
|
-
resolvedFrom: effort.registrySource === "user" ? "user" : effort.registrySource === "bundled" ? "bundled" : "default",
|
|
2725
|
-
isEstimated: false
|
|
2726
|
-
}));
|
|
2727
|
-
const resolveEffortExact = (key) => {
|
|
2728
|
-
const found = efforts.lookup(key.trim());
|
|
2729
|
-
if (found.entity) return { entity: found.entity };
|
|
2730
|
-
if (found.ambiguous.length > 0) {
|
|
2731
|
-
return {
|
|
2732
|
-
entity: null,
|
|
2733
|
-
diagnostic: {
|
|
2734
|
-
reason: "ambiguous-effort",
|
|
2735
|
-
message: `"${key}" matches multiple efforts.`,
|
|
2736
|
-
token: key,
|
|
2737
|
-
candidates: found.ambiguous.map((e) => e.slug)
|
|
2738
|
-
}
|
|
2739
|
-
};
|
|
2740
|
-
}
|
|
2741
|
-
return {
|
|
2742
|
-
entity: null,
|
|
2743
|
-
diagnostic: { reason: "unknown-effort", message: `Unknown effort "${key}".`, token: key }
|
|
2744
|
-
};
|
|
2745
|
-
};
|
|
2746
|
-
const resolveEquipmentValues = (values) => {
|
|
2747
|
-
const refs = [];
|
|
2748
|
-
const diagnostics = [];
|
|
2749
|
-
for (const value of values) {
|
|
2750
|
-
if (typeof value !== "string" || !value.trim()) {
|
|
2751
|
-
diagnostics.push({
|
|
2752
|
-
reason: "invalid-explicit",
|
|
2753
|
-
message: `Equipment reference must be a slug string, got ${JSON.stringify(value) ?? "null"}.`,
|
|
2754
|
-
token: String(value)
|
|
2755
|
-
});
|
|
2756
|
-
continue;
|
|
2757
|
-
}
|
|
2758
|
-
const found = equipment.lookup(value.trim());
|
|
2759
|
-
if (found.entity) {
|
|
2760
|
-
refs.push(equipmentRef(found.entity));
|
|
2761
|
-
} else if (found.ambiguous.length > 0) {
|
|
2762
|
-
diagnostics.push({
|
|
2763
|
-
reason: "ambiguous-equipment",
|
|
2764
|
-
message: `"${value}" matches multiple equipment entries.`,
|
|
2765
|
-
token: value,
|
|
2766
|
-
candidates: found.ambiguous.map((e) => e.slug)
|
|
2767
|
-
});
|
|
2768
|
-
} else {
|
|
2769
|
-
diagnostics.push({
|
|
2770
|
-
reason: "invalid-explicit",
|
|
2771
|
-
message: `Unknown equipment "${value}".`,
|
|
2772
|
-
token: value
|
|
2773
|
-
});
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
return { refs: dedupeRefs(refs), diagnostics };
|
|
2777
|
-
};
|
|
2778
|
-
const index = {
|
|
2779
|
-
resolveEffort(slugOrAlias) {
|
|
2780
|
-
return efforts.lookup(slugOrAlias).entity;
|
|
2781
|
-
},
|
|
2782
|
-
resolveEquipment(slugOrAlias) {
|
|
2783
|
-
return equipment.lookup(slugOrAlias).entity;
|
|
2784
|
-
},
|
|
2785
|
-
materialize(effort) {
|
|
2786
|
-
return materializer(effort);
|
|
2787
|
-
},
|
|
2788
|
-
resolve(label, defaults) {
|
|
2789
|
-
const source = (label ?? "").trim();
|
|
2790
|
-
const diagnostics = [];
|
|
2791
|
-
const push = (diagnostic) => {
|
|
2792
|
-
if (diagnostic) diagnostics.push(diagnostic);
|
|
2793
|
-
};
|
|
2794
|
-
let matched = "none";
|
|
2795
|
-
let effortEntity = null;
|
|
2796
|
-
let remaining = "";
|
|
2797
|
-
let equipmentRefs = [];
|
|
2798
|
-
let equipmentAuthored = false;
|
|
2799
|
-
const fillEquipment = () => {
|
|
2800
|
-
if (equipmentRefs.length > 0 || equipmentAuthored) return;
|
|
2801
|
-
if (effortEntity === null) return;
|
|
2802
|
-
if (defaults?.equipment?.length) {
|
|
2803
|
-
const resolvedDefaults = resolveEquipmentValues(defaults.equipment);
|
|
2804
|
-
equipmentRefs = resolvedDefaults.refs;
|
|
2805
|
-
for (const diagnostic of resolvedDefaults.diagnostics) {
|
|
2806
|
-
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
2807
|
-
}
|
|
2808
|
-
if (equipmentRefs.length > 0) return;
|
|
2809
|
-
}
|
|
2810
|
-
if (effortEntity.equipment?.length) {
|
|
2811
|
-
const resolvedDefEquipment = resolveEquipmentValues(effortEntity.equipment);
|
|
2812
|
-
equipmentRefs = resolvedDefEquipment.refs;
|
|
2813
|
-
for (const diagnostic of resolvedDefEquipment.diagnostics) {
|
|
2814
|
-
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
2815
|
-
}
|
|
2816
|
-
}
|
|
2817
|
-
};
|
|
2818
|
-
if (!source) {
|
|
2819
|
-
if (defaults?.effort) {
|
|
2820
|
-
const found = resolveEffortExact(defaults.effort);
|
|
2821
|
-
if (found.entity) {
|
|
2822
|
-
effortEntity = found.entity;
|
|
2823
|
-
matched = "default";
|
|
2824
|
-
} else {
|
|
2825
|
-
push({
|
|
2826
|
-
reason: found.diagnostic?.reason === "ambiguous-effort" ? "ambiguous-effort" : "unknown-effort",
|
|
2827
|
-
message: `Default effort "${defaults.effort}" is not in the catalog.`,
|
|
2828
|
-
token: defaults.effort,
|
|
2829
|
-
candidates: found.diagnostic?.candidates
|
|
2830
|
-
});
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
fillEquipment();
|
|
2834
|
-
return {
|
|
2835
|
-
matched,
|
|
2836
|
-
label: source,
|
|
2837
|
-
effort: effortEntity ? materializer(effortEntity) : null,
|
|
2838
|
-
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
2839
|
-
equipment: equipmentRefs,
|
|
2840
|
-
remaining: "",
|
|
2841
|
-
diagnostics
|
|
2842
|
-
};
|
|
2843
|
-
}
|
|
2844
|
-
if (source.startsWith("{")) {
|
|
2845
|
-
let parsed;
|
|
2846
|
-
try {
|
|
2847
|
-
parsed = JSON.parse(source);
|
|
2848
|
-
} catch {
|
|
2849
|
-
return {
|
|
2850
|
-
matched: "none",
|
|
2851
|
-
label: source,
|
|
2852
|
-
effort: null,
|
|
2853
|
-
effortRef: null,
|
|
2854
|
-
equipment: [],
|
|
2855
|
-
remaining: "",
|
|
2856
|
-
diagnostics: [{
|
|
2857
|
-
reason: "malformed-json",
|
|
2858
|
-
message: "Reference object is not valid JSON.",
|
|
2859
|
-
token: source
|
|
2860
|
-
}]
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && ("effort" in parsed || "equipment" in parsed)) {
|
|
2864
|
-
matched = "explicit";
|
|
2865
|
-
const obj = parsed;
|
|
2866
|
-
if (obj.effort !== void 0) {
|
|
2867
|
-
if (typeof obj.effort === "string" && obj.effort.trim()) {
|
|
2868
|
-
const found = resolveEffortExact(obj.effort);
|
|
2869
|
-
effortEntity = found.entity;
|
|
2870
|
-
if (found.diagnostic) {
|
|
2871
|
-
push(found.diagnostic.reason === "ambiguous-effort" ? found.diagnostic : { ...found.diagnostic, reason: "invalid-explicit", message: `Explicit effort "${obj.effort}" is not in the catalog.` });
|
|
2872
|
-
}
|
|
2873
|
-
} else {
|
|
2874
|
-
push({
|
|
2875
|
-
reason: "invalid-explicit",
|
|
2876
|
-
message: 'Explicit "effort" must be a slug string.',
|
|
2877
|
-
token: String(obj.effort)
|
|
2878
|
-
});
|
|
2879
|
-
}
|
|
2880
|
-
}
|
|
2881
|
-
if (obj.equipment !== void 0) {
|
|
2882
|
-
equipmentAuthored = true;
|
|
2883
|
-
const values = Array.isArray(obj.equipment) ? obj.equipment : [obj.equipment];
|
|
2884
|
-
const resolvedEquipment = resolveEquipmentValues(values);
|
|
2885
|
-
equipmentRefs = resolvedEquipment.refs;
|
|
2886
|
-
diagnostics.push(...resolvedEquipment.diagnostics);
|
|
2887
|
-
if (effortEntity?.equipment?.length) {
|
|
2888
|
-
const definitionSlugs = new Set(effortEntity.equipment);
|
|
2889
|
-
const authoredSlugs = equipmentRefs.map((ref) => ref.slug);
|
|
2890
|
-
if (authoredSlugs.length > 0 && authoredSlugs.some((slug) => !definitionSlugs.has(slug))) {
|
|
2891
|
-
push({
|
|
2892
|
-
reason: "conflict",
|
|
2893
|
-
message: `Explicit equipment [${authoredSlugs.join(", ")}] contradicts effort "${effortEntity.slug}" definition equipment [${effortEntity.equipment.join(", ")}].`,
|
|
2894
|
-
token: source
|
|
2895
|
-
});
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
}
|
|
2899
|
-
fillEquipment();
|
|
2900
|
-
return {
|
|
2901
|
-
matched,
|
|
2902
|
-
label: source,
|
|
2903
|
-
effort: effortEntity ? materializer(effortEntity) : null,
|
|
2904
|
-
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
2905
|
-
equipment: equipmentRefs,
|
|
2906
|
-
remaining: "",
|
|
2907
|
-
diagnostics
|
|
2908
|
-
};
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
const lowerSource = source.toLowerCase();
|
|
2912
|
-
for (const candidate of prefixCandidates) {
|
|
2913
|
-
if (!lowerSource.startsWith(candidate.lower)) continue;
|
|
2914
|
-
const after = source.slice(candidate.lower.length);
|
|
2915
|
-
if (after && !/^\s/.test(after)) continue;
|
|
2916
|
-
const candidateRemaining = after.trim();
|
|
2917
|
-
if (!candidateRemaining) continue;
|
|
2918
|
-
const equipmentFound = equipment.lookup(candidate.lower);
|
|
2919
|
-
if (equipmentFound.ambiguous.length > 0) {
|
|
2920
|
-
push({
|
|
2921
|
-
reason: "ambiguous-equipment",
|
|
2922
|
-
message: `"${candidate.lower}" matches multiple equipment entries.`,
|
|
2923
|
-
token: source,
|
|
2924
|
-
candidates: equipmentFound.ambiguous.map((e) => e.slug)
|
|
2925
|
-
});
|
|
2926
|
-
return {
|
|
2927
|
-
matched: "none",
|
|
2928
|
-
label: source,
|
|
2929
|
-
effort: null,
|
|
2930
|
-
effortRef: null,
|
|
2931
|
-
equipment: [],
|
|
2932
|
-
remaining: "",
|
|
2933
|
-
diagnostics
|
|
2934
|
-
};
|
|
2935
|
-
}
|
|
2936
|
-
if (!equipmentFound.entity) continue;
|
|
2937
|
-
const effortFound = resolveEffortExact(candidateRemaining);
|
|
2938
|
-
if (effortFound.entity) {
|
|
2939
|
-
matched = "split";
|
|
2940
|
-
effortEntity = effortFound.entity;
|
|
2941
|
-
equipmentRefs = [equipmentRef(equipmentFound.entity)];
|
|
2942
|
-
equipmentAuthored = true;
|
|
2943
|
-
remaining = candidateRemaining;
|
|
2944
|
-
break;
|
|
2945
|
-
}
|
|
2946
|
-
if (effortFound.diagnostic?.reason === "ambiguous-effort") {
|
|
2947
|
-
push(effortFound.diagnostic);
|
|
2948
|
-
return {
|
|
2949
|
-
matched: "none",
|
|
2950
|
-
label: source,
|
|
2951
|
-
effort: null,
|
|
2952
|
-
effortRef: null,
|
|
2953
|
-
equipment: [],
|
|
2954
|
-
remaining: "",
|
|
2955
|
-
diagnostics
|
|
2956
|
-
};
|
|
2957
|
-
}
|
|
2958
|
-
}
|
|
2959
|
-
if (matched === "none") {
|
|
2960
|
-
const found = resolveEffortExact(source);
|
|
2961
|
-
if (found.entity) {
|
|
2962
|
-
effortEntity = found.entity;
|
|
2963
|
-
matched = "whole";
|
|
2964
|
-
} else if (found.diagnostic?.reason === "ambiguous-effort") {
|
|
2965
|
-
push(found.diagnostic);
|
|
2966
|
-
return {
|
|
2967
|
-
matched: "none",
|
|
2968
|
-
label: source,
|
|
2969
|
-
effort: null,
|
|
2970
|
-
effortRef: null,
|
|
2971
|
-
equipment: [],
|
|
2972
|
-
remaining: "",
|
|
2973
|
-
diagnostics
|
|
2974
|
-
};
|
|
2975
|
-
} else if (found.diagnostic) {
|
|
2976
|
-
push(found.diagnostic);
|
|
2977
|
-
}
|
|
2978
|
-
}
|
|
2979
|
-
fillEquipment();
|
|
2980
|
-
return {
|
|
2981
|
-
matched,
|
|
2982
|
-
label: source,
|
|
2983
|
-
effort: effortEntity ? materializer(effortEntity) : null,
|
|
2984
|
-
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
2985
|
-
equipment: equipmentRefs,
|
|
2986
|
-
remaining,
|
|
2987
|
-
diagnostics
|
|
2988
|
-
};
|
|
2989
|
-
}
|
|
2990
|
-
};
|
|
2991
|
-
return index;
|
|
2992
|
-
}
|
|
2993
|
-
function extractStatementRefs(statements) {
|
|
2994
|
-
const efforts = [];
|
|
2995
|
-
const equipment = [];
|
|
2996
|
-
const seenEffort = /* @__PURE__ */ new Set();
|
|
2997
|
-
const seenEquipment = /* @__PURE__ */ new Set();
|
|
2998
|
-
const pushUnique = (list, seen, value) => {
|
|
2999
|
-
if (!value) return;
|
|
3000
|
-
if (seen.has(value)) return;
|
|
3001
|
-
seen.add(value);
|
|
3002
|
-
list.push(value);
|
|
3003
|
-
};
|
|
3004
|
-
for (const statement of statements ?? []) {
|
|
3005
|
-
const metrics = Array.isArray(statement.metrics) ? statement.metrics : statement.metrics?.toArray?.() ?? [];
|
|
3006
|
-
for (const metric of metrics) {
|
|
3007
|
-
const refs = getReferenceMetadata(metric.metadata)?.refs;
|
|
3008
|
-
if (!refs) continue;
|
|
3009
|
-
for (const ref of refs) {
|
|
3010
|
-
if (ref.kind === "effort") pushUnique(efforts, seenEffort, ref.slug);
|
|
3011
|
-
if (ref.kind === "equipment") pushUnique(equipment, seenEquipment, ref.slug);
|
|
3012
|
-
}
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
return { efforts, equipment };
|
|
3016
|
-
}
|
|
3017
|
-
function slugifyRefLabel(label) {
|
|
3018
|
-
return normalizeForFuzzy(label).replace(/\s+/g, "-");
|
|
3019
|
-
}
|
|
3020
|
-
|
|
3021
|
-
// src/parser/parseScript.ts
|
|
3022
|
-
function parseScript(inputText, options = {}) {
|
|
3023
|
-
if (!inputText || !inputText.trim()) {
|
|
3024
|
-
return new WhiteboardScript(inputText, [], []);
|
|
3025
|
-
}
|
|
3026
|
-
try {
|
|
3027
|
-
const doc = inputText.endsWith("\n") ? inputText : inputText + "\n";
|
|
3028
|
-
const tree = parser.parse(doc);
|
|
3029
|
-
const sport = options.sport ?? options.dialect;
|
|
3030
|
-
const references = options.references || options.tags ? {
|
|
3031
|
-
index: options.references ?? createReferenceIndex({ efforts: [], equipment: [] }),
|
|
3032
|
-
defaults: options.defaults,
|
|
3033
|
-
tags: options.tags
|
|
3034
|
-
} : void 0;
|
|
3035
|
-
const statements = options.withoutDialects ? extractStatementsRaw(inputText, tree, references) : extractStatements(inputText, tree, sport, references);
|
|
3036
|
-
return new WhiteboardScript(inputText, statements, []);
|
|
3037
|
-
} catch (error) {
|
|
3038
|
-
const msg = error instanceof Error ? error.message : "Unknown parse error";
|
|
3039
|
-
return new WhiteboardScript(inputText, [], [{ message: msg }]);
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
|
|
3043
|
-
// src/parser/md-timer.ts
|
|
3044
|
-
var MdTimerRuntime = class {
|
|
3045
|
-
config;
|
|
3046
|
-
constructor(config) {
|
|
3047
|
-
this.config = config;
|
|
3048
|
-
}
|
|
3049
|
-
/**
|
|
3050
|
-
* Parse WhiteboardScript source into a {@link WhiteboardScript}, running the
|
|
3051
|
-
* Dialect Stack on every statement.
|
|
3052
|
-
*
|
|
3053
|
-
* @param sport - The block's `:sport` fence suffix (` ```log:climbing `).
|
|
3054
|
-
*/
|
|
3055
|
-
read(inputText, sport) {
|
|
3056
|
-
return parseScript(inputText, { sport, ...this.config });
|
|
3057
|
-
}
|
|
3058
|
-
/**
|
|
3059
|
-
* Parse without running the Dialect Stack. Used by the parser test harness
|
|
3060
|
-
* which applies its own Dialect set. Production consumers should use {@link read}.
|
|
3061
|
-
*/
|
|
3062
|
-
readWithoutDialects(inputText) {
|
|
3063
|
-
return parseScript(inputText, { withoutDialects: true, ...this.config });
|
|
3064
|
-
}
|
|
3065
|
-
};
|
|
3066
|
-
|
|
3067
|
-
// src/parser/parserInstance.ts
|
|
3068
|
-
function createParser(config) {
|
|
3069
|
-
return new MdTimerRuntime(config);
|
|
3070
|
-
}
|
|
3071
|
-
var nextTagID = 0;
|
|
3072
|
-
var Tag = class _Tag {
|
|
3073
|
-
/**
|
|
3074
|
-
@internal
|
|
3075
|
-
*/
|
|
3076
|
-
constructor(name2, set, base, modified) {
|
|
3077
|
-
this.name = name2;
|
|
3078
|
-
this.set = set;
|
|
3079
|
-
this.base = base;
|
|
3080
|
-
this.modified = modified;
|
|
3081
|
-
this.id = nextTagID++;
|
|
3082
|
-
}
|
|
3083
|
-
toString() {
|
|
3084
|
-
let { name: name2 } = this;
|
|
3085
|
-
for (let mod of this.modified)
|
|
3086
|
-
if (mod.name)
|
|
3087
|
-
name2 = `${mod.name}(${name2})`;
|
|
3088
|
-
return name2;
|
|
3089
|
-
}
|
|
3090
|
-
static define(nameOrParent, parent) {
|
|
3091
|
-
let name2 = typeof nameOrParent == "string" ? nameOrParent : "?";
|
|
3092
|
-
if (nameOrParent instanceof _Tag)
|
|
3093
|
-
parent = nameOrParent;
|
|
3094
|
-
if (parent === null || parent === void 0 ? void 0 : parent.base)
|
|
3095
|
-
throw new Error("Can not derive from a modified tag");
|
|
3096
|
-
let tag = new _Tag(name2, [], null, []);
|
|
3097
|
-
tag.set.push(tag);
|
|
3098
|
-
if (parent)
|
|
3099
|
-
for (let t2 of parent.set)
|
|
3100
|
-
tag.set.push(t2);
|
|
3101
|
-
return tag;
|
|
2690
|
+
static define(nameOrParent, parent) {
|
|
2691
|
+
let name2 = typeof nameOrParent == "string" ? nameOrParent : "?";
|
|
2692
|
+
if (nameOrParent instanceof _Tag)
|
|
2693
|
+
parent = nameOrParent;
|
|
2694
|
+
if (parent === null || parent === void 0 ? void 0 : parent.base)
|
|
2695
|
+
throw new Error("Can not derive from a modified tag");
|
|
2696
|
+
let tag = new _Tag(name2, [], null, []);
|
|
2697
|
+
tag.set.push(tag);
|
|
2698
|
+
if (parent)
|
|
2699
|
+
for (let t2 of parent.set)
|
|
2700
|
+
tag.set.push(t2);
|
|
2701
|
+
return tag;
|
|
3102
2702
|
}
|
|
3103
2703
|
/**
|
|
3104
2704
|
Define a tag _modifier_, which is a function that, given a tag,
|
|
@@ -3811,17 +3411,16 @@ function structureKeywords() {
|
|
|
3811
3411
|
}));
|
|
3812
3412
|
}
|
|
3813
3413
|
function toCompletions(values, detail, quoteValues = false) {
|
|
3814
|
-
return values.map((value) =>
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
}));
|
|
3414
|
+
return values.map((value) => {
|
|
3415
|
+
const insertText = quoteValues ? !/^[A-Za-z][A-Za-z0-9]*$/.test(value.value) ? JSON.stringify(value.value) : value.value : value.label ?? value.value;
|
|
3416
|
+
return {
|
|
3417
|
+
label: value.label ?? value.value,
|
|
3418
|
+
apply: insertText,
|
|
3419
|
+
detail: value.detail ?? (value.label && value.label !== value.value ? value.value : detail),
|
|
3420
|
+
type: "text",
|
|
3421
|
+
info: value.info
|
|
3422
|
+
};
|
|
3423
|
+
});
|
|
3825
3424
|
}
|
|
3826
3425
|
function dialectWords(sport) {
|
|
3827
3426
|
if (!sport) return [];
|
|
@@ -3904,7 +3503,13 @@ function whiteboardScriptCompletionSource(options = {}) {
|
|
|
3904
3503
|
return null;
|
|
3905
3504
|
}
|
|
3906
3505
|
const efforts = options.efforts ? toCompletions(await options.efforts(), "effort") : [];
|
|
3907
|
-
const
|
|
3506
|
+
const equipmentPrefixes = options.equipment ? (await options.equipment()).map((eq) => ({
|
|
3507
|
+
label: eq.label ?? eq.value,
|
|
3508
|
+
apply: `${eq.label ?? eq.value} `,
|
|
3509
|
+
detail: eq.detail ?? "equipment",
|
|
3510
|
+
type: "text"
|
|
3511
|
+
})) : [];
|
|
3512
|
+
const effortOptions = [...keywords, ...efforts, ...equipmentPrefixes, ...sportWords];
|
|
3908
3513
|
if (/^[ \t]*[A-Za-z][A-Za-z0-9]*$/.test(before)) {
|
|
3909
3514
|
const supplied = suppliedPropertyKeys(context);
|
|
3910
3515
|
return {
|
|
@@ -3947,7 +3552,7 @@ function whiteboardScriptCompletionSource(options = {}) {
|
|
|
3947
3552
|
const typed = context.state.doc.sliceString(first.from, pos).toLowerCase();
|
|
3948
3553
|
const compatible = effortOptions.filter((option) => {
|
|
3949
3554
|
const inserted = typeof option.apply === "string" ? option.apply : option.label;
|
|
3950
|
-
return inserted.toLowerCase().startsWith(typed);
|
|
3555
|
+
return inserted.toLowerCase().startsWith(typed) || option.label.toLowerCase().startsWith(typed);
|
|
3951
3556
|
});
|
|
3952
3557
|
return { from: first.from, options: compatible, validFor: /^[A-Za-z ]*$/ };
|
|
3953
3558
|
}
|
|
@@ -3996,6 +3601,50 @@ function whiteboardScript(options) {
|
|
|
3996
3601
|
return new LanguageSupport(whiteboardScriptLanguage, whiteboardScriptCompletion(options ?? {}));
|
|
3997
3602
|
}
|
|
3998
3603
|
|
|
3604
|
+
// src/effort-registry/types.ts
|
|
3605
|
+
var EFFORT_REGISTRY_ORIGINS = ["bundled", "user"];
|
|
3606
|
+
var SEED_PRECEDENCE_RANK = {
|
|
3607
|
+
core: 0,
|
|
3608
|
+
community: 1,
|
|
3609
|
+
imported: 2,
|
|
3610
|
+
user: 3
|
|
3611
|
+
};
|
|
3612
|
+
function resolveSourceTier(sourceIdOrMetaKey, explicitTier) {
|
|
3613
|
+
if (explicitTier) return explicitTier;
|
|
3614
|
+
if (!sourceIdOrMetaKey) return "core";
|
|
3615
|
+
if (sourceIdOrMetaKey === "user") return "user";
|
|
3616
|
+
const id = sourceIdOrMetaKey.startsWith("seed:") ? sourceIdOrMetaKey.slice(5) : sourceIdOrMetaKey;
|
|
3617
|
+
if (id === "core" || id === "efforts" || id === "equipment") return "core";
|
|
3618
|
+
if (id === "community" || id.startsWith("community-") || id === "common") return "community";
|
|
3619
|
+
return "imported";
|
|
3620
|
+
}
|
|
3621
|
+
function resolveSourceRank(sourceIdOrMetaKey, explicitTier) {
|
|
3622
|
+
return SEED_PRECEDENCE_RANK[resolveSourceTier(sourceIdOrMetaKey, explicitTier)];
|
|
3623
|
+
}
|
|
3624
|
+
function resolveEntityTier(entity) {
|
|
3625
|
+
if (entity.registrySource === "user") return "user";
|
|
3626
|
+
return resolveSourceTier(entity.sourceId);
|
|
3627
|
+
}
|
|
3628
|
+
function resolveEntityRank(entity) {
|
|
3629
|
+
return SEED_PRECEDENCE_RANK[resolveEntityTier(entity)];
|
|
3630
|
+
}
|
|
3631
|
+
function dedupeEntitiesByPrecedence(entities) {
|
|
3632
|
+
const bySlug = /* @__PURE__ */ new Map();
|
|
3633
|
+
for (const entity of entities) {
|
|
3634
|
+
const key = entity.slug.toLowerCase();
|
|
3635
|
+
const prior = bySlug.get(key);
|
|
3636
|
+
if (!prior || resolveEntityRank(entity) >= resolveEntityRank(prior)) {
|
|
3637
|
+
bySlug.set(key, entity);
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
return Array.from(bySlug.values());
|
|
3641
|
+
}
|
|
3642
|
+
var INTENSITY_TIERS = ["low", "moderate", "high"];
|
|
3643
|
+
var DEFAULT_RESOLVER_OPTIONS = {
|
|
3644
|
+
defaultMet: 5,
|
|
3645
|
+
defaultThreshold: 2
|
|
3646
|
+
};
|
|
3647
|
+
|
|
3999
3648
|
// src/effort-registry/disciplines.ts
|
|
4000
3649
|
var EFFORT_DISCIPLINES = [
|
|
4001
3650
|
"bodyweight",
|
|
@@ -4601,6 +4250,71 @@ function tagCategoryToDocument(category) {
|
|
|
4601
4250
|
}
|
|
4602
4251
|
return lines.join("\n");
|
|
4603
4252
|
}
|
|
4253
|
+
|
|
4254
|
+
// src/effort-registry/fuzzyMatch.ts
|
|
4255
|
+
function levenshteinDistance(a, b) {
|
|
4256
|
+
if (a === b) return 0;
|
|
4257
|
+
if (a.length === 0) return b.length;
|
|
4258
|
+
if (b.length === 0) return a.length;
|
|
4259
|
+
if (a.length > b.length) {
|
|
4260
|
+
const tmp = a;
|
|
4261
|
+
a = b;
|
|
4262
|
+
b = tmp;
|
|
4263
|
+
}
|
|
4264
|
+
const m = a.length;
|
|
4265
|
+
const n = b.length;
|
|
4266
|
+
let prev = new Uint32Array(m + 1);
|
|
4267
|
+
let curr = new Uint32Array(m + 1);
|
|
4268
|
+
for (let i = 0; i <= m; i++) {
|
|
4269
|
+
prev[i] = i;
|
|
4270
|
+
}
|
|
4271
|
+
for (let j = 1; j <= n; j++) {
|
|
4272
|
+
curr[0] = j;
|
|
4273
|
+
const bj = b.charCodeAt(j - 1);
|
|
4274
|
+
for (let i = 1; i <= m; i++) {
|
|
4275
|
+
const cost = a.charCodeAt(i - 1) === bj ? 0 : 1;
|
|
4276
|
+
curr[i] = Math.min(
|
|
4277
|
+
prev[i] + 1,
|
|
4278
|
+
// deletion
|
|
4279
|
+
curr[i - 1] + 1,
|
|
4280
|
+
// insertion
|
|
4281
|
+
prev[i - 1] + cost
|
|
4282
|
+
// substitution
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4285
|
+
const swap = prev;
|
|
4286
|
+
prev = curr;
|
|
4287
|
+
curr = swap;
|
|
4288
|
+
}
|
|
4289
|
+
return prev[m];
|
|
4290
|
+
}
|
|
4291
|
+
function normalizeForFuzzy(input) {
|
|
4292
|
+
return input.toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, " ").replace(/[-_]+/g, " ").trim().replace(/\s+/g, " ");
|
|
4293
|
+
}
|
|
4294
|
+
function isWithinThreshold(a, b, threshold) {
|
|
4295
|
+
const na = normalizeForFuzzy(a);
|
|
4296
|
+
const nb = normalizeForFuzzy(b);
|
|
4297
|
+
if (na === nb) return true;
|
|
4298
|
+
if (Math.abs(na.length - nb.length) > threshold) return false;
|
|
4299
|
+
return levenshteinDistance(na, nb) <= threshold;
|
|
4300
|
+
}
|
|
4301
|
+
function findBestFuzzyMatch(query, candidates, threshold) {
|
|
4302
|
+
const nq = normalizeForFuzzy(query);
|
|
4303
|
+
let best = null;
|
|
4304
|
+
for (const candidate of candidates) {
|
|
4305
|
+
const nc = normalizeForFuzzy(candidate);
|
|
4306
|
+
if (Math.abs(nq.length - nc.length) > threshold) continue;
|
|
4307
|
+
const distance = levenshteinDistance(nq, nc);
|
|
4308
|
+
if (distance > threshold) continue;
|
|
4309
|
+
if (!best || distance < best.distance) {
|
|
4310
|
+
best = { target: candidate, distance };
|
|
4311
|
+
if (distance === 0) break;
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
return best;
|
|
4315
|
+
}
|
|
4316
|
+
|
|
4317
|
+
// src/effort-registry/EffortResolver.ts
|
|
4604
4318
|
function normalizeSlug(label) {
|
|
4605
4319
|
return normalizeForFuzzy(label).replace(/\s+/g, "-");
|
|
4606
4320
|
}
|
|
@@ -4962,148 +4676,519 @@ var CompositePropertyRegistry = class {
|
|
|
4962
4676
|
for (const item of this.userItems.values()) merged.set(item.slug, item);
|
|
4963
4677
|
return [...merged.values()];
|
|
4964
4678
|
}
|
|
4965
|
-
listByOrigin(origin) {
|
|
4966
|
-
return origin === "user" ? [...this.userItems.values()] : [...this.bundledItems.values()];
|
|
4679
|
+
listByOrigin(origin) {
|
|
4680
|
+
return origin === "user" ? [...this.userItems.values()] : [...this.bundledItems.values()];
|
|
4681
|
+
}
|
|
4682
|
+
async upsert(item) {
|
|
4683
|
+
if (item.registrySource !== "user") {
|
|
4684
|
+
throw new Error(`Cannot upsert property with registrySource '${item.registrySource}'; clone it as 'user' first`);
|
|
4685
|
+
}
|
|
4686
|
+
this.userItems.set(item.slug, item);
|
|
4687
|
+
if (this.storage) await this.storage.save(item);
|
|
4688
|
+
}
|
|
4689
|
+
async delete(slug) {
|
|
4690
|
+
const item = this.userItems.get(slug);
|
|
4691
|
+
if (!item) throw new Error(`Cannot delete '${slug}': not a user-owned record`);
|
|
4692
|
+
this.userItems.delete(slug);
|
|
4693
|
+
if (this.storage) await this.storage.delete(slug);
|
|
4694
|
+
}
|
|
4695
|
+
};
|
|
4696
|
+
var CompositeEquipmentRegistry = class extends CompositePropertyRegistry {
|
|
4697
|
+
constructor(options) {
|
|
4698
|
+
super({ storage: options?.storage });
|
|
4699
|
+
}
|
|
4700
|
+
};
|
|
4701
|
+
|
|
4702
|
+
// src/effort-registry/CompositeEffortRegistry.ts
|
|
4703
|
+
var CompositeEffortRegistry = class extends CompositePropertyRegistry {
|
|
4704
|
+
constructor(options) {
|
|
4705
|
+
super({
|
|
4706
|
+
bundled: options?.bundled,
|
|
4707
|
+
storage: options?.storage
|
|
4708
|
+
});
|
|
4709
|
+
}
|
|
4710
|
+
async loadBundled(customBundled) {
|
|
4711
|
+
this.bundledItems.clear();
|
|
4712
|
+
const source = customBundled ?? this.initialBundled ?? bundledEfforts;
|
|
4713
|
+
for (const effort of source) {
|
|
4714
|
+
const prior = this.bundledItems.get(effort.slug);
|
|
4715
|
+
if (!prior || resolveEntityRank(effort) >= resolveEntityRank(prior)) {
|
|
4716
|
+
this.bundledItems.set(effort.slug, effort);
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
this.userItems.clear();
|
|
4720
|
+
if (this.storage) {
|
|
4721
|
+
try {
|
|
4722
|
+
const userEfforts = await this.storage.load();
|
|
4723
|
+
for (const effort of userEfforts) {
|
|
4724
|
+
if (effort.registrySource === "user") {
|
|
4725
|
+
this.userItems.set(effort.slug, effort);
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
} catch (err) {
|
|
4729
|
+
console.warn("[CompositeEffortRegistry] Failed to load user efforts from storage adapter:", err);
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
this.initialized = true;
|
|
4733
|
+
}
|
|
4734
|
+
/** True after loadBundled() has resolved */
|
|
4735
|
+
isInitialized() {
|
|
4736
|
+
return this.initialized;
|
|
4737
|
+
}
|
|
4738
|
+
listByOrigin(origin) {
|
|
4739
|
+
if (origin === "user") {
|
|
4740
|
+
return Array.from(this.userItems.values());
|
|
4741
|
+
}
|
|
4742
|
+
if (origin === "bundled") {
|
|
4743
|
+
return Array.from(this.bundledItems.values());
|
|
4744
|
+
}
|
|
4745
|
+
return this.list().filter((e) => e.registrySource === origin);
|
|
4746
|
+
}
|
|
4747
|
+
async upsert(effort) {
|
|
4748
|
+
if (effort.registrySource !== "user") {
|
|
4749
|
+
throw new Error(
|
|
4750
|
+
`CompositeEffortRegistry.upsert: only user efforts can be written. Received registrySource="${effort.registrySource}"`
|
|
4751
|
+
);
|
|
4752
|
+
}
|
|
4753
|
+
this.userItems.set(effort.slug, effort);
|
|
4754
|
+
if (this.storage) {
|
|
4755
|
+
try {
|
|
4756
|
+
await this.storage.save(effort);
|
|
4757
|
+
} catch (err) {
|
|
4758
|
+
console.warn("[CompositeEffortRegistry] Failed to persist effort to storage adapter:", err);
|
|
4759
|
+
}
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
async delete(slug) {
|
|
4763
|
+
const existing = this.resolve(slug);
|
|
4764
|
+
if (existing && existing.registrySource !== "user") {
|
|
4765
|
+
throw new Error(
|
|
4766
|
+
`CompositeEffortRegistry.delete: cannot delete non-user effort "${slug}"`
|
|
4767
|
+
);
|
|
4768
|
+
}
|
|
4769
|
+
this.userItems.delete(slug);
|
|
4770
|
+
if (this.storage) {
|
|
4771
|
+
try {
|
|
4772
|
+
await this.storage.delete(slug);
|
|
4773
|
+
} catch (err) {
|
|
4774
|
+
console.warn("[CompositeEffortRegistry] Failed to delete effort from storage adapter:", err);
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
};
|
|
4779
|
+
|
|
4780
|
+
// src/effort-registry/aliasManagement.ts
|
|
4781
|
+
async function addAliasToEffort(registry, targetSlug, newAlias) {
|
|
4782
|
+
const trimmed = newAlias.trim();
|
|
4783
|
+
if (!trimmed) {
|
|
4784
|
+
throw new Error("Alias cannot be empty");
|
|
4785
|
+
}
|
|
4786
|
+
const existing = registry.resolve(targetSlug);
|
|
4787
|
+
if (!existing) {
|
|
4788
|
+
throw new Error(`Effort "${targetSlug}" not found in registry`);
|
|
4967
4789
|
}
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
this.userItems.set(item.slug, item);
|
|
4973
|
-
if (this.storage) await this.storage.save(item);
|
|
4790
|
+
const normalizedNew = normalizeForFuzzy(trimmed);
|
|
4791
|
+
const alreadyHas = normalizeForFuzzy(existing.label) === normalizedNew || existing.aliases.some((a) => normalizeForFuzzy(a) === normalizedNew);
|
|
4792
|
+
if (alreadyHas) {
|
|
4793
|
+
return existing;
|
|
4974
4794
|
}
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4795
|
+
const updatedAliases = [...existing.aliases, trimmed];
|
|
4796
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4797
|
+
if (existing.registrySource === "bundled") {
|
|
4798
|
+
const id = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? `effort-user-${crypto.randomUUID()}` : `effort-user-${Date.now()}`;
|
|
4799
|
+
const userEffort = {
|
|
4800
|
+
...existing,
|
|
4801
|
+
id,
|
|
4802
|
+
registrySource: "user",
|
|
4803
|
+
aliases: updatedAliases,
|
|
4804
|
+
updatedAt: now,
|
|
4805
|
+
derivation: existing.derivation ?? {
|
|
4806
|
+
parentSlug: existing.slug,
|
|
4807
|
+
coefficients: {},
|
|
4808
|
+
hardOverrides: {}
|
|
4809
|
+
}
|
|
4810
|
+
};
|
|
4811
|
+
await registry.upsert(userEffort);
|
|
4812
|
+
return userEffort;
|
|
4980
4813
|
}
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4814
|
+
const updatedUserEffort = {
|
|
4815
|
+
...existing,
|
|
4816
|
+
aliases: updatedAliases,
|
|
4817
|
+
updatedAt: now
|
|
4818
|
+
};
|
|
4819
|
+
await registry.upsert(updatedUserEffort);
|
|
4820
|
+
return updatedUserEffort;
|
|
4821
|
+
}
|
|
4822
|
+
function buildExactIndex(entities) {
|
|
4823
|
+
const deduped = dedupeEntitiesByPrecedence(entities);
|
|
4824
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
4825
|
+
const add = (key, entity) => {
|
|
4826
|
+
if (!key) return;
|
|
4827
|
+
const existing = byKey.get(key);
|
|
4828
|
+
if (existing) {
|
|
4829
|
+
if (!existing.includes(entity)) existing.push(entity);
|
|
4830
|
+
} else {
|
|
4831
|
+
byKey.set(key, [entity]);
|
|
4832
|
+
}
|
|
4833
|
+
};
|
|
4834
|
+
for (const entity of deduped) {
|
|
4835
|
+
add(entity.slug.toLowerCase(), entity);
|
|
4836
|
+
add(normalizeForFuzzy(entity.label), entity);
|
|
4837
|
+
for (const alias of entity.aliases) add(normalizeForFuzzy(alias), entity);
|
|
4985
4838
|
}
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4839
|
+
return {
|
|
4840
|
+
byKey,
|
|
4841
|
+
lookup(key) {
|
|
4842
|
+
const direct = byKey.get(key) ?? byKey.get(normalizeForFuzzy(key));
|
|
4843
|
+
if (!direct || direct.length === 0) return { entity: null, ambiguous: [] };
|
|
4844
|
+
if (direct.length === 1) return { entity: direct[0], ambiguous: [] };
|
|
4845
|
+
let maxRank = -1;
|
|
4846
|
+
for (const candidate of direct) {
|
|
4847
|
+
const rank = resolveEntityRank(candidate);
|
|
4848
|
+
if (rank > maxRank) maxRank = rank;
|
|
4849
|
+
}
|
|
4850
|
+
const topTier = direct.filter((candidate) => resolveEntityRank(candidate) === maxRank);
|
|
4851
|
+
if (topTier.length === 1) return { entity: topTier[0], ambiguous: [] };
|
|
4852
|
+
return { entity: null, ambiguous: topTier };
|
|
4853
|
+
}
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
function equipmentRef(entity) {
|
|
4857
|
+
return { kind: "equipment", slug: entity.slug, label: entity.label };
|
|
4858
|
+
}
|
|
4859
|
+
function dedupeRefs(refs) {
|
|
4860
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4861
|
+
const result = [];
|
|
4862
|
+
for (const ref of refs) {
|
|
4863
|
+
const key = `${ref.kind}:${ref.slug}`;
|
|
4864
|
+
if (seen.has(key)) continue;
|
|
4865
|
+
seen.add(key);
|
|
4866
|
+
result.push(ref);
|
|
4995
4867
|
}
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
4868
|
+
return result;
|
|
4869
|
+
}
|
|
4870
|
+
function createReferenceIndex(catalog, materialize) {
|
|
4871
|
+
const dedupedEquipment = dedupeEntitiesByPrecedence(catalog.equipment ?? []);
|
|
4872
|
+
const efforts = buildExactIndex(catalog.efforts ?? []);
|
|
4873
|
+
const equipment = buildExactIndex(dedupedEquipment);
|
|
4874
|
+
const prefixCandidates = dedupedEquipment.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);
|
|
4875
|
+
const materializer = materialize ?? ((effort) => ({
|
|
4876
|
+
effort,
|
|
4877
|
+
definition: effort,
|
|
4878
|
+
slug: effort.slug,
|
|
4879
|
+
label: effort.label,
|
|
4880
|
+
met: effort.baseAttributes.met,
|
|
4881
|
+
baseAttributes: effort.baseAttributes,
|
|
4882
|
+
discipline: effort.baseAttributes.discipline,
|
|
4883
|
+
disciplineFactor: effort.baseAttributes.disciplineFactor ?? 1,
|
|
4884
|
+
intensityTier: effort.baseAttributes.intensityTier,
|
|
4885
|
+
modifiers: {},
|
|
4886
|
+
registrySource: effort.registrySource,
|
|
4887
|
+
resolvedFrom: effort.registrySource === "user" ? "user" : effort.registrySource === "bundled" ? "bundled" : "default",
|
|
4888
|
+
isEstimated: false
|
|
4889
|
+
}));
|
|
4890
|
+
const resolveEffortExact = (key) => {
|
|
4891
|
+
const found = efforts.lookup(key.trim());
|
|
4892
|
+
if (found.entity) return { entity: found.entity };
|
|
4893
|
+
if (found.ambiguous.length > 0) {
|
|
4894
|
+
return {
|
|
4895
|
+
entity: null,
|
|
4896
|
+
diagnostic: {
|
|
4897
|
+
reason: "ambiguous-effort",
|
|
4898
|
+
message: `"${key}" matches multiple efforts.`,
|
|
4899
|
+
token: key,
|
|
4900
|
+
candidates: found.ambiguous.map((e) => e.slug)
|
|
4901
|
+
}
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4904
|
+
return {
|
|
4905
|
+
entity: null,
|
|
4906
|
+
diagnostic: { reason: "unknown-effort", message: `Unknown effort "${key}".`, token: key }
|
|
4907
|
+
};
|
|
4908
|
+
};
|
|
4909
|
+
const resolveEquipmentValues = (values) => {
|
|
4910
|
+
const refs = [];
|
|
4911
|
+
const diagnostics = [];
|
|
4912
|
+
for (const value of values) {
|
|
4913
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
4914
|
+
diagnostics.push({
|
|
4915
|
+
reason: "invalid-explicit",
|
|
4916
|
+
message: `Equipment reference must be a slug string, got ${JSON.stringify(value) ?? "null"}.`,
|
|
4917
|
+
token: String(value)
|
|
4918
|
+
});
|
|
4919
|
+
continue;
|
|
4920
|
+
}
|
|
4921
|
+
const found = equipment.lookup(value.trim());
|
|
4922
|
+
if (found.entity) {
|
|
4923
|
+
refs.push(equipmentRef(found.entity));
|
|
4924
|
+
} else if (found.ambiguous.length > 0) {
|
|
4925
|
+
diagnostics.push({
|
|
4926
|
+
reason: "ambiguous-equipment",
|
|
4927
|
+
message: `"${value}" matches multiple equipment entries.`,
|
|
4928
|
+
token: value,
|
|
4929
|
+
candidates: found.ambiguous.map((e) => e.slug)
|
|
4930
|
+
});
|
|
4931
|
+
} else {
|
|
4932
|
+
diagnostics.push({
|
|
4933
|
+
reason: "invalid-explicit",
|
|
4934
|
+
message: `Unknown equipment "${value}".`,
|
|
4935
|
+
token: value
|
|
4936
|
+
});
|
|
5003
4937
|
}
|
|
5004
4938
|
}
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
4939
|
+
return { refs: dedupeRefs(refs), diagnostics };
|
|
4940
|
+
};
|
|
4941
|
+
const index = {
|
|
4942
|
+
resolveEffort(slugOrAlias) {
|
|
4943
|
+
return efforts.lookup(slugOrAlias).entity;
|
|
4944
|
+
},
|
|
4945
|
+
resolveEquipment(slugOrAlias) {
|
|
4946
|
+
return equipment.lookup(slugOrAlias).entity;
|
|
4947
|
+
},
|
|
4948
|
+
materialize(effort) {
|
|
4949
|
+
return materializer(effort);
|
|
4950
|
+
},
|
|
4951
|
+
resolve(label, defaults) {
|
|
4952
|
+
const source = (label ?? "").trim();
|
|
4953
|
+
const diagnostics = [];
|
|
4954
|
+
const push = (diagnostic) => {
|
|
4955
|
+
if (diagnostic) diagnostics.push(diagnostic);
|
|
4956
|
+
};
|
|
4957
|
+
let matched = "none";
|
|
4958
|
+
let effortEntity = null;
|
|
4959
|
+
let remaining = "";
|
|
4960
|
+
let equipmentRefs = [];
|
|
4961
|
+
let equipmentAuthored = false;
|
|
4962
|
+
const fillEquipment = () => {
|
|
4963
|
+
if (equipmentRefs.length > 0 || equipmentAuthored) return;
|
|
4964
|
+
if (effortEntity === null) return;
|
|
4965
|
+
if (defaults?.equipment?.length) {
|
|
4966
|
+
const resolvedDefaults = resolveEquipmentValues(defaults.equipment);
|
|
4967
|
+
equipmentRefs = resolvedDefaults.refs;
|
|
4968
|
+
for (const diagnostic of resolvedDefaults.diagnostics) {
|
|
4969
|
+
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
4970
|
+
}
|
|
4971
|
+
if (equipmentRefs.length > 0) return;
|
|
4972
|
+
}
|
|
4973
|
+
if (effortEntity.equipment?.length) {
|
|
4974
|
+
const resolvedDefEquipment = resolveEquipmentValues(effortEntity.equipment);
|
|
4975
|
+
equipmentRefs = resolvedDefEquipment.refs;
|
|
4976
|
+
for (const diagnostic of resolvedDefEquipment.diagnostics) {
|
|
4977
|
+
push({ ...diagnostic, reason: "unknown-equipment" });
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
};
|
|
4981
|
+
if (!source) {
|
|
4982
|
+
if (defaults?.effort) {
|
|
4983
|
+
const found = resolveEffortExact(defaults.effort);
|
|
4984
|
+
if (found.entity) {
|
|
4985
|
+
effortEntity = found.entity;
|
|
4986
|
+
matched = "default";
|
|
4987
|
+
} else {
|
|
4988
|
+
push({
|
|
4989
|
+
reason: found.diagnostic?.reason === "ambiguous-effort" ? "ambiguous-effort" : "unknown-effort",
|
|
4990
|
+
message: `Default effort "${defaults.effort}" is not in the catalog.`,
|
|
4991
|
+
token: defaults.effort,
|
|
4992
|
+
candidates: found.diagnostic?.candidates
|
|
4993
|
+
});
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
fillEquipment();
|
|
4997
|
+
return {
|
|
4998
|
+
matched,
|
|
4999
|
+
label: source,
|
|
5000
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
5001
|
+
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
5002
|
+
equipment: equipmentRefs,
|
|
5003
|
+
remaining: "",
|
|
5004
|
+
diagnostics
|
|
5005
|
+
};
|
|
5006
|
+
}
|
|
5007
|
+
if (source.startsWith("{")) {
|
|
5008
|
+
let parsed;
|
|
5009
|
+
try {
|
|
5010
|
+
parsed = JSON.parse(source);
|
|
5011
|
+
} catch {
|
|
5012
|
+
return {
|
|
5013
|
+
matched: "none",
|
|
5014
|
+
label: source,
|
|
5015
|
+
effort: null,
|
|
5016
|
+
effortRef: null,
|
|
5017
|
+
equipment: [],
|
|
5018
|
+
remaining: "",
|
|
5019
|
+
diagnostics: [{
|
|
5020
|
+
reason: "malformed-json",
|
|
5021
|
+
message: "Reference object is not valid JSON.",
|
|
5022
|
+
token: source
|
|
5023
|
+
}]
|
|
5024
|
+
};
|
|
5025
|
+
}
|
|
5026
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed) && ("effort" in parsed || "equipment" in parsed)) {
|
|
5027
|
+
matched = "explicit";
|
|
5028
|
+
const obj = parsed;
|
|
5029
|
+
if (obj.effort !== void 0) {
|
|
5030
|
+
if (typeof obj.effort === "string" && obj.effort.trim()) {
|
|
5031
|
+
const found = resolveEffortExact(obj.effort);
|
|
5032
|
+
effortEntity = found.entity;
|
|
5033
|
+
if (found.diagnostic) {
|
|
5034
|
+
push(found.diagnostic.reason === "ambiguous-effort" ? found.diagnostic : { ...found.diagnostic, reason: "invalid-explicit", message: `Explicit effort "${obj.effort}" is not in the catalog.` });
|
|
5035
|
+
}
|
|
5036
|
+
} else {
|
|
5037
|
+
push({
|
|
5038
|
+
reason: "invalid-explicit",
|
|
5039
|
+
message: 'Explicit "effort" must be a slug string.',
|
|
5040
|
+
token: String(obj.effort)
|
|
5041
|
+
});
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
if (obj.equipment !== void 0) {
|
|
5045
|
+
equipmentAuthored = true;
|
|
5046
|
+
const values = Array.isArray(obj.equipment) ? obj.equipment : [obj.equipment];
|
|
5047
|
+
const resolvedEquipment = resolveEquipmentValues(values);
|
|
5048
|
+
equipmentRefs = resolvedEquipment.refs;
|
|
5049
|
+
diagnostics.push(...resolvedEquipment.diagnostics);
|
|
5050
|
+
if (effortEntity?.equipment?.length) {
|
|
5051
|
+
const definitionSlugs = new Set(effortEntity.equipment);
|
|
5052
|
+
const authoredSlugs = equipmentRefs.map((ref) => ref.slug);
|
|
5053
|
+
if (authoredSlugs.length > 0 && authoredSlugs.some((slug) => !definitionSlugs.has(slug))) {
|
|
5054
|
+
push({
|
|
5055
|
+
reason: "conflict",
|
|
5056
|
+
message: `Explicit equipment [${authoredSlugs.join(", ")}] contradicts effort "${effortEntity.slug}" definition equipment [${effortEntity.equipment.join(", ")}].`,
|
|
5057
|
+
token: source
|
|
5058
|
+
});
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
5012
5061
|
}
|
|
5062
|
+
fillEquipment();
|
|
5063
|
+
return {
|
|
5064
|
+
matched,
|
|
5065
|
+
label: source,
|
|
5066
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
5067
|
+
effortRef: effortEntity ? { kind: "effort", slug: effortEntity.slug, label: effortEntity.label } : null,
|
|
5068
|
+
equipment: equipmentRefs,
|
|
5069
|
+
remaining: "",
|
|
5070
|
+
diagnostics
|
|
5071
|
+
};
|
|
5013
5072
|
}
|
|
5014
|
-
} catch (err) {
|
|
5015
|
-
console.warn("[CompositeEffortRegistry] Failed to load user efforts from storage adapter:", err);
|
|
5016
5073
|
}
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5074
|
+
const lowerSource = source.toLowerCase();
|
|
5075
|
+
for (const candidate of prefixCandidates) {
|
|
5076
|
+
if (!lowerSource.startsWith(candidate.lower)) continue;
|
|
5077
|
+
const after = source.slice(candidate.lower.length);
|
|
5078
|
+
if (after && !/^\s/.test(after)) continue;
|
|
5079
|
+
const candidateRemaining = after.trim();
|
|
5080
|
+
if (!candidateRemaining) continue;
|
|
5081
|
+
const equipmentFound = equipment.lookup(candidate.lower);
|
|
5082
|
+
if (equipmentFound.ambiguous.length > 0) {
|
|
5083
|
+
push({
|
|
5084
|
+
reason: "ambiguous-equipment",
|
|
5085
|
+
message: `"${candidate.lower}" matches multiple equipment entries.`,
|
|
5086
|
+
token: source,
|
|
5087
|
+
candidates: equipmentFound.ambiguous.map((e) => e.slug)
|
|
5088
|
+
});
|
|
5089
|
+
return {
|
|
5090
|
+
matched: "none",
|
|
5091
|
+
label: source,
|
|
5092
|
+
effort: null,
|
|
5093
|
+
effortRef: null,
|
|
5094
|
+
equipment: [],
|
|
5095
|
+
remaining: "",
|
|
5096
|
+
diagnostics
|
|
5097
|
+
};
|
|
5098
|
+
}
|
|
5099
|
+
if (!equipmentFound.entity) continue;
|
|
5100
|
+
const effortFound = resolveEffortExact(candidateRemaining);
|
|
5101
|
+
if (effortFound.entity) {
|
|
5102
|
+
matched = "split";
|
|
5103
|
+
effortEntity = effortFound.entity;
|
|
5104
|
+
const rawPrefix = source.slice(0, candidate.lower.length).trim();
|
|
5105
|
+
equipmentRefs = [{
|
|
5106
|
+
...equipmentRef(equipmentFound.entity),
|
|
5107
|
+
...rawPrefix.toLowerCase() !== equipmentFound.entity.label.toLowerCase() ? { matchedAlias: rawPrefix } : {}
|
|
5108
|
+
}];
|
|
5109
|
+
equipmentAuthored = true;
|
|
5110
|
+
remaining = candidateRemaining;
|
|
5111
|
+
break;
|
|
5112
|
+
}
|
|
5113
|
+
if (effortFound.diagnostic?.reason === "ambiguous-effort") {
|
|
5114
|
+
push(effortFound.diagnostic);
|
|
5115
|
+
return {
|
|
5116
|
+
matched: "none",
|
|
5117
|
+
label: source,
|
|
5118
|
+
effort: null,
|
|
5119
|
+
effortRef: null,
|
|
5120
|
+
equipment: [],
|
|
5121
|
+
remaining: "",
|
|
5122
|
+
diagnostics
|
|
5123
|
+
};
|
|
5124
|
+
}
|
|
5045
5125
|
}
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5126
|
+
if (matched === "none") {
|
|
5127
|
+
const found = resolveEffortExact(source);
|
|
5128
|
+
if (found.entity) {
|
|
5129
|
+
effortEntity = found.entity;
|
|
5130
|
+
matched = "whole";
|
|
5131
|
+
} else if (found.diagnostic?.reason === "ambiguous-effort") {
|
|
5132
|
+
push(found.diagnostic);
|
|
5133
|
+
return {
|
|
5134
|
+
matched: "none",
|
|
5135
|
+
label: source,
|
|
5136
|
+
effort: null,
|
|
5137
|
+
effortRef: null,
|
|
5138
|
+
equipment: [],
|
|
5139
|
+
remaining: "",
|
|
5140
|
+
diagnostics
|
|
5141
|
+
};
|
|
5142
|
+
} else if (found.diagnostic) {
|
|
5143
|
+
push(found.diagnostic);
|
|
5144
|
+
}
|
|
5061
5145
|
}
|
|
5146
|
+
fillEquipment();
|
|
5147
|
+
const effortMatchText = matched === "split" ? remaining : source;
|
|
5148
|
+
return {
|
|
5149
|
+
matched,
|
|
5150
|
+
label: source,
|
|
5151
|
+
effort: effortEntity ? materializer(effortEntity) : null,
|
|
5152
|
+
effortRef: effortEntity ? {
|
|
5153
|
+
kind: "effort",
|
|
5154
|
+
slug: effortEntity.slug,
|
|
5155
|
+
label: effortEntity.label,
|
|
5156
|
+
...effortMatchText && effortMatchText.toLowerCase() !== effortEntity.label.toLowerCase() ? { matchedAlias: effortMatchText } : {}
|
|
5157
|
+
} : null,
|
|
5158
|
+
equipment: equipmentRefs,
|
|
5159
|
+
remaining,
|
|
5160
|
+
diagnostics
|
|
5161
|
+
};
|
|
5062
5162
|
}
|
|
5063
|
-
}
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
const
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
const userEffort = {
|
|
5086
|
-
...existing,
|
|
5087
|
-
id,
|
|
5088
|
-
registrySource: "user",
|
|
5089
|
-
aliases: updatedAliases,
|
|
5090
|
-
updatedAt: now,
|
|
5091
|
-
derivation: existing.derivation ?? {
|
|
5092
|
-
parentSlug: existing.slug,
|
|
5093
|
-
coefficients: {},
|
|
5094
|
-
hardOverrides: {}
|
|
5163
|
+
};
|
|
5164
|
+
return index;
|
|
5165
|
+
}
|
|
5166
|
+
function extractStatementRefs(statements) {
|
|
5167
|
+
const efforts = [];
|
|
5168
|
+
const equipment = [];
|
|
5169
|
+
const seenEffort = /* @__PURE__ */ new Set();
|
|
5170
|
+
const seenEquipment = /* @__PURE__ */ new Set();
|
|
5171
|
+
const pushUnique = (list, seen, value) => {
|
|
5172
|
+
if (!value) return;
|
|
5173
|
+
if (seen.has(value)) return;
|
|
5174
|
+
seen.add(value);
|
|
5175
|
+
list.push(value);
|
|
5176
|
+
};
|
|
5177
|
+
for (const statement of statements ?? []) {
|
|
5178
|
+
const metrics = Array.isArray(statement.metrics) ? statement.metrics : statement.metrics?.toArray?.() ?? [];
|
|
5179
|
+
for (const metric of metrics) {
|
|
5180
|
+
const refs = getReferenceMetadata(metric.metadata)?.refs;
|
|
5181
|
+
if (!refs) continue;
|
|
5182
|
+
for (const ref of refs) {
|
|
5183
|
+
if (ref.kind === "effort") pushUnique(efforts, seenEffort, ref.slug);
|
|
5184
|
+
if (ref.kind === "equipment") pushUnique(equipment, seenEquipment, ref.slug);
|
|
5095
5185
|
}
|
|
5096
|
-
}
|
|
5097
|
-
await registry.upsert(userEffort);
|
|
5098
|
-
return userEffort;
|
|
5186
|
+
}
|
|
5099
5187
|
}
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
};
|
|
5105
|
-
await registry.upsert(updatedUserEffort);
|
|
5106
|
-
return updatedUserEffort;
|
|
5188
|
+
return { efforts, equipment };
|
|
5189
|
+
}
|
|
5190
|
+
function slugifyRefLabel(label) {
|
|
5191
|
+
return normalizeForFuzzy(label).replace(/\s+/g, "-");
|
|
5107
5192
|
}
|
|
5108
5193
|
|
|
5109
5194
|
// src/runtime/contracts/IRuntimeOptions.ts
|
|
@@ -10105,8 +10190,11 @@ function tryEnrichBlock(block, resolver, references, overwrite, debug, propertyS
|
|
|
10105
10190
|
if (normalizedBlockType !== "effort" && normalizedBlockType !== "exercise") {
|
|
10106
10191
|
return;
|
|
10107
10192
|
}
|
|
10108
|
-
const
|
|
10109
|
-
|
|
10193
|
+
const statementEffortMetric = block.getAllMemory().flatMap((loc) => loc.metrics.toArray()).find((m) => m.type === MetricType.Effort);
|
|
10194
|
+
const preResolvedMeta = getReferenceMetadata(statementEffortMetric?.metadata);
|
|
10195
|
+
const preResolvedEffortRef = preResolvedMeta?.refs?.find((r) => r.kind === "effort");
|
|
10196
|
+
const label = block.label || preResolvedMeta?.source || (typeof statementEffortMetric?.image === "string" ? statementEffortMetric.image : "");
|
|
10197
|
+
if (!label && !preResolvedEffortRef && !block.context?.exerciseId) {
|
|
10110
10198
|
if (debug) logTrace(`[EffortEnrichment] Skipping block with no label: ${block.key}`);
|
|
10111
10199
|
return;
|
|
10112
10200
|
}
|
|
@@ -10123,13 +10211,23 @@ function tryEnrichBlock(block, resolver, references, overwrite, debug, propertyS
|
|
|
10123
10211
|
let equipmentRefs = [];
|
|
10124
10212
|
let diagnostics = [];
|
|
10125
10213
|
if (references) {
|
|
10126
|
-
const
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10214
|
+
const directSlug = preResolvedEffortRef?.slug || block.context?.exerciseId;
|
|
10215
|
+
const directEntity = directSlug ? references.resolveEffort(directSlug) : null;
|
|
10216
|
+
if (directEntity && preResolvedMeta) {
|
|
10217
|
+
resolved = references.materialize(directEntity);
|
|
10218
|
+
matchedKind = preResolvedMeta.matched;
|
|
10219
|
+
equipmentRefs = preResolvedMeta.refs?.filter((r) => r.kind === "equipment") ?? [];
|
|
10220
|
+
diagnostics = [...preResolvedMeta.diagnostics ?? []];
|
|
10221
|
+
} else {
|
|
10222
|
+
const resolution = references.resolve(label);
|
|
10223
|
+
resolved = resolution.effort ?? void 0;
|
|
10224
|
+
matchedKind = resolution.matched;
|
|
10225
|
+
equipmentRefs = resolution.equipment;
|
|
10226
|
+
diagnostics = resolution.diagnostics;
|
|
10227
|
+
}
|
|
10131
10228
|
} else {
|
|
10132
|
-
|
|
10229
|
+
const lookupKey = preResolvedEffortRef?.slug || block.context?.exerciseId || label;
|
|
10230
|
+
resolved = resolver.resolveEffort(lookupKey) ?? (lookupKey !== label ? resolver.resolveEffort(label) : void 0);
|
|
10133
10231
|
}
|
|
10134
10232
|
if (!resolved) {
|
|
10135
10233
|
if (diagnostics.length > 0) {
|
|
@@ -15865,6 +15963,6 @@ function compareStatement(expectations, actual, mode) {
|
|
|
15865
15963
|
return diffs;
|
|
15866
15964
|
}
|
|
15867
15965
|
|
|
15868
|
-
export { ABSENT, AGGREGATE_BUILTINS, AUTHORITATIVE_CASTS, AbortSessionAction, AmrapLogicStrategy, AnalyticsEngine, AnalyticsTransformer, BUILTIN_CALCS, BUNDLED_EFFORT_COUNT, BehaviorContext, BlockBuilder, BlockContext, ButtonBehavior, COMPLETION_PROPERTY_KEYS, CONSUMED_HINTS, CONSUMED_HINT_KEYS, CONTEXT_ATOMS, CalcEngine, CalcEvalError, CalcParseError, CalcRegistrationError, CalcUnitError, CalculationRegistry, CardioDialect, ChildSelectionBehavior, ChildrenStrategy, ChoiceGroupMetric, ClearChildrenAction, ClimbDialect, ClimbMetricType, CompileAndPushBlockAction, CompletionTimestampBehavior, CompositeEffortRegistry, CompositeEquipmentRegistry, CompositePropertyRegistry, CountdownTimerBehavior, CountupTimerBehavior, CrossFitDialect, DAY, DEFAULT_RESOLVER_OPTIONS, DEFAULT_RUNTIME_OPTIONS, DEFAULT_SOUND_PROFILE, DEFAULT_UNRESOLVED_EFFORT_MET, DIM_COUNT, DIM_ENERGY, DIM_LENGTH, DIM_MASS, DIM_TIME, DIM_ZERO, DISCIPLINE_FACTORS, DialectStack, EFFORT_DATA_METRIC_TYPE, EFFORT_DISCIPLINES, EFFORT_REGISTRY_ORIGINS, EffortBlock, EffortFallbackStrategy, EffortResolver, EmitEventAction, EmitSystemOutputAction, ErrorAction, EventBus, ExecutionContext, ExitBehavior, FixtureSyntaxError, GenericGroupStrategy, GenericLoopStrategy, GenericTimerStrategy, HabitsDialect, INTENSITY_TIERS, InMemoryEffortRegistry, IntervalLogicStrategy, JitCompiler, LabelingBehavior, LocalEventProvider, LocalRuntimeSubscription, LookupRegistry, MAX_TAG_CONTRIBUTION_DEPTH, MdTimerRuntime, MemoryLocation, MemoryTypeEnum, MetricPromotionBehavior, NAMED_COMPOUNDS, NextAction, NextEvent, NextEventHandler, OutputEmitter, PRODUCTION_STRATEGIES, PROPERTY_KEY_TO_METRIC_TYPE, PopBlockAction, PushBlockAction, PushRestBlockAction, REFERENCE_PAIR_KEYS, REST_EXCLUSION, RPE_LABELS, RUNTIME_LOG_SUBJECT_WIDTH, RegisterEventHandlerAction, Registry, ReportOutputBehavior, ReportOutputStrategy, RestBlock, RoundsMetric, RuntimeAdapter, RuntimeBlock, RuntimeBuilder, RuntimeClock, RuntimeFactory, RuntimeLogger, RuntimeMemory, RuntimeObservers, RuntimeStack, SEED_PRECEDENCE_RANK, SOUND_PLAY_EVENT, SOUND_PROFILE_METRIC, STANDARD_UNITS, STORE_CALCS, STREAM_ATOMS, STRUCTURAL_EFFORT_WORDS, ScriptRuntime, SessionRootBlock, SessionRootStrategy, SnapshotClock, SoundProfileBehavior, SoundStrategy, SpanTrackingBehavior, StandardAnalyticsProfile, StartSessionAction, StartWorkoutAction, SubscriptionManager, TAG_MARKER_PREFIX, ThrowError, ThrowErrorAction, TimeSpan, TwoPassEffortResolutionProcess, TypedMemoryReference, UNITS, UnitRegistry, UnitSet, UnitsDialect, UnregisterEventHandlerAction, UpdateNextPreviewAction, VISIBILITY_ICONS, VISIBILITY_LABELS, WaitingToStartBlock, WaitingToStartInjectorBehavior, WhiteboardScript, WodDialect, YogaDialect, addAliasToEffort, addDim, applyTemplateDefaults, buildStaticLookups, buildTooltip, bundledEfforts, calculateElapsed, canonicalizeType, checkCalcLine, classifyStatements, closeCurrentSpan, collapseUnresolvedChoices, commonFixtureSet, compareStatement, compileLineForm, compilePropertySummaryCalcs, compilePropertySummaryCalcsWithDiagnostics, composeDivUnit, composeMulUnit, compoundName, computeColumnLabel, computeLabel, computeWorkloadRollups, convertScalar, createAnalyticsEngineForBlock, createCalcEngine, createCalcStack, createCompiler, createDialectStack, createDisciplinesTable, createEffortTable, createLineLocator, createPolicy as createMetricPresentationPolicy, createMockClock, createParser, createProfileTable, createReferenceIndex, createRpeLabelsTable, dailySessionLoads, dayBucket, dedupeEntitiesByPrecedence, dialectRegistry, dialectStack, diffStatement, dimEquals, disciplineFactorFor, documentToEffort, documentToEquipment, documentToTag, effortToDocument, equipmentToDocument, evaluate, extractEffortData, extractMetrics, extractPropertyRulesFromMeta, extractStatementRefs, extractStatements, extractStatementsRaw, extractSyntaxFacts, extractTagPropertyRules, filterTagsByVisibility, findBestFuzzyMatch, findTagContributionCycles, findUnresolvedChoices, fixtureBackSquat, fixtureBurpee, fixturePlank, fixtureRowing, fixtureRunning, fixtureUserCustom, fixtureUserOverride, formatDim, formatDuration, formatRuntimeLogLine, formatRuntimeLogRule, formatRuntimeTimestamp, formatSoundTrigger, metricsToLabel as fragmentsToLabel, fullFixtureSet, fuseUnits, fuseUnitsInMetrics, getAnalyticsFromLogs, getAnalyticsFromRuntime, getHints, getMetricVisibility, getUnit, hasHint, hintMetric, hintsToContainer, inferDim, isCastSubscription, isChoiceResolved, isDimensionless, isEffortDiscipline, isFragmentTag, isTimeLikeMetric, isWithinThreshold, levenshteinDistance, metricPresentation, mutateTimerSpans, normalizeForFuzzy, num, openSpan, outputNodeId, outputNodeIds, padRuntimeLogEvent, padRuntimeLogSubject, parseCalcLine, parseEffortFile, parseEquipmentFile, parseExpression, parseMetricLine, parseScript, parseSoundTrigger, printCalculation, readNestedSection, readTimer, realtimeProcessorRegistry, renderMetric, resolveDominantOrigin, resolveEntityRank, resolveEntityTier, resolveProfileRules, resolveSoundProfile, resolveSourceRank, resolveSourceTier, resolveTagContributions, runMetricShakeOutQueue, slugifyRefLabel, snippetCompletions, startSpan, str, strategyRegistry, subDim, summaryProcessorRegistry, tagCategoryToDocument, tagContributionMetrics, tagMarkerSlug, tagMarkerSlugs, tagMarkerType, tagToDocument, toStoredOutputStatement, truthy, validateOutputUnit, parser as whiteboardParser, whiteboardScript, whiteboardScriptCompletion, whiteboardScriptCompletionSource, whiteboardScriptLanguage, whiteboardScriptSnippets, writeChoiceSelection };
|
|
15966
|
+
export { ABSENT, AGGREGATE_BUILTINS, AUTHORITATIVE_CASTS, AbortSessionAction, AmrapLogicStrategy, AnalyticsEngine, AnalyticsTransformer, BUILTIN_CALCS, BUNDLED_EFFORT_COUNT, BehaviorContext, BlockBuilder, BlockContext, ButtonBehavior, COMPLETION_PROPERTY_KEYS, CONSUMED_HINTS, CONSUMED_HINT_KEYS, CONTEXT_ATOMS, CalcEngine, CalcEvalError, CalcParseError, CalcRegistrationError, CalcUnitError, CalculationRegistry, CardioDialect, ChildSelectionBehavior, ChildrenStrategy, ChoiceGroupMetric, ClearChildrenAction, ClimbDialect, ClimbMetricType, CompileAndPushBlockAction, CompletionTimestampBehavior, CompositeEffortRegistry, CompositeEquipmentRegistry, CompositePropertyRegistry, CountdownTimerBehavior, CountupTimerBehavior, CrossFitDialect, DAY, DEFAULT_RESOLVER_OPTIONS, DEFAULT_RUNTIME_OPTIONS, DEFAULT_SOUND_PROFILE, DEFAULT_UNRESOLVED_EFFORT_MET, DIM_COUNT, DIM_ENERGY, DIM_LENGTH, DIM_MASS, DIM_TIME, DIM_ZERO, DISCIPLINE_FACTORS, DialectStack, EFFORT_DATA_METRIC_TYPE, EFFORT_DISCIPLINES, EFFORT_REGISTRY_ORIGINS, EffortBlock, EffortFallbackStrategy, EffortResolver, EmitEventAction, EmitSystemOutputAction, ErrorAction, EventBus, ExecutionContext, ExitBehavior, FixtureSyntaxError, GenericGroupStrategy, GenericLoopStrategy, GenericTimerStrategy, HabitsDialect, INTENSITY_TIERS, InMemoryEffortRegistry, IntervalLogicStrategy, JitCompiler, LabelingBehavior, LocalEventProvider, LocalRuntimeSubscription, LookupRegistry, MAX_TAG_CONTRIBUTION_DEPTH, MdTimerRuntime, MemoryLocation, MemoryTypeEnum, MetricPromotionBehavior, NAMED_COMPOUNDS, NextAction, NextEvent, NextEventHandler, OutputEmitter, PRODUCTION_STRATEGIES, PROPERTY_KEY_TO_METRIC_TYPE, PopBlockAction, PushBlockAction, PushRestBlockAction, REFERENCE_PAIR_KEYS, REST_EXCLUSION, RPE_LABELS, RUNTIME_LOG_SUBJECT_WIDTH, RegisterEventHandlerAction, Registry, ReportOutputBehavior, ReportOutputStrategy, RestBlock, RoundsMetric, RuntimeAdapter, RuntimeBlock, RuntimeBuilder, RuntimeClock, RuntimeFactory, RuntimeLogger, RuntimeMemory, RuntimeObservers, RuntimeStack, SEED_PRECEDENCE_RANK, SOUND_PLAY_EVENT, SOUND_PROFILE_METRIC, STANDARD_UNITS, STORE_CALCS, STREAM_ATOMS, STRUCTURAL_EFFORT_WORDS, ScriptRuntime, SessionRootBlock, SessionRootStrategy, SnapshotClock, SoundProfileBehavior, SoundStrategy, SpanTrackingBehavior, StandardAnalyticsProfile, StartSessionAction, StartWorkoutAction, SubscriptionManager, TAG_MARKER_PREFIX, ThrowError, ThrowErrorAction, TimeSpan, TwoPassEffortResolutionProcess, TypedMemoryReference, UNITS, UnitRegistry, UnitSet, UnitsDialect, UnregisterEventHandlerAction, UpdateNextPreviewAction, VISIBILITY_ICONS, VISIBILITY_LABELS, WaitingToStartBlock, WaitingToStartInjectorBehavior, WhiteboardScript, WodDialect, YogaDialect, addAliasToEffort, addDim, applyTemplateDefaults, buildStaticLookups, buildTooltip, bundledEfforts, calculateElapsed, canonicalizeType, checkCalcLine, classifyStatements, closeCurrentSpan, collapseUnresolvedChoices, commonFixtureSet, compareStatement, compileLineForm, compilePropertySummaryCalcs, compilePropertySummaryCalcsWithDiagnostics, composeDivUnit, composeMulUnit, compoundName, computeColumnLabel, computeLabel, computeWorkloadRollups, convertScalar, createAnalyticsEngineForBlock, createCalcEngine, createCalcStack, createCompiler, createDialectStack, createDisciplinesTable, createEffortTable, createLineLocator, createPolicy as createMetricPresentationPolicy, createMockClock, createParser, createProfileTable, createReferenceIndex, createRpeLabelsTable, dailySessionLoads, dayBucket, dedupeEntitiesByPrecedence, dialectRegistry, dialectStack, diffStatement, dimEquals, disciplineFactorFor, documentToEffort, documentToEquipment, documentToTag, effortToDocument, equipmentToDocument, evaluate, extractEffortData, extractMetrics, extractPropertyRulesFromMeta, extractStatementRefs, extractStatements, extractStatementsRaw, extractSyntaxFacts, extractTagPropertyRules, filterTagsByVisibility, findBestFuzzyMatch, findTagContributionCycles, findUnresolvedChoices, fixtureBackSquat, fixtureBurpee, fixturePlank, fixtureRowing, fixtureRunning, fixtureUserCustom, fixtureUserOverride, formatDim, formatDuration, formatRuntimeLogLine, formatRuntimeLogRule, formatRuntimeTimestamp, formatSoundTrigger, metricsToLabel as fragmentsToLabel, fullFixtureSet, fuseUnits, fuseUnitsInMetrics, getAnalyticsFromLogs, getAnalyticsFromRuntime, getHints, getMetricVisibility, getUnit, hasHint, hintMetric, hintsToContainer, inferDim, isCastSubscription, isChoiceResolved, isDimensionless, isEffortDiscipline, isFragmentTag, isTimeLikeMetric, isWithinThreshold, levenshteinDistance, metricPresentation, mutateTimerSpans, normalizeForFuzzy, num, openSpan, outputNodeId, outputNodeIds, padRuntimeLogEvent, padRuntimeLogSubject, parseCalcLine, parseEffortFile, parseEquipmentFile, parseExpression, parseMetricLine, parseScript, parseSoundTrigger, printCalculation, readNestedSection, readTimer, realtimeProcessorRegistry, renderMetric, resolveDominantOrigin, resolveEntityRank, resolveEntityTier, resolveFusedEffortReferences, resolveProfileRules, resolveSoundProfile, resolveSourceRank, resolveSourceTier, resolveTagContributions, runMetricShakeOutQueue, slugifyRefLabel, snippetCompletions, startSpan, str, strategyRegistry, subDim, summaryProcessorRegistry, tagCategoryToDocument, tagContributionMetrics, tagMarkerSlug, tagMarkerSlugs, tagMarkerType, tagToDocument, toStoredOutputStatement, truthy, validateOutputUnit, parser as whiteboardParser, whiteboardScript, whiteboardScriptCompletion, whiteboardScriptCompletionSource, whiteboardScriptLanguage, whiteboardScriptSnippets, writeChoiceSelection };
|
|
15869
15967
|
//# sourceMappingURL=index.mjs.map
|
|
15870
15968
|
//# sourceMappingURL=index.mjs.map
|