@bitcobblers/wod-wiki-core 0.1.0
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 +854 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1075 -0
- package/dist/index.d.ts +1075 -0
- package/dist/index.mjs +830 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
// src/ownership/types.ts
|
|
2
|
+
var METRIC_OWNERSHIP_LAYER_CHAIN = [
|
|
3
|
+
"parser",
|
|
4
|
+
"dialect",
|
|
5
|
+
"user-plan",
|
|
6
|
+
"runtime",
|
|
7
|
+
"user-entry"
|
|
8
|
+
];
|
|
9
|
+
var LEGACY_ORIGIN_TO_OWNERSHIP_LAYER = {
|
|
10
|
+
parser: "parser",
|
|
11
|
+
compiler: "dialect",
|
|
12
|
+
dialect: "dialect",
|
|
13
|
+
hinted: "dialect",
|
|
14
|
+
"user-plan": "user-plan",
|
|
15
|
+
runtime: "runtime",
|
|
16
|
+
tracked: "runtime",
|
|
17
|
+
analyzed: "runtime",
|
|
18
|
+
"analyzed-estimated": "runtime",
|
|
19
|
+
execution: "runtime",
|
|
20
|
+
user: "user-entry",
|
|
21
|
+
collected: "user-entry"
|
|
22
|
+
};
|
|
23
|
+
function getMetricOwnershipLayer(origin) {
|
|
24
|
+
return LEGACY_ORIGIN_TO_OWNERSHIP_LAYER[origin ?? "parser"] ?? "parser";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/ownership/ledger.ts
|
|
28
|
+
var LAYER_RANK = {
|
|
29
|
+
parser: 0,
|
|
30
|
+
dialect: 1,
|
|
31
|
+
"user-plan": 2,
|
|
32
|
+
runtime: 3,
|
|
33
|
+
"user-entry": 4
|
|
34
|
+
};
|
|
35
|
+
function resolveLayer(metric) {
|
|
36
|
+
const maybeLayer = metric.ownershipLayer;
|
|
37
|
+
if (maybeLayer != null) {
|
|
38
|
+
return maybeLayer;
|
|
39
|
+
}
|
|
40
|
+
return getMetricOwnershipLayer(metric.origin);
|
|
41
|
+
}
|
|
42
|
+
function normalize(metrics) {
|
|
43
|
+
return metrics.map((metric, index) => {
|
|
44
|
+
const layer = resolveLayer(metric);
|
|
45
|
+
return {
|
|
46
|
+
metric,
|
|
47
|
+
type: metric.type,
|
|
48
|
+
layer,
|
|
49
|
+
layerRank: LAYER_RANK[layer] ?? 0,
|
|
50
|
+
index,
|
|
51
|
+
isSuppressor: metric.action === "suppress"
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function applyQuery(contributions, query) {
|
|
56
|
+
if (!query) {
|
|
57
|
+
return [...contributions];
|
|
58
|
+
}
|
|
59
|
+
return contributions.filter((contribution) => {
|
|
60
|
+
if (query.types && !query.types.includes(contribution.type)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (query.layers && !query.layers.includes(contribution.layer)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function groupByType(contributions) {
|
|
70
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const contribution of contributions) {
|
|
72
|
+
const bucket = grouped.get(contribution.type);
|
|
73
|
+
if (bucket) {
|
|
74
|
+
bucket.push(contribution);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
grouped.set(contribution.type, [contribution]);
|
|
78
|
+
}
|
|
79
|
+
return grouped;
|
|
80
|
+
}
|
|
81
|
+
function getWinningLayerRank(contributions) {
|
|
82
|
+
let winningLayerRank;
|
|
83
|
+
for (const contribution of contributions) {
|
|
84
|
+
if (contribution.isSuppressor) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (winningLayerRank == null || contribution.layerRank > winningLayerRank) {
|
|
88
|
+
winningLayerRank = contribution.layerRank;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return winningLayerRank;
|
|
92
|
+
}
|
|
93
|
+
function toResolvedContribution(contribution, outcome) {
|
|
94
|
+
return {
|
|
95
|
+
metric: contribution.metric,
|
|
96
|
+
type: contribution.type,
|
|
97
|
+
layer: contribution.layer,
|
|
98
|
+
outcome
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function compareByOriginalIndex(a, b) {
|
|
102
|
+
return a.index - b.index;
|
|
103
|
+
}
|
|
104
|
+
function createMetricOwnershipLedger(metrics) {
|
|
105
|
+
const contributions = normalize(metrics);
|
|
106
|
+
return {
|
|
107
|
+
raw(query) {
|
|
108
|
+
return applyQuery(contributions, query).map((contribution) => contribution.metric);
|
|
109
|
+
},
|
|
110
|
+
visible(query) {
|
|
111
|
+
const filtered = applyQuery(contributions, query);
|
|
112
|
+
const grouped = groupByType(filtered);
|
|
113
|
+
const visible = [];
|
|
114
|
+
for (const groupedContributions of grouped.values()) {
|
|
115
|
+
if (groupedContributions.some((contribution) => contribution.isSuppressor)) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const winningLayerRank = getWinningLayerRank(groupedContributions);
|
|
119
|
+
if (winningLayerRank == null) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
for (const contribution of groupedContributions) {
|
|
123
|
+
if (!contribution.isSuppressor && contribution.layerRank === winningLayerRank) {
|
|
124
|
+
visible.push(contribution);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
visible.sort(compareByOriginalIndex);
|
|
129
|
+
return visible.map((contribution) => contribution.metric);
|
|
130
|
+
},
|
|
131
|
+
byLayer(query) {
|
|
132
|
+
const filtered = applyQuery(contributions, query);
|
|
133
|
+
const grouped = {};
|
|
134
|
+
for (const layer of METRIC_OWNERSHIP_LAYER_CHAIN) {
|
|
135
|
+
grouped[layer] = [];
|
|
136
|
+
}
|
|
137
|
+
for (const contribution of filtered) {
|
|
138
|
+
grouped[contribution.layer].push(contribution.metric);
|
|
139
|
+
}
|
|
140
|
+
for (const layer of METRIC_OWNERSHIP_LAYER_CHAIN) {
|
|
141
|
+
if (grouped[layer]?.length === 0) {
|
|
142
|
+
delete grouped[layer];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return grouped;
|
|
146
|
+
},
|
|
147
|
+
promotionCandidates(query) {
|
|
148
|
+
const filtered = applyQuery(contributions, query);
|
|
149
|
+
const grouped = groupByType(filtered);
|
|
150
|
+
const candidates = [];
|
|
151
|
+
for (const groupedContributions of grouped.values()) {
|
|
152
|
+
const suppressors = groupedContributions.filter((contribution) => contribution.isSuppressor);
|
|
153
|
+
const nonSuppressors = groupedContributions.filter((contribution) => !contribution.isSuppressor);
|
|
154
|
+
if (nonSuppressors.length === 0) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (suppressors.length > 0) {
|
|
158
|
+
const highestCandidateRank = Math.max(...nonSuppressors.map((contribution) => contribution.layerRank));
|
|
159
|
+
const suppressingLayerRank = Math.max(...suppressors.map((contribution) => contribution.layerRank));
|
|
160
|
+
for (const contribution of nonSuppressors) {
|
|
161
|
+
if (contribution.layerRank === highestCandidateRank) {
|
|
162
|
+
candidates.push({
|
|
163
|
+
metric: contribution.metric,
|
|
164
|
+
type: contribution.type,
|
|
165
|
+
layer: contribution.layer,
|
|
166
|
+
reason: "suppressed",
|
|
167
|
+
blockedByLayer: METRIC_OWNERSHIP_LAYER_CHAIN[suppressingLayerRank]
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const winningLayerRank = getWinningLayerRank(nonSuppressors);
|
|
174
|
+
if (winningLayerRank == null) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const lowerLayerContributions = nonSuppressors.filter(
|
|
178
|
+
(contribution) => contribution.layerRank < winningLayerRank
|
|
179
|
+
);
|
|
180
|
+
if (lowerLayerContributions.length === 0) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const promotionLayerRank = Math.max(...lowerLayerContributions.map((contribution) => contribution.layerRank));
|
|
184
|
+
for (const contribution of lowerLayerContributions) {
|
|
185
|
+
if (contribution.layerRank === promotionLayerRank) {
|
|
186
|
+
candidates.push({
|
|
187
|
+
metric: contribution.metric,
|
|
188
|
+
type: contribution.type,
|
|
189
|
+
layer: contribution.layer,
|
|
190
|
+
reason: "shadowed-by-higher-layer",
|
|
191
|
+
blockedByLayer: METRIC_OWNERSHIP_LAYER_CHAIN[winningLayerRank]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
candidates.sort((a, b) => {
|
|
197
|
+
const rankDelta = (LAYER_RANK[b.layer] ?? 0) - (LAYER_RANK[a.layer] ?? 0);
|
|
198
|
+
if (rankDelta !== 0) {
|
|
199
|
+
return rankDelta;
|
|
200
|
+
}
|
|
201
|
+
return 0;
|
|
202
|
+
});
|
|
203
|
+
return candidates;
|
|
204
|
+
},
|
|
205
|
+
explain(query) {
|
|
206
|
+
const filtered = applyQuery(contributions, query);
|
|
207
|
+
const grouped = groupByType(filtered);
|
|
208
|
+
const explanations = [];
|
|
209
|
+
for (const [type, groupedContributions] of grouped.entries()) {
|
|
210
|
+
const suppressed = groupedContributions.some((contribution) => contribution.isSuppressor);
|
|
211
|
+
const winningLayerRank = suppressed ? void 0 : getWinningLayerRank(groupedContributions);
|
|
212
|
+
const entries = groupedContributions.map((contribution) => {
|
|
213
|
+
if (contribution.isSuppressor) {
|
|
214
|
+
return toResolvedContribution(contribution, "suppressor");
|
|
215
|
+
}
|
|
216
|
+
if (suppressed) {
|
|
217
|
+
return toResolvedContribution(contribution, "hidden-by-suppressor");
|
|
218
|
+
}
|
|
219
|
+
if (winningLayerRank != null && contribution.layerRank === winningLayerRank) {
|
|
220
|
+
return toResolvedContribution(contribution, "visible");
|
|
221
|
+
}
|
|
222
|
+
return toResolvedContribution(contribution, "hidden-by-layer");
|
|
223
|
+
});
|
|
224
|
+
explanations.push({
|
|
225
|
+
type,
|
|
226
|
+
winnerLayer: winningLayerRank == null ? void 0 : METRIC_OWNERSHIP_LAYER_CHAIN[winningLayerRank],
|
|
227
|
+
suppressed,
|
|
228
|
+
entries
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return explanations;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/models/Metric.ts
|
|
237
|
+
var MetricType = /* @__PURE__ */ ((MetricType2) => {
|
|
238
|
+
MetricType2["Duration"] = "duration";
|
|
239
|
+
MetricType2["Spans"] = "spans";
|
|
240
|
+
MetricType2["SystemTime"] = "system-time";
|
|
241
|
+
MetricType2["Time"] = "time";
|
|
242
|
+
MetricType2["Elapsed"] = "elapsed";
|
|
243
|
+
MetricType2["Total"] = "total";
|
|
244
|
+
MetricType2["Rep"] = "rep";
|
|
245
|
+
MetricType2["Effort"] = "effort";
|
|
246
|
+
MetricType2["Distance"] = "distance";
|
|
247
|
+
MetricType2["Rounds"] = "rounds";
|
|
248
|
+
MetricType2["CurrentRound"] = "current-round";
|
|
249
|
+
MetricType2["Action"] = "action";
|
|
250
|
+
MetricType2["Increment"] = "increment";
|
|
251
|
+
MetricType2["Group"] = "group";
|
|
252
|
+
MetricType2["Text"] = "text";
|
|
253
|
+
MetricType2["Resistance"] = "resistance";
|
|
254
|
+
MetricType2["Sound"] = "sound";
|
|
255
|
+
MetricType2["System"] = "system";
|
|
256
|
+
MetricType2["Label"] = "label";
|
|
257
|
+
MetricType2["Lap"] = "lap";
|
|
258
|
+
MetricType2["Metric"] = "metric";
|
|
259
|
+
MetricType2["Hint"] = "hint";
|
|
260
|
+
MetricType2["Custom"] = "custom";
|
|
261
|
+
MetricType2["Volume"] = "volume";
|
|
262
|
+
MetricType2["Intensity"] = "intensity";
|
|
263
|
+
MetricType2["Load"] = "load";
|
|
264
|
+
MetricType2["Work"] = "work";
|
|
265
|
+
MetricType2["RIR"] = "rir";
|
|
266
|
+
MetricType2["SessionRPE"] = "session-rpe";
|
|
267
|
+
MetricType2["SessionLoad"] = "session-load";
|
|
268
|
+
MetricType2["METScore"] = "met-score";
|
|
269
|
+
MetricType2["TIS"] = "tis";
|
|
270
|
+
MetricType2["Calculated"] = "calculated";
|
|
271
|
+
MetricType2["Choice"] = "choice";
|
|
272
|
+
return MetricType2;
|
|
273
|
+
})(MetricType || {});
|
|
274
|
+
var Metric = class {
|
|
275
|
+
type;
|
|
276
|
+
metricType;
|
|
277
|
+
value;
|
|
278
|
+
origin;
|
|
279
|
+
unit;
|
|
280
|
+
action;
|
|
281
|
+
image;
|
|
282
|
+
sourceBlockKey;
|
|
283
|
+
timestamp;
|
|
284
|
+
metadata;
|
|
285
|
+
name;
|
|
286
|
+
ownershipLayer;
|
|
287
|
+
constructor(typeOrName, valueOrType, originOrValue, unit, origin, action, image) {
|
|
288
|
+
if (typeof valueOrType === "string" && (typeof originOrValue === "number" || typeof originOrValue === "string" || typeof originOrValue === "boolean" || originOrValue === null || originOrValue === void 0)) {
|
|
289
|
+
this.name = String(typeOrName);
|
|
290
|
+
this.type = valueOrType;
|
|
291
|
+
this.metricType = valueOrType;
|
|
292
|
+
this.value = originOrValue;
|
|
293
|
+
this.unit = unit;
|
|
294
|
+
this.origin = origin || "parser";
|
|
295
|
+
this.action = action === "suppress" ? "suppress" : void 0;
|
|
296
|
+
this.image = image ?? String(originOrValue ?? valueOrType);
|
|
297
|
+
} else {
|
|
298
|
+
this.type = typeOrName;
|
|
299
|
+
this.metricType = typeOrName;
|
|
300
|
+
this.value = valueOrType;
|
|
301
|
+
this.origin = originOrValue || "parser";
|
|
302
|
+
this.unit = unit;
|
|
303
|
+
this.action = typeof origin === "string" && origin === "suppress" ? "suppress" : action === "suppress" ? "suppress" : void 0;
|
|
304
|
+
this.image = image ?? String(valueOrType ?? typeOrName);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
toNumber() {
|
|
308
|
+
if (typeof this.value === "number") return this.value;
|
|
309
|
+
if (typeof this.value === "string") {
|
|
310
|
+
const parsed = parseFloat(this.value);
|
|
311
|
+
return isNaN(parsed) ? 0 : parsed;
|
|
312
|
+
}
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/ownership/OwnershipResolver.ts
|
|
318
|
+
function resolveLayer2(metric) {
|
|
319
|
+
const maybeLayer = metric.ownershipLayer;
|
|
320
|
+
if (maybeLayer != null) return maybeLayer;
|
|
321
|
+
return getMetricOwnershipLayer(metric.origin ?? "parser");
|
|
322
|
+
}
|
|
323
|
+
function ownershipRank(metric) {
|
|
324
|
+
const layer = resolveLayer2(metric);
|
|
325
|
+
return METRIC_OWNERSHIP_LAYER_CHAIN.indexOf(layer);
|
|
326
|
+
}
|
|
327
|
+
function applyFilter(metrics, filter) {
|
|
328
|
+
let filtered = [...metrics];
|
|
329
|
+
const wantsHints = filter?.types?.includes("hint" /* Hint */) ?? false;
|
|
330
|
+
if (!wantsHints) {
|
|
331
|
+
filtered = filtered.filter((m) => m.type !== "hint" /* Hint */);
|
|
332
|
+
}
|
|
333
|
+
if (!filter) return filtered;
|
|
334
|
+
if (filter.origins) {
|
|
335
|
+
filtered = filtered.filter((m) => filter.origins.includes(m.origin ?? "parser"));
|
|
336
|
+
}
|
|
337
|
+
if (filter.types) {
|
|
338
|
+
filtered = filtered.filter((m) => filter.types.includes(m.type));
|
|
339
|
+
}
|
|
340
|
+
if (filter.excludeTypes) {
|
|
341
|
+
filtered = filtered.filter((m) => !filter.excludeTypes.includes(m.type));
|
|
342
|
+
}
|
|
343
|
+
return filtered;
|
|
344
|
+
}
|
|
345
|
+
var OwnershipResolver = class {
|
|
346
|
+
resolve(metrics, filter) {
|
|
347
|
+
const filtered = applyFilter(metrics, filter);
|
|
348
|
+
const ledger = createMetricOwnershipLedger(filtered);
|
|
349
|
+
return ledger.visible();
|
|
350
|
+
}
|
|
351
|
+
resolveOne(metrics, type) {
|
|
352
|
+
return this.resolve(metrics, { types: [type] })[0];
|
|
353
|
+
}
|
|
354
|
+
resolveAll(metrics, type) {
|
|
355
|
+
const ofType = metrics.filter((m) => m.type === type);
|
|
356
|
+
if (ofType.length <= 1) return ofType;
|
|
357
|
+
return [...ofType].sort((a, b) => ownershipRank(b) - ownershipRank(a));
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/models/CodeMetadata.ts
|
|
362
|
+
var CodeMetadata = class {
|
|
363
|
+
constructor(line, startOffset, endOffset, length, columnStart = 0, columnEnd = 0, raw = "") {
|
|
364
|
+
this.line = line;
|
|
365
|
+
this.startOffset = startOffset;
|
|
366
|
+
this.endOffset = endOffset;
|
|
367
|
+
this.length = length;
|
|
368
|
+
this.columnStart = columnStart;
|
|
369
|
+
this.columnEnd = columnEnd;
|
|
370
|
+
this.raw = raw;
|
|
371
|
+
}
|
|
372
|
+
line;
|
|
373
|
+
startOffset;
|
|
374
|
+
endOffset;
|
|
375
|
+
length;
|
|
376
|
+
columnStart;
|
|
377
|
+
columnEnd;
|
|
378
|
+
raw;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/models/MetricContainer.ts
|
|
382
|
+
var MetricContainer = class _MetricContainer {
|
|
383
|
+
_metrics;
|
|
384
|
+
_indexedLength = 0;
|
|
385
|
+
_resolver = new OwnershipResolver();
|
|
386
|
+
id;
|
|
387
|
+
// ── Construction ───────────────────────────────────────────
|
|
388
|
+
constructor(metrics, id = "metrics") {
|
|
389
|
+
this.id = id;
|
|
390
|
+
if (metrics instanceof _MetricContainer) {
|
|
391
|
+
this._metrics = metrics.toArray();
|
|
392
|
+
this.syncIndexProperties();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
if (Array.isArray(metrics)) {
|
|
396
|
+
this._metrics = [...metrics];
|
|
397
|
+
} else if (metrics != null) {
|
|
398
|
+
this._metrics = [];
|
|
399
|
+
} else {
|
|
400
|
+
this._metrics = [];
|
|
401
|
+
}
|
|
402
|
+
this.syncIndexProperties();
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Create a container from an existing metric array, MetricContainer, or
|
|
406
|
+
* a deserialized plain object.
|
|
407
|
+
*/
|
|
408
|
+
static from(metrics, id = "metrics") {
|
|
409
|
+
if (metrics instanceof _MetricContainer) {
|
|
410
|
+
return metrics.clone(id);
|
|
411
|
+
}
|
|
412
|
+
if (Array.isArray(metrics)) {
|
|
413
|
+
return new _MetricContainer(metrics, id);
|
|
414
|
+
}
|
|
415
|
+
if (metrics != null && typeof metrics === "object" && "_metrics" in metrics) {
|
|
416
|
+
const raw = metrics._metrics;
|
|
417
|
+
if (Array.isArray(raw)) {
|
|
418
|
+
return new _MetricContainer(raw, id);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return _MetricContainer.empty(id);
|
|
422
|
+
}
|
|
423
|
+
/** Create an empty container. */
|
|
424
|
+
static empty(id = "metrics") {
|
|
425
|
+
return new _MetricContainer(void 0, id);
|
|
426
|
+
}
|
|
427
|
+
/** Create a defensive copy of this container. */
|
|
428
|
+
clone(id = this.id) {
|
|
429
|
+
return new _MetricContainer(this._metrics, id);
|
|
430
|
+
}
|
|
431
|
+
// ── Read ───────────────────────────────────────────────────
|
|
432
|
+
/** All metrics in insertion order. Returns a defensive copy. */
|
|
433
|
+
get all() {
|
|
434
|
+
return [...this._metrics];
|
|
435
|
+
}
|
|
436
|
+
/** Number of metrics in the container. */
|
|
437
|
+
get length() {
|
|
438
|
+
return this._metrics.length;
|
|
439
|
+
}
|
|
440
|
+
/** Alias for length */
|
|
441
|
+
get size() {
|
|
442
|
+
return this._metrics.length;
|
|
443
|
+
}
|
|
444
|
+
/** Whether the container has no metrics. */
|
|
445
|
+
get isEmpty() {
|
|
446
|
+
return this._metrics.length === 0;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get all metrics of a given MetricType / name, sorted by ownership layer
|
|
450
|
+
* (highest precedence first).
|
|
451
|
+
*/
|
|
452
|
+
getByType(type) {
|
|
453
|
+
const ofType = this._metrics.filter((m) => m.type === type || m.name === type);
|
|
454
|
+
if (ofType.length <= 1) return ofType;
|
|
455
|
+
return [...ofType].sort((a, b) => ownershipRank(b) - ownershipRank(a));
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Get the highest-precedence metric of a given type/name, or undefined.
|
|
459
|
+
*/
|
|
460
|
+
getFirst(type) {
|
|
461
|
+
const byType = this.getByType(type);
|
|
462
|
+
return byType.length > 0 ? byType[0] : void 0;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Lookup a metric by type or name.
|
|
466
|
+
*/
|
|
467
|
+
get(type) {
|
|
468
|
+
return this.getFirst(type);
|
|
469
|
+
}
|
|
470
|
+
/** Whether any metric of the given type/name exists. */
|
|
471
|
+
has(type) {
|
|
472
|
+
return this._metrics.some((m) => m.type === type || m.name === type);
|
|
473
|
+
}
|
|
474
|
+
hasMetric(type) {
|
|
475
|
+
return this.has(type);
|
|
476
|
+
}
|
|
477
|
+
getDisplayMetrics(filter) {
|
|
478
|
+
return this.resolve(filter);
|
|
479
|
+
}
|
|
480
|
+
getMetric(type) {
|
|
481
|
+
return this._resolver.resolveOne(this._metrics, type);
|
|
482
|
+
}
|
|
483
|
+
getAllMetricsByType(type) {
|
|
484
|
+
return this.getByType(type);
|
|
485
|
+
}
|
|
486
|
+
get rawMetrics() {
|
|
487
|
+
return this.toArray();
|
|
488
|
+
}
|
|
489
|
+
getAll() {
|
|
490
|
+
return this.toArray();
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Get all metrics matching the given origin.
|
|
494
|
+
*/
|
|
495
|
+
getByOrigin(origin) {
|
|
496
|
+
return this._metrics.filter((m) => (m.origin ?? "parser") === origin);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Apply the standard precedence resolution algorithm.
|
|
500
|
+
*/
|
|
501
|
+
resolve(filter) {
|
|
502
|
+
return this._resolver.resolve(this._metrics, filter);
|
|
503
|
+
}
|
|
504
|
+
// ── Write (mutating, returns `this` for chaining) ──────────
|
|
505
|
+
/** Append one or more metrics. */
|
|
506
|
+
add(...metrics) {
|
|
507
|
+
this._metrics.push(...metrics);
|
|
508
|
+
this.syncIndexProperties();
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Remove metrics matching a predicate.
|
|
513
|
+
* Returns the removed metrics.
|
|
514
|
+
*/
|
|
515
|
+
remove(predicate) {
|
|
516
|
+
const removed = [];
|
|
517
|
+
this._metrics = this._metrics.filter((m) => {
|
|
518
|
+
if (predicate(m)) {
|
|
519
|
+
removed.push(m);
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
return true;
|
|
523
|
+
});
|
|
524
|
+
this.syncIndexProperties();
|
|
525
|
+
return removed;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Remove all metrics of a given type.
|
|
529
|
+
* Returns the removed metrics.
|
|
530
|
+
*/
|
|
531
|
+
removeByType(type) {
|
|
532
|
+
return this.remove((m) => m.type === type || m.name === type);
|
|
533
|
+
}
|
|
534
|
+
/** Remove all metrics. */
|
|
535
|
+
clear() {
|
|
536
|
+
this._metrics = [];
|
|
537
|
+
this.syncIndexProperties();
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Replace all metrics of a given type with new ones.
|
|
542
|
+
*/
|
|
543
|
+
replaceByType(type, ...replacements) {
|
|
544
|
+
const removed = this.removeByType(type);
|
|
545
|
+
this._metrics.push(...replacements);
|
|
546
|
+
this.syncIndexProperties();
|
|
547
|
+
return removed;
|
|
548
|
+
}
|
|
549
|
+
// ── Merge ──────────────────────────────────────────────────
|
|
550
|
+
/**
|
|
551
|
+
* Merge another container (or raw array) into this one.
|
|
552
|
+
*/
|
|
553
|
+
merge(other) {
|
|
554
|
+
const incoming = other instanceof _MetricContainer ? other._metrics : Array.isArray(other) ? other : other.metrics.toArray();
|
|
555
|
+
const byType = /* @__PURE__ */ new Map();
|
|
556
|
+
for (const m of incoming) {
|
|
557
|
+
const group = byType.get(m.type) ?? [];
|
|
558
|
+
group.push(m);
|
|
559
|
+
byType.set(m.type, group);
|
|
560
|
+
}
|
|
561
|
+
for (const [type, incomingGroup] of byType) {
|
|
562
|
+
const existing = this._metrics.filter((m) => m.type === type);
|
|
563
|
+
if (existing.length === 0) {
|
|
564
|
+
this._metrics.push(...incomingGroup);
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
const existingRank = Math.max(...existing.map((m) => ownershipRank(m)));
|
|
568
|
+
const incomingRank = Math.max(...incomingGroup.map((m) => ownershipRank(m)));
|
|
569
|
+
if (incomingRank > existingRank) {
|
|
570
|
+
this._metrics = this._metrics.filter((m) => m.type !== type);
|
|
571
|
+
this._metrics.push(...incomingGroup);
|
|
572
|
+
} else if (incomingRank === existingRank) {
|
|
573
|
+
this._metrics.push(...incomingGroup);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
this.syncIndexProperties();
|
|
577
|
+
return this;
|
|
578
|
+
}
|
|
579
|
+
// ── Iteration / Projection ─────────────────────────────────
|
|
580
|
+
/** Iterate over all metrics. */
|
|
581
|
+
[Symbol.iterator]() {
|
|
582
|
+
return this._metrics[Symbol.iterator]();
|
|
583
|
+
}
|
|
584
|
+
/** Filter metrics, returning a new array. */
|
|
585
|
+
filter(predicate) {
|
|
586
|
+
return this._metrics.filter(predicate);
|
|
587
|
+
}
|
|
588
|
+
/** Map metrics to a new array. */
|
|
589
|
+
map(fn) {
|
|
590
|
+
return this._metrics.map(fn);
|
|
591
|
+
}
|
|
592
|
+
/** Flat-map metrics to a new array. */
|
|
593
|
+
flatMap(fn) {
|
|
594
|
+
return this._metrics.flatMap(fn);
|
|
595
|
+
}
|
|
596
|
+
/** Find the first metric matching a predicate. */
|
|
597
|
+
find(predicate) {
|
|
598
|
+
return this._metrics.find(predicate);
|
|
599
|
+
}
|
|
600
|
+
/** Whether any metric matches the predicate. */
|
|
601
|
+
some(predicate) {
|
|
602
|
+
return this._metrics.some(predicate);
|
|
603
|
+
}
|
|
604
|
+
/** Whether all metrics match the predicate. */
|
|
605
|
+
every(predicate) {
|
|
606
|
+
return this._metrics.every(predicate);
|
|
607
|
+
}
|
|
608
|
+
// ── Conversion ─────────────────────────────────────────────
|
|
609
|
+
/** Return a mutable copy of the underlying array. */
|
|
610
|
+
toArray() {
|
|
611
|
+
return [...this._metrics];
|
|
612
|
+
}
|
|
613
|
+
/** Spread-friendly: returns the underlying array (no copy). */
|
|
614
|
+
get raw() {
|
|
615
|
+
return this._metrics;
|
|
616
|
+
}
|
|
617
|
+
syncIndexProperties() {
|
|
618
|
+
for (let i = 0; i < this._indexedLength; i++) {
|
|
619
|
+
delete this[i];
|
|
620
|
+
}
|
|
621
|
+
for (let i = 0; i < this._metrics.length; i++) {
|
|
622
|
+
this[i] = this._metrics[i];
|
|
623
|
+
}
|
|
624
|
+
this._indexedLength = this._metrics.length;
|
|
625
|
+
}
|
|
626
|
+
toString() {
|
|
627
|
+
return `MetricContainer(${this._metrics.length})[${this._metrics.map((m) => m.type).join(", ")}]`;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// src/models/CodeStatement.ts
|
|
632
|
+
var CodeStatement = class {
|
|
633
|
+
exerciseId;
|
|
634
|
+
line;
|
|
635
|
+
text;
|
|
636
|
+
raw;
|
|
637
|
+
dialect;
|
|
638
|
+
// ── IMetricSource ─────────────────────────────────────────────
|
|
639
|
+
hasMetric(type) {
|
|
640
|
+
return this.metricContainer.hasMetric(type);
|
|
641
|
+
}
|
|
642
|
+
getDisplayMetrics(filter) {
|
|
643
|
+
return this.metricContainer.getDisplayMetrics(filter);
|
|
644
|
+
}
|
|
645
|
+
getMetric(type) {
|
|
646
|
+
return this.metricContainer.getMetric(type);
|
|
647
|
+
}
|
|
648
|
+
getAllMetricsByType(type) {
|
|
649
|
+
return this.metricContainer.getAllMetricsByType(type);
|
|
650
|
+
}
|
|
651
|
+
get rawMetrics() {
|
|
652
|
+
return this.metricContainer.rawMetrics;
|
|
653
|
+
}
|
|
654
|
+
get metricContainer() {
|
|
655
|
+
return this.metrics instanceof MetricContainer ? this.metrics : MetricContainer.from(this.metrics, this.id);
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
var ParsedCodeStatement = class extends CodeStatement {
|
|
659
|
+
id = 0;
|
|
660
|
+
parent;
|
|
661
|
+
children = [];
|
|
662
|
+
meta = new CodeMetadata(0, 0, 0, 0);
|
|
663
|
+
_metrics = MetricContainer.empty();
|
|
664
|
+
metricMeta = /* @__PURE__ */ new Map();
|
|
665
|
+
isLeaf;
|
|
666
|
+
get metrics() {
|
|
667
|
+
return this._metrics;
|
|
668
|
+
}
|
|
669
|
+
set metrics(metrics) {
|
|
670
|
+
this._metrics = MetricContainer.from(metrics, this.id);
|
|
671
|
+
}
|
|
672
|
+
constructor(init) {
|
|
673
|
+
super();
|
|
674
|
+
if (init) {
|
|
675
|
+
Object.assign(this, init);
|
|
676
|
+
if (init.metrics) {
|
|
677
|
+
this.metrics = MetricContainer.from(init.metrics, this.id);
|
|
678
|
+
}
|
|
679
|
+
if (!this.metricMeta) {
|
|
680
|
+
this.metricMeta = /* @__PURE__ */ new Map();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// src/models/OutputStatement.ts
|
|
687
|
+
var OutputStatement = class _OutputStatement {
|
|
688
|
+
static nextId = 1e6;
|
|
689
|
+
id;
|
|
690
|
+
outputType;
|
|
691
|
+
timeSpan;
|
|
692
|
+
sourceStatementId;
|
|
693
|
+
sourceBlockKey;
|
|
694
|
+
stackLevel;
|
|
695
|
+
metrics;
|
|
696
|
+
metricMeta;
|
|
697
|
+
completionReason;
|
|
698
|
+
parent;
|
|
699
|
+
children;
|
|
700
|
+
isLeaf;
|
|
701
|
+
meta;
|
|
702
|
+
statement;
|
|
703
|
+
timestamp;
|
|
704
|
+
constructor(optionsOrStatement, metricsArg, timestamp) {
|
|
705
|
+
if (optionsOrStatement && "outputType" in optionsOrStatement && "timeSpan" in optionsOrStatement) {
|
|
706
|
+
const options = optionsOrStatement;
|
|
707
|
+
this.id = _OutputStatement.nextId++;
|
|
708
|
+
this.outputType = options.outputType;
|
|
709
|
+
this.timeSpan = options.timeSpan;
|
|
710
|
+
this.sourceBlockKey = options.sourceBlockKey ?? "";
|
|
711
|
+
this.sourceStatementId = options.sourceStatementId;
|
|
712
|
+
this.stackLevel = options.stackLevel ?? 0;
|
|
713
|
+
this.metrics = MetricContainer.from(options.metrics, this.id);
|
|
714
|
+
this.metricMeta = options.metricMeta ?? /* @__PURE__ */ new Map();
|
|
715
|
+
this.completionReason = options.completionReason;
|
|
716
|
+
this.parent = options.parent;
|
|
717
|
+
this.children = options.children ?? [];
|
|
718
|
+
this.isLeaf = this.children.length === 0;
|
|
719
|
+
this.meta = new CodeMetadata(0, 0, 0, 0);
|
|
720
|
+
} else {
|
|
721
|
+
const stmt = optionsOrStatement;
|
|
722
|
+
this.id = _OutputStatement.nextId++;
|
|
723
|
+
this.statement = stmt;
|
|
724
|
+
this.outputType = "segment";
|
|
725
|
+
this.timeSpan = { started: timestamp ?? Date.now() };
|
|
726
|
+
this.sourceBlockKey = "";
|
|
727
|
+
this.sourceStatementId = typeof stmt?.id === "number" ? stmt.id : void 0;
|
|
728
|
+
this.stackLevel = 0;
|
|
729
|
+
this.metrics = MetricContainer.from(metricsArg, this.id);
|
|
730
|
+
this.metricMeta = /* @__PURE__ */ new Map();
|
|
731
|
+
this.children = [];
|
|
732
|
+
this.isLeaf = true;
|
|
733
|
+
this.meta = stmt?.meta ?? new CodeMetadata(0, 0, 0, 0);
|
|
734
|
+
this.timestamp = timestamp ?? Date.now();
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
// ═══════════════════════════════════════════════════════════════
|
|
738
|
+
// IMetricSource implementation
|
|
739
|
+
// ═══════════════════════════════════════════════════════════════
|
|
740
|
+
getDisplayMetrics(filter) {
|
|
741
|
+
return this.metrics.getDisplayMetrics(filter);
|
|
742
|
+
}
|
|
743
|
+
getMetric(type) {
|
|
744
|
+
return this.metrics.getMetric(type);
|
|
745
|
+
}
|
|
746
|
+
getAllMetricsByType(type) {
|
|
747
|
+
return this.metrics.getAllMetricsByType(type);
|
|
748
|
+
}
|
|
749
|
+
hasMetric(type) {
|
|
750
|
+
return this.metrics.hasMetric(type);
|
|
751
|
+
}
|
|
752
|
+
get rawMetrics() {
|
|
753
|
+
return this.metrics.rawMetrics;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Reset the ID counter (useful for testing)
|
|
757
|
+
*/
|
|
758
|
+
static resetIdCounter(startAt = 1e6) {
|
|
759
|
+
_OutputStatement.nextId = startAt;
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// src/models/Duration.ts
|
|
764
|
+
var Duration = class {
|
|
765
|
+
constructor(original) {
|
|
766
|
+
this.original = original;
|
|
767
|
+
let remaining = original ?? 0;
|
|
768
|
+
this.days = Math.floor(remaining / 864e5);
|
|
769
|
+
remaining %= 864e5;
|
|
770
|
+
this.hours = Math.floor(remaining / 36e5);
|
|
771
|
+
remaining %= 36e5;
|
|
772
|
+
this.minutes = Math.floor(remaining / 6e4);
|
|
773
|
+
remaining %= 6e4;
|
|
774
|
+
this.seconds = Math.floor(remaining / 1e3);
|
|
775
|
+
this.milliseconds = Math.round(remaining - this.seconds * 1e3);
|
|
776
|
+
}
|
|
777
|
+
original;
|
|
778
|
+
days;
|
|
779
|
+
hours;
|
|
780
|
+
minutes;
|
|
781
|
+
seconds;
|
|
782
|
+
milliseconds;
|
|
783
|
+
};
|
|
784
|
+
var SpanDuration = class extends Duration {
|
|
785
|
+
constructor(spans) {
|
|
786
|
+
const total = spans.reduce((sum, span) => {
|
|
787
|
+
const end = span.ended ?? Date.now();
|
|
788
|
+
return sum + Math.max(0, end - span.started);
|
|
789
|
+
}, 0);
|
|
790
|
+
super(total);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
// src/models/BlockKey.ts
|
|
795
|
+
var BlockKey = class {
|
|
796
|
+
value;
|
|
797
|
+
constructor(value) {
|
|
798
|
+
this.value = value ?? (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2));
|
|
799
|
+
}
|
|
800
|
+
toString() {
|
|
801
|
+
return this.value;
|
|
802
|
+
}
|
|
803
|
+
valueOf() {
|
|
804
|
+
return this.value;
|
|
805
|
+
}
|
|
806
|
+
equals(other) {
|
|
807
|
+
return this.value === other?.value;
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/types/section.ts
|
|
812
|
+
var VALID_FENCE_DIALECTS = ["time", "log"];
|
|
813
|
+
function runAffordance(baseTag) {
|
|
814
|
+
if (baseTag === "time") return "run";
|
|
815
|
+
if (baseTag === "log") return "log";
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
function isWorkoutSectionType(type) {
|
|
819
|
+
return type === "time" || type === "log";
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// src/types/notebook.ts
|
|
823
|
+
var NOTEBOOK_TAG_PREFIX = "notebook:";
|
|
824
|
+
var toNotebookTag = (notebookId) => `${NOTEBOOK_TAG_PREFIX}${notebookId}`;
|
|
825
|
+
var fromNotebookTag = (tag) => tag.startsWith(NOTEBOOK_TAG_PREFIX) ? tag.slice(NOTEBOOK_TAG_PREFIX.length) : null;
|
|
826
|
+
var isNotebookTag = (tag) => tag.startsWith(NOTEBOOK_TAG_PREFIX);
|
|
827
|
+
|
|
828
|
+
export { BlockKey, CodeMetadata, CodeStatement, Duration, LEGACY_ORIGIN_TO_OWNERSHIP_LAYER, METRIC_OWNERSHIP_LAYER_CHAIN, Metric, MetricContainer, MetricType, NOTEBOOK_TAG_PREFIX, OutputStatement, OwnershipResolver, ParsedCodeStatement, SpanDuration, VALID_FENCE_DIALECTS, createMetricOwnershipLedger, fromNotebookTag, getMetricOwnershipLayer, isNotebookTag, isWorkoutSectionType, ownershipRank, runAffordance, toNotebookTag };
|
|
829
|
+
//# sourceMappingURL=index.mjs.map
|
|
830
|
+
//# sourceMappingURL=index.mjs.map
|