@bitcobblers/wod-wiki-wql 0.44.2159 → 0.45.2166
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 +462 -242
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -88
- package/dist/index.d.ts +240 -88
- package/dist/index.mjs +452 -243
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -31,7 +31,7 @@ var WQL_TAG_KEYS = [
|
|
|
31
31
|
"result",
|
|
32
32
|
"tags"
|
|
33
33
|
];
|
|
34
|
-
var WQL_CONTENT_ONLY_KEYS = ["type", "text", "
|
|
34
|
+
var WQL_CONTENT_ONLY_KEYS = ["type", "text", "source", "notebook", "contributes"];
|
|
35
35
|
var WQL_EFFORT_FILTER_KEYS = ["effort", "discipline", "intensity", "origin", "text"];
|
|
36
36
|
var WQL_VIRTUAL_DIMS = ["day", "week", "session", "round"];
|
|
37
37
|
var WQL_CALC_TARGETS = [
|
|
@@ -59,8 +59,8 @@ var WQL_CALC_TARGETS = [
|
|
|
59
59
|
];
|
|
60
60
|
var WQL_INTENSITY_TIERS = ["low", "moderate", "high"];
|
|
61
61
|
var WQL_GRAINS = ["summary", "event"];
|
|
62
|
-
var WQL_FIND_TARGETS = ["note", "block", "effort", "session", "segment", "event"];
|
|
63
|
-
var WQL_SOURCE_VALUES = ["journal", "
|
|
62
|
+
var WQL_FIND_TARGETS = ["note", "block", "effort", "session", "segment", "event", "notebook", "page", "tag", "equipment"];
|
|
63
|
+
var WQL_SOURCE_VALUES = ["journal", "notebooks", "guides", "playground", "dashboards", "efforts", "equipment"];
|
|
64
64
|
var WQL_SOURCE_HEADS = [
|
|
65
65
|
"note",
|
|
66
66
|
"notes",
|
|
@@ -72,23 +72,18 @@ var WQL_SOURCE_HEADS = [
|
|
|
72
72
|
"journal",
|
|
73
73
|
"notebook",
|
|
74
74
|
"notebooks",
|
|
75
|
-
"feed",
|
|
76
|
-
"feeds",
|
|
77
|
-
"catalog",
|
|
78
|
-
"catalogs",
|
|
79
75
|
"playground",
|
|
76
|
+
"page",
|
|
77
|
+
"tag",
|
|
78
|
+
"equipment",
|
|
80
79
|
"dashboard"
|
|
81
80
|
];
|
|
82
81
|
var WQL_SOURCE_HEAD_SCOPES = {
|
|
83
82
|
journal: "journal",
|
|
84
|
-
|
|
85
|
-
notebooks: "feeds",
|
|
86
|
-
feed: "feeds",
|
|
87
|
-
feeds: "feeds",
|
|
88
|
-
catalog: "feeds",
|
|
89
|
-
catalogs: "feeds",
|
|
83
|
+
notebooks: "notebooks",
|
|
90
84
|
playground: "playground"
|
|
91
85
|
};
|
|
86
|
+
var WQL_NOTE_HEAD_ALIASES = ["notes"];
|
|
92
87
|
var WQL_TYPE_HEADS = {
|
|
93
88
|
dashboard: "dashboard"
|
|
94
89
|
};
|
|
@@ -97,8 +92,8 @@ var WQL_FUNCTION_HEADS = WQL_AGGREGATORS;
|
|
|
97
92
|
var WQL_CHART_HEADS = ["timeseries", "bar", "table", "donut", "toplist", "value"];
|
|
98
93
|
var WQL_STANDARD_DATASETS = ["@session", "@today"];
|
|
99
94
|
var WQL_RESULT_PLANES = wodWikiCore.KNOWN_OUTPUT_TYPES;
|
|
100
|
-
var WQL_CONTENT_FILTER_KEYS = ["type", "text", "
|
|
101
|
-
var WQL_SOURCES =
|
|
95
|
+
var WQL_CONTENT_FILTER_KEYS = ["type", "text", "source", "notebook", "page", "contributes", ...WQL_TAG_KEYS];
|
|
96
|
+
var WQL_SOURCES = WQL_FIND_TARGETS;
|
|
102
97
|
var WQL_ROLLUP_PERIODS = ["2w", "4w"];
|
|
103
98
|
var WQL_DISPLAY_UNITS = ["kg", "lb"];
|
|
104
99
|
var WQL_KEYWORDS = [
|
|
@@ -134,12 +129,25 @@ var AGGREGATE_TYPES = /* @__PURE__ */ new Set([
|
|
|
134
129
|
"metric"
|
|
135
130
|
]);
|
|
136
131
|
var TARGET_TYPES = {
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
// The notebook plane IS the note plane (its executor delegates), so both
|
|
133
|
+
// advertise the same member-criteria keys — container criteria (tags:,
|
|
134
|
+
// effort:, typed tags, text:) narrow notebook results, never ignored.
|
|
135
|
+
notebook: /* @__PURE__ */ new Set(["text", "notebook", "tag", "effort", ...WQL_TYPED_TAG_KEYS, "type", "page", "source", "note", "time", "groupby", "where"]),
|
|
136
|
+
note: /* @__PURE__ */ new Set(["text", "notebook", "tag", "effort", ...WQL_TYPED_TAG_KEYS, "type", "page", "source", "note", "time", "groupby", "where"]),
|
|
137
|
+
block: /* @__PURE__ */ new Set(["text", "notebook", "tag", "effort", "type", "source", "note", "time", "groupby", "where"]),
|
|
139
138
|
effort: /* @__PURE__ */ new Set([...WQL_EFFORT_FILTER_KEYS, "groupby"]),
|
|
140
139
|
session: /* @__PURE__ */ new Set(["result", "block", "note", "plane", "time", "groupby"]),
|
|
141
140
|
segment: /* @__PURE__ */ new Set([...TABLE_KEYS, "time", "groupby", "unit"]),
|
|
142
|
-
event: /* @__PURE__ */ new Set([...TABLE_KEYS, "time", "groupby", "unit"])
|
|
141
|
+
event: /* @__PURE__ */ new Set([...TABLE_KEYS, "time", "groupby", "unit"]),
|
|
142
|
+
// Container planes (ticket 08). `:notebook{}` delegates to the note plane
|
|
143
|
+
// (see the notebook entry above); `:page{}` lists the page placements;
|
|
144
|
+
// `:tag{}` the tags (results are notes); `:equipment{}` the equipment
|
|
145
|
+
// notes. The tag plane's `contributes:` filter matches what a tag
|
|
146
|
+
// contributes — never offered as a `tags:` label filter. These executors
|
|
147
|
+
// return flat rows (no `by {}` grouping), so groupby stays unadvertised.
|
|
148
|
+
page: /* @__PURE__ */ new Set(["page", "notebook", "text", "note", "time"]),
|
|
149
|
+
tag: /* @__PURE__ */ new Set(["tag", "type", "text", "contributes", "time"]),
|
|
150
|
+
equipment: /* @__PURE__ */ new Set(["equipment", "tags", "text", "notebook", "time"])
|
|
143
151
|
};
|
|
144
152
|
var EMPTY_TYPES = /* @__PURE__ */ new Set();
|
|
145
153
|
var AGGREGATE_KEYS = WQL_TAG_KEYS;
|
|
@@ -654,18 +662,24 @@ function validateSourceFilter(filters) {
|
|
|
654
662
|
for (const v of f.values) {
|
|
655
663
|
const val = v.value;
|
|
656
664
|
if (val === "page" || val === "pages") {
|
|
657
|
-
return `source:${val} is retired \u2014 page-ness is handled by type:
|
|
665
|
+
return `source:${val} is retired \u2014 page-ness is handled by type:page or source:guides.`;
|
|
658
666
|
}
|
|
659
667
|
if (val === "all") {
|
|
660
668
|
return `source:all is retired \u2014 omit the source: filter to query all sources.`;
|
|
661
669
|
}
|
|
662
|
-
if (val === "
|
|
663
|
-
return `source
|
|
670
|
+
if (val === "notebook") {
|
|
671
|
+
return `source:notebook is retired \u2014 :notebook{} is the notebook plane (every notebook); narrow it with the notebook: filter, e.g. :notebook{notebook:journal}.`;
|
|
672
|
+
}
|
|
673
|
+
if (val === "feed" || val === "feeds" || val.startsWith("feed:")) {
|
|
674
|
+
return `source:${val} is retired \u2014 the feed corpus is the notebook plane; use source:notebooks or notebook:<slug>.`;
|
|
675
|
+
}
|
|
676
|
+
if (val === "collection" || val === "collections" || val.startsWith("collection:")) {
|
|
677
|
+
return `source:${val} is retired \u2014 collections are notebooks; use source:notebooks or notebook:<slug>.`;
|
|
664
678
|
}
|
|
665
|
-
if (WQL_SOURCE_VALUES.includes(val) || val
|
|
679
|
+
if (WQL_SOURCE_VALUES.includes(val) || val.startsWith("notebook:") || val.startsWith("notebook/")) {
|
|
666
680
|
continue;
|
|
667
681
|
}
|
|
668
|
-
return `Unknown source "${val}". Try: ${WQL_SOURCE_VALUES.join(", ")} (or
|
|
682
|
+
return `Unknown source "${val}". Try: ${WQL_SOURCE_VALUES.join(", ")} (or notebook:<slug>)`;
|
|
669
683
|
}
|
|
670
684
|
}
|
|
671
685
|
return void 0;
|
|
@@ -948,11 +962,7 @@ function parseFindQuery(raw, opts) {
|
|
|
948
962
|
if (scope || typeScope) {
|
|
949
963
|
result.target = "note";
|
|
950
964
|
} else {
|
|
951
|
-
result.target = headName
|
|
952
|
-
if (result.target === "page") {
|
|
953
|
-
result.error = "find:page is retired \u2014 page-ness is handled by type: (e.g. :note{source:guides} or type:feed).";
|
|
954
|
-
return result;
|
|
955
|
-
}
|
|
965
|
+
result.target = WQL_NOTE_HEAD_ALIASES.includes(headName) ? "note" : headName;
|
|
956
966
|
if (!WQL_FIND_TARGETS.includes(result.target)) {
|
|
957
967
|
result.error = `Unknown find target "${result.target}". Try: ${WQL_FIND_TARGETS.join(", ")}`;
|
|
958
968
|
return result;
|
|
@@ -972,13 +982,6 @@ function parseFindQuery(raw, opts) {
|
|
|
972
982
|
negate: false,
|
|
973
983
|
values: [{ value: scope, wildcard: false }]
|
|
974
984
|
});
|
|
975
|
-
if (headName === "catalog" || headName === "catalogs" || headName === "notebook" || headName === "notebooks") {
|
|
976
|
-
result.filters.push({
|
|
977
|
-
key: "type",
|
|
978
|
-
negate: false,
|
|
979
|
-
values: [{ value: "feed", wildcard: false }]
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
985
|
}
|
|
983
986
|
const inScope = opts?.colon && result.displayUnit && result.displayUnit !== "kg" && result.displayUnit !== "lb" ? result.displayUnit : legacyScope;
|
|
984
987
|
if (opts?.colon && inScope) {
|
|
@@ -1487,10 +1490,12 @@ function chained(list, suffix) {
|
|
|
1487
1490
|
...c,
|
|
1488
1491
|
apply: (view, _c, from, to) => {
|
|
1489
1492
|
const text = typeof c.apply === "string" ? c.apply : c.label;
|
|
1493
|
+
const selfTerminated = /\W$/.test(text);
|
|
1490
1494
|
const present = view.state.sliceDoc(to, to + suffix.length) === suffix;
|
|
1495
|
+
const insert = selfTerminated || present ? text : text + suffix;
|
|
1491
1496
|
view.dispatch({
|
|
1492
|
-
changes: { from, to
|
|
1493
|
-
selection: { anchor: from +
|
|
1497
|
+
changes: { from, to: to + (selfTerminated && present ? suffix.length : 0), insert },
|
|
1498
|
+
selection: { anchor: from + insert.length },
|
|
1494
1499
|
userEvent: "input.complete"
|
|
1495
1500
|
});
|
|
1496
1501
|
autocomplete.startCompletion(view);
|
|
@@ -1508,7 +1513,7 @@ function headApply(o, colonTyped) {
|
|
|
1508
1513
|
return { ...o, apply: `${colonTyped ? "" : ":"}${o.label}{` };
|
|
1509
1514
|
}
|
|
1510
1515
|
var LEGACY_HEAD_OPTIONS = WQL_AGGREGATORS.map((label) => ({ label, type: "keyword" }));
|
|
1511
|
-
var NOTE_COLUMNS = ["title", "date", "createdAt", "type", "sourceId", "
|
|
1516
|
+
var NOTE_COLUMNS = ["title", "date", "createdAt", "type", "sourceId", "notebookId"];
|
|
1512
1517
|
var BLOCK_COLUMNS = ["noteTitle", "dataType", "position", "createdAt", "noteId", "sourceId"];
|
|
1513
1518
|
var EFFORT_COLUMNS = ["slug", "label", "registrySource"];
|
|
1514
1519
|
var SESSION_COLUMNS = ["duration", "startTime", "origin"];
|
|
@@ -1536,7 +1541,7 @@ function resolveColonHead(name) {
|
|
|
1536
1541
|
if (WQL_FUNCTION_HEADS.includes(n)) return { family: "aggregate" };
|
|
1537
1542
|
if (WQL_CHART_HEADS.includes(n)) return { family: "find" };
|
|
1538
1543
|
const headTarget = (head) => {
|
|
1539
|
-
if (head
|
|
1544
|
+
if (WQL_NOTE_HEAD_ALIASES.includes(head)) return "note";
|
|
1540
1545
|
const scoped = WQL_SOURCE_HEAD_SCOPES[head] || WQL_TYPE_HEADS[head];
|
|
1541
1546
|
return scoped ? "note" : head;
|
|
1542
1547
|
};
|
|
@@ -1705,12 +1710,17 @@ function wqlCompletionSource(options_ = {}) {
|
|
|
1705
1710
|
if (/\s+to\s+$/i.test(tail)) {
|
|
1706
1711
|
return finish(context, context.pos, [{ label: "2026-03-31", detail: "end date (YYYY-MM-DD)", type: "constant" }], "");
|
|
1707
1712
|
}
|
|
1713
|
+
const hasBy = /\bby\s*\{[^{}]*\}/.test(tail);
|
|
1714
|
+
const hasRollup = /\.rollup\([^()]*\)/.test(tail);
|
|
1715
|
+
const hasWindow = /\s+last\s+\d+[dw]\b/i.test(tail) || /\s+from\s+\d{4}-\d{2}-\d{2}(?:\s+to\s+\d{4}-\d{2}-\d{2})?\b/i.test(tail);
|
|
1708
1716
|
const dims = wqlGroupingDimensions(head.target ?? "", head.family);
|
|
1709
1717
|
const keywords = [
|
|
1710
|
-
{ label: "by {}", apply: "by ", detail: dims.length ? "group by dimensions" : `advisory: no grouping dimensions for find:${head.target}`, type: "keyword" },
|
|
1711
|
-
...head.family === "aggregate" ? [{ label: ".rollup()", apply: ".rollup(", detail: "bucket period", type: "keyword" }] : [],
|
|
1712
|
-
|
|
1713
|
-
|
|
1718
|
+
...hasBy ? [] : [{ label: "by {}", apply: "by ", detail: dims.length ? "group by dimensions" : `advisory: no grouping dimensions for find:${head.target}`, type: "keyword" }],
|
|
1719
|
+
...head.family === "aggregate" && !hasRollup ? [{ label: ".rollup()", apply: ".rollup(", detail: "bucket period", type: "keyword" }] : [],
|
|
1720
|
+
...hasWindow ? [] : [
|
|
1721
|
+
{ label: "last", apply: "last ", detail: "relative window: last <n>d|w", type: "keyword" },
|
|
1722
|
+
{ label: "from", apply: "from ", detail: "date range: from YYYY-MM-DD [to YYYY-MM-DD]", type: "keyword" }
|
|
1723
|
+
],
|
|
1714
1724
|
...head.family === "find" ? [{ label: "|", apply: "| ", detail: "presentation pipes: order by / select / limit", type: "keyword" }] : []
|
|
1715
1725
|
];
|
|
1716
1726
|
return finish(context, from, chained(keywords, " "), typed);
|
|
@@ -2276,6 +2286,7 @@ var QueryDocumentRunner = class {
|
|
|
2276
2286
|
...findRun.blocks ? { blocks: findRun.blocks } : {},
|
|
2277
2287
|
...findRun.efforts ? { efforts: findRun.efforts } : {},
|
|
2278
2288
|
...findRun.containers ? { containers: findRun.containers } : {},
|
|
2289
|
+
...findRun.placements ? { placements: findRun.placements } : {},
|
|
2279
2290
|
...findRun.runs ? { runs: findRun.runs } : {},
|
|
2280
2291
|
...findRun.table ? { table: findRun.table } : {},
|
|
2281
2292
|
...findRun.unit ? { unit: findRun.unit } : {},
|
|
@@ -2306,6 +2317,7 @@ var QueryDocumentRunner = class {
|
|
|
2306
2317
|
...pipelineRun.blocks ? { blocks: pipelineRun.blocks } : {},
|
|
2307
2318
|
...pipelineRun.efforts ? { efforts: pipelineRun.efforts } : {},
|
|
2308
2319
|
...pipelineRun.containers ? { containers: pipelineRun.containers } : {},
|
|
2320
|
+
...pipelineRun.placements ? { placements: pipelineRun.placements } : {},
|
|
2309
2321
|
...pipelineRun.runs ? { runs: pipelineRun.runs } : {},
|
|
2310
2322
|
...pipelineRun.unit ? { unit: pipelineRun.unit } : {},
|
|
2311
2323
|
...pipelineRun.chart ? { chart: pipelineRun.chart } : {},
|
|
@@ -2912,16 +2924,18 @@ function dedupeById(facts) {
|
|
|
2912
2924
|
}
|
|
2913
2925
|
return out;
|
|
2914
2926
|
}
|
|
2927
|
+
|
|
2928
|
+
// src/QueryService.ts
|
|
2915
2929
|
var NOTE_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2916
|
-
function
|
|
2917
|
-
if (
|
|
2918
|
-
const
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
return
|
|
2930
|
+
function notebookSlugOfSourceId(sourceId) {
|
|
2931
|
+
if (!sourceId?.startsWith("notebook:")) return void 0;
|
|
2932
|
+
const rest = sourceId.slice("notebook:".length);
|
|
2933
|
+
if (!rest || rest === "_root") return void 0;
|
|
2934
|
+
return rest.split("/")[0];
|
|
2935
|
+
}
|
|
2936
|
+
function feedOfItem(item) {
|
|
2937
|
+
const id = item.feedSourceId;
|
|
2938
|
+
return typeof id === "string" && id.trim() ? id : void 0;
|
|
2925
2939
|
}
|
|
2926
2940
|
function notebookOfItem(item) {
|
|
2927
2941
|
if (item.notebookId && item.notebookId.trim()) {
|
|
@@ -2930,46 +2944,75 @@ function notebookOfItem(item) {
|
|
|
2930
2944
|
if (item.tags) {
|
|
2931
2945
|
for (const tag of item.tags) {
|
|
2932
2946
|
if (tag.startsWith("notebook:") || tag.startsWith("notebook/")) {
|
|
2933
|
-
const
|
|
2934
|
-
if (
|
|
2947
|
+
const slug2 = wodWikiCore.notebookSlugFromId(tag);
|
|
2948
|
+
if (slug2) return wodWikiCore.toNotebookId(slug2);
|
|
2935
2949
|
}
|
|
2936
2950
|
}
|
|
2937
2951
|
}
|
|
2952
|
+
const corpus = /^markdown\/(notebooks|canvas|efforts|dashboards|equipment)\/(.+)$/.exec(item.sourcePath ?? "");
|
|
2953
|
+
if (corpus) {
|
|
2954
|
+
const [, family, rest] = corpus;
|
|
2955
|
+
if (family === "canvas") return "notebook/guides";
|
|
2956
|
+
if (family !== "notebooks") return wodWikiCore.toNotebookId(family);
|
|
2957
|
+
const segs = rest.split("/");
|
|
2958
|
+
if (segs.length > 1) return wodWikiCore.toNotebookId(segs[0]);
|
|
2959
|
+
}
|
|
2938
2960
|
if (sourceMatches(item, "playground")) return "notebook/playground";
|
|
2939
2961
|
if (sourceMatches(item, "guides")) return "notebook/guides";
|
|
2940
|
-
|
|
2941
|
-
if (
|
|
2942
|
-
if (
|
|
2962
|
+
if (item.sourceId === "efforts") return "notebook/efforts";
|
|
2963
|
+
if (item.sourceId === "equipment") return "notebook/equipment";
|
|
2964
|
+
if (item.sourceId === "dashboards" || item.type === "dashboard") return "notebook/dashboards";
|
|
2965
|
+
const slug = notebookSlugOfSourceId(item.sourceId);
|
|
2966
|
+
if (slug) return wodWikiCore.toNotebookId(slug);
|
|
2967
|
+
if (isJournalRow(item)) return "notebook/journal";
|
|
2943
2968
|
return void 0;
|
|
2944
2969
|
}
|
|
2970
|
+
function notebookOriginOfItem(item) {
|
|
2971
|
+
const notebook = notebookOfItem(item);
|
|
2972
|
+
if (!notebook) return void 0;
|
|
2973
|
+
if (item.notebookId?.trim()) return "user";
|
|
2974
|
+
if (item.tags?.some((t2) => t2.startsWith("notebook:") || t2.startsWith("notebook/"))) return "user";
|
|
2975
|
+
if (notebook === "notebook/journal" || notebook === "notebook/playground") return "default";
|
|
2976
|
+
return "feed";
|
|
2977
|
+
}
|
|
2978
|
+
function isJournalRow(item) {
|
|
2979
|
+
const sourceId = item.sourceId;
|
|
2980
|
+
if (item.type === "playground" || item.id?.startsWith("playground/") || item.noteId?.startsWith("playground/")) return false;
|
|
2981
|
+
if (sourceId && sourceId !== "journal" && !NOTE_UUID_RE.test(sourceId) && !/^journal\/\d{4}-\d{2}-\d{2}$/.test(sourceId)) return false;
|
|
2982
|
+
return item.type !== "dashboard" && item.type !== "effort" && item.type !== "equipment" && item.seedOrigin !== "seed" && item.feedOrigin !== "feed" && item.seedChunkId === void 0 && !(!!item.sourcePath && item.sourcePath.startsWith("markdown/"));
|
|
2983
|
+
}
|
|
2984
|
+
var SOURCE_ID_SHAPES = {
|
|
2985
|
+
notebooks: { prefixes: ["notebook:"] },
|
|
2986
|
+
pages: { exact: ["page"], prefixes: ["page:", "guides:"] },
|
|
2987
|
+
guides: { prefixes: ["guides:"] },
|
|
2988
|
+
playground: { exact: ["playground"], legacyType: "playground" },
|
|
2989
|
+
efforts: { exact: ["efforts"] },
|
|
2990
|
+
equipment: { exact: ["equipment"] },
|
|
2991
|
+
dashboards: { exact: ["dashboards"] }
|
|
2992
|
+
};
|
|
2993
|
+
var SOURCE_SHAPE_ALIASES = {
|
|
2994
|
+
page: "pages",
|
|
2995
|
+
guide: "guides"
|
|
2996
|
+
};
|
|
2997
|
+
function sourceShape(kind) {
|
|
2998
|
+
return SOURCE_ID_SHAPES[SOURCE_SHAPE_ALIASES[kind] ?? kind];
|
|
2999
|
+
}
|
|
2945
3000
|
function sourceMatches(item, kind) {
|
|
2946
3001
|
const sourceId = item.sourceId;
|
|
2947
3002
|
if (kind === "all") return !sourceId || WQL_SOURCE_VALUES.some((k) => sourceMatches(item, k));
|
|
2948
3003
|
if (kind === "journal") {
|
|
2949
|
-
|
|
2950
|
-
if (sourceId && sourceId !== "journal" && !NOTE_UUID_RE.test(sourceId) && !/^journal\/\d{4}-\d{2}-\d{2}$/.test(sourceId)) return false;
|
|
2951
|
-
return item.type !== "dashboard" && item.type !== "effort" && item.type !== "equipment" && item.seedOrigin !== "seed" && item.seedChunkId === void 0 && !item.catalog && !(!!item.sourcePath && item.sourcePath.startsWith("markdown/"));
|
|
3004
|
+
return notebookOfItem(item) === "notebook/journal";
|
|
2952
3005
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
return !!sourceId && (sourceId === "page" || sourceId.startsWith("page:") || sourceId.startsWith("guides:"));
|
|
2958
|
-
}
|
|
2959
|
-
if (kind === "guide" || kind === "guides") {
|
|
2960
|
-
return !!sourceId && sourceId.startsWith("guides:");
|
|
2961
|
-
}
|
|
2962
|
-
if (kind === "playground") {
|
|
2963
|
-
return sourceId === "playground" || item.type === "playground";
|
|
2964
|
-
}
|
|
2965
|
-
if (kind.startsWith("feed:")) {
|
|
2966
|
-
const rest = kind.slice("feed:".length).replace(/^feeds\//, "");
|
|
2967
|
-
if (!rest || !sourceId?.startsWith("feed:")) return false;
|
|
2968
|
-
const idRest = sourceId.slice("feed:".length).replace(/^feeds\//, "");
|
|
2969
|
-
return idRest === rest || idRest.startsWith(`${rest}/`);
|
|
3006
|
+
const shape = sourceShape(kind);
|
|
3007
|
+
if (shape) {
|
|
3008
|
+
const idHit = !!sourceId && (shape.exact?.includes(sourceId) || shape.prefixes?.some((p) => sourceId.startsWith(p)) === true);
|
|
3009
|
+
return idHit || !!shape.legacyType && item.type === shape.legacyType;
|
|
2970
3010
|
}
|
|
2971
3011
|
if (kind.startsWith("notebook:") || kind.startsWith("notebook/")) {
|
|
2972
|
-
|
|
3012
|
+
const rest = wodWikiCore.notebookSlugFromId(kind);
|
|
3013
|
+
const idRest = sourceId?.startsWith("notebook:") ? sourceId.slice("notebook:".length) : void 0;
|
|
3014
|
+
if (!idRest) return notebookOfItem(item) === wodWikiCore.toNotebookId(kind);
|
|
3015
|
+
return idRest === rest || idRest.startsWith(`${rest}/`);
|
|
2973
3016
|
}
|
|
2974
3017
|
return sourceId === kind;
|
|
2975
3018
|
}
|
|
@@ -2998,8 +3041,7 @@ function applyNotebookFilter(items, filters) {
|
|
|
2998
3041
|
return items;
|
|
2999
3042
|
}
|
|
3000
3043
|
var DOMAIN_SOURCE_KINDS = {
|
|
3001
|
-
|
|
3002
|
-
feeds: true,
|
|
3044
|
+
notebooks: true,
|
|
3003
3045
|
page: true,
|
|
3004
3046
|
pages: true,
|
|
3005
3047
|
guide: true,
|
|
@@ -3025,6 +3067,10 @@ function compileContentRead(filters, plane, sourceScope) {
|
|
|
3025
3067
|
residual = true;
|
|
3026
3068
|
continue;
|
|
3027
3069
|
}
|
|
3070
|
+
if (plane === "note" && WQL_TYPED_TAG_KEYS.includes(filter.key) && literal) {
|
|
3071
|
+
compiled.push({ field: "typedTag", tagType: filter.key, values });
|
|
3072
|
+
continue;
|
|
3073
|
+
}
|
|
3028
3074
|
if (filter.key === "text" && literal) {
|
|
3029
3075
|
compiled.push({ field: "text", value: values.join(" ") });
|
|
3030
3076
|
continue;
|
|
@@ -3687,9 +3733,17 @@ var QueryService = class {
|
|
|
3687
3733
|
if (parsed.target === "segment" || parsed.target === "event") {
|
|
3688
3734
|
return this.runFindTable(parsed, options2);
|
|
3689
3735
|
}
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3736
|
+
if (parsed.target === "notebook") {
|
|
3737
|
+
return this.runFindNotebook(parsed, options2);
|
|
3738
|
+
}
|
|
3739
|
+
if (parsed.target === "page") {
|
|
3740
|
+
return this.runFindPage(parsed, options2);
|
|
3741
|
+
}
|
|
3742
|
+
if (parsed.target === "tag") {
|
|
3743
|
+
return this.runFindTag(parsed, options2);
|
|
3744
|
+
}
|
|
3745
|
+
if (parsed.target === "equipment") {
|
|
3746
|
+
return this.runFindEquipment(parsed, options2);
|
|
3693
3747
|
}
|
|
3694
3748
|
const read = compileContentRead(parsed.filters, "note", parsed.sourceScope);
|
|
3695
3749
|
const paging = domainPaging(parsed, options2, read.residual);
|
|
@@ -3709,14 +3763,14 @@ var QueryService = class {
|
|
|
3709
3763
|
const selectedCount = selectedFromDomain ?? notes.length;
|
|
3710
3764
|
const ctx = runContext(options2);
|
|
3711
3765
|
for (const filter of parsed.filters) {
|
|
3712
|
-
if (filter.key
|
|
3766
|
+
if (isTagLabelKey(filter.key) && !filter.negate) {
|
|
3713
3767
|
const matchingIds = /* @__PURE__ */ new Set();
|
|
3714
3768
|
for (const v of filter.values) {
|
|
3715
3769
|
const ids = await this.noteStore.getNoteIdsForTag(v.value);
|
|
3716
3770
|
ids.forEach((id) => matchingIds.add(id));
|
|
3717
3771
|
}
|
|
3718
3772
|
notes = notes.filter((n) => matchingIds.has(n.id));
|
|
3719
|
-
} else if (filter.key
|
|
3773
|
+
} else if (isTagLabelKey(filter.key) && filter.negate) {
|
|
3720
3774
|
for (const v of filter.values) {
|
|
3721
3775
|
const ids = await this.noteStore.getNoteIdsForTag(v.value);
|
|
3722
3776
|
notes = notes.filter((n) => !ids.has(n.id));
|
|
@@ -3759,17 +3813,6 @@ var QueryService = class {
|
|
|
3759
3813
|
notes = notes.filter((n) => wantsPage ? n.type === "page" : n.type !== "page");
|
|
3760
3814
|
}
|
|
3761
3815
|
}
|
|
3762
|
-
for (const filter of parsed.filters) {
|
|
3763
|
-
if (filter.key === "catalog") {
|
|
3764
|
-
const wanted = new Set(filter.values.map((v) => v.value));
|
|
3765
|
-
notes = notes.filter((n) => {
|
|
3766
|
-
const cat = catalogOfItem(n);
|
|
3767
|
-
if (!cat) return filter.negate;
|
|
3768
|
-
const hit = wanted.has(cat);
|
|
3769
|
-
return filter.negate ? !hit : hit;
|
|
3770
|
-
});
|
|
3771
|
-
}
|
|
3772
|
-
}
|
|
3773
3816
|
notes = applyNotebookFilter(notes, parsed.filters);
|
|
3774
3817
|
for (const filter of parsed.filters) {
|
|
3775
3818
|
if (filter.key === "note") {
|
|
@@ -3800,46 +3843,56 @@ var QueryService = class {
|
|
|
3800
3843
|
const offset = pipes.offset ?? 0;
|
|
3801
3844
|
notes = pipes.limit !== void 0 ? notes.slice(offset, offset + pipes.limit) : notes.slice(offset);
|
|
3802
3845
|
}
|
|
3846
|
+
const relations = notes.length && (this.pageStore || this.relationStore) ? await this.containersAndPlacements(notes) : void 0;
|
|
3803
3847
|
return {
|
|
3804
3848
|
parsed,
|
|
3805
3849
|
notes,
|
|
3806
3850
|
blocks: [],
|
|
3807
|
-
...
|
|
3851
|
+
...relations ? { containers: relations.containers, placements: relations.placements } : {},
|
|
3808
3852
|
stages: { selected: selectedCount, matched: notes.length }
|
|
3809
3853
|
};
|
|
3810
3854
|
}
|
|
3811
|
-
/**
|
|
3812
|
-
*
|
|
3813
|
-
*
|
|
3814
|
-
*
|
|
3815
|
-
*
|
|
3816
|
-
|
|
3855
|
+
/** The container projection and the full placement set per note id.
|
|
3856
|
+
*
|
|
3857
|
+
* The container is the parent Page from the canonical page_notes junction
|
|
3858
|
+
* (the primary placement wins); a note with no page link stays a
|
|
3859
|
+
* standalone Note container. Batched when a relationStore is injected —
|
|
3860
|
+
* one bundle read per run instead of a link/page read per matched note.
|
|
3861
|
+
*
|
|
3862
|
+
* The same read resolves EVERY page the note is placed on: the junction
|
|
3863
|
+
* is many-to-many, so the container is a projection of that set, not the
|
|
3864
|
+
* set itself. */
|
|
3865
|
+
async containersAndPlacements(notes) {
|
|
3817
3866
|
const containers = {};
|
|
3867
|
+
const placements = {};
|
|
3818
3868
|
const bundles = this.relationStore ? await this.relationStore.getBatches([...new Set(notes.map((n) => n.id))]) : void 0;
|
|
3819
3869
|
const pageCache = /* @__PURE__ */ new Map();
|
|
3820
3870
|
for (const note of notes) {
|
|
3821
|
-
let page;
|
|
3822
3871
|
let bundle;
|
|
3872
|
+
let resolved;
|
|
3823
3873
|
if (bundles) {
|
|
3824
3874
|
bundle = bundles.get(note.id);
|
|
3825
|
-
|
|
3826
|
-
page = first ? bundle.pages.find((p) => p.id === first.pageId) : void 0;
|
|
3875
|
+
resolved = wodWikiCore.resolvePlacements(bundle?.links ?? [], bundle?.pages ?? []);
|
|
3827
3876
|
if (bundle && bundle.tags.length > 0 && (!note.tags || note.tags.length === 0)) {
|
|
3828
3877
|
note.tags = bundle.tags.map((t2) => t2.label);
|
|
3829
3878
|
}
|
|
3830
3879
|
} else {
|
|
3831
3880
|
const links = await this.pageStore.getNotePages(note.id);
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
if (!pageCache.has(
|
|
3835
|
-
pageCache.set(
|
|
3881
|
+
const pages = [];
|
|
3882
|
+
for (const link of links) {
|
|
3883
|
+
if (!pageCache.has(link.pageId)) {
|
|
3884
|
+
pageCache.set(link.pageId, await this.pageStore.getPage(link.pageId));
|
|
3836
3885
|
}
|
|
3837
|
-
page = pageCache.get(
|
|
3886
|
+
const page = pageCache.get(link.pageId);
|
|
3887
|
+
if (page) pages.push(page);
|
|
3838
3888
|
}
|
|
3889
|
+
resolved = wodWikiCore.resolvePlacements(links, pages);
|
|
3839
3890
|
}
|
|
3840
|
-
|
|
3891
|
+
placements[note.id] = resolved;
|
|
3892
|
+
const primary = resolved[0];
|
|
3893
|
+
containers[note.id] = primary ? { kind: "page", id: primary.pageId, ...primary.slug ? { slug: primary.slug } : {}, ...primary.date ? { date: primary.date } : {} } : { kind: "note", id: note.id };
|
|
3841
3894
|
}
|
|
3842
|
-
return containers;
|
|
3895
|
+
return { containers, placements };
|
|
3843
3896
|
}
|
|
3844
3897
|
async hydrateBlocks(rows) {
|
|
3845
3898
|
const ids = [...new Set(rows.map((row) => row.noteId))];
|
|
@@ -3898,20 +3951,9 @@ var QueryService = class {
|
|
|
3898
3951
|
blocks = blocks.filter((b) => wanted.has(b.dataType));
|
|
3899
3952
|
}
|
|
3900
3953
|
}
|
|
3901
|
-
for (const filter of parsed.filters) {
|
|
3902
|
-
if (filter.key === "catalog") {
|
|
3903
|
-
const wanted = new Set(filter.values.map((v) => v.value));
|
|
3904
|
-
blocks = blocks.filter((b) => {
|
|
3905
|
-
const cat = catalogOfItem(b);
|
|
3906
|
-
if (!cat) return filter.negate;
|
|
3907
|
-
const hit = wanted.has(cat);
|
|
3908
|
-
return filter.negate ? !hit : hit;
|
|
3909
|
-
});
|
|
3910
|
-
}
|
|
3911
|
-
}
|
|
3912
3954
|
blocks = applyNotebookFilter(blocks, parsed.filters);
|
|
3913
3955
|
for (const filter of parsed.filters) {
|
|
3914
|
-
if (filter.key
|
|
3956
|
+
if (isTagLabelKey(filter.key) && !filter.negate) {
|
|
3915
3957
|
const matchingNoteIds = /* @__PURE__ */ new Set();
|
|
3916
3958
|
for (const v of filter.values) {
|
|
3917
3959
|
const ids = await this.noteStore.getNoteIdsForTag(v.value);
|
|
@@ -3961,7 +4003,9 @@ var QueryService = class {
|
|
|
3961
4003
|
return { parsed, notes: [], blocks, stages: { selected: selectedCount, matched: blocks.length } };
|
|
3962
4004
|
}
|
|
3963
4005
|
/**
|
|
3964
|
-
* Execute a :effort query
|
|
4006
|
+
* Execute a :effort query: the registry is the filter plane (discipline,
|
|
4007
|
+
* intensity, origin, text), the note plane is the result plane — matched
|
|
4008
|
+
* projections resolve to their owning definition notes.
|
|
3965
4009
|
*/
|
|
3966
4010
|
async runFindEffort(parsed) {
|
|
3967
4011
|
const all = await this.effortStore.getAllEfforts();
|
|
@@ -4010,7 +4054,17 @@ var QueryService = class {
|
|
|
4010
4054
|
const offset = pipes.offset ?? 0;
|
|
4011
4055
|
efforts = pipes.limit !== void 0 ? efforts.slice(offset, offset + pipes.limit) : efforts.slice(offset);
|
|
4012
4056
|
}
|
|
4013
|
-
|
|
4057
|
+
const ownerIds = [...new Set(efforts.map((e) => e.ownerNoteId).filter((id) => typeof id === "string" && id.length > 0))];
|
|
4058
|
+
let notes = [];
|
|
4059
|
+
if (ownerIds.length > 0) {
|
|
4060
|
+
const fetched = this.noteStore.getNotesByIds ? await this.noteStore.getNotesByIds(ownerIds) : (await this.noteStore.getAllNotes()).filter((n) => ownerIds.includes(n.id));
|
|
4061
|
+
const byId = new Map(fetched.map((n) => [n.id, n]));
|
|
4062
|
+
notes = ownerIds.flatMap((id) => {
|
|
4063
|
+
const note = byId.get(id);
|
|
4064
|
+
return note ? [note] : [];
|
|
4065
|
+
});
|
|
4066
|
+
}
|
|
4067
|
+
return { parsed, notes, blocks: [], efforts, stages: { selected: selectedCount, matched: efforts.length } };
|
|
4014
4068
|
}
|
|
4015
4069
|
async run(parsed, options2 = {}) {
|
|
4016
4070
|
if (isPipelineQuery(parsed)) {
|
|
@@ -4122,6 +4176,7 @@ var QueryService = class {
|
|
|
4122
4176
|
...content.runs?.length ? { runs: content.runs } : {},
|
|
4123
4177
|
...content.table ? { table: content.table } : {},
|
|
4124
4178
|
...content.containers ? { containers: content.containers } : {},
|
|
4179
|
+
...content.placements ? { placements: content.placements } : {},
|
|
4125
4180
|
...parsed.sink ? { chart: parsed.sink } : {}
|
|
4126
4181
|
};
|
|
4127
4182
|
}
|
|
@@ -4494,139 +4549,293 @@ var QueryService = class {
|
|
|
4494
4549
|
return new Set(links.map((l) => l.noteId));
|
|
4495
4550
|
}
|
|
4496
4551
|
/**
|
|
4497
|
-
*
|
|
4498
|
-
*
|
|
4552
|
+
* Execute a :notebook query (ticket 08) — the notebook plane is a NOTE
|
|
4553
|
+
* plane (ticket 05 semantics preserved): bare `:notebook{}` returns the
|
|
4554
|
+
* notes of every notebook — journal, playground, defaults, user-created,
|
|
4555
|
+
* feed-delivered. `notebook:{slug}` narrows to members of those
|
|
4556
|
+
* notebooks and `!notebook:` excludes them; the result shape never
|
|
4557
|
+
* changes with polarity, and container criteria (tags:, effort:, typed
|
|
4558
|
+
* tags, text:) narrow the members exactly like the note plane — this
|
|
4559
|
+
* delegates to it. The notebook CONTAINER listing (the /notebooks
|
|
4560
|
+
* management surface) is the app's NotebookService model, not WQL rows.
|
|
4499
4561
|
*/
|
|
4500
|
-
async
|
|
4562
|
+
async runFindNotebook(parsed, options2 = {}) {
|
|
4563
|
+
return this.runFind({ ...parsed, target: "note" }, options2);
|
|
4564
|
+
}
|
|
4565
|
+
/**
|
|
4566
|
+
* Execute a :page query (ticket 08) — the placement plane. Results are
|
|
4567
|
+
* the placed notes; `placements` carries every page each note sits on
|
|
4568
|
+
* (many-to-many) and `containers` the primary projection. `page:{slug}`
|
|
4569
|
+
* (or a `YYYY-MM-DD` date, or a page id) narrows the placements.
|
|
4570
|
+
*/
|
|
4571
|
+
async runFindPage(parsed, options2 = {}) {
|
|
4572
|
+
const empty = { parsed, notes: [], blocks: [], stages: { selected: 0, matched: 0 } };
|
|
4573
|
+
if (!this.pageStore?.getAllPages || !this.pageStore?.getAllLinks) return empty;
|
|
4501
4574
|
const ctx = runContext(options2);
|
|
4502
|
-
const
|
|
4503
|
-
let
|
|
4504
|
-
const
|
|
4505
|
-
for (const
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
existingCatIds.add(cat);
|
|
4575
|
+
const [pages, links] = await Promise.all([this.pageStore.getAllPages(), this.pageStore.getAllLinks()]);
|
|
4576
|
+
let placements = wodWikiCore.resolvePlacements(links, pages);
|
|
4577
|
+
const selected = new Set(placements.map((p) => p.noteId)).size;
|
|
4578
|
+
for (const filter of parsed.filters) {
|
|
4579
|
+
if (filter.key !== "page") continue;
|
|
4580
|
+
const wanted = new Set(filter.values.map((v) => v.value));
|
|
4581
|
+
placements = placements.filter((p) => {
|
|
4582
|
+
const hit = wanted.has(p.pageId) || p.slug !== void 0 && wanted.has(p.slug) || p.date !== void 0 && wanted.has(p.date);
|
|
4583
|
+
return filter.negate ? !hit : hit;
|
|
4584
|
+
});
|
|
4585
|
+
}
|
|
4586
|
+
const placedIds = [...new Set(placements.map((p) => p.noteId))];
|
|
4587
|
+
let notes = this.noteStore.getNotesByIds ? await this.noteStore.getNotesByIds(placedIds) : (await this.noteStore.getAllNotes()).filter((n) => placedIds.includes(n.id));
|
|
4588
|
+
for (const filter of parsed.filters) {
|
|
4589
|
+
if (filter.key === "note") {
|
|
4590
|
+
const wanted = new Set(filter.values.map((v) => v.value));
|
|
4591
|
+
notes = notes.filter((n) => filter.negate ? !wanted.has(n.id) : wanted.has(n.id));
|
|
4520
4592
|
}
|
|
4521
4593
|
}
|
|
4522
|
-
const
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
if (catId) {
|
|
4530
|
-
const list = membersByCat.get(catId);
|
|
4531
|
-
if (list) {
|
|
4532
|
-
if (!list.includes(n)) list.push(n);
|
|
4533
|
-
} else {
|
|
4534
|
-
membersByCat.set(catId, [n]);
|
|
4535
|
-
}
|
|
4594
|
+
for (const filter of parsed.filters) {
|
|
4595
|
+
if (filter.key === "text") {
|
|
4596
|
+
const search = filter.values.map((v) => v.value).join(" ").toLowerCase();
|
|
4597
|
+
notes = notes.filter((n) => {
|
|
4598
|
+
const hit = n.title.toLowerCase().includes(search) || n.rawContent.toLowerCase().includes(search);
|
|
4599
|
+
return filter.negate ? !hit : hit;
|
|
4600
|
+
});
|
|
4536
4601
|
}
|
|
4537
4602
|
}
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4603
|
+
notes = applyNotebookFilter(notes, parsed.filters);
|
|
4604
|
+
if (parsed.window || options2.range) {
|
|
4605
|
+
notes = notes.filter((n) => effectiveTimeWindow(n.date ?? n.createdAt, parsed.window, options2.range, ctx));
|
|
4606
|
+
}
|
|
4607
|
+
notes = applyOrderAndPage(notes, parsed);
|
|
4608
|
+
const survived = new Set(notes.map((n) => n.id));
|
|
4609
|
+
const byNote = /* @__PURE__ */ new Map();
|
|
4610
|
+
for (const p of placements) {
|
|
4611
|
+
if (!survived.has(p.noteId)) continue;
|
|
4612
|
+
const list = byNote.get(p.noteId);
|
|
4613
|
+
if (list) list.push(p);
|
|
4614
|
+
else byNote.set(p.noteId, [p]);
|
|
4615
|
+
}
|
|
4616
|
+
const containers = {};
|
|
4617
|
+
for (const note of notes) {
|
|
4618
|
+
const primary = byNote.get(note.id)?.[0];
|
|
4619
|
+
containers[note.id] = primary ? { kind: "page", id: primary.pageId, ...primary.slug ? { slug: primary.slug } : {}, ...primary.date ? { date: primary.date } : {} } : { kind: "note", id: note.id };
|
|
4620
|
+
}
|
|
4621
|
+
return {
|
|
4622
|
+
parsed,
|
|
4623
|
+
notes,
|
|
4624
|
+
blocks: [],
|
|
4625
|
+
containers,
|
|
4626
|
+
placements: Object.fromEntries(byNote),
|
|
4627
|
+
stages: { selected, matched: notes.length }
|
|
4628
|
+
};
|
|
4629
|
+
}
|
|
4630
|
+
/**
|
|
4631
|
+
* Execute a :tag query (ticket 08) — the tag plane. Every tag record
|
|
4632
|
+
* surfaces as one synthetic note row, so results are notes end to end.
|
|
4633
|
+
* `tag:{label}` narrows; `type:` narrows by the tag's configured type;
|
|
4634
|
+
* `contributes:` narrows by what a tag contributes (`metric`,
|
|
4635
|
+
* `metric:<type>`, `tag:<slug>`, `function:<name>`) — a contribution is
|
|
4636
|
+
* exposed here, never as a `tags:` label filter. The synthetic row's body
|
|
4637
|
+
* summarizes the contributions so `text:` reaches them too.
|
|
4638
|
+
*/
|
|
4639
|
+
async runFindTag(parsed, options2 = {}) {
|
|
4640
|
+
const ctx = runContext(options2);
|
|
4641
|
+
const tags = this.tagsStore.getAll ? await this.tagsStore.getAll() : [];
|
|
4642
|
+
const selected = tags.length;
|
|
4643
|
+
let matched = tags;
|
|
4644
|
+
for (const filter of parsed.filters) {
|
|
4645
|
+
if (isTagLabelKey(filter.key)) {
|
|
4646
|
+
const wanted = new Set(filter.values.map((v) => v.value.toLowerCase()));
|
|
4647
|
+
matched = matched.filter((t2) => {
|
|
4648
|
+
const hit = typeof t2.label === "string" && wanted.has(t2.label.toLowerCase());
|
|
4548
4649
|
return filter.negate ? !hit : hit;
|
|
4549
4650
|
});
|
|
4550
|
-
} else if (filter.key === "
|
|
4551
|
-
const
|
|
4552
|
-
|
|
4553
|
-
const
|
|
4554
|
-
ids.forEach((id) => matchingIds.add(id));
|
|
4555
|
-
}
|
|
4556
|
-
matchingCatalogs = matchingCatalogs.filter((c) => {
|
|
4557
|
-
const catId = catalogOfItem(c) ?? c.id;
|
|
4558
|
-
const members = membersByCat.get(catId) ?? [];
|
|
4559
|
-
const hit = members.some((m) => matchingIds.has(m.id));
|
|
4651
|
+
} else if (filter.key === "type") {
|
|
4652
|
+
const wanted = new Set(filter.values.map((v) => v.value));
|
|
4653
|
+
matched = matched.filter((t2) => {
|
|
4654
|
+
const hit = t2.type !== void 0 && wanted.has(t2.type);
|
|
4560
4655
|
return filter.negate ? !hit : hit;
|
|
4561
4656
|
});
|
|
4562
|
-
} else if (filter.key === "
|
|
4657
|
+
} else if (filter.key === "contributes") {
|
|
4658
|
+
matched = matched.filter((t2) => {
|
|
4659
|
+
const hit = (t2.contributions ?? []).some((c) => filter.values.some((v) => tagContributionMatches(c, v.value)));
|
|
4660
|
+
return filter.negate ? !hit : hit;
|
|
4661
|
+
});
|
|
4662
|
+
} else if (filter.key === "text") {
|
|
4663
|
+
const search = filter.values.map((v) => v.value).join(" ").toLowerCase();
|
|
4664
|
+
matched = matched.filter((t2) => filter.negate !== `${t2.label ?? ""} ${tagContributionSummary(t2)}`.toLowerCase().includes(search));
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
let rows = matched.map((t2) => ({
|
|
4668
|
+
id: `tag/${typeof t2.type === "string" && t2.type ? t2.type : "general"}/${t2.label}`,
|
|
4669
|
+
title: t2.label,
|
|
4670
|
+
type: "tag",
|
|
4671
|
+
tagType: typeof t2.type === "string" ? t2.type : void 0,
|
|
4672
|
+
contributions: t2.contributions ?? [],
|
|
4673
|
+
createdAt: typeof t2.createdAt === "number" ? t2.createdAt : 0,
|
|
4674
|
+
content: tagContributionSummary(t2),
|
|
4675
|
+
rawContent: tagContributionSummary(t2),
|
|
4676
|
+
sourceRevision: 1,
|
|
4677
|
+
blocks: {}
|
|
4678
|
+
}));
|
|
4679
|
+
if (parsed.window || options2.range) {
|
|
4680
|
+
rows = rows.filter((r) => effectiveTimeWindow(r.createdAt, parsed.window, options2.range, ctx));
|
|
4681
|
+
}
|
|
4682
|
+
rows = applyOrderAndPage(rows, parsed);
|
|
4683
|
+
return { parsed, notes: rows, blocks: [], stages: { selected, matched: rows.length } };
|
|
4684
|
+
}
|
|
4685
|
+
/**
|
|
4686
|
+
* Execute an :equipment query (ticket 08) — the equipment plane. Results
|
|
4687
|
+
* are the equipment notes (the equipment notebook's members, typed
|
|
4688
|
+
* 'equipment' rows included). `equipment:{label}` narrows by the
|
|
4689
|
+
* equipment typed tag ("every equipment note tagged kettlebell" is
|
|
4690
|
+
* `:equipment{equipment:kettlebell}`); `tags:` and `text:` narrow like
|
|
4691
|
+
* the note plane.
|
|
4692
|
+
*/
|
|
4693
|
+
async runFindEquipment(parsed, options2 = {}) {
|
|
4694
|
+
const allNotes = await this.noteStore.getAllNotes();
|
|
4695
|
+
const ctx = runContext(options2);
|
|
4696
|
+
let notes = allNotes.filter((n) => notebookOfItem(n) === "notebook/equipment" || n.type === "equipment" || sourceMatches(n, "equipment"));
|
|
4697
|
+
const selected = notes.length;
|
|
4698
|
+
for (const filter of parsed.filters) {
|
|
4699
|
+
if (filter.key === "equipment") {
|
|
4563
4700
|
const matchingIds = /* @__PURE__ */ new Set();
|
|
4564
4701
|
for (const v of filter.values) {
|
|
4565
|
-
|
|
4566
|
-
ids.forEach((id) => matchingIds.add(id));
|
|
4702
|
+
(await this.getNoteIdsForTypedTag("equipment", v.value)).forEach((id) => matchingIds.add(id));
|
|
4567
4703
|
}
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
const members = membersByCat.get(catId) ?? [];
|
|
4571
|
-
const hit = members.some((m) => matchingIds.has(m.id) || m.tags && filter.values.some((v) => m.tags.includes(v.value)));
|
|
4572
|
-
return filter.negate ? !hit : hit;
|
|
4573
|
-
});
|
|
4574
|
-
} else if (WQL_TYPED_TAG_KEYS.includes(filter.key)) {
|
|
4704
|
+
notes = notes.filter((n) => filter.negate ? !matchingIds.has(n.id) : matchingIds.has(n.id));
|
|
4705
|
+
} else if (isTagLabelKey(filter.key) && !filter.negate) {
|
|
4575
4706
|
const matchingIds = /* @__PURE__ */ new Set();
|
|
4576
4707
|
for (const v of filter.values) {
|
|
4577
|
-
|
|
4578
|
-
ids.forEach((id) => matchingIds.add(id));
|
|
4708
|
+
(await this.noteStore.getNoteIdsForTag(v.value)).forEach((id) => matchingIds.add(id));
|
|
4579
4709
|
}
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
const
|
|
4584
|
-
|
|
4585
|
-
}
|
|
4586
|
-
}
|
|
4710
|
+
notes = notes.filter((n) => matchingIds.has(n.id));
|
|
4711
|
+
} else if (isTagLabelKey(filter.key) && filter.negate) {
|
|
4712
|
+
for (const v of filter.values) {
|
|
4713
|
+
const ids = await this.noteStore.getNoteIdsForTag(v.value);
|
|
4714
|
+
notes = notes.filter((n) => !ids.has(n.id));
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
for (const filter of parsed.filters) {
|
|
4719
|
+
if (filter.key === "text") {
|
|
4587
4720
|
const search = filter.values.map((v) => v.value).join(" ").toLowerCase();
|
|
4588
|
-
|
|
4589
|
-
const
|
|
4590
|
-
const members = membersByCat.get(catId) ?? [];
|
|
4591
|
-
const hit = members.some((m) => m.title.toLowerCase().includes(search) || m.rawContent.toLowerCase().includes(search));
|
|
4592
|
-
return filter.negate ? !hit : hit;
|
|
4593
|
-
});
|
|
4594
|
-
} else if (filter.key === "note") {
|
|
4595
|
-
const wanted = new Set(filter.values.map((v) => v.value));
|
|
4596
|
-
matchingCatalogs = matchingCatalogs.filter((c) => {
|
|
4597
|
-
const catId = catalogOfItem(c) ?? c.id;
|
|
4598
|
-
const members = membersByCat.get(catId) ?? [];
|
|
4599
|
-
const hit = members.some((m) => wanted.has(m.id));
|
|
4721
|
+
notes = notes.filter((n) => {
|
|
4722
|
+
const hit = n.title.toLowerCase().includes(search) || n.rawContent.toLowerCase().includes(search);
|
|
4600
4723
|
return filter.negate ? !hit : hit;
|
|
4601
4724
|
});
|
|
4602
4725
|
}
|
|
4603
4726
|
}
|
|
4727
|
+
notes = applyNotebookFilter(notes, parsed.filters);
|
|
4604
4728
|
if (parsed.window || options2.range) {
|
|
4605
|
-
|
|
4606
|
-
const catId = catalogOfItem(c) ?? c.id;
|
|
4607
|
-
const members = membersByCat.get(catId) ?? [];
|
|
4608
|
-
return members.some((m) => effectiveTimeWindow(m.date ?? m.createdAt, parsed.window, options2.range, ctx));
|
|
4609
|
-
});
|
|
4610
|
-
}
|
|
4611
|
-
if (parsed.pipes?.order) {
|
|
4612
|
-
for (const order of [...parsed.pipes.order].reverse()) {
|
|
4613
|
-
const dir = order.dir === "desc" ? -1 : 1;
|
|
4614
|
-
matchingCatalogs = [...matchingCatalogs].sort((a, b) => {
|
|
4615
|
-
const av = a[order.col] ?? "";
|
|
4616
|
-
const bv = b[order.col] ?? "";
|
|
4617
|
-
if (av === bv) return 0;
|
|
4618
|
-
return (av > bv ? 1 : -1) * dir;
|
|
4619
|
-
});
|
|
4620
|
-
}
|
|
4729
|
+
notes = notes.filter((n) => effectiveTimeWindow(n.date ?? n.createdAt, parsed.window, options2.range, ctx));
|
|
4621
4730
|
}
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
notes: matchingCatalogs,
|
|
4625
|
-
blocks: [],
|
|
4626
|
-
stages: { selected: catalogNotes.length, matched: matchingCatalogs.length }
|
|
4627
|
-
};
|
|
4731
|
+
notes = applyOrderAndPage(notes, parsed);
|
|
4732
|
+
return { parsed, notes, blocks: [], stages: { selected, matched: notes.length } };
|
|
4628
4733
|
}
|
|
4629
4734
|
};
|
|
4735
|
+
function applyOrderAndPage(rows, parsed) {
|
|
4736
|
+
let out = rows;
|
|
4737
|
+
if (parsed.pipes?.order) {
|
|
4738
|
+
for (const order of [...parsed.pipes.order].reverse()) {
|
|
4739
|
+
const dir = order.dir === "desc" ? -1 : 1;
|
|
4740
|
+
out = [...out].sort((a, b) => {
|
|
4741
|
+
const av = a[order.col] ?? "";
|
|
4742
|
+
const bv = b[order.col] ?? "";
|
|
4743
|
+
if (av === bv) return 0;
|
|
4744
|
+
return (av > bv ? 1 : -1) * dir;
|
|
4745
|
+
});
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
const pipes = parsed.pipes;
|
|
4749
|
+
if (pipes && (pipes.limit !== void 0 || (pipes.offset ?? 0) > 0)) {
|
|
4750
|
+
const offset = pipes.offset ?? 0;
|
|
4751
|
+
out = pipes.limit !== void 0 ? out.slice(offset, offset + pipes.limit) : out.slice(offset);
|
|
4752
|
+
}
|
|
4753
|
+
return out;
|
|
4754
|
+
}
|
|
4755
|
+
function isTagLabelKey(key) {
|
|
4756
|
+
return key === "tags" || key === "tag";
|
|
4757
|
+
}
|
|
4758
|
+
function tagContributionMatches(contribution, value) {
|
|
4759
|
+
const sep = value.indexOf(":");
|
|
4760
|
+
const kind = sep === -1 ? value : value.slice(0, sep);
|
|
4761
|
+
const qualifier = sep === -1 ? "" : value.slice(sep + 1);
|
|
4762
|
+
if (contribution.kind !== kind) return false;
|
|
4763
|
+
if (!qualifier) return true;
|
|
4764
|
+
if (contribution.kind === "metric") return contribution.type === qualifier;
|
|
4765
|
+
if (contribution.kind === "tag") return contribution.slug === qualifier;
|
|
4766
|
+
return contribution.name === qualifier;
|
|
4767
|
+
}
|
|
4768
|
+
function tagContributionSummary(tag) {
|
|
4769
|
+
const parts = (tag.contributions ?? []).map((c) => c.kind === "metric" ? `metric ${c.type}` : c.kind === "tag" ? `tag ${c.slug}` : `function ${c.name}`);
|
|
4770
|
+
return parts.length ? `contributes ${parts.join(", ")}` : "";
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
// src/notebookVocabulary.ts
|
|
4774
|
+
function migrateLegacyChunkId(chunkId) {
|
|
4775
|
+
if (chunkId.startsWith("collection.")) return `notebook.${chunkId.slice("collection.".length)}`;
|
|
4776
|
+
if (chunkId.startsWith("feed.")) return `notebook.${chunkId.slice("feed.".length)}`;
|
|
4777
|
+
return chunkId;
|
|
4778
|
+
}
|
|
4779
|
+
function migrateLegacySourcePath(path) {
|
|
4780
|
+
if (path.startsWith("markdown/collections/")) return `markdown/notebooks/${path.slice("markdown/collections/".length)}`;
|
|
4781
|
+
if (path.startsWith("markdown/feeds/")) return `markdown/notebooks/${path.slice("markdown/feeds/".length)}`;
|
|
4782
|
+
return path;
|
|
4783
|
+
}
|
|
4784
|
+
function migrateLegacySourceId(sourceId) {
|
|
4785
|
+
if (sourceId.startsWith("page:collection:")) return `page:${sourceId.slice("page:collection:".length)}`;
|
|
4786
|
+
if (sourceId.startsWith("collection:")) return `notebook:${sourceId.slice("collection:".length)}`;
|
|
4787
|
+
if (sourceId.startsWith("feed:")) {
|
|
4788
|
+
return `notebook:${sourceId.slice("feed:".length).replace(/^feeds\//, "")}`;
|
|
4789
|
+
}
|
|
4790
|
+
return sourceId;
|
|
4791
|
+
}
|
|
4792
|
+
function migrateLegacyRouteId(routeId) {
|
|
4793
|
+
return routeId.startsWith("feeds/") ? routeId.slice("feeds/".length) : routeId;
|
|
4794
|
+
}
|
|
4795
|
+
function migrateLegacyRow(row) {
|
|
4796
|
+
let next = row;
|
|
4797
|
+
const apply = (patch) => {
|
|
4798
|
+
next = { ...next, ...patch };
|
|
4799
|
+
};
|
|
4800
|
+
const sourceId = row["sourceId"];
|
|
4801
|
+
if (typeof sourceId === "string") {
|
|
4802
|
+
const migrated = migrateLegacySourceId(sourceId);
|
|
4803
|
+
if (migrated !== sourceId) apply({ sourceId: migrated });
|
|
4804
|
+
}
|
|
4805
|
+
const sourcePath = row["sourcePath"];
|
|
4806
|
+
if (typeof sourcePath === "string") {
|
|
4807
|
+
const migrated = migrateLegacySourcePath(sourcePath);
|
|
4808
|
+
if (migrated !== sourcePath) apply({ sourcePath: migrated });
|
|
4809
|
+
}
|
|
4810
|
+
const seedChunkId = row["seedChunkId"];
|
|
4811
|
+
if (typeof seedChunkId === "string") {
|
|
4812
|
+
const migrated = migrateLegacyChunkId(seedChunkId);
|
|
4813
|
+
if (migrated !== seedChunkId) apply({ seedChunkId: migrated });
|
|
4814
|
+
}
|
|
4815
|
+
const routeId = row["routeId"];
|
|
4816
|
+
if (typeof routeId === "string") {
|
|
4817
|
+
const migrated = migrateLegacyRouteId(routeId);
|
|
4818
|
+
if (migrated !== routeId) apply({ routeId: migrated });
|
|
4819
|
+
}
|
|
4820
|
+
if ("catalog" in next) {
|
|
4821
|
+
next = { ...next };
|
|
4822
|
+
delete next["catalog"];
|
|
4823
|
+
}
|
|
4824
|
+
if (next["type"] === "feed") apply({ type: "note" });
|
|
4825
|
+
return next;
|
|
4826
|
+
}
|
|
4827
|
+
function hasLegacyIdentity(row) {
|
|
4828
|
+
const sourceId = row["sourceId"];
|
|
4829
|
+
if (typeof sourceId === "string" && (/^(collection|feed):/.test(sourceId) || sourceId.startsWith("page:collection:"))) return true;
|
|
4830
|
+
const sourcePath = row["sourcePath"];
|
|
4831
|
+
if (typeof sourcePath === "string" && (sourcePath.startsWith("markdown/collections/") || sourcePath.startsWith("markdown/feeds/"))) return true;
|
|
4832
|
+
const seedChunkId = row["seedChunkId"];
|
|
4833
|
+
if (typeof seedChunkId === "string" && /^(collection|feed)\./.test(seedChunkId)) return true;
|
|
4834
|
+
const routeId = row["routeId"];
|
|
4835
|
+
if (typeof routeId === "string" && routeId.startsWith("feeds/")) return true;
|
|
4836
|
+
if ("catalog" in row || row["type"] === "feed") return true;
|
|
4837
|
+
return false;
|
|
4838
|
+
}
|
|
4630
4839
|
|
|
4631
4840
|
// src/dashboard/model.ts
|
|
4632
4841
|
var DASHBOARD_WIDGET_TYPES = [
|
|
@@ -5269,13 +5478,13 @@ ${query}
|
|
|
5269
5478
|
}
|
|
5270
5479
|
|
|
5271
5480
|
// src/version.ts
|
|
5272
|
-
var VERSION = "0.
|
|
5273
|
-
var GIT_SHA = "
|
|
5274
|
-
var BUILD_TIME = "2026-10-
|
|
5481
|
+
var VERSION = "0.45.2166";
|
|
5482
|
+
var GIT_SHA = "32bda10";
|
|
5483
|
+
var BUILD_TIME = "2026-10-26T20:01:09.868Z";
|
|
5275
5484
|
var SEMVER = {
|
|
5276
|
-
version: "0.
|
|
5277
|
-
gitSha: "
|
|
5278
|
-
buildTime: "2026-10-
|
|
5485
|
+
version: "0.45.2166",
|
|
5486
|
+
gitSha: "32bda10",
|
|
5487
|
+
buildTime: "2026-10-26T20:01:09.868Z"
|
|
5279
5488
|
};
|
|
5280
5489
|
|
|
5281
5490
|
Object.defineProperty(exports, "DEFAULT_NOTEBOOK_SLUGS", {
|
|
@@ -5331,6 +5540,7 @@ exports.PageSourceRegistry = PageSourceRegistry;
|
|
|
5331
5540
|
exports.QueryDocumentRunner = QueryDocumentRunner;
|
|
5332
5541
|
exports.QueryService = QueryService;
|
|
5333
5542
|
exports.SEMVER = SEMVER;
|
|
5543
|
+
exports.SOURCE_ID_SHAPES = SOURCE_ID_SHAPES;
|
|
5334
5544
|
exports.SharedQueryDocumentRunner = QueryDocumentRunner2;
|
|
5335
5545
|
exports.UNASSIGNED = UNASSIGNED;
|
|
5336
5546
|
exports.VERSION = VERSION;
|
|
@@ -5350,6 +5560,7 @@ exports.WQL_KEYWORDS = WQL_KEYWORDS;
|
|
|
5350
5560
|
exports.WQL_METRIC_AGGREGATES = WQL_METRIC_AGGREGATES;
|
|
5351
5561
|
exports.WQL_METRIC_FAMILIES = WQL_METRIC_FAMILIES;
|
|
5352
5562
|
exports.WQL_NOTE_DEFAULT_SOURCES = WQL_NOTE_DEFAULT_SOURCES;
|
|
5563
|
+
exports.WQL_NOTE_HEAD_ALIASES = WQL_NOTE_HEAD_ALIASES;
|
|
5353
5564
|
exports.WQL_RESULT_PLANES = WQL_RESULT_PLANES;
|
|
5354
5565
|
exports.WQL_ROLLUP_PERIODS = WQL_ROLLUP_PERIODS;
|
|
5355
5566
|
exports.WQL_SOURCES = WQL_SOURCES;
|
|
@@ -5368,7 +5579,6 @@ exports.buildDashboardDocument = buildDashboardDocument;
|
|
|
5368
5579
|
exports.buildDashboardScaffold = buildDashboardScaffold;
|
|
5369
5580
|
exports.buildDrillDownQuery = buildDrillDownQuery;
|
|
5370
5581
|
exports.captureContext = captureContext;
|
|
5371
|
-
exports.catalogOfItem = catalogOfItem;
|
|
5372
5582
|
exports.civilDateAdd = civilDateAdd;
|
|
5373
5583
|
exports.civilDateDiff = civilDateDiff;
|
|
5374
5584
|
exports.civilDateOf = civilDateOf;
|
|
@@ -5384,10 +5594,12 @@ exports.duplicateWidget = duplicateWidget;
|
|
|
5384
5594
|
exports.eventsToStoredLogs = eventsToStoredLogs;
|
|
5385
5595
|
exports.extractDashboardTokens = extractDashboardTokens;
|
|
5386
5596
|
exports.extractFrontmatterTags = extractFrontmatterTags;
|
|
5597
|
+
exports.feedOfItem = feedOfItem;
|
|
5387
5598
|
exports.findTargetAdvisories = findTargetAdvisories;
|
|
5388
5599
|
exports.getList = getList;
|
|
5389
5600
|
exports.getScalar = getScalar;
|
|
5390
5601
|
exports.getUnitFamily = getUnitFamily;
|
|
5602
|
+
exports.hasLegacyIdentity = hasLegacyIdentity;
|
|
5391
5603
|
exports.inRange = inRange;
|
|
5392
5604
|
exports.isAggregateQuery = isAggregateQuery;
|
|
5393
5605
|
exports.isDashboardMeta = isDashboardMeta;
|
|
@@ -5396,11 +5608,18 @@ exports.isEffortDiscipline = isEffortDiscipline;
|
|
|
5396
5608
|
exports.isFindQuery = isFindQuery;
|
|
5397
5609
|
exports.isPipelineQuery = isPipelineQuery;
|
|
5398
5610
|
exports.isProposedMetric = isProposedMetric;
|
|
5611
|
+
exports.migrateLegacyChunkId = migrateLegacyChunkId;
|
|
5399
5612
|
exports.migrateLegacyDashboardBodies = migrateLegacyDashboardBodies;
|
|
5613
|
+
exports.migrateLegacyRouteId = migrateLegacyRouteId;
|
|
5614
|
+
exports.migrateLegacyRow = migrateLegacyRow;
|
|
5615
|
+
exports.migrateLegacySourceId = migrateLegacySourceId;
|
|
5616
|
+
exports.migrateLegacySourcePath = migrateLegacySourcePath;
|
|
5400
5617
|
exports.moveWidget = moveWidget;
|
|
5401
5618
|
exports.normalizeSummaryFacts = normalizeSummaryFacts;
|
|
5402
5619
|
exports.normalizeWql = normalizeWql;
|
|
5403
5620
|
exports.notebookOfItem = notebookOfItem;
|
|
5621
|
+
exports.notebookOriginOfItem = notebookOriginOfItem;
|
|
5622
|
+
exports.notebookSlugOfSourceId = notebookSlugOfSourceId;
|
|
5404
5623
|
exports.parseDashboardNote = parseDashboardNote;
|
|
5405
5624
|
exports.parseDocument = parseDocument;
|
|
5406
5625
|
exports.parseFlatProperties = parseFlatProperties;
|
|
@@ -5426,6 +5645,7 @@ exports.serializeDocument = serializeDocument;
|
|
|
5426
5645
|
exports.serializeFrontmatter = serializeFrontmatter;
|
|
5427
5646
|
exports.setDashboardTokenValue = setDashboardTokenValue;
|
|
5428
5647
|
exports.sourceMatches = sourceMatches;
|
|
5648
|
+
exports.sourceShape = sourceShape;
|
|
5429
5649
|
exports.splitAtWhere = splitAtWhere;
|
|
5430
5650
|
exports.stripFrontmatter = stripFrontmatter;
|
|
5431
5651
|
exports.substituteTokens = substituteTokens2;
|