@bitcobblers/wod-wiki-wql 0.7.38 → 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"];
@@ -281,14 +282,28 @@ function parseQuery(raw) {
281
282
  return parseAnalyticsQuery(raw);
282
283
  }
283
284
  function cannotParseRows(text) {
284
- 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`;
285
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}.';
286
301
  function parseRowsQuery(raw) {
287
302
  const suffixes = parseWqlSuffixes(raw);
288
303
  const { where: whereText, last, groupBy, rollup, primaryText } = suffixes;
289
304
  const result = {
290
- raw,
291
305
  family: "rows",
306
+ raw,
292
307
  filters: [],
293
308
  last: last ? { size: last.size, unit: last.unit } : void 0
294
309
  };
@@ -301,41 +316,48 @@ function parseRowsQuery(raw) {
301
316
  return result;
302
317
  }
303
318
  const text = primaryText.trim();
304
- const brace = text.indexOf("{");
305
- const head = (brace === -1 ? text : text.slice(0, brace)).trim();
306
- const filterText = brace === -1 ? "" : text.slice(brace).trim();
307
- if (head === "rows" || head === "rows:") ; else if (head.startsWith("rows:")) {
308
- const target = head.slice("rows:".length).trim();
309
- if (!target || /\s/.test(target)) {
310
- result.error = cannotParseRows(text);
311
- return result;
312
- }
313
- result.outputType = target;
314
- } 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) {
315
329
  result.error = cannotParseRows(text);
316
330
  return result;
317
331
  }
318
- if (filterText) {
319
- if (!/^\{.*\}$/.test(filterText)) {
320
- result.error = cannotParseRows(text);
321
- return result;
322
- }
323
- const synthetic = `find:_${filterText}`;
324
- const tree = parser.parse(synthetic);
325
- let syntaxError = false;
326
- tree.iterate({ enter(node) {
327
- if (node.type.isError) syntaxError = true;
328
- } });
329
- if (syntaxError) {
330
- result.error = cannotParseRows(text);
331
- return result;
332
- }
333
- result.filters = extractFilters(tree.topNode, synthetic);
334
- const grainError = retiredGrainRollup(result.filters);
335
- if (grainError) {
336
- result.error = grainError;
337
- return result;
338
- }
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;
339
361
  }
340
362
  return result;
341
363
  }
@@ -478,6 +500,10 @@ function parseFindQuery(raw) {
478
500
  return result;
479
501
  }
480
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
+ }
481
507
  result.filters = extractFilters(query, text);
482
508
  const findGrainError = retiredGrainRollup(result.filters);
483
509
  if (findGrainError) {
@@ -1095,6 +1121,7 @@ function staticTagIndexFromBlocks(blocks) {
1095
1121
 
1096
1122
  // src/QueryService.ts
1097
1123
  var DAY = 864e5;
1124
+ var ROWS_CONTENT_PLANES = new Set(WQL_FIND_TARGETS);
1098
1125
  function localDateString(ts) {
1099
1126
  return new Date(ts).toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
1100
1127
  }
@@ -1320,29 +1347,20 @@ var QueryService = class {
1320
1347
  return this.run(parsed, options2);
1321
1348
  }
1322
1349
  /**
1323
- * Execute a rows query (rows:{…}, #949) — the session results table plane.
1324
- * Reads event rows directly over the unified store (ticket 003): no
1325
- * 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.
1326
1354
  */
1327
1355
  async runRows(parsed, options2 = {}) {
1328
1356
  const empty = { parsed, runs: [] };
1329
1357
  if (parsed.error) return { ...empty, error: parsed.error };
1330
- const ROWS_SCOPE_KEYS = /* @__PURE__ */ new Set(["result", "block", "note"]);
1331
- const unsupported = parsed.filters.filter(
1332
- (f) => !ROWS_SCOPE_KEYS.has(f.key) || f.negate || f.values.some((v) => v.wildcard)
1333
- );
1334
- if (unsupported.length > 0) {
1335
- return {
1336
- ...empty,
1337
- error: `Unsupported rows filter(s): ${unsupported.map((f) => (f.negate ? "!" : "") + f.key).join(", ")}. Rows queries support exact result:, block:, note: values.`
1338
- };
1339
- }
1340
1358
  const scopeValues = (key) => parsed.filters.filter((f) => f.key === key).flatMap((f) => f.values.map((v) => v.value));
1341
1359
  const resultIds = scopeValues("result");
1342
1360
  const blockIds = scopeValues("block");
1343
1361
  const noteIds = scopeValues("note");
1344
1362
  if (resultIds.length + blockIds.length + noteIds.length === 0) {
1345
- return { ...empty, error: "Rows query needs a scope: result:, block:, or note:." };
1363
+ return { ...empty, runs: [] };
1346
1364
  }
1347
1365
  const byResult = /* @__PURE__ */ new Map();
1348
1366
  const collect = (rows) => {
@@ -1366,7 +1384,7 @@ var QueryService = class {
1366
1384
  resultId,
1367
1385
  noteId: rows[0].noteId,
1368
1386
  timestamp: rows[0].timestamp,
1369
- 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
1370
1388
  })).filter((run) => run.events.length > 0);
1371
1389
  return { parsed, runs };
1372
1390
  }
@@ -2052,13 +2070,13 @@ sum:$metric{} rollup:1w
2052
2070
  }
2053
2071
 
2054
2072
  // src/version.ts
2055
- var VERSION = "0.7.38";
2056
- var GIT_SHA = "172cc75";
2057
- var BUILD_TIME = "2026-08-25T23:00:14.937Z";
2073
+ var VERSION = "0.8.39";
2074
+ var GIT_SHA = "3c19185";
2075
+ var BUILD_TIME = "2026-08-26T00:07:31.934Z";
2058
2076
  var SEMVER = {
2059
- version: "0.7.38",
2060
- gitSha: "172cc75",
2061
- buildTime: "2026-08-25T23:00:14.937Z"
2077
+ version: "0.8.39",
2078
+ gitSha: "3c19185",
2079
+ buildTime: "2026-08-26T00:07:31.934Z"
2062
2080
  };
2063
2081
 
2064
2082
  exports.BUILD_TIME = BUILD_TIME;
@@ -2085,7 +2103,10 @@ exports.WQL_INTENSITY_TIERS = WQL_INTENSITY_TIERS;
2085
2103
  exports.WQL_KEYWORDS = WQL_KEYWORDS;
2086
2104
  exports.WQL_METRIC_AGGREGATES = WQL_METRIC_AGGREGATES;
2087
2105
  exports.WQL_METRIC_FAMILIES = WQL_METRIC_FAMILIES;
2106
+ exports.WQL_RESULT_PLANES = WQL_RESULT_PLANES;
2088
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;
2089
2110
  exports.WQL_SCOPES = WQL_SCOPES;
2090
2111
  exports.WQL_SOURCES = WQL_SOURCES;
2091
2112
  exports.WQL_TAG_KEYS = WQL_TAG_KEYS;