@bitcobblers/wod-wiki-wql 0.7.37 → 0.8.39

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 CHANGED
@@ -1,18 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ var wodWikiCore = require('@bitcobblers/wod-wiki-core');
3
4
  var lr = require('@lezer/lr');
4
5
  var language = require('@codemirror/language');
5
6
  var autocomplete = require('@codemirror/autocomplete');
6
7
  var highlight = require('@lezer/highlight');
7
- var wodWikiCore = require('@bitcobblers/wod-wiki-core');
8
8
 
9
9
  var __defProp = Object.defineProperty;
10
10
  var __export = (target, all) => {
11
11
  for (var name in all)
12
12
  __defProp(target, name, { get: all[name], enumerable: true });
13
13
  };
14
-
15
- // src/vocabulary.ts
16
14
  var WQL_METRIC_FAMILIES = ["reps", "distance", "resistance", "elapsed", "power", "pace"];
17
15
  var WQL_METRIC_AGGREGATES = ["totalVolume", "totalDistance", "tis", "sessionLoad"];
18
16
  var WQL_AGGREGATORS = ["sum", "avg", "min", "max", "count", "last", "delta"];
@@ -58,6 +56,9 @@ var WQL_INTENSITY_TIERS = ["low", "moderate", "high"];
58
56
  var WQL_GRAINS = ["summary", "event"];
59
57
  var WQL_FIND_TARGETS = ["note", "block", "effort"];
60
58
  var WQL_SCOPES = ["journal", "collections", "feeds", "all"];
59
+ var WQL_RESULT_PLANES = wodWikiCore.KNOWN_OUTPUT_TYPES;
60
+ var WQL_ROWS_TARGETS = [...WQL_FIND_TARGETS, ...WQL_RESULT_PLANES, "all"];
61
+ var WQL_ROWS_SCOPE_KEYS = ["result", "block", "note"];
61
62
  var WQL_CONTENT_FILTER_KEYS = ["type", "text", "has", "source", "catalog", ...WQL_TAG_KEYS];
62
63
  var WQL_SOURCES = ["journal", "collections", "feeds", "notes", "blocks", "efforts", "metrics"];
63
64
  var WQL_ROLLUP_PERIODS = ["1d", "1w"];
@@ -234,11 +235,14 @@ function parseWqlSuffixes(raw) {
234
235
 
235
236
  // src/wql.ts
236
237
  function isFindQuery(parsed) {
237
- return "target" in parsed;
238
+ return parsed.family === "find";
238
239
  }
239
240
  function isRowsQuery(parsed) {
240
241
  return parsed.family === "rows";
241
242
  }
243
+ function isAggregateQuery(parsed) {
244
+ return parsed.family === "aggregate";
245
+ }
242
246
  var AGGS = WQL_AGGREGATORS;
243
247
  function cannotParse(text) {
244
248
  return `Cannot parse "${text}". Expected agg:metric{filters} by {dims} .rollup(period)`;
@@ -278,14 +282,28 @@ function parseQuery(raw) {
278
282
  return parseAnalyticsQuery(raw);
279
283
  }
280
284
  function cannotParseRows(text) {
281
- return `Cannot parse "${text}". Expected rows:{result:\u2026|block:\u2026|note:\u2026} or rows:segment{\u2026} last 8w`;
285
+ return `Cannot parse "${text}". Expected rows:all{result:\u2026|block:\u2026|note:\u2026}, rows:<plane>{\u2026}, or rows:segment{\u2026} last 8w`;
282
286
  }
287
+ function validateRowsFilters(filters) {
288
+ const scopeKeys = new Set(WQL_ROWS_SCOPE_KEYS);
289
+ const unsupported = filters.filter(
290
+ (f) => !scopeKeys.has(f.key) || f.negate || f.values.some((v) => v.wildcard)
291
+ );
292
+ if (unsupported.length > 0) {
293
+ return `Unsupported rows filter(s): ${unsupported.map((f) => (f.negate ? "!" : "") + f.key).join(", ")}. Rows queries support exact ${WQL_ROWS_SCOPE_KEYS.map((k) => `${k}:`).join(", ")} values.`;
294
+ }
295
+ if (!filters.some((f) => scopeKeys.has(f.key))) {
296
+ return `Rows query needs a scope: ${WQL_ROWS_SCOPE_KEYS.map((k) => `${k}:`).join(", ")}.`;
297
+ }
298
+ return void 0;
299
+ }
300
+ var BARE_ROWS_RETIRED = 'Bare "rows:" is retired \u2014 name a target: rows:all{\u2026} for every output type, or a plane like rows:segment{\u2026}.';
283
301
  function parseRowsQuery(raw) {
284
302
  const suffixes = parseWqlSuffixes(raw);
285
303
  const { where: whereText, last, groupBy, rollup, primaryText } = suffixes;
286
304
  const result = {
287
- raw,
288
305
  family: "rows",
306
+ raw,
289
307
  filters: [],
290
308
  last: last ? { size: last.size, unit: last.unit } : void 0
291
309
  };
@@ -298,41 +316,48 @@ function parseRowsQuery(raw) {
298
316
  return result;
299
317
  }
300
318
  const text = primaryText.trim();
301
- const brace = text.indexOf("{");
302
- const head = (brace === -1 ? text : text.slice(0, brace)).trim();
303
- const filterText = brace === -1 ? "" : text.slice(brace).trim();
304
- if (head === "rows" || head === "rows:") ; else if (head.startsWith("rows:")) {
305
- const target = head.slice("rows:".length).trim();
306
- if (!target || /\s/.test(target)) {
307
- result.error = cannotParseRows(text);
308
- return result;
309
- }
310
- result.outputType = target;
311
- } else {
319
+ if (/^rows:?\s*[{]?$/.test(text) || /^rows:?\s*[{]/.test(text)) {
320
+ result.error = BARE_ROWS_RETIRED;
321
+ return result;
322
+ }
323
+ const tree = parser.parse(text);
324
+ let syntaxError = false;
325
+ tree.iterate({ enter(node) {
326
+ if (node.type.isError) syntaxError = true;
327
+ } });
328
+ if (syntaxError) {
312
329
  result.error = cannotParseRows(text);
313
330
  return result;
314
331
  }
315
- if (filterText) {
316
- if (!/^\{.*\}$/.test(filterText)) {
317
- result.error = cannotParseRows(text);
318
- return result;
319
- }
320
- const synthetic = `find:_${filterText}`;
321
- const tree = parser.parse(synthetic);
322
- let syntaxError = false;
323
- tree.iterate({ enter(node) {
324
- if (node.type.isError) syntaxError = true;
325
- } });
326
- if (syntaxError) {
327
- result.error = cannotParseRows(text);
328
- return result;
329
- }
330
- result.filters = extractFilters(tree.topNode, synthetic);
331
- const grainError = retiredGrainRollup(result.filters);
332
- if (grainError) {
333
- result.error = grainError;
334
- return result;
335
- }
332
+ const query = tree.topNode;
333
+ const head = query.getChild(Head);
334
+ const aggNode = head?.getChild(Aggregator);
335
+ const metricNode = head?.getChild(Metric);
336
+ if (!head || !aggNode || !metricNode) {
337
+ result.error = BARE_ROWS_RETIRED;
338
+ return result;
339
+ }
340
+ const aggText = text.slice(aggNode.from, aggNode.to);
341
+ if (aggText !== "rows") {
342
+ result.error = cannotParseRows(text);
343
+ return result;
344
+ }
345
+ const target = text.slice(metricNode.from, metricNode.to);
346
+ if (!WQL_ROWS_TARGETS.includes(target)) {
347
+ result.error = `Unknown rows target "${target}". Try: ${WQL_ROWS_TARGETS.join(", ")}`;
348
+ return result;
349
+ }
350
+ if (target !== "all") result.outputType = target;
351
+ result.filters = extractFilters(query, text);
352
+ const grainError = retiredGrainRollup(result.filters);
353
+ if (grainError) {
354
+ result.error = grainError;
355
+ return result;
356
+ }
357
+ const filterError = validateRowsFilters(result.filters);
358
+ if (filterError) {
359
+ result.error = filterError;
360
+ return result;
336
361
  }
337
362
  return result;
338
363
  }
@@ -373,6 +398,7 @@ function parseAnalyticsQuery(raw) {
373
398
  const suffixes = parseWqlSuffixes(raw);
374
399
  const { where: whereText, displayUnit, groupBy, rollup, primaryText: text } = suffixes;
375
400
  const base = {
401
+ family: "aggregate",
376
402
  raw,
377
403
  agg: "sum",
378
404
  metric: "",
@@ -440,6 +466,7 @@ function parseFindQuery(raw) {
440
466
  const suffixes = parseWqlSuffixes(raw);
441
467
  const { where: whereText, last, scope, primaryText: text } = suffixes;
442
468
  const result = {
469
+ family: "find",
443
470
  raw,
444
471
  target: "",
445
472
  filters: [],
@@ -473,6 +500,10 @@ function parseFindQuery(raw) {
473
500
  return result;
474
501
  }
475
502
  result.target = text.slice(metricNode.from, metricNode.to);
503
+ if (!WQL_FIND_TARGETS.includes(result.target)) {
504
+ result.error = `Unknown find target "${result.target}". Try: ${WQL_FIND_TARGETS.join(", ")}`;
505
+ return result;
506
+ }
476
507
  result.filters = extractFilters(query, text);
477
508
  const findGrainError = retiredGrainRollup(result.filters);
478
509
  if (findGrainError) {
@@ -1090,6 +1121,7 @@ function staticTagIndexFromBlocks(blocks) {
1090
1121
 
1091
1122
  // src/QueryService.ts
1092
1123
  var DAY = 864e5;
1124
+ var ROWS_CONTENT_PLANES = new Set(WQL_FIND_TARGETS);
1093
1125
  function localDateString(ts) {
1094
1126
  return new Date(ts).toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
1095
1127
  }
@@ -1298,7 +1330,7 @@ var QueryService = class {
1298
1330
  const parsed = parseQuery(raw);
1299
1331
  if (isFindQuery(parsed)) {
1300
1332
  return {
1301
- parsed: { raw, agg: "count", metric: parsed.target, filters: [], groupBy: [] },
1333
+ parsed: { family: "aggregate", raw, agg: "count", metric: parsed.target, filters: [], groupBy: [] },
1302
1334
  series: [],
1303
1335
  stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
1304
1336
  matched: []
@@ -1306,7 +1338,7 @@ var QueryService = class {
1306
1338
  }
1307
1339
  if (isRowsQuery(parsed)) {
1308
1340
  return {
1309
- parsed: { raw, agg: "count", metric: "rows", filters: [], groupBy: [] },
1341
+ parsed: { family: "aggregate", raw, agg: "count", metric: "rows", filters: [], groupBy: [] },
1310
1342
  series: [],
1311
1343
  stages: { selected: 0, buckets: 0, aggregated: 0, groups: 0 },
1312
1344
  matched: []
@@ -1315,29 +1347,20 @@ var QueryService = class {
1315
1347
  return this.run(parsed, options2);
1316
1348
  }
1317
1349
  /**
1318
- * Execute a rows query (rows:{…}, #949) — the session results table plane.
1319
- * Reads event rows directly over the unified store (ticket 003): no
1320
- * WorkoutResult blob parsing; outputType narrowing hits the promoted column.
1350
+ * Execute a rows query (rows:<target>{…}, #949/C4) — the session results
1351
+ * table plane. Filter rules are validated at parse; this executes only.
1352
+ * Reads event rows directly over the unified store: outputType narrowing
1353
+ * hits the promoted column; content-plane targets scope by content.
1321
1354
  */
1322
1355
  async runRows(parsed, options2 = {}) {
1323
1356
  const empty = { parsed, runs: [] };
1324
1357
  if (parsed.error) return { ...empty, error: parsed.error };
1325
- const ROWS_SCOPE_KEYS = /* @__PURE__ */ new Set(["result", "block", "note"]);
1326
- const unsupported = parsed.filters.filter(
1327
- (f) => !ROWS_SCOPE_KEYS.has(f.key) || f.negate || f.values.some((v) => v.wildcard)
1328
- );
1329
- if (unsupported.length > 0) {
1330
- return {
1331
- ...empty,
1332
- error: `Unsupported rows filter(s): ${unsupported.map((f) => (f.negate ? "!" : "") + f.key).join(", ")}. Rows queries support exact result:, block:, note: values.`
1333
- };
1334
- }
1335
1358
  const scopeValues = (key) => parsed.filters.filter((f) => f.key === key).flatMap((f) => f.values.map((v) => v.value));
1336
1359
  const resultIds = scopeValues("result");
1337
1360
  const blockIds = scopeValues("block");
1338
1361
  const noteIds = scopeValues("note");
1339
1362
  if (resultIds.length + blockIds.length + noteIds.length === 0) {
1340
- return { ...empty, error: "Rows query needs a scope: result:, block:, or note:." };
1363
+ return { ...empty, runs: [] };
1341
1364
  }
1342
1365
  const byResult = /* @__PURE__ */ new Map();
1343
1366
  const collect = (rows) => {
@@ -1361,7 +1384,7 @@ var QueryService = class {
1361
1384
  resultId,
1362
1385
  noteId: rows[0].noteId,
1363
1386
  timestamp: rows[0].timestamp,
1364
- events: parsed.outputType ? rows.filter((row) => row.outputType === parsed.outputType) : rows
1387
+ events: parsed.outputType && !ROWS_CONTENT_PLANES.has(parsed.outputType) ? rows.filter((row) => row.outputType === parsed.outputType) : rows
1365
1388
  })).filter((run) => run.events.length > 0);
1366
1389
  return { parsed, runs };
1367
1390
  }
@@ -1628,6 +1651,7 @@ var QueryService = class {
1628
1651
  };
1629
1652
  const join = parsed.join;
1630
1653
  const findResult = await this.runFind({
1654
+ family: "find",
1631
1655
  raw: "",
1632
1656
  target: join.target,
1633
1657
  filters: join.filters,
@@ -2046,13 +2070,13 @@ sum:$metric{} rollup:1w
2046
2070
  }
2047
2071
 
2048
2072
  // src/version.ts
2049
- var VERSION = "0.7.37";
2050
- var GIT_SHA = "713c593";
2051
- var BUILD_TIME = "2026-08-25T00:18:25.714Z";
2073
+ var VERSION = "0.8.39";
2074
+ var GIT_SHA = "3c19185";
2075
+ var BUILD_TIME = "2026-08-26T00:07:31.934Z";
2052
2076
  var SEMVER = {
2053
- version: "0.7.37",
2054
- gitSha: "713c593",
2055
- buildTime: "2026-08-25T00:18:25.714Z"
2077
+ version: "0.8.39",
2078
+ gitSha: "3c19185",
2079
+ buildTime: "2026-08-26T00:07:31.934Z"
2056
2080
  };
2057
2081
 
2058
2082
  exports.BUILD_TIME = BUILD_TIME;
@@ -2079,7 +2103,10 @@ exports.WQL_INTENSITY_TIERS = WQL_INTENSITY_TIERS;
2079
2103
  exports.WQL_KEYWORDS = WQL_KEYWORDS;
2080
2104
  exports.WQL_METRIC_AGGREGATES = WQL_METRIC_AGGREGATES;
2081
2105
  exports.WQL_METRIC_FAMILIES = WQL_METRIC_FAMILIES;
2106
+ exports.WQL_RESULT_PLANES = WQL_RESULT_PLANES;
2082
2107
  exports.WQL_ROLLUP_PERIODS = WQL_ROLLUP_PERIODS;
2108
+ exports.WQL_ROWS_SCOPE_KEYS = WQL_ROWS_SCOPE_KEYS;
2109
+ exports.WQL_ROWS_TARGETS = WQL_ROWS_TARGETS;
2083
2110
  exports.WQL_SCOPES = WQL_SCOPES;
2084
2111
  exports.WQL_SOURCES = WQL_SOURCES;
2085
2112
  exports.WQL_TAG_KEYS = WQL_TAG_KEYS;
@@ -2095,6 +2122,7 @@ exports.extractFrontmatterTags = extractFrontmatterTags;
2095
2122
  exports.getList = getList;
2096
2123
  exports.getScalar = getScalar;
2097
2124
  exports.getUnitFamily = getUnitFamily;
2125
+ exports.isAggregateQuery = isAggregateQuery;
2098
2126
  exports.isDashboardMeta = isDashboardMeta;
2099
2127
  exports.isDashboardWidgetType = isDashboardWidgetType;
2100
2128
  exports.isEffortDiscipline = isEffortDiscipline;