@bitcobblers/wod-wiki-core 0.37.2137 → 0.37.2140

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.d.cts CHANGED
@@ -114,6 +114,14 @@ declare enum MetricType {
114
114
  Total = "total",
115
115
  Rep = "rep",
116
116
  Effort = "effort",
117
+ /**
118
+ * **Equipment** (Core) — ordered deduplicated canonical equipment slugs for a
119
+ * statement (e.g. `["dumbbell"]` from `10 Dumbbell Snatch`). One metric per
120
+ * statement; `metadata.refs` carries structured ReferenceRef entries,
121
+ * `metadata.source` the authored label, `metadata.diagnostics` any
122
+ * unknown/ambiguous/malformed reference findings.
123
+ */
124
+ Equipment = "equipment",
117
125
  Distance = "distance",
118
126
  Rounds = "rounds",
119
127
  CurrentRound = "current-round",
@@ -483,6 +491,66 @@ declare function systemDefaultUnit(dimension: DimensionKey): string | undefined;
483
491
  */
484
492
  declare function convertViaCatalog(value: number, from: string, to: string): number;
485
493
 
494
+ /**
495
+ * Structured canonical reference metadata — the additive payload shapes hung on
496
+ * `IMetric.metadata` by the shared reference resolver
497
+ * (`@bitcobblers/wod-wiki-lang` → `createReferenceIndex`).
498
+ *
499
+ * Pure data: survives the stored-logs / JSON round-trip so runtime consumers
500
+ * and the editor can read canonical refs without re-parsing authored labels.
501
+ */
502
+ /** Kind of registry entity a canonical reference points at. */
503
+ type ReferenceKind = 'effort' | 'equipment';
504
+ /**
505
+ * A canonical, resolved reference (slug + canonical display label).
506
+ * Durable identity lives in `slug`; `label` is display-only.
507
+ */
508
+ interface ReferenceRef {
509
+ kind: ReferenceKind;
510
+ /** Canonical identifier — lowercase, hyphenated, unique per kind. */
511
+ slug: string;
512
+ /** Canonical human-readable label (e.g. "Snatch", "Dumbbell"). */
513
+ label: string;
514
+ }
515
+ /** Why a reference could not be resolved. Stable vocabulary for editor UIs. */
516
+ type ReferenceDiagnosticReason = /** Remaining movement not found exactly (no slug/alias match). */ 'unknown-effort' | /** Authored equipment token is not a known equipment slug/alias. */ 'unknown-equipment' | /** Multiple efforts claim the same exact slug/alias. */ 'ambiguous-effort' | /** Multiple equipment entities claim the same exact slug/alias. */ 'ambiguous-equipment' | /** Explicit ref names an entity missing from the catalog. */ 'invalid-explicit' | /** Explicit ref contradicts the authored line (e.g. different equipment prefix). */ 'conflict' | /** Explicit JSON ref could not be parsed. */ 'malformed-json';
517
+ /** A visible, consumable reference-resolution finding. */
518
+ interface ReferenceDiagnostic {
519
+ reason: ReferenceDiagnosticReason;
520
+ /** Human-readable explanation (author-facing). */
521
+ message: string;
522
+ /** The authored fragment that failed, when attributable. */
523
+ token?: string;
524
+ /** Stable-ordered candidate slugs for "select existing" actions. */
525
+ candidates?: string[];
526
+ }
527
+ /** How a statement's references were resolved. */
528
+ type ReferenceMatchKind = 'explicit' | 'split' | 'whole' | 'default' | 'none';
529
+ /**
530
+ * `IMetric.metadata` payload under the `references` key: canonical refs,
531
+ * authored source, and diagnostics for one statement/block label.
532
+ */
533
+ interface ReferenceMetadata {
534
+ /** How the label was resolved. */
535
+ matched: ReferenceMatchKind;
536
+ /** Full authored label — never rewritten. */
537
+ source: string;
538
+ /** Canonical resolved refs (effort first, then ordered deduped equipment). */
539
+ refs?: ReferenceRef[];
540
+ /** Remaining movement text after equipment-prefix strip (split path). */
541
+ remaining?: string;
542
+ /** Unknown/ambiguous/invalid findings — never silently dropped. */
543
+ diagnostics?: ReferenceDiagnostic[];
544
+ }
545
+ /** Metadata key under which {@link ReferenceMetadata} is stored on a metric. */
546
+ declare const REFERENCE_METADATA_KEY = "references";
547
+ /**
548
+ * Read reference metadata off a metric's metadata record. Validates shape
549
+ * before trusting persisted input: unknown-shape payloads are ignored rather
550
+ * than partially honored.
551
+ */
552
+ declare function getReferenceMetadata(metadata: Record<string, unknown> | undefined): ReferenceMetadata | undefined;
553
+
486
554
  interface ICodeMetadata {
487
555
  line: number;
488
556
  startOffset: number;
@@ -1271,6 +1339,17 @@ interface BlockEffort {
1271
1339
  sourcePath?: string;
1272
1340
  createdAt: number;
1273
1341
  }
1342
+ /** Containment link block → canonical equipment, mirroring BlockEffort. */
1343
+ interface BlockEquipment {
1344
+ id: string;
1345
+ noteId: string;
1346
+ blockId: string;
1347
+ blockContentId?: string;
1348
+ equipmentSlug: string;
1349
+ isStatic?: boolean;
1350
+ sourcePath?: string;
1351
+ createdAt: number;
1352
+ }
1274
1353
  interface Membership {
1275
1354
  id: string;
1276
1355
  displayName: string;
@@ -1314,8 +1393,11 @@ type SectionType = 'title' | 'markdown' | 'time' | 'log' | 'frontmatter' | 'embe
1314
1393
  type WorkoutSectionType = Extract<SectionType, FenceDialect>;
1315
1394
  /** Type guard for workout sections */
1316
1395
  declare function isWorkoutSectionType(type: SectionType): type is WorkoutSectionType;
1317
- /** Typed front matter subtypes */
1318
- type FrontMatterSubtype = 'default' | 'youtube' | 'strava' | 'amazon' | 'file' | 'effort';
1396
+ /**
1397
+ * Typed front matter subtypes. `effort`/`equipment` are registry definition
1398
+ * notes (family `markdown/efforts|equipment/<slug>.md`).
1399
+ */
1400
+ type FrontMatterSubtype = 'default' | 'youtube' | 'strava' | 'amazon' | 'file' | 'effort' | 'equipment';
1319
1401
  /**
1320
1402
  * Represents a single WOD block within the markdown document
1321
1403
  */
@@ -1477,17 +1559,49 @@ declare function mapNoteBlockChanges(blocks: Record<string, NoteBlock>, changes:
1477
1559
  declare function buildRawContent(sections: Section[]): string;
1478
1560
  declare function calculateTotalLines(sections: Section[]): number;
1479
1561
 
1562
+ /**
1563
+ * Source-preserving frontmatter property patching.
1564
+ *
1565
+ * `patchFrontmatterProperty` rewrites exactly one property's source range
1566
+ * (key line plus any deeper-indented subtree) inside the leading YAML
1567
+ * frontmatter block of a markdown document. Everything else — comments,
1568
+ * sibling/unknown nested properties, line-ending style, body — passes
1569
+ * through byte-identical, so editors can apply the old→new diff as a single
1570
+ * undo step instead of reserializing flat metadata.
1571
+ *
1572
+ * ponytail: bounded line/indent range patch, not a YAML engine — explicit
1573
+ * path segments only (no key parsing beyond `[A-Za-z][\w.-]*`), two-space
1574
+ * nesting; upgrade path is swapping the range finder for a CST if formats
1575
+ * ever outgrow it.
1576
+ */
1577
+ type FrontmatterPatchValue = string | number | readonly string[] | null;
1578
+ /**
1579
+ * Patch a single frontmatter property in a markdown document.
1580
+ *
1581
+ * `path` segments are explicit: a single segment is a literal key verbatim
1582
+ * (`["metrics.equipment"]` stays a flat dotted key); multiple segments walk
1583
+ * nested two-space-indented maps (`["baseAttributes","met"]`).
1584
+ *
1585
+ * `value` scalars replace the property line (a safe inline comment on the old
1586
+ * line is carried over); arrays serialize as block lists; `null` removes the
1587
+ * property range — and any now-childless nested parent left behind.
1588
+ * Documents without frontmatter gain a minimal block with the original raw
1589
+ * text preserved as body; absent properties are inserted at the end of their
1590
+ * parent block. Unpatchable input (empty path, invalid key) returns `raw`.
1591
+ */
1592
+ declare function patchFrontmatterProperty(raw: string, path: readonly string[], value: FrontmatterPatchValue): string;
1593
+
1480
1594
  /**
1481
1595
  * Auto-generated build version metadata.
1482
1596
  * Do not edit directly; updated by scripts/stamp-version.ts
1483
1597
  */
1484
- declare const VERSION = "0.37.2137";
1485
- declare const GIT_SHA = "b82a721";
1486
- declare const BUILD_TIME = "2026-10-16T20:56:02.597Z";
1598
+ declare const VERSION = "0.37.2140";
1599
+ declare const GIT_SHA = "bcf7960";
1600
+ declare const BUILD_TIME = "2026-10-18T21:43:29.081Z";
1487
1601
  declare const SEMVER: {
1488
- readonly version: "0.37.2137";
1489
- readonly gitSha: "b82a721";
1490
- readonly buildTime: "2026-10-16T20:56:02.597Z";
1602
+ readonly version: "0.37.2140";
1603
+ readonly gitSha: "bcf7960";
1604
+ readonly buildTime: "2026-10-18T21:43:29.081Z";
1491
1605
  };
1492
1606
 
1493
- export { type AnalyticsDataPoint, type AnalyticsGraphConfig, type AnalyticsGroup, type Attachment, BUILD_TIME, type BlockEffort, type BlockIndexRow, BlockKey, type BlockRevision, type CatalogBackfillState, CodeMetadata, CodeStatement, type DialectAnalysis, type DimensionKey, type DisplayStatus, type EventGrain, type EventRecord, type FenceDialect, type FieldCatalogEntry, type FieldContribution, type FieldLeaf, type FieldRef, type FieldSourceRecord, type FieldValueKind, type FieldValueRecord, type FrontMatterSubtype, GIT_SHA, type IBlockKey, type ICodeMetadata, type ICodeStatement, type IDialect, type IDialectTagDescriptor, type IMetric, type IMetricOwnershipResolver, type IMetricSource, type IOutputStatement, type IScript, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, type Membership, Metric, type MetricAction, MetricContainer, type MetricFilter, type MetricOrigin, type MetricOwnershipLayer, type MetricOwnershipLedger, type MetricOwnershipOutcome, type MetricOwnershipPromotionCandidate, type MetricOwnershipQuery, type MetricOwnershipResolvedContribution, type MetricOwnershipTypeExplanation, type MetricTemporal, MetricType, type MetricWithOptionalOwnershipLayer, NOTEBOOK_TAG_PREFIX, type Note, type NoteBlock, type NoteBlockChange, type NoteBlockKind, type NoteKind, type NoteTag, type Notebook, type NullValueKind, type Origin, OutputStatement, type OutputStatementOptions, type OutputStatementType, OwnershipResolver, type Page, type PageNote, type ParseError, ParsedCodeStatement, type ReconcileNoteBlocksOptions, type ReducerStats, type ResultOrigin, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, type ScriptBlock, type ScriptBlockState, type Section, type SectionDocument, type SectionRecognitionOptions, type SectionType, type Segment, type Session, type StoredOutputStatement, type SummaryCoverage, type Tag, type TagType, type TagTypeRecord, type TimeSpan, UNIT_CATALOG, UnitConversionError, type UnitDefinition, type UnitErrorCode, VALID_FENCE_DIALECTS, VERSION, type VisualizerFilter, type VisualizerSize, type WorkoutResults, type WorkoutSectionType, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
1607
+ export { type AnalyticsDataPoint, type AnalyticsGraphConfig, type AnalyticsGroup, type Attachment, BUILD_TIME, type BlockEffort, type BlockEquipment, type BlockIndexRow, BlockKey, type BlockRevision, type CatalogBackfillState, CodeMetadata, CodeStatement, type DialectAnalysis, type DimensionKey, type DisplayStatus, type EventGrain, type EventRecord, type FenceDialect, type FieldCatalogEntry, type FieldContribution, type FieldLeaf, type FieldRef, type FieldSourceRecord, type FieldValueKind, type FieldValueRecord, type FrontMatterSubtype, type FrontmatterPatchValue, GIT_SHA, type IBlockKey, type ICodeMetadata, type ICodeStatement, type IDialect, type IDialectTagDescriptor, type IMetric, type IMetricOwnershipResolver, type IMetricSource, type IOutputStatement, type IScript, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, type Membership, Metric, type MetricAction, MetricContainer, type MetricFilter, type MetricOrigin, type MetricOwnershipLayer, type MetricOwnershipLedger, type MetricOwnershipOutcome, type MetricOwnershipPromotionCandidate, type MetricOwnershipQuery, type MetricOwnershipResolvedContribution, type MetricOwnershipTypeExplanation, type MetricTemporal, MetricType, type MetricWithOptionalOwnershipLayer, NOTEBOOK_TAG_PREFIX, type Note, type NoteBlock, type NoteBlockChange, type NoteBlockKind, type NoteKind, type NoteTag, type Notebook, type NullValueKind, type Origin, OutputStatement, type OutputStatementOptions, type OutputStatementType, OwnershipResolver, type Page, type PageNote, type ParseError, ParsedCodeStatement, REFERENCE_METADATA_KEY, type ReconcileNoteBlocksOptions, type ReducerStats, type ReferenceDiagnostic, type ReferenceDiagnosticReason, type ReferenceKind, type ReferenceMatchKind, type ReferenceMetadata, type ReferenceRef, type ResultOrigin, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, type ScriptBlock, type ScriptBlockState, type Section, type SectionDocument, type SectionRecognitionOptions, type SectionType, type Segment, type Session, type StoredOutputStatement, type SummaryCoverage, type Tag, type TagType, type TagTypeRecord, type TimeSpan, UNIT_CATALOG, UnitConversionError, type UnitDefinition, type UnitErrorCode, VALID_FENCE_DIALECTS, VERSION, type VisualizerFilter, type VisualizerSize, type WorkoutResults, type WorkoutSectionType, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, getReferenceMetadata, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, patchFrontmatterProperty, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
package/dist/index.d.ts CHANGED
@@ -114,6 +114,14 @@ declare enum MetricType {
114
114
  Total = "total",
115
115
  Rep = "rep",
116
116
  Effort = "effort",
117
+ /**
118
+ * **Equipment** (Core) — ordered deduplicated canonical equipment slugs for a
119
+ * statement (e.g. `["dumbbell"]` from `10 Dumbbell Snatch`). One metric per
120
+ * statement; `metadata.refs` carries structured ReferenceRef entries,
121
+ * `metadata.source` the authored label, `metadata.diagnostics` any
122
+ * unknown/ambiguous/malformed reference findings.
123
+ */
124
+ Equipment = "equipment",
117
125
  Distance = "distance",
118
126
  Rounds = "rounds",
119
127
  CurrentRound = "current-round",
@@ -483,6 +491,66 @@ declare function systemDefaultUnit(dimension: DimensionKey): string | undefined;
483
491
  */
484
492
  declare function convertViaCatalog(value: number, from: string, to: string): number;
485
493
 
494
+ /**
495
+ * Structured canonical reference metadata — the additive payload shapes hung on
496
+ * `IMetric.metadata` by the shared reference resolver
497
+ * (`@bitcobblers/wod-wiki-lang` → `createReferenceIndex`).
498
+ *
499
+ * Pure data: survives the stored-logs / JSON round-trip so runtime consumers
500
+ * and the editor can read canonical refs without re-parsing authored labels.
501
+ */
502
+ /** Kind of registry entity a canonical reference points at. */
503
+ type ReferenceKind = 'effort' | 'equipment';
504
+ /**
505
+ * A canonical, resolved reference (slug + canonical display label).
506
+ * Durable identity lives in `slug`; `label` is display-only.
507
+ */
508
+ interface ReferenceRef {
509
+ kind: ReferenceKind;
510
+ /** Canonical identifier — lowercase, hyphenated, unique per kind. */
511
+ slug: string;
512
+ /** Canonical human-readable label (e.g. "Snatch", "Dumbbell"). */
513
+ label: string;
514
+ }
515
+ /** Why a reference could not be resolved. Stable vocabulary for editor UIs. */
516
+ type ReferenceDiagnosticReason = /** Remaining movement not found exactly (no slug/alias match). */ 'unknown-effort' | /** Authored equipment token is not a known equipment slug/alias. */ 'unknown-equipment' | /** Multiple efforts claim the same exact slug/alias. */ 'ambiguous-effort' | /** Multiple equipment entities claim the same exact slug/alias. */ 'ambiguous-equipment' | /** Explicit ref names an entity missing from the catalog. */ 'invalid-explicit' | /** Explicit ref contradicts the authored line (e.g. different equipment prefix). */ 'conflict' | /** Explicit JSON ref could not be parsed. */ 'malformed-json';
517
+ /** A visible, consumable reference-resolution finding. */
518
+ interface ReferenceDiagnostic {
519
+ reason: ReferenceDiagnosticReason;
520
+ /** Human-readable explanation (author-facing). */
521
+ message: string;
522
+ /** The authored fragment that failed, when attributable. */
523
+ token?: string;
524
+ /** Stable-ordered candidate slugs for "select existing" actions. */
525
+ candidates?: string[];
526
+ }
527
+ /** How a statement's references were resolved. */
528
+ type ReferenceMatchKind = 'explicit' | 'split' | 'whole' | 'default' | 'none';
529
+ /**
530
+ * `IMetric.metadata` payload under the `references` key: canonical refs,
531
+ * authored source, and diagnostics for one statement/block label.
532
+ */
533
+ interface ReferenceMetadata {
534
+ /** How the label was resolved. */
535
+ matched: ReferenceMatchKind;
536
+ /** Full authored label — never rewritten. */
537
+ source: string;
538
+ /** Canonical resolved refs (effort first, then ordered deduped equipment). */
539
+ refs?: ReferenceRef[];
540
+ /** Remaining movement text after equipment-prefix strip (split path). */
541
+ remaining?: string;
542
+ /** Unknown/ambiguous/invalid findings — never silently dropped. */
543
+ diagnostics?: ReferenceDiagnostic[];
544
+ }
545
+ /** Metadata key under which {@link ReferenceMetadata} is stored on a metric. */
546
+ declare const REFERENCE_METADATA_KEY = "references";
547
+ /**
548
+ * Read reference metadata off a metric's metadata record. Validates shape
549
+ * before trusting persisted input: unknown-shape payloads are ignored rather
550
+ * than partially honored.
551
+ */
552
+ declare function getReferenceMetadata(metadata: Record<string, unknown> | undefined): ReferenceMetadata | undefined;
553
+
486
554
  interface ICodeMetadata {
487
555
  line: number;
488
556
  startOffset: number;
@@ -1271,6 +1339,17 @@ interface BlockEffort {
1271
1339
  sourcePath?: string;
1272
1340
  createdAt: number;
1273
1341
  }
1342
+ /** Containment link block → canonical equipment, mirroring BlockEffort. */
1343
+ interface BlockEquipment {
1344
+ id: string;
1345
+ noteId: string;
1346
+ blockId: string;
1347
+ blockContentId?: string;
1348
+ equipmentSlug: string;
1349
+ isStatic?: boolean;
1350
+ sourcePath?: string;
1351
+ createdAt: number;
1352
+ }
1274
1353
  interface Membership {
1275
1354
  id: string;
1276
1355
  displayName: string;
@@ -1314,8 +1393,11 @@ type SectionType = 'title' | 'markdown' | 'time' | 'log' | 'frontmatter' | 'embe
1314
1393
  type WorkoutSectionType = Extract<SectionType, FenceDialect>;
1315
1394
  /** Type guard for workout sections */
1316
1395
  declare function isWorkoutSectionType(type: SectionType): type is WorkoutSectionType;
1317
- /** Typed front matter subtypes */
1318
- type FrontMatterSubtype = 'default' | 'youtube' | 'strava' | 'amazon' | 'file' | 'effort';
1396
+ /**
1397
+ * Typed front matter subtypes. `effort`/`equipment` are registry definition
1398
+ * notes (family `markdown/efforts|equipment/<slug>.md`).
1399
+ */
1400
+ type FrontMatterSubtype = 'default' | 'youtube' | 'strava' | 'amazon' | 'file' | 'effort' | 'equipment';
1319
1401
  /**
1320
1402
  * Represents a single WOD block within the markdown document
1321
1403
  */
@@ -1477,17 +1559,49 @@ declare function mapNoteBlockChanges(blocks: Record<string, NoteBlock>, changes:
1477
1559
  declare function buildRawContent(sections: Section[]): string;
1478
1560
  declare function calculateTotalLines(sections: Section[]): number;
1479
1561
 
1562
+ /**
1563
+ * Source-preserving frontmatter property patching.
1564
+ *
1565
+ * `patchFrontmatterProperty` rewrites exactly one property's source range
1566
+ * (key line plus any deeper-indented subtree) inside the leading YAML
1567
+ * frontmatter block of a markdown document. Everything else — comments,
1568
+ * sibling/unknown nested properties, line-ending style, body — passes
1569
+ * through byte-identical, so editors can apply the old→new diff as a single
1570
+ * undo step instead of reserializing flat metadata.
1571
+ *
1572
+ * ponytail: bounded line/indent range patch, not a YAML engine — explicit
1573
+ * path segments only (no key parsing beyond `[A-Za-z][\w.-]*`), two-space
1574
+ * nesting; upgrade path is swapping the range finder for a CST if formats
1575
+ * ever outgrow it.
1576
+ */
1577
+ type FrontmatterPatchValue = string | number | readonly string[] | null;
1578
+ /**
1579
+ * Patch a single frontmatter property in a markdown document.
1580
+ *
1581
+ * `path` segments are explicit: a single segment is a literal key verbatim
1582
+ * (`["metrics.equipment"]` stays a flat dotted key); multiple segments walk
1583
+ * nested two-space-indented maps (`["baseAttributes","met"]`).
1584
+ *
1585
+ * `value` scalars replace the property line (a safe inline comment on the old
1586
+ * line is carried over); arrays serialize as block lists; `null` removes the
1587
+ * property range — and any now-childless nested parent left behind.
1588
+ * Documents without frontmatter gain a minimal block with the original raw
1589
+ * text preserved as body; absent properties are inserted at the end of their
1590
+ * parent block. Unpatchable input (empty path, invalid key) returns `raw`.
1591
+ */
1592
+ declare function patchFrontmatterProperty(raw: string, path: readonly string[], value: FrontmatterPatchValue): string;
1593
+
1480
1594
  /**
1481
1595
  * Auto-generated build version metadata.
1482
1596
  * Do not edit directly; updated by scripts/stamp-version.ts
1483
1597
  */
1484
- declare const VERSION = "0.37.2137";
1485
- declare const GIT_SHA = "b82a721";
1486
- declare const BUILD_TIME = "2026-10-16T20:56:02.597Z";
1598
+ declare const VERSION = "0.37.2140";
1599
+ declare const GIT_SHA = "bcf7960";
1600
+ declare const BUILD_TIME = "2026-10-18T21:43:29.081Z";
1487
1601
  declare const SEMVER: {
1488
- readonly version: "0.37.2137";
1489
- readonly gitSha: "b82a721";
1490
- readonly buildTime: "2026-10-16T20:56:02.597Z";
1602
+ readonly version: "0.37.2140";
1603
+ readonly gitSha: "bcf7960";
1604
+ readonly buildTime: "2026-10-18T21:43:29.081Z";
1491
1605
  };
1492
1606
 
1493
- export { type AnalyticsDataPoint, type AnalyticsGraphConfig, type AnalyticsGroup, type Attachment, BUILD_TIME, type BlockEffort, type BlockIndexRow, BlockKey, type BlockRevision, type CatalogBackfillState, CodeMetadata, CodeStatement, type DialectAnalysis, type DimensionKey, type DisplayStatus, type EventGrain, type EventRecord, type FenceDialect, type FieldCatalogEntry, type FieldContribution, type FieldLeaf, type FieldRef, type FieldSourceRecord, type FieldValueKind, type FieldValueRecord, type FrontMatterSubtype, GIT_SHA, type IBlockKey, type ICodeMetadata, type ICodeStatement, type IDialect, type IDialectTagDescriptor, type IMetric, type IMetricOwnershipResolver, type IMetricSource, type IOutputStatement, type IScript, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, type Membership, Metric, type MetricAction, MetricContainer, type MetricFilter, type MetricOrigin, type MetricOwnershipLayer, type MetricOwnershipLedger, type MetricOwnershipOutcome, type MetricOwnershipPromotionCandidate, type MetricOwnershipQuery, type MetricOwnershipResolvedContribution, type MetricOwnershipTypeExplanation, type MetricTemporal, MetricType, type MetricWithOptionalOwnershipLayer, NOTEBOOK_TAG_PREFIX, type Note, type NoteBlock, type NoteBlockChange, type NoteBlockKind, type NoteKind, type NoteTag, type Notebook, type NullValueKind, type Origin, OutputStatement, type OutputStatementOptions, type OutputStatementType, OwnershipResolver, type Page, type PageNote, type ParseError, ParsedCodeStatement, type ReconcileNoteBlocksOptions, type ReducerStats, type ResultOrigin, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, type ScriptBlock, type ScriptBlockState, type Section, type SectionDocument, type SectionRecognitionOptions, type SectionType, type Segment, type Session, type StoredOutputStatement, type SummaryCoverage, type Tag, type TagType, type TagTypeRecord, type TimeSpan, UNIT_CATALOG, UnitConversionError, type UnitDefinition, type UnitErrorCode, VALID_FENCE_DIALECTS, VERSION, type VisualizerFilter, type VisualizerSize, type WorkoutResults, type WorkoutSectionType, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
1607
+ export { type AnalyticsDataPoint, type AnalyticsGraphConfig, type AnalyticsGroup, type Attachment, BUILD_TIME, type BlockEffort, type BlockEquipment, type BlockIndexRow, BlockKey, type BlockRevision, type CatalogBackfillState, CodeMetadata, CodeStatement, type DialectAnalysis, type DimensionKey, type DisplayStatus, type EventGrain, type EventRecord, type FenceDialect, type FieldCatalogEntry, type FieldContribution, type FieldLeaf, type FieldRef, type FieldSourceRecord, type FieldValueKind, type FieldValueRecord, type FrontMatterSubtype, type FrontmatterPatchValue, GIT_SHA, type IBlockKey, type ICodeMetadata, type ICodeStatement, type IDialect, type IDialectTagDescriptor, type IMetric, type IMetricOwnershipResolver, type IMetricSource, type IOutputStatement, type IScript, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, type Membership, Metric, type MetricAction, MetricContainer, type MetricFilter, type MetricOrigin, type MetricOwnershipLayer, type MetricOwnershipLedger, type MetricOwnershipOutcome, type MetricOwnershipPromotionCandidate, type MetricOwnershipQuery, type MetricOwnershipResolvedContribution, type MetricOwnershipTypeExplanation, type MetricTemporal, MetricType, type MetricWithOptionalOwnershipLayer, NOTEBOOK_TAG_PREFIX, type Note, type NoteBlock, type NoteBlockChange, type NoteBlockKind, type NoteKind, type NoteTag, type Notebook, type NullValueKind, type Origin, OutputStatement, type OutputStatementOptions, type OutputStatementType, OwnershipResolver, type Page, type PageNote, type ParseError, ParsedCodeStatement, REFERENCE_METADATA_KEY, type ReconcileNoteBlocksOptions, type ReducerStats, type ReferenceDiagnostic, type ReferenceDiagnosticReason, type ReferenceKind, type ReferenceMatchKind, type ReferenceMetadata, type ReferenceRef, type ResultOrigin, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, type ScriptBlock, type ScriptBlockState, type Section, type SectionDocument, type SectionRecognitionOptions, type SectionType, type Segment, type Session, type StoredOutputStatement, type SummaryCoverage, type Tag, type TagType, type TagTypeRecord, type TimeSpan, UNIT_CATALOG, UnitConversionError, type UnitDefinition, type UnitErrorCode, VALID_FENCE_DIALECTS, VERSION, type VisualizerFilter, type VisualizerSize, type WorkoutResults, type WorkoutSectionType, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, getReferenceMetadata, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, patchFrontmatterProperty, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
package/dist/index.mjs CHANGED
@@ -34,6 +34,7 @@ var MetricType = /* @__PURE__ */ ((MetricType2) => {
34
34
  MetricType2["Total"] = "total";
35
35
  MetricType2["Rep"] = "rep";
36
36
  MetricType2["Effort"] = "effort";
37
+ MetricType2["Equipment"] = "equipment";
37
38
  MetricType2["Distance"] = "distance";
38
39
  MetricType2["Rounds"] = "rounds";
39
40
  MetricType2["CurrentRound"] = "current-round";
@@ -528,6 +529,29 @@ function convertViaCatalog(value, from, to) {
528
529
  return value * fromDef.factor / toDef.factor;
529
530
  }
530
531
 
532
+ // src/models/ReferenceMetadata.ts
533
+ var REFERENCE_METADATA_KEY = "references";
534
+ var REFERENCE_MATCH_KINDS = ["explicit", "split", "whole", "default", "none"];
535
+ function isValidReferenceRef(value) {
536
+ if (!value || typeof value !== "object") return false;
537
+ const candidate = value;
538
+ return (candidate.kind === "effort" || candidate.kind === "equipment") && typeof candidate.slug === "string" && !!candidate.slug && typeof candidate.label === "string";
539
+ }
540
+ function isValidReferenceDiagnostic(value) {
541
+ if (!value || typeof value !== "object") return false;
542
+ return typeof value.reason === "string";
543
+ }
544
+ function getReferenceMetadata(metadata) {
545
+ const value = metadata?.[REFERENCE_METADATA_KEY];
546
+ if (!value || typeof value !== "object") return void 0;
547
+ const candidate = value;
548
+ if (typeof candidate.matched !== "string" || !REFERENCE_MATCH_KINDS.includes(candidate.matched)) return void 0;
549
+ if (typeof candidate.source !== "string") return void 0;
550
+ if (candidate.refs !== void 0 && (!Array.isArray(candidate.refs) || !candidate.refs.every(isValidReferenceRef))) return void 0;
551
+ if (candidate.diagnostics !== void 0 && (!Array.isArray(candidate.diagnostics) || !candidate.diagnostics.every(isValidReferenceDiagnostic))) return void 0;
552
+ return value;
553
+ }
554
+
531
555
  // src/models/CodeMetadata.ts
532
556
  var CodeMetadata = class {
533
557
  constructor(line, startOffset, endOffset, length, columnStart = 0, columnEnd = 0, raw = "") {
@@ -986,13 +1010,14 @@ function matchDialectFence(trimmedLine) {
986
1010
  }
987
1011
  function frontmatterSubtype(properties) {
988
1012
  const type = properties.type?.toLowerCase();
989
- if (type === "youtube" || type === "strava" || type === "amazon" || type === "file" || type === "effort") return type;
1013
+ if (type === "youtube" || type === "strava" || type === "amazon" || type === "file" || type === "effort" || type === "equipment") return type;
990
1014
  const url = properties.url || properties.link || "";
991
1015
  const host = url.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "").split(/[/?#:]/, 1)[0].toLowerCase();
992
1016
  if (host === "youtu.be" || host === "youtube.com" || host.endsWith(".youtube.com")) return "youtube";
993
1017
  if (host === "strava.com" || host.endsWith(".strava.com")) return "strava";
994
1018
  if (host === "amzn.to" || host === "amazon.com" || host.endsWith(".amazon.com")) return "amazon";
995
1019
  if (["baseAttributes", "discipline", "effortType", "targetRPE", "equipment", "aliases", "derivation"].some((key) => key in properties)) return "effort";
1020
+ if (["slug", "label", "registrySource", "ownerNoteId", "sourcePath"].some((key) => key in properties)) return "equipment";
996
1021
  return "default";
997
1022
  }
998
1023
  function markdownSubtype(text) {
@@ -1209,16 +1234,175 @@ function extractBlockContent(content, startLine, endLine) {
1209
1234
  return parseDocumentSections(content).find((section) => section.startLine === startLine && section.endLine === endLine && section.scriptBlock)?.scriptBlock?.content ?? "";
1210
1235
  }
1211
1236
 
1237
+ // src/sections/frontmatter.ts
1238
+ var KEY_RE = /^[A-Za-z][\w.-]*$/;
1239
+ var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
1240
+ function escapeRe(s) {
1241
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1242
+ }
1243
+ function isBlankOrComment(line) {
1244
+ const t = line.trim();
1245
+ return t === "" || t.startsWith("#");
1246
+ }
1247
+ function isList(value) {
1248
+ return Array.isArray(value);
1249
+ }
1250
+ function quoteYamlScalar(value) {
1251
+ if (value === "") return '""';
1252
+ const looksNumeric = !isNaN(Number(value));
1253
+ const looksKeyword = /^(true|false|null|yes|no|on|off)$/i.test(value);
1254
+ if (/[:"'\n#{}[\],&*?|<>=%!@`]/.test(value) || value !== value.trim() || value.startsWith("-") || looksNumeric || looksKeyword) {
1255
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t")}"`;
1256
+ }
1257
+ return value;
1258
+ }
1259
+ function propertyLines(indent, key, value) {
1260
+ const pad = " ".repeat(indent);
1261
+ if (isList(value)) {
1262
+ if (value.length === 0) return [`${pad}${key}: []`];
1263
+ return [`${pad}${key}:`, ...value.map((item) => `${pad} - ${typeof item === "number" ? String(item) : quoteYamlScalar(item)}`)];
1264
+ }
1265
+ return [`${pad}${key}: ${typeof value === "number" ? String(value) : quoteYamlScalar(value)}`];
1266
+ }
1267
+ function missingChainLines(path, from, value, baseIndent) {
1268
+ const out = [];
1269
+ let indent = baseIndent;
1270
+ for (let i = from; i < path.length; i++) {
1271
+ if (i === path.length - 1) {
1272
+ out.push(...propertyLines(indent, path[i], value));
1273
+ } else {
1274
+ out.push(`${" ".repeat(indent)}${path[i]}:`);
1275
+ }
1276
+ indent += 2;
1277
+ }
1278
+ return out;
1279
+ }
1280
+ function inlineCommentIndex(line) {
1281
+ let inSingle = false;
1282
+ let inDouble = false;
1283
+ for (let i = 0; i < line.length; i++) {
1284
+ const c = line[i];
1285
+ if (inDouble) {
1286
+ if (c === "\\") i++;
1287
+ else if (c === '"') inDouble = false;
1288
+ continue;
1289
+ }
1290
+ if (inSingle) {
1291
+ if (c === "'") inSingle = false;
1292
+ continue;
1293
+ }
1294
+ if (c === '"') inDouble = true;
1295
+ else if (c === "'") inSingle = true;
1296
+ else if (c === "#" && (i === 0 || /\s/.test(line[i - 1]))) return i;
1297
+ }
1298
+ return -1;
1299
+ }
1300
+ function propertyExtent(lines, start, indent) {
1301
+ const extent = [start];
1302
+ let pending = [];
1303
+ for (let j = start + 1; j < lines.length; j++) {
1304
+ const line = lines[j];
1305
+ if (isBlankOrComment(line)) {
1306
+ pending.push(j);
1307
+ continue;
1308
+ }
1309
+ if (line.length - line.trimStart().length > indent) {
1310
+ extent.push(...pending, j);
1311
+ pending = [];
1312
+ } else {
1313
+ break;
1314
+ }
1315
+ }
1316
+ return extent;
1317
+ }
1318
+ function findProperty(lines, key, indent, from, to) {
1319
+ const re = new RegExp(`^ {${indent}}${escapeRe(key)}\\s*:`);
1320
+ for (let i = from; i < to; i++) {
1321
+ if (re.test(lines[i])) return i;
1322
+ }
1323
+ return -1;
1324
+ }
1325
+ function patchFrontmatterProperty(raw, path, value) {
1326
+ if (path.length === 0 || !path.every((k) => typeof k === "string" && KEY_RE.test(k))) return raw;
1327
+ const eol = raw.includes("\r\n") ? "\r\n" : "\n";
1328
+ const m = raw.match(FRONTMATTER_RE);
1329
+ if (!m) {
1330
+ if (value === null) return raw;
1331
+ const inner = missingChainLines(path, 0, value, 0);
1332
+ const block = `---${eol}${inner.join(eol)}${eol}---${eol}`;
1333
+ return raw.length ? block + raw : block;
1334
+ }
1335
+ const lines = m[1] === "" ? [] : m[1].split(/\r?\n/);
1336
+ let searchStart = 0;
1337
+ let searchEnd = lines.length;
1338
+ let foundDepth = -1;
1339
+ let foundIdx = -1;
1340
+ let foundExtent = [];
1341
+ for (let d = 0; d < path.length; d++) {
1342
+ const indent = d * 2;
1343
+ const idx = findProperty(lines, path[d], indent, searchStart, searchEnd);
1344
+ if (idx === -1) break;
1345
+ foundDepth = d;
1346
+ foundIdx = idx;
1347
+ foundExtent = propertyExtent(lines, idx, indent);
1348
+ searchStart = idx + 1;
1349
+ searchEnd = foundExtent[foundExtent.length - 1] + 1;
1350
+ }
1351
+ if (value === null) {
1352
+ if (foundDepth !== path.length - 1) return raw;
1353
+ const doomed = new Set(foundExtent);
1354
+ for (let d = foundDepth - 1; d >= 0; d--) {
1355
+ const indent = d * 2;
1356
+ const idx = findProperty(lines, path[d], indent, 0, lines.length);
1357
+ if (idx === -1 || doomed.has(idx)) break;
1358
+ let hasChild = false;
1359
+ for (let j = idx + 1; j < lines.length; j++) {
1360
+ if (doomed.has(j)) continue;
1361
+ const line = lines[j];
1362
+ if (isBlankOrComment(line)) continue;
1363
+ if (line.length - line.trimStart().length > indent) hasChild = true;
1364
+ break;
1365
+ }
1366
+ if (hasChild) break;
1367
+ doomed.add(idx);
1368
+ }
1369
+ const next2 = lines.filter((_, i) => !doomed.has(i));
1370
+ return `---${eol}${next2.length ? next2.join(eol) + eol : ""}---` + raw.slice(m[0].length);
1371
+ }
1372
+ let spliceIndex;
1373
+ let removeCount;
1374
+ let replacement;
1375
+ if (foundDepth === path.length - 1) {
1376
+ replacement = propertyLines(foundDepth * 2, path[foundDepth], value);
1377
+ if (!isList(value)) {
1378
+ const ci = inlineCommentIndex(lines[foundIdx]);
1379
+ if (ci !== -1) {
1380
+ const comment = lines[foundIdx].slice(ci).trim();
1381
+ replacement[0] = `${replacement[0]} ${comment}`;
1382
+ }
1383
+ }
1384
+ spliceIndex = foundExtent[0];
1385
+ removeCount = foundExtent.length;
1386
+ } else {
1387
+ spliceIndex = foundDepth === -1 ? lines.length : searchEnd;
1388
+ removeCount = 0;
1389
+ replacement = missingChainLines(path, foundDepth + 1, value, (foundDepth + 1) * 2);
1390
+ }
1391
+ const next = [...lines];
1392
+ next.splice(spliceIndex, removeCount, ...replacement);
1393
+ return `---${eol}${next.length ? next.join(eol) + eol : ""}---` + raw.slice(m[0].length);
1394
+ }
1395
+
1212
1396
  // src/version.ts
1213
- var VERSION = "0.37.2137";
1214
- var GIT_SHA = "b82a721";
1215
- var BUILD_TIME = "2026-10-16T20:56:02.597Z";
1397
+ var VERSION = "0.37.2140";
1398
+ var GIT_SHA = "bcf7960";
1399
+ var BUILD_TIME = "2026-10-18T21:43:29.081Z";
1216
1400
  var SEMVER = {
1217
- version: "0.37.2137",
1218
- gitSha: "b82a721",
1219
- buildTime: "2026-10-16T20:56:02.597Z"
1401
+ version: "0.37.2140",
1402
+ gitSha: "bcf7960",
1403
+ buildTime: "2026-10-18T21:43:29.081Z"
1220
1404
  };
1221
1405
 
1222
- export { BUILD_TIME, BlockKey, CodeMetadata, CodeStatement, GIT_SHA, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, Metric, MetricContainer, MetricType, NOTEBOOK_TAG_PREFIX, OutputStatement, OwnershipResolver, ParsedCodeStatement, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, UNIT_CATALOG, UnitConversionError, VALID_FENCE_DIALECTS, VERSION, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
1406
+ export { BUILD_TIME, BlockKey, CodeMetadata, CodeStatement, GIT_SHA, KNOWN_OUTPUT_TYPES, KNOWN_SECTION_WIDGETS, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, Metric, MetricContainer, MetricType, NOTEBOOK_TAG_PREFIX, OutputStatement, OwnershipResolver, ParsedCodeStatement, REFERENCE_METADATA_KEY, SEMVER, SYSTEM_DEFAULT_OUTPUT_UNIT, UNIT_CATALOG, UnitConversionError, VALID_FENCE_DIALECTS, VERSION, actionableSectionKind, blockContentId, buildRawContent, calculateTotalLines, convertViaCatalog, createMetricOwnershipLedger, detectScriptBlocks, dimensionKeyOf, extractBlockContent, fieldRefFromKey, fieldRefKey, findBlockAtLine, flattenFieldLeaves, fromNotebookTag, getMetricOwnershipLayer, getReferenceMetadata, isNotebookTag, isWorkoutSectionType, lookupUnit, mapNoteBlockChanges, matchDialectFence, normalizeFieldComponent, normalizeFieldPath, ownershipRank, parseDocumentSections, patchFrontmatterProperty, reconcileNoteBlocks, resolveLayer, runAffordance, systemDefaultUnit, toNotebookTag, validateNoteBlocks, valueKindOf };
1223
1407
  //# sourceMappingURL=index.mjs.map
1224
1408
  //# sourceMappingURL=index.mjs.map