@bitcobblers/wod-wiki-engine 0.11.43 → 0.35.2116
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/bin/wod.cjs +2761 -232
- package/dist/bin/wod.cjs.map +1 -1
- package/dist/bin/wod.mjs +7 -1
- package/dist/bin/wod.mjs.map +1 -1
- package/dist/chunk-6L5ZDZJ7.mjs +15 -0
- package/dist/chunk-6L5ZDZJ7.mjs.map +1 -0
- package/dist/chunk-B2OYLNEZ.mjs +2722 -0
- package/dist/chunk-B2OYLNEZ.mjs.map +1 -0
- package/dist/compiler-KTBR6OEL.mjs +787 -0
- package/dist/compiler-KTBR6OEL.mjs.map +1 -0
- package/dist/index.cjs +2816 -251
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -29
- package/dist/index.d.ts +146 -29
- package/dist/index.mjs +15 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -4
- package/dist/chunk-QFWJKM45.mjs +0 -1041
- package/dist/chunk-QFWJKM45.mjs.map +0 -1
package/dist/chunk-QFWJKM45.mjs
DELETED
|
@@ -1,1041 +0,0 @@
|
|
|
1
|
-
import { dialectRegistry, realtimeProcessorRegistry, summaryProcessorRegistry, getHints, createParser, toStoredOutputStatement, frozenNow, createCompiler, createMockClock, RuntimeStack, EventBus, ScriptRuntime, createAnalyticsEngineForBlock, StartSessionAction, NextEvent, bundledEfforts } from '@bitcobblers/wod-wiki-lang';
|
|
2
|
-
import { MetricType } from '@bitcobblers/wod-wiki-core';
|
|
3
|
-
import { parseQuery, QueryService, isFindQuery, isRowsQuery, toEventRows, toSummaryEventRows } from '@bitcobblers/wod-wiki-wql';
|
|
4
|
-
|
|
5
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
6
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
7
|
-
}) : x)(function(x) {
|
|
8
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
|
-
});
|
|
11
|
-
var activePacks = /* @__PURE__ */ new Map();
|
|
12
|
-
function defineLanguagePack(pack) {
|
|
13
|
-
return pack;
|
|
14
|
-
}
|
|
15
|
-
function registerLanguagePack(pack) {
|
|
16
|
-
const packId = pack.identity?.id ?? pack.identity?.name ?? "anonymous-pack";
|
|
17
|
-
activePacks.set(packId, pack);
|
|
18
|
-
if (pack.lang?.analyzer) {
|
|
19
|
-
const AnalyzerCtor = pack.lang.analyzer;
|
|
20
|
-
const instance = new AnalyzerCtor();
|
|
21
|
-
dialectRegistry.register(instance);
|
|
22
|
-
}
|
|
23
|
-
if (pack.lang?.analytics) {
|
|
24
|
-
for (const ProcessorCtor of pack.lang.analytics) {
|
|
25
|
-
const instance = new ProcessorCtor();
|
|
26
|
-
if ("processOutput" in instance) {
|
|
27
|
-
realtimeProcessorRegistry.register(instance);
|
|
28
|
-
}
|
|
29
|
-
if ("processSummary" in instance) {
|
|
30
|
-
summaryProcessorRegistry.register(instance);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function unregisterLanguagePack(packOrId) {
|
|
36
|
-
const packId = typeof packOrId === "string" ? packOrId : packOrId.identity?.id ?? packOrId.identity?.name ?? "anonymous-pack";
|
|
37
|
-
const pack = activePacks.get(packId);
|
|
38
|
-
activePacks.delete(packId);
|
|
39
|
-
if (pack?.lang?.analyzer) {
|
|
40
|
-
const instance = new pack.lang.analyzer();
|
|
41
|
-
dialectRegistry.unregister(instance.id);
|
|
42
|
-
} else if (typeof packOrId === "string") {
|
|
43
|
-
dialectRegistry.unregister(packOrId);
|
|
44
|
-
}
|
|
45
|
-
if (pack?.lang?.analytics) {
|
|
46
|
-
for (const ProcessorCtor of pack.lang.analytics) {
|
|
47
|
-
const instance = new ProcessorCtor();
|
|
48
|
-
if ("processOutput" in instance) {
|
|
49
|
-
realtimeProcessorRegistry.unregister(instance.id);
|
|
50
|
-
}
|
|
51
|
-
if ("processSummary" in instance) {
|
|
52
|
-
summaryProcessorRegistry.unregister(instance.id);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
function listLanguagePacks() {
|
|
58
|
-
return Array.from(activePacks.values());
|
|
59
|
-
}
|
|
60
|
-
function getRegisteredLanguagePacks() {
|
|
61
|
-
return Array.from(activePacks.values());
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// src/store.ts
|
|
65
|
-
function factRowsToEventRows(facts) {
|
|
66
|
-
return facts.map((f, i) => {
|
|
67
|
-
const metricKey = f.metricKey ?? f.type;
|
|
68
|
-
return {
|
|
69
|
-
id: `fact:${f.resultId}:${metricKey}:${i}`,
|
|
70
|
-
resultId: f.resultId,
|
|
71
|
-
noteId: f.noteId,
|
|
72
|
-
blockContentId: f.blockContentId,
|
|
73
|
-
pageId: f.pageId,
|
|
74
|
-
origin: f.origin,
|
|
75
|
-
timestamp: f.timestamp,
|
|
76
|
-
grain: f.grain === "event" ? "event" : "summary",
|
|
77
|
-
outputType: "analytics",
|
|
78
|
-
effortSlug: f.effortSlug,
|
|
79
|
-
metrics: [{
|
|
80
|
-
type: metricKey,
|
|
81
|
-
value: f.value,
|
|
82
|
-
...f.unit ? { unit: f.unit } : {},
|
|
83
|
-
metadata: {
|
|
84
|
-
canonicalKey: metricKey,
|
|
85
|
-
...f.effortSlug ? { effortSlug: f.effortSlug } : {},
|
|
86
|
-
...f.discipline ? { effortDiscipline: f.discipline } : {},
|
|
87
|
-
...f.intensityTier ? { effortIntensityTier: f.intensityTier } : {}
|
|
88
|
-
}
|
|
89
|
-
}],
|
|
90
|
-
segmentId: f.segmentId,
|
|
91
|
-
segmentVersion: f.segmentVersion
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
function inMemoryEventStore(events) {
|
|
96
|
-
const rows = [...events];
|
|
97
|
-
return {
|
|
98
|
-
getEventsByTimeRange: async (start, end) => rows.filter((r) => r.timestamp >= start && r.timestamp <= end),
|
|
99
|
-
getEventsByResult: async (resultId) => rows.filter((r) => r.resultId === resultId),
|
|
100
|
-
getEventsForNote: async (noteId) => rows.filter((r) => r.noteId === noteId),
|
|
101
|
-
getEventsByContent: async (blockContentId) => rows.filter((r) => r.blockContentId === blockContentId),
|
|
102
|
-
scanAll: async () => rows,
|
|
103
|
-
appendEvents: async (appended) => {
|
|
104
|
-
rows.push(...appended);
|
|
105
|
-
},
|
|
106
|
-
finalizeSummaries: async (resultId, finals) => {
|
|
107
|
-
for (let i = rows.length - 1; i >= 0; i--) {
|
|
108
|
-
const row = rows[i];
|
|
109
|
-
if (row.resultId === resultId && row.grain === "summary" && row.origin !== "user") rows.splice(i, 1);
|
|
110
|
-
}
|
|
111
|
-
rows.push(...finals);
|
|
112
|
-
},
|
|
113
|
-
deleteEvents: async (ids) => {
|
|
114
|
-
const doomed = new Set(ids);
|
|
115
|
-
for (let i = rows.length - 1; i >= 0; i--) {
|
|
116
|
-
if (doomed.has(rows[i].id)) rows.splice(i, 1);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
function inMemoryEventStoreFromFacts(facts) {
|
|
122
|
-
return inMemoryEventStore(factRowsToEventRows(facts));
|
|
123
|
-
}
|
|
124
|
-
var inMemoryFactStore = inMemoryEventStoreFromFacts;
|
|
125
|
-
function createIRFile(kind, data, optionsOrSource, generatedAt) {
|
|
126
|
-
let source = "wod:headless";
|
|
127
|
-
let genAt = Date.now();
|
|
128
|
-
if (typeof optionsOrSource === "string") {
|
|
129
|
-
source = optionsOrSource;
|
|
130
|
-
if (generatedAt !== void 0) genAt = generatedAt;
|
|
131
|
-
} else if (optionsOrSource && typeof optionsOrSource === "object") {
|
|
132
|
-
if (optionsOrSource.source) source = optionsOrSource.source;
|
|
133
|
-
if (optionsOrSource.generatedAt !== void 0) genAt = optionsOrSource.generatedAt;
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
$schema: "https://wod-wiki.dev/ir/v1.json",
|
|
137
|
-
kind,
|
|
138
|
-
generatedAt: genAt,
|
|
139
|
-
source,
|
|
140
|
-
data
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function isIRFile(obj) {
|
|
144
|
-
if (!obj || typeof obj !== "object") return false;
|
|
145
|
-
const cand = obj;
|
|
146
|
-
return cand.$schema === "https://wod-wiki.dev/ir/v1.json" && typeof cand.kind === "string" && typeof cand.generatedAt === "number" && typeof cand.source === "string" && "data" in cand;
|
|
147
|
-
}
|
|
148
|
-
function statementToNode(stmt, idMap) {
|
|
149
|
-
const meta = stmt.meta;
|
|
150
|
-
const metrics = stmt.metrics.toArray().map((m) => ({
|
|
151
|
-
type: m.type,
|
|
152
|
-
...m.image !== void 0 ? { image: m.image } : {},
|
|
153
|
-
...m.value !== void 0 ? { value: m.value } : {},
|
|
154
|
-
...m.origin !== void 0 ? { origin: m.origin } : {}
|
|
155
|
-
}));
|
|
156
|
-
const raw = meta?.raw ?? "";
|
|
157
|
-
const from = meta?.startOffset ?? 0;
|
|
158
|
-
const to = meta?.endOffset ?? from + raw.length;
|
|
159
|
-
const durationFrag = stmt.metrics.find((m) => m.type === MetricType.Duration || m.type === "duration");
|
|
160
|
-
const roundsFrag = stmt.metrics.find((m) => m.type === MetricType.Rounds || m.type === "rounds");
|
|
161
|
-
const type = durationFrag ? "duration" : roundsFrag ? "rounds" : "statement";
|
|
162
|
-
const hints = getHints(stmt);
|
|
163
|
-
const childIds = (stmt.children ?? []).flat();
|
|
164
|
-
const children = childIds.map((cid) => idMap.get(cid)).filter((c) => c !== void 0).map((c) => statementToNode(c, idMap));
|
|
165
|
-
return {
|
|
166
|
-
id: stmt.id,
|
|
167
|
-
type,
|
|
168
|
-
raw,
|
|
169
|
-
from,
|
|
170
|
-
to,
|
|
171
|
-
metrics,
|
|
172
|
-
...hints.length > 0 ? { hints } : {},
|
|
173
|
-
...children.length > 0 ? { children } : {}
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
function buildStatementTree(script) {
|
|
177
|
-
const idMap = new Map(script.statements.map((s) => [s.id, s]));
|
|
178
|
-
const topLevel = script.statements.filter((s) => s.parent === void 0 || !idMap.has(s.parent));
|
|
179
|
-
if (topLevel.length === 1 && topLevel[0].children.length > 0) {
|
|
180
|
-
return statementToNode(topLevel[0], idMap);
|
|
181
|
-
}
|
|
182
|
-
return {
|
|
183
|
-
id: 0,
|
|
184
|
-
type: "root",
|
|
185
|
-
raw: script.source,
|
|
186
|
-
from: 0,
|
|
187
|
-
to: script.source.length,
|
|
188
|
-
metrics: [],
|
|
189
|
-
children: topLevel.map((s) => statementToNode(s, idMap))
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
var ParseSyntaxError = class extends Error {
|
|
193
|
-
constructor(errors, message = "Parse error in Whiteboard Script") {
|
|
194
|
-
const details = errors.map((e) => e.message).join("; ");
|
|
195
|
-
super(`${message}: ${details}`);
|
|
196
|
-
this.errors = errors;
|
|
197
|
-
this.name = "ParseSyntaxError";
|
|
198
|
-
}
|
|
199
|
-
errors;
|
|
200
|
-
};
|
|
201
|
-
function runParse(source, options = {}) {
|
|
202
|
-
const parser = createParser();
|
|
203
|
-
const script = parser.read(source, options.sport);
|
|
204
|
-
if (script.errors && script.errors.length > 0) {
|
|
205
|
-
throw new ParseSyntaxError(script.errors);
|
|
206
|
-
}
|
|
207
|
-
const tree = buildStatementTree(script);
|
|
208
|
-
return createIRFile("parse-tree", tree, { source: options.sourceLabel ?? "cli:wod parse" });
|
|
209
|
-
}
|
|
210
|
-
function buildWorkoutResults(outputs, options) {
|
|
211
|
-
return {
|
|
212
|
-
startTime: options.startTime ?? options.now.nowMs(),
|
|
213
|
-
endTime: options.now.nowMs(),
|
|
214
|
-
duration: options.elapsedTime,
|
|
215
|
-
completed: options.completed,
|
|
216
|
-
logs: outputs.map(toStoredOutputStatement)
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
async function runExecution(source, options = {}) {
|
|
220
|
-
const parser = createParser();
|
|
221
|
-
const script = parser.read(source, options.sport);
|
|
222
|
-
if (script.errors && script.errors.length > 0) {
|
|
223
|
-
throw new ParseSyntaxError(script.errors);
|
|
224
|
-
}
|
|
225
|
-
const startTime = options.startTime ?? Date.now();
|
|
226
|
-
const now = options.now ?? frozenNow(new Date(startTime));
|
|
227
|
-
const compiler = createCompiler();
|
|
228
|
-
const clock = createMockClock(new Date(startTime));
|
|
229
|
-
const stack = new RuntimeStack();
|
|
230
|
-
const eventBus = new EventBus();
|
|
231
|
-
const runtime = new ScriptRuntime(
|
|
232
|
-
script,
|
|
233
|
-
compiler,
|
|
234
|
-
{ stack, clock, eventBus },
|
|
235
|
-
{},
|
|
236
|
-
now
|
|
237
|
-
);
|
|
238
|
-
const { engine, analyticsContext } = createAnalyticsEngineForBlock({
|
|
239
|
-
id: "cli-block",
|
|
240
|
-
content: source,
|
|
241
|
-
statements: script.statements,
|
|
242
|
-
sport: options.sport,
|
|
243
|
-
version: 1,
|
|
244
|
-
startLine: 0,
|
|
245
|
-
endLine: 0,
|
|
246
|
-
dialect: "time",
|
|
247
|
-
state: "idle",
|
|
248
|
-
createdAt: Date.now()
|
|
249
|
-
});
|
|
250
|
-
runtime.analyticsContext = analyticsContext;
|
|
251
|
-
runtime.setAnalyticsEngine(engine);
|
|
252
|
-
const outputs = [];
|
|
253
|
-
runtime.subscribeToOutput((out) => {
|
|
254
|
-
outputs.push(out);
|
|
255
|
-
});
|
|
256
|
-
runtime.do(new StartSessionAction());
|
|
257
|
-
const maxSteps = options.maxIterations ?? 5e3;
|
|
258
|
-
let steps = 0;
|
|
259
|
-
while (stack.count > 0 && steps < maxSteps) {
|
|
260
|
-
runtime.handle(new NextEvent(void 0, runtime.nowProvider));
|
|
261
|
-
clock.advance(1e3);
|
|
262
|
-
steps++;
|
|
263
|
-
}
|
|
264
|
-
const workoutResults = buildWorkoutResults(outputs, {
|
|
265
|
-
startTime,
|
|
266
|
-
elapsedTime: steps * 1e3,
|
|
267
|
-
completed: stack.count === 0,
|
|
268
|
-
now
|
|
269
|
-
});
|
|
270
|
-
const logs = outputs.map(toStoredOutputStatement);
|
|
271
|
-
return createIRFile(
|
|
272
|
-
"execution-log",
|
|
273
|
-
{
|
|
274
|
-
results: workoutResults,
|
|
275
|
-
logs,
|
|
276
|
-
statements: logs
|
|
277
|
-
},
|
|
278
|
-
{ source: options.sourceLabel ?? "cli:wod run" }
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
function getFs() {
|
|
282
|
-
if (typeof globalThis.process?.versions?.node === "undefined" && typeof globalThis.Bun === "undefined") {
|
|
283
|
-
throw new Error("File system operations are only supported in Node / Bun environments");
|
|
284
|
-
}
|
|
285
|
-
return __require("fs");
|
|
286
|
-
}
|
|
287
|
-
var WqlSyntaxError = class extends Error {
|
|
288
|
-
constructor(query, syntaxError) {
|
|
289
|
-
super(`WQL Syntax Error in "${query}": ${syntaxError}`);
|
|
290
|
-
this.query = query;
|
|
291
|
-
this.syntaxError = syntaxError;
|
|
292
|
-
this.name = "WqlSyntaxError";
|
|
293
|
-
}
|
|
294
|
-
query;
|
|
295
|
-
syntaxError;
|
|
296
|
-
};
|
|
297
|
-
function factsFromExecutionLog(log, resultId = "stdin-result-1") {
|
|
298
|
-
const facts = [];
|
|
299
|
-
for (const s of log.logs) {
|
|
300
|
-
const started = s.timeSpan?.started ?? s.timestamp ?? Date.now();
|
|
301
|
-
for (const m of s.metrics) {
|
|
302
|
-
const mType = m.type || "metric";
|
|
303
|
-
const numVal = typeof m.value === "number" ? m.value : Number(m.value);
|
|
304
|
-
facts.push({
|
|
305
|
-
id: `fact-${s.id ?? 0}-${mType}-${facts.length + 1}`,
|
|
306
|
-
noteId: "stdin-note-1",
|
|
307
|
-
blockContentId: s.sourceBlockKey || "stdin-block-1",
|
|
308
|
-
segmentId: s.sourceBlockKey || "stdin-seg-1",
|
|
309
|
-
segmentVersion: 1,
|
|
310
|
-
resultId,
|
|
311
|
-
grain: s.outputType === "analytics" ? "summary" : "event",
|
|
312
|
-
type: mType,
|
|
313
|
-
value: Number.isNaN(numVal) ? 0 : numVal,
|
|
314
|
-
unit: m.unit,
|
|
315
|
-
label: m.label || mType,
|
|
316
|
-
metricKey: mType,
|
|
317
|
-
metricLabel: m.label || mType,
|
|
318
|
-
timestamp: started,
|
|
319
|
-
createdAt: started
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return facts;
|
|
324
|
-
}
|
|
325
|
-
function extractPayload(rawJson) {
|
|
326
|
-
try {
|
|
327
|
-
const parsed = JSON.parse(rawJson);
|
|
328
|
-
if (isIRFile(parsed)) {
|
|
329
|
-
return parsed.data;
|
|
330
|
-
}
|
|
331
|
-
return parsed;
|
|
332
|
-
} catch (err) {
|
|
333
|
-
throw new Error(`Failed to parse JSON input: ${err instanceof Error ? err.message : String(err)}`);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function buildStoresFromData(data) {
|
|
337
|
-
const identity = (r) => ({
|
|
338
|
-
noteId: r.noteId,
|
|
339
|
-
resultId: r.id,
|
|
340
|
-
segmentId: r.segmentId,
|
|
341
|
-
segmentVersion: r.segmentVersion,
|
|
342
|
-
blockContentId: r.blockContentId,
|
|
343
|
-
origin: r.origin,
|
|
344
|
-
pageId: r.pageId,
|
|
345
|
-
workoutTimestamp: r.createdAt
|
|
346
|
-
});
|
|
347
|
-
const events = [
|
|
348
|
-
...factRowsToEventRows(data.facts),
|
|
349
|
-
...data.results.flatMap((r) => {
|
|
350
|
-
const logs = r.data.logs ?? [];
|
|
351
|
-
return [...toEventRows(logs, identity(r)), ...toSummaryEventRows(logs, identity(r))];
|
|
352
|
-
})
|
|
353
|
-
];
|
|
354
|
-
const eventStore = inMemoryEventStore(events);
|
|
355
|
-
const noteStore = {
|
|
356
|
-
getAllNotes: async () => [...data.notes],
|
|
357
|
-
getNoteIdsForTag: async (tag) => {
|
|
358
|
-
const matching = /* @__PURE__ */ new Set();
|
|
359
|
-
for (const [noteId, tags] of Object.entries(data.noteTags)) {
|
|
360
|
-
if (tags.includes(tag)) matching.add(noteId);
|
|
361
|
-
}
|
|
362
|
-
return matching;
|
|
363
|
-
},
|
|
364
|
-
getNoteTagLabels: async (noteId) => [...data.noteTags[noteId] ?? []]
|
|
365
|
-
};
|
|
366
|
-
const blockStore = {
|
|
367
|
-
getAllBlocks: async () => [...data.blocks]
|
|
368
|
-
};
|
|
369
|
-
const effortStore = {
|
|
370
|
-
getAllEfforts: async () => [...data.efforts]
|
|
371
|
-
};
|
|
372
|
-
return { eventStore, noteStore, blockStore, effortStore };
|
|
373
|
-
}
|
|
374
|
-
function loadQueryData(options) {
|
|
375
|
-
const data = {
|
|
376
|
-
facts: [],
|
|
377
|
-
results: [],
|
|
378
|
-
notes: [],
|
|
379
|
-
blocks: [],
|
|
380
|
-
efforts: [...bundledEfforts],
|
|
381
|
-
noteTags: {}
|
|
382
|
-
};
|
|
383
|
-
if (options.corpusPath) {
|
|
384
|
-
const fs = getFs();
|
|
385
|
-
if (!fs.existsSync(options.corpusPath)) {
|
|
386
|
-
throw new Error(`Corpus file not found at path: ${options.corpusPath}`);
|
|
387
|
-
}
|
|
388
|
-
const raw = fs.readFileSync(options.corpusPath, "utf-8");
|
|
389
|
-
const payload = extractPayload(raw);
|
|
390
|
-
if (Array.isArray(payload)) {
|
|
391
|
-
data.facts = payload;
|
|
392
|
-
} else if (payload && typeof payload === "object") {
|
|
393
|
-
const corpus = payload;
|
|
394
|
-
if (corpus.facts) data.facts = corpus.facts;
|
|
395
|
-
if (corpus.results) data.results = corpus.results;
|
|
396
|
-
if (corpus.notes) data.notes = corpus.notes;
|
|
397
|
-
if (corpus.blocks) data.blocks = corpus.blocks;
|
|
398
|
-
if (corpus.efforts) data.efforts = corpus.efforts;
|
|
399
|
-
if (corpus.tags) data.noteTags = corpus.tags;
|
|
400
|
-
if (corpus.logs && (!corpus.results || corpus.results.length === 0)) {
|
|
401
|
-
const syntheticResult = {
|
|
402
|
-
id: "corpus-result-1",
|
|
403
|
-
noteId: "corpus-note-1",
|
|
404
|
-
blockContentId: "corpus-block-1",
|
|
405
|
-
origin: "journal",
|
|
406
|
-
createdAt: corpus.logs[0]?.timeSpan?.started ?? Date.now(),
|
|
407
|
-
data: {
|
|
408
|
-
startTime: corpus.logs[0]?.timeSpan?.started ?? Date.now(),
|
|
409
|
-
endTime: corpus.logs[corpus.logs.length - 1]?.timeSpan?.ended ?? Date.now(),
|
|
410
|
-
duration: 0,
|
|
411
|
-
completed: true,
|
|
412
|
-
logs: corpus.logs
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
data.results = [syntheticResult];
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
} else if (options.stdinLog) {
|
|
419
|
-
const payload = extractPayload(options.stdinLog);
|
|
420
|
-
let executionLog;
|
|
421
|
-
const rawLogs = payload.logs ?? payload.statements ?? [];
|
|
422
|
-
if ("results" in payload) {
|
|
423
|
-
executionLog = { results: payload.results, logs: rawLogs, statements: rawLogs };
|
|
424
|
-
} else {
|
|
425
|
-
executionLog = { results: payload, logs: rawLogs, statements: rawLogs };
|
|
426
|
-
}
|
|
427
|
-
const resultId = "stdin-result-1";
|
|
428
|
-
const workoutResult = {
|
|
429
|
-
id: resultId,
|
|
430
|
-
noteId: "stdin-note-1",
|
|
431
|
-
blockContentId: "stdin-block-1",
|
|
432
|
-
origin: "journal",
|
|
433
|
-
createdAt: executionLog.results.startTime,
|
|
434
|
-
data: {
|
|
435
|
-
...executionLog.results,
|
|
436
|
-
logs: executionLog.logs
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
data.results = [workoutResult];
|
|
440
|
-
data.facts = factsFromExecutionLog(executionLog, resultId);
|
|
441
|
-
} else if (options.stdinFacts) {
|
|
442
|
-
const payload = extractPayload(options.stdinFacts);
|
|
443
|
-
if (Array.isArray(payload)) {
|
|
444
|
-
data.facts = payload;
|
|
445
|
-
} else if (payload && typeof payload === "object" && "facts" in payload) {
|
|
446
|
-
data.facts = payload.facts;
|
|
447
|
-
}
|
|
448
|
-
} else {
|
|
449
|
-
throw new Error("No input dataset provided. Specify --corpus <path>, --stdin-log, or --stdin-facts.");
|
|
450
|
-
}
|
|
451
|
-
return data;
|
|
452
|
-
}
|
|
453
|
-
async function runQueryCli(wqlString, options = {}) {
|
|
454
|
-
const parsed = parseQuery(wqlString);
|
|
455
|
-
if (parsed.error) {
|
|
456
|
-
throw new WqlSyntaxError(wqlString, parsed.error);
|
|
457
|
-
}
|
|
458
|
-
const stores = buildStoresFromData(loadQueryData(options));
|
|
459
|
-
const service = new QueryService(
|
|
460
|
-
stores.eventStore,
|
|
461
|
-
stores.noteStore,
|
|
462
|
-
stores.blockStore,
|
|
463
|
-
stores.effortStore
|
|
464
|
-
);
|
|
465
|
-
if (isFindQuery(parsed)) {
|
|
466
|
-
const result2 = await service.runFind(parsed);
|
|
467
|
-
return createIRFile("find-result", result2, { source: options.sourceLabel ?? "cli:wod query" });
|
|
468
|
-
}
|
|
469
|
-
if (isRowsQuery(parsed)) {
|
|
470
|
-
const result2 = await service.runRows(parsed);
|
|
471
|
-
return createIRFile("rows-result", result2, { source: options.sourceLabel ?? "cli:wod query" });
|
|
472
|
-
}
|
|
473
|
-
const result = await service.runQuery(wqlString, {
|
|
474
|
-
preferredUnit: options.preferredUnit
|
|
475
|
-
});
|
|
476
|
-
return createIRFile("query-result", result, { source: options.sourceLabel ?? "cli:wod query" });
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// src/cli/loader.ts
|
|
480
|
-
function getPathUtils() {
|
|
481
|
-
if (typeof globalThis.process?.versions?.node === "undefined" && typeof globalThis.Bun === "undefined") {
|
|
482
|
-
throw new Error("Path resolution is only supported in Node / Bun environments");
|
|
483
|
-
}
|
|
484
|
-
return {
|
|
485
|
-
resolve: __require("path").resolve,
|
|
486
|
-
pathToFileURL: __require("url").pathToFileURL
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
var PackLoadError = class extends Error {
|
|
490
|
-
constructor(spec, originalError) {
|
|
491
|
-
const msg = originalError instanceof Error ? originalError.message : String(originalError);
|
|
492
|
-
super(`Failed to load Language Pack "${spec}": ${msg}`);
|
|
493
|
-
this.spec = spec;
|
|
494
|
-
this.originalError = originalError;
|
|
495
|
-
this.name = "PackLoadError";
|
|
496
|
-
}
|
|
497
|
-
spec;
|
|
498
|
-
originalError;
|
|
499
|
-
};
|
|
500
|
-
async function loadLanguagePack(moduleSpec) {
|
|
501
|
-
try {
|
|
502
|
-
let importPath = moduleSpec;
|
|
503
|
-
if (moduleSpec.startsWith(".") || moduleSpec.startsWith("/") || moduleSpec.includes("\\") || moduleSpec.endsWith(".ts") || moduleSpec.endsWith(".js") || moduleSpec.endsWith(".mjs")) {
|
|
504
|
-
const { resolve, pathToFileURL } = getPathUtils();
|
|
505
|
-
const resolved = resolve(process.cwd(), moduleSpec);
|
|
506
|
-
importPath = pathToFileURL(resolved).href;
|
|
507
|
-
}
|
|
508
|
-
const imported = await import(
|
|
509
|
-
/* @vite-ignore */
|
|
510
|
-
importPath
|
|
511
|
-
);
|
|
512
|
-
const candidate = imported.default ?? imported.pack ?? imported;
|
|
513
|
-
if (!candidate || typeof candidate !== "object") {
|
|
514
|
-
throw new Error(`Module "${moduleSpec}" does not export a valid LanguagePack object`);
|
|
515
|
-
}
|
|
516
|
-
const pack = candidate;
|
|
517
|
-
registerLanguagePack(pack);
|
|
518
|
-
return pack;
|
|
519
|
-
} catch (error) {
|
|
520
|
-
if (error instanceof PackLoadError) throw error;
|
|
521
|
-
throw new PackLoadError(moduleSpec, error);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// src/cli/formatters.ts
|
|
526
|
-
function escapeCsvField(field) {
|
|
527
|
-
if (field === void 0 || field === null) return "";
|
|
528
|
-
const str = String(field);
|
|
529
|
-
if (str.includes(",") || str.includes('"') || str.includes("\n") || str.includes("\r")) {
|
|
530
|
-
return `"${str.replace(/"/g, '""')}"`;
|
|
531
|
-
}
|
|
532
|
-
return str;
|
|
533
|
-
}
|
|
534
|
-
function formatDate(ts) {
|
|
535
|
-
if (!ts) return "-";
|
|
536
|
-
try {
|
|
537
|
-
return new Date(ts).toISOString();
|
|
538
|
-
} catch {
|
|
539
|
-
return String(ts);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function formatMetricValue(m) {
|
|
543
|
-
if (m.image) return m.image;
|
|
544
|
-
if (m.value === void 0 || m.value === null) return "";
|
|
545
|
-
if (typeof m.value === "object") return JSON.stringify(m.value);
|
|
546
|
-
return String(m.value);
|
|
547
|
-
}
|
|
548
|
-
function formatMetric(m) {
|
|
549
|
-
const t = m.type || "metric";
|
|
550
|
-
return `${t}:${formatMetricValue(m)}`;
|
|
551
|
-
}
|
|
552
|
-
function formatParseOutput(ir, format, pretty = true) {
|
|
553
|
-
if (format === "json") {
|
|
554
|
-
return JSON.stringify(ir, null, pretty ? 2 : void 0);
|
|
555
|
-
}
|
|
556
|
-
const flatNodes = [];
|
|
557
|
-
const walk = (node) => {
|
|
558
|
-
flatNodes.push(node);
|
|
559
|
-
for (const child of node.children ?? []) {
|
|
560
|
-
walk(child);
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
walk(ir.data);
|
|
564
|
-
if (format === "csv") {
|
|
565
|
-
const header = "id,type,from,to,raw,metrics,hints";
|
|
566
|
-
const rows = flatNodes.map(
|
|
567
|
-
(n) => [
|
|
568
|
-
n.id,
|
|
569
|
-
n.type,
|
|
570
|
-
n.from,
|
|
571
|
-
n.to,
|
|
572
|
-
escapeCsvField(n.raw),
|
|
573
|
-
escapeCsvField(n.metrics.map(formatMetric).join("; ")),
|
|
574
|
-
escapeCsvField(n.hints?.join("; ") ?? "")
|
|
575
|
-
].join(",")
|
|
576
|
-
);
|
|
577
|
-
return [header, ...rows].join("\n");
|
|
578
|
-
}
|
|
579
|
-
const lines = [];
|
|
580
|
-
lines.push(`Parse Tree (${flatNodes.length} statements):`);
|
|
581
|
-
lines.push("\u2500".repeat(80));
|
|
582
|
-
lines.push(
|
|
583
|
-
["ID".padEnd(6), "Type".padEnd(12), "Span".padEnd(12), "Hints".padEnd(20), "Raw Text"].join(" | ")
|
|
584
|
-
);
|
|
585
|
-
lines.push("\u2500".repeat(80));
|
|
586
|
-
for (const n of flatNodes) {
|
|
587
|
-
const span = `${n.from}-${n.to}`;
|
|
588
|
-
const hints = (n.hints ?? []).join(", ");
|
|
589
|
-
lines.push(
|
|
590
|
-
[
|
|
591
|
-
String(n.id).padEnd(6),
|
|
592
|
-
n.type.padEnd(12),
|
|
593
|
-
span.padEnd(12),
|
|
594
|
-
(hints.length > 18 ? hints.slice(0, 15) + "..." : hints).padEnd(20),
|
|
595
|
-
n.raw.replace(/\n/g, "\\n")
|
|
596
|
-
].join(" | ")
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
return lines.join("\n");
|
|
600
|
-
}
|
|
601
|
-
function formatExecutionOutput(ir, format, pretty = true) {
|
|
602
|
-
if (format === "json") {
|
|
603
|
-
return JSON.stringify(ir, null, pretty ? 2 : void 0);
|
|
604
|
-
}
|
|
605
|
-
const { results, logs } = ir.data;
|
|
606
|
-
const statements = logs;
|
|
607
|
-
if (format === "csv") {
|
|
608
|
-
const header = "id,outputType,started,ended,sourceBlockKey,stackLevel,completionReason,metrics,hints";
|
|
609
|
-
const rows = statements.map(
|
|
610
|
-
(s) => [
|
|
611
|
-
s.id,
|
|
612
|
-
s.outputType,
|
|
613
|
-
s.timeSpan?.started ?? s.timestamp ?? 0,
|
|
614
|
-
s.timeSpan?.ended ?? "",
|
|
615
|
-
escapeCsvField(s.sourceBlockKey),
|
|
616
|
-
s.stackLevel,
|
|
617
|
-
escapeCsvField(s.completionReason ?? ""),
|
|
618
|
-
escapeCsvField(s.metrics.map(formatMetric).join("; ")),
|
|
619
|
-
escapeCsvField(s.hints?.join("; ") ?? "")
|
|
620
|
-
].join(",")
|
|
621
|
-
);
|
|
622
|
-
return [header, ...rows].join("\n");
|
|
623
|
-
}
|
|
624
|
-
const lines = [];
|
|
625
|
-
lines.push(`Workout Execution Results:`);
|
|
626
|
-
lines.push(` Duration: ${results.duration}ms`);
|
|
627
|
-
lines.push(` Completed: ${results.completed ? "yes" : "no"}`);
|
|
628
|
-
lines.push(` Start: ${formatDate(results.startTime)}`);
|
|
629
|
-
lines.push(` End: ${formatDate(results.endTime)}`);
|
|
630
|
-
lines.push(` Logs: ${statements.length} statements emitted`);
|
|
631
|
-
lines.push("\u2500".repeat(80));
|
|
632
|
-
lines.push(
|
|
633
|
-
["ID".padEnd(8), "Type".padEnd(12), "Block".padEnd(18), "Reason".padEnd(15), "Metrics"].join(" | ")
|
|
634
|
-
);
|
|
635
|
-
lines.push("\u2500".repeat(80));
|
|
636
|
-
for (const s of statements) {
|
|
637
|
-
const metricsStr = s.metrics.map(formatMetric).join(", ");
|
|
638
|
-
lines.push(
|
|
639
|
-
[
|
|
640
|
-
String(s.id).padEnd(8),
|
|
641
|
-
(s.outputType ?? "segment").padEnd(12),
|
|
642
|
-
(s.sourceBlockKey ?? "-").padEnd(18),
|
|
643
|
-
(s.completionReason ?? "-").padEnd(15),
|
|
644
|
-
metricsStr
|
|
645
|
-
].join(" | ")
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
return lines.join("\n");
|
|
649
|
-
}
|
|
650
|
-
function formatQueryOutput(ir, format, pretty = true) {
|
|
651
|
-
if (format === "json") {
|
|
652
|
-
return JSON.stringify(ir, null, pretty ? 2 : void 0);
|
|
653
|
-
}
|
|
654
|
-
const kind = ir.kind;
|
|
655
|
-
if (kind === "rows-result") {
|
|
656
|
-
const data2 = ir.data;
|
|
657
|
-
if (format === "csv") {
|
|
658
|
-
const header = "resultId,timestamp,outputType,eventId,metrics";
|
|
659
|
-
const rows = [];
|
|
660
|
-
for (const run of data2.runs) {
|
|
661
|
-
for (const event of run.events) {
|
|
662
|
-
rows.push(
|
|
663
|
-
[
|
|
664
|
-
run.resultId,
|
|
665
|
-
run.timestamp,
|
|
666
|
-
event.outputType,
|
|
667
|
-
event.id,
|
|
668
|
-
escapeCsvField(event.metrics.map(formatMetric).join("; "))
|
|
669
|
-
].join(",")
|
|
670
|
-
);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
return [header, ...rows].join("\n");
|
|
674
|
-
}
|
|
675
|
-
const lines2 = [];
|
|
676
|
-
lines2.push(`Rows Query Result (${data2.runs.length} runs):`);
|
|
677
|
-
if (data2.error) lines2.push(`Error: ${data2.error}`);
|
|
678
|
-
lines2.push("\u2500".repeat(80));
|
|
679
|
-
lines2.push(
|
|
680
|
-
["Result ID".padEnd(20), "Date".padEnd(24), "Events"].join(" | ")
|
|
681
|
-
);
|
|
682
|
-
lines2.push("\u2500".repeat(80));
|
|
683
|
-
for (const run of data2.runs) {
|
|
684
|
-
lines2.push(
|
|
685
|
-
[
|
|
686
|
-
run.resultId.padEnd(20),
|
|
687
|
-
formatDate(run.timestamp).padEnd(24),
|
|
688
|
-
String(run.events.length)
|
|
689
|
-
].join(" | ")
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
return lines2.join("\n");
|
|
693
|
-
}
|
|
694
|
-
if (kind === "find-result") {
|
|
695
|
-
const data2 = ir.data;
|
|
696
|
-
if (format === "csv") {
|
|
697
|
-
const header = "target,id,title_or_label,sourceId";
|
|
698
|
-
const rows = [];
|
|
699
|
-
for (const n of data2.notes) {
|
|
700
|
-
rows.push(["note", n.id, escapeCsvField(n.title), escapeCsvField(n.sourceId ?? "")].join(","));
|
|
701
|
-
}
|
|
702
|
-
for (const b of data2.blocks) {
|
|
703
|
-
rows.push(["block", b.id, escapeCsvField(b.noteTitle), escapeCsvField(b.sourceId ?? "")].join(","));
|
|
704
|
-
}
|
|
705
|
-
for (const e of data2.efforts ?? []) {
|
|
706
|
-
rows.push(["effort", e.id, escapeCsvField(e.label), escapeCsvField(e.slug)].join(","));
|
|
707
|
-
}
|
|
708
|
-
return [header, ...rows].join("\n");
|
|
709
|
-
}
|
|
710
|
-
const lines2 = [];
|
|
711
|
-
const matchedCount2 = data2.stages?.matched ?? 0;
|
|
712
|
-
const selectedCount = data2.stages?.selected ?? 0;
|
|
713
|
-
lines2.push(`Find Result (matched: ${matchedCount2} / selected: ${selectedCount}):`);
|
|
714
|
-
lines2.push("\u2500".repeat(80));
|
|
715
|
-
lines2.push(["Type".padEnd(8), "ID".padEnd(24), "Title / Label".padEnd(30), "Source ID"].join(" | "));
|
|
716
|
-
lines2.push("\u2500".repeat(80));
|
|
717
|
-
for (const n of data2.notes) {
|
|
718
|
-
lines2.push(["note".padEnd(8), n.id.padEnd(24), n.title.padEnd(30), n.sourceId ?? "-"].join(" | "));
|
|
719
|
-
}
|
|
720
|
-
for (const b of data2.blocks) {
|
|
721
|
-
lines2.push(["block".padEnd(8), b.id.padEnd(24), b.noteTitle.padEnd(30), b.sourceId ?? "-"].join(" | "));
|
|
722
|
-
}
|
|
723
|
-
for (const e of data2.efforts ?? []) {
|
|
724
|
-
lines2.push(["effort".padEnd(8), e.id.padEnd(24), e.label.padEnd(30), e.slug].join(" | "));
|
|
725
|
-
}
|
|
726
|
-
return lines2.join("\n");
|
|
727
|
-
}
|
|
728
|
-
const data = ir.data;
|
|
729
|
-
if (data.scalar !== void 0 && data.series.length === 0) {
|
|
730
|
-
if (format === "csv") {
|
|
731
|
-
return `metric,scalar,unit
|
|
732
|
-
${escapeCsvField(data.parsed.metric)},${data.scalar},${escapeCsvField(data.unit ?? "")}`;
|
|
733
|
-
}
|
|
734
|
-
return `Scalar Result: ${data.scalar}${data.unit ? " " + data.unit : ""}`;
|
|
735
|
-
}
|
|
736
|
-
if (format === "csv") {
|
|
737
|
-
const header = "series,timestamp,date,value,unit";
|
|
738
|
-
const rows = [];
|
|
739
|
-
for (const s of data.series) {
|
|
740
|
-
for (const pt of s.points) {
|
|
741
|
-
rows.push(
|
|
742
|
-
[
|
|
743
|
-
escapeCsvField(s.label || s.key),
|
|
744
|
-
pt.ts,
|
|
745
|
-
formatDate(pt.ts),
|
|
746
|
-
pt.value,
|
|
747
|
-
escapeCsvField(s.unit ?? data.unit ?? "")
|
|
748
|
-
].join(",")
|
|
749
|
-
);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
return [header, ...rows].join("\n");
|
|
753
|
-
}
|
|
754
|
-
const lines = [];
|
|
755
|
-
const matchedCount = data.stages?.matched ?? data.stages?.selected ?? data.matched?.length ?? 0;
|
|
756
|
-
lines.push(`Query Result (${data.series.length} series, ${matchedCount} matched facts):`);
|
|
757
|
-
lines.push(
|
|
758
|
-
["Series / Group".padEnd(20), "Timestamp".padEnd(16), "Date".padEnd(24), "Value".padEnd(12), "Unit"].join(" | ")
|
|
759
|
-
);
|
|
760
|
-
lines.push("\u2500".repeat(80));
|
|
761
|
-
for (const s of data.series) {
|
|
762
|
-
for (const pt of s.points) {
|
|
763
|
-
lines.push(
|
|
764
|
-
[
|
|
765
|
-
(s.label || s.key).padEnd(20),
|
|
766
|
-
String(pt.ts).padEnd(16),
|
|
767
|
-
formatDate(pt.ts).padEnd(24),
|
|
768
|
-
String(pt.value).padEnd(12),
|
|
769
|
-
s.unit ?? data.unit ?? "-"
|
|
770
|
-
].join(" | ")
|
|
771
|
-
);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
return lines.join("\n");
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// src/cli/runner.ts
|
|
778
|
-
function getFs2() {
|
|
779
|
-
if (typeof globalThis.process?.versions?.node === "undefined" && typeof globalThis.Bun === "undefined") {
|
|
780
|
-
throw new Error("File system operations are only supported in Node / Bun environments");
|
|
781
|
-
}
|
|
782
|
-
return __require("fs");
|
|
783
|
-
}
|
|
784
|
-
var VERSION = "0.6.0";
|
|
785
|
-
function parseCliArgs(args) {
|
|
786
|
-
const result = {
|
|
787
|
-
format: "json",
|
|
788
|
-
packs: []
|
|
789
|
-
};
|
|
790
|
-
let i = 0;
|
|
791
|
-
while (i < args.length) {
|
|
792
|
-
const arg = args[i];
|
|
793
|
-
if (arg === "--help" || arg === "-h") {
|
|
794
|
-
result.help = true;
|
|
795
|
-
i++;
|
|
796
|
-
} else if (arg === "--version" || arg === "-v") {
|
|
797
|
-
result.version = true;
|
|
798
|
-
i++;
|
|
799
|
-
} else if (arg === "--corpus" || arg === "-c") {
|
|
800
|
-
result.corpus = args[++i];
|
|
801
|
-
i++;
|
|
802
|
-
} else if (arg === "--stdin-log") {
|
|
803
|
-
result.stdinLog = true;
|
|
804
|
-
i++;
|
|
805
|
-
} else if (arg === "--stdin-facts") {
|
|
806
|
-
result.stdinFacts = true;
|
|
807
|
-
i++;
|
|
808
|
-
} else if (arg === "--format") {
|
|
809
|
-
const fmt = args[++i];
|
|
810
|
-
if (fmt === "json" || fmt === "table" || fmt === "csv") {
|
|
811
|
-
result.format = fmt;
|
|
812
|
-
}
|
|
813
|
-
i++;
|
|
814
|
-
} else if (arg.startsWith("--format=")) {
|
|
815
|
-
const fmt = arg.slice("--format=".length);
|
|
816
|
-
if (fmt === "json" || fmt === "table" || fmt === "csv") {
|
|
817
|
-
result.format = fmt;
|
|
818
|
-
}
|
|
819
|
-
i++;
|
|
820
|
-
} else if (arg === "-o" || arg === "--output") {
|
|
821
|
-
result.output = args[++i];
|
|
822
|
-
i++;
|
|
823
|
-
} else if (arg.startsWith("--output=")) {
|
|
824
|
-
result.output = arg.slice("--output=".length);
|
|
825
|
-
i++;
|
|
826
|
-
} else if (arg === "--pack") {
|
|
827
|
-
const packSpec = args[++i];
|
|
828
|
-
if (packSpec) result.packs.push(packSpec);
|
|
829
|
-
i++;
|
|
830
|
-
} else if (arg.startsWith("--pack=")) {
|
|
831
|
-
result.packs.push(arg.slice("--pack=".length));
|
|
832
|
-
i++;
|
|
833
|
-
} else if (arg === "--sport") {
|
|
834
|
-
result.sport = args[++i];
|
|
835
|
-
i++;
|
|
836
|
-
} else if (arg === "--unit") {
|
|
837
|
-
result.unit = args[++i];
|
|
838
|
-
i++;
|
|
839
|
-
} else if (!result.subcommand) {
|
|
840
|
-
if (arg === "parse" || arg === "run" || arg === "query" || arg === "help" || arg === "version") {
|
|
841
|
-
result.subcommand = arg;
|
|
842
|
-
} else {
|
|
843
|
-
result.target = arg;
|
|
844
|
-
}
|
|
845
|
-
i++;
|
|
846
|
-
} else if (!result.target) {
|
|
847
|
-
result.target = arg;
|
|
848
|
-
i++;
|
|
849
|
-
} else {
|
|
850
|
-
i++;
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
return result;
|
|
854
|
-
}
|
|
855
|
-
async function readStdin() {
|
|
856
|
-
if (typeof globalThis.Bun?.stdin?.text === "function") {
|
|
857
|
-
return globalThis.Bun.stdin.text();
|
|
858
|
-
}
|
|
859
|
-
return new Promise((resolve, reject) => {
|
|
860
|
-
const chunks = [];
|
|
861
|
-
process.stdin.on("data", (chunk) => {
|
|
862
|
-
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
863
|
-
});
|
|
864
|
-
process.stdin.on("end", () => {
|
|
865
|
-
resolve(Buffer.concat(chunks).toString("utf-8"));
|
|
866
|
-
});
|
|
867
|
-
process.stdin.on("error", (err) => {
|
|
868
|
-
reject(err);
|
|
869
|
-
});
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
function getUsageText() {
|
|
873
|
-
return `
|
|
874
|
-
wod CLI \u2014 Headless Whiteboard Language & WQL Engine runner (v${VERSION})
|
|
875
|
-
|
|
876
|
-
USAGE:
|
|
877
|
-
wod parse <file | -> [options]
|
|
878
|
-
wod run <file | -> [options]
|
|
879
|
-
wod query '<wql-string>' [options]
|
|
880
|
-
|
|
881
|
-
SUBCOMMANDS:
|
|
882
|
-
parse <file | -> Parse Whiteboard Script into AST StatementNode IR (kind: 'parse-tree')
|
|
883
|
-
run <file | -> Execute Whiteboard Script state machine and emit ExecutionLog IR (kind: 'execution-log')
|
|
884
|
-
query '<wql>' Evaluate WQL query and emit QueryResult / RowsQueryResult / FindQueryResult IR
|
|
885
|
-
|
|
886
|
-
INPUT SOURCING (for 'query'):
|
|
887
|
-
-c, --corpus <path> Load historical corpus dataset from a versioned JSON file
|
|
888
|
-
--stdin-log Read kind: 'execution-log' JSON payload from stdin
|
|
889
|
-
--stdin-facts Read raw kind: 'fact-set' JSON payload from stdin
|
|
890
|
-
|
|
891
|
-
OPTIONS:
|
|
892
|
-
--format <fmt> Output format: json (default), table, csv
|
|
893
|
-
-o, --output <path> Write output to a file instead of stdout
|
|
894
|
-
--pack <module> Dynamically load and register a Language Pack (repeatable)
|
|
895
|
-
--sport <sport> Target sport fence dialect (e.g. climbing, crossfit)
|
|
896
|
-
--unit <unit> Preferred display unit for queries (e.g. lb, kg)
|
|
897
|
-
-h, --help Show help message
|
|
898
|
-
-v, --version Show version number
|
|
899
|
-
|
|
900
|
-
EXIT CODES:
|
|
901
|
-
0 - OK / Success
|
|
902
|
-
1 - General / Runtime error
|
|
903
|
-
2 - Parse / Syntax error
|
|
904
|
-
3 - Language Pack load error
|
|
905
|
-
`;
|
|
906
|
-
}
|
|
907
|
-
function printUsage(writer) {
|
|
908
|
-
const text = getUsageText();
|
|
909
|
-
if (writer) {
|
|
910
|
-
writer(text);
|
|
911
|
-
} else {
|
|
912
|
-
console.log(text);
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
async function cliMain(argv = process.argv.slice(2), io = {}) {
|
|
916
|
-
const writeOut = io.stdout ?? ((t) => process.stdout.write(t + "\n"));
|
|
917
|
-
const writeErr = io.stderr ?? ((t) => process.stderr.write(t + "\n"));
|
|
918
|
-
const getStdin = io.readStdinFn ?? readStdin;
|
|
919
|
-
const parsed = parseCliArgs(argv);
|
|
920
|
-
if (parsed.version || parsed.subcommand === "version") {
|
|
921
|
-
writeOut(`wod v${VERSION}`);
|
|
922
|
-
return 0;
|
|
923
|
-
}
|
|
924
|
-
if (parsed.help || parsed.subcommand === "help") {
|
|
925
|
-
printUsage(writeOut);
|
|
926
|
-
return 0;
|
|
927
|
-
}
|
|
928
|
-
if (!parsed.subcommand) {
|
|
929
|
-
printUsage(writeErr);
|
|
930
|
-
return 1;
|
|
931
|
-
}
|
|
932
|
-
for (const packSpec of parsed.packs) {
|
|
933
|
-
try {
|
|
934
|
-
await loadLanguagePack(packSpec);
|
|
935
|
-
} catch (err) {
|
|
936
|
-
if (err instanceof PackLoadError) {
|
|
937
|
-
writeErr(err.message);
|
|
938
|
-
} else {
|
|
939
|
-
writeErr(`[wod] Language pack load error: ${err instanceof Error ? err.message : String(err)}`);
|
|
940
|
-
}
|
|
941
|
-
return 3;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
const deliverOutput = (formattedText) => {
|
|
945
|
-
if (parsed.output) {
|
|
946
|
-
getFs2().writeFileSync(parsed.output, formattedText, "utf-8");
|
|
947
|
-
} else {
|
|
948
|
-
writeOut(formattedText);
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
try {
|
|
952
|
-
if (parsed.subcommand === "parse") {
|
|
953
|
-
if (!parsed.target) {
|
|
954
|
-
writeErr('[wod parse] Error: Missing input file or "-" for stdin');
|
|
955
|
-
return 1;
|
|
956
|
-
}
|
|
957
|
-
let source;
|
|
958
|
-
if (parsed.target === "-") {
|
|
959
|
-
source = await getStdin();
|
|
960
|
-
} else {
|
|
961
|
-
const fs = getFs2();
|
|
962
|
-
if (!fs.existsSync(parsed.target)) {
|
|
963
|
-
writeErr(`[wod parse] File not found: ${parsed.target}`);
|
|
964
|
-
return 1;
|
|
965
|
-
}
|
|
966
|
-
source = fs.readFileSync(parsed.target, "utf-8");
|
|
967
|
-
}
|
|
968
|
-
const ir = runParse(source, { sport: parsed.sport, sourceLabel: `cli:wod parse ${parsed.target}` });
|
|
969
|
-
const pretty = !parsed.output && Boolean(process.stdout.isTTY);
|
|
970
|
-
const formatted = formatParseOutput(ir, parsed.format, pretty);
|
|
971
|
-
deliverOutput(formatted);
|
|
972
|
-
return 0;
|
|
973
|
-
}
|
|
974
|
-
if (parsed.subcommand === "run") {
|
|
975
|
-
if (!parsed.target) {
|
|
976
|
-
writeErr('[wod run] Error: Missing input file or "-" for stdin');
|
|
977
|
-
return 1;
|
|
978
|
-
}
|
|
979
|
-
let source;
|
|
980
|
-
if (parsed.target === "-") {
|
|
981
|
-
source = await getStdin();
|
|
982
|
-
} else {
|
|
983
|
-
const fs = getFs2();
|
|
984
|
-
if (!fs.existsSync(parsed.target)) {
|
|
985
|
-
writeErr(`[wod run] File not found: ${parsed.target}`);
|
|
986
|
-
return 1;
|
|
987
|
-
}
|
|
988
|
-
source = fs.readFileSync(parsed.target, "utf-8");
|
|
989
|
-
}
|
|
990
|
-
const ir = await runExecution(source, { sport: parsed.sport, sourceLabel: `cli:wod run ${parsed.target}` });
|
|
991
|
-
const pretty = !parsed.output && Boolean(process.stdout.isTTY);
|
|
992
|
-
const formatted = formatExecutionOutput(ir, parsed.format, pretty);
|
|
993
|
-
deliverOutput(formatted);
|
|
994
|
-
return 0;
|
|
995
|
-
}
|
|
996
|
-
if (parsed.subcommand === "query") {
|
|
997
|
-
if (!parsed.target) {
|
|
998
|
-
writeErr("[wod query] Error: Missing WQL query argument");
|
|
999
|
-
return 1;
|
|
1000
|
-
}
|
|
1001
|
-
let stdinLogContent;
|
|
1002
|
-
let stdinFactsContent;
|
|
1003
|
-
if (parsed.stdinLog) {
|
|
1004
|
-
stdinLogContent = await getStdin();
|
|
1005
|
-
} else if (parsed.stdinFacts) {
|
|
1006
|
-
stdinFactsContent = await getStdin();
|
|
1007
|
-
} else if (!parsed.corpus) {
|
|
1008
|
-
writeErr("[wod query] Error: Query requires input data. Specify --corpus <file>, --stdin-log, or --stdin-facts.");
|
|
1009
|
-
return 1;
|
|
1010
|
-
}
|
|
1011
|
-
const ir = await runQueryCli(parsed.target, {
|
|
1012
|
-
corpusPath: parsed.corpus,
|
|
1013
|
-
stdinLog: stdinLogContent,
|
|
1014
|
-
stdinFacts: stdinFactsContent,
|
|
1015
|
-
preferredUnit: parsed.unit,
|
|
1016
|
-
sourceLabel: `cli:wod query "${parsed.target}"`
|
|
1017
|
-
});
|
|
1018
|
-
const pretty = !parsed.output && Boolean(process.stdout.isTTY);
|
|
1019
|
-
const formatted = formatQueryOutput(ir, parsed.format, pretty);
|
|
1020
|
-
deliverOutput(formatted);
|
|
1021
|
-
return 0;
|
|
1022
|
-
}
|
|
1023
|
-
writeErr(`[wod] Unknown subcommand "${parsed.subcommand}"`);
|
|
1024
|
-
return 1;
|
|
1025
|
-
} catch (error) {
|
|
1026
|
-
if (error instanceof ParseSyntaxError || error instanceof WqlSyntaxError) {
|
|
1027
|
-
writeErr(error.message);
|
|
1028
|
-
return 2;
|
|
1029
|
-
}
|
|
1030
|
-
if (error instanceof PackLoadError) {
|
|
1031
|
-
writeErr(error.message);
|
|
1032
|
-
return 3;
|
|
1033
|
-
}
|
|
1034
|
-
writeErr(`[wod] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1035
|
-
return 1;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
export { PackLoadError, ParseSyntaxError, WqlSyntaxError, buildStatementTree, buildWorkoutResults, cliMain, createIRFile, defineLanguagePack, factRowsToEventRows, formatExecutionOutput, formatParseOutput, formatQueryOutput, getRegisteredLanguagePacks, inMemoryEventStore, inMemoryEventStoreFromFacts, inMemoryFactStore, isIRFile, listLanguagePacks, loadLanguagePack, loadQueryData, parseCliArgs, readStdin, registerLanguagePack, runExecution, runParse, runQueryCli, statementToNode, unregisterLanguagePack };
|
|
1040
|
-
//# sourceMappingURL=chunk-QFWJKM45.mjs.map
|
|
1041
|
-
//# sourceMappingURL=chunk-QFWJKM45.mjs.map
|