@bitcobblers/wod-wiki-wql 0.35.2116 → 0.35.2117

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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { KNOWN_OUTPUT_TYPES, dimensionKeyOf, convertViaCatalog, systemDefaultUnit, MetricType, valueKindOf, normalizeFieldPath, fieldRefKey, normalizeFieldComponent } from '@bitcobblers/wod-wiki-core';
1
+ import { KNOWN_OUTPUT_TYPES, dimensionKeyOf, convertViaCatalog, systemDefaultUnit, MetricType, valueKindOf, normalizeFieldPath, validateNoteBlocks, fieldRefKey, normalizeFieldComponent } from '@bitcobblers/wod-wiki-core';
2
2
  export { SYSTEM_DEFAULT_OUTPUT_UNIT, UnitConversionError, convertViaCatalog, dimensionKeyOf, lookupUnit, systemDefaultUnit } from '@bitcobblers/wod-wiki-core';
3
3
  import { LRParser } from '@lezer/lr';
4
4
  import { LRLanguage, HighlightStyle, LanguageSupport, syntaxHighlighting } from '@codemirror/language';
@@ -2591,8 +2591,8 @@ function normalizeSummaryFacts(logs, identity) {
2591
2591
  origin: identity.origin,
2592
2592
  pageId: identity.pageId,
2593
2593
  grain: "summary",
2594
- segmentId: identity.segmentId ?? "",
2595
- segmentVersion: identity.segmentVersion ?? 0,
2594
+ blockId: identity.blockId ?? "",
2595
+ blockVersion: identity.blockVersion ?? 0,
2596
2596
  resultId: identity.resultId,
2597
2597
  type: f.metricKey,
2598
2598
  value: f.value,
@@ -2644,8 +2644,8 @@ function toEventRows(logs, identity) {
2644
2644
  text: output.text,
2645
2645
  dialect: output.dialect,
2646
2646
  completionReason: output.completionReason,
2647
- segmentId: identity.segmentId,
2648
- segmentVersion: identity.segmentVersion
2647
+ blockId: identity.blockId,
2648
+ blockVersion: identity.blockVersion
2649
2649
  }));
2650
2650
  }
2651
2651
  function toSummaryEventRows(logs, identity) {
@@ -2692,8 +2692,8 @@ function toSummaryEventRows(logs, identity) {
2692
2692
  ...f.grade ? { grade: f.grade } : {}
2693
2693
  }
2694
2694
  }],
2695
- segmentId: identity.segmentId,
2696
- segmentVersion: identity.segmentVersion
2695
+ blockId: identity.blockId,
2696
+ blockVersion: identity.blockVersion
2697
2697
  }));
2698
2698
  }
2699
2699
  function projectEventToFacts(record) {
@@ -2712,8 +2712,8 @@ function projectEventToFacts(record) {
2712
2712
  origin: record.origin,
2713
2713
  pageId: record.pageId,
2714
2714
  grain: "summary",
2715
- segmentId: record.segmentId ?? "",
2716
- segmentVersion: record.segmentVersion ?? 0,
2715
+ blockId: record.blockId ?? "",
2716
+ blockVersion: record.blockVersion ?? 0,
2717
2717
  resultId: record.resultId,
2718
2718
  type: metricKey,
2719
2719
  value: m.value,
@@ -2771,8 +2771,8 @@ function projectEventToFacts(record) {
2771
2771
  origin: record.origin,
2772
2772
  pageId: record.pageId,
2773
2773
  grain: "event",
2774
- segmentId: record.segmentId ?? "",
2775
- segmentVersion: record.segmentVersion ?? 0,
2774
+ blockId: record.blockId ?? "",
2775
+ blockVersion: record.blockVersion ?? 0,
2776
2776
  resultId: record.resultId,
2777
2777
  type: metricKey,
2778
2778
  value: m.value,
@@ -2814,117 +2814,6 @@ function eventsToStoredLogs(events) {
2814
2814
  }));
2815
2815
  }
2816
2816
 
2817
- // src/dashboard/frontmatter.ts
2818
- var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
2819
- function unquote(value) {
2820
- return value.replace(
2821
- /^(['"])(.*)\1$/,
2822
- (_match, quote, inner) => quote === '"' ? inner.replace(/\\(["\\])/g, "$1") : inner
2823
- );
2824
- }
2825
- function parseFrontmatter(raw) {
2826
- const match = raw.match(FRONTMATTER_RE);
2827
- if (!match) return { meta: {}, body: raw };
2828
- return { meta: parseMetaLines(match[1].split(/\r?\n/)), body: raw.slice(match[0].length) };
2829
- }
2830
- function parseMetaLines(lines) {
2831
- const meta = {};
2832
- for (let i = 0; i < lines.length; i++) {
2833
- const keyMatch = lines[i].match(/^([A-Za-z][\w.-]*)\s*:(.*)$/);
2834
- if (!keyMatch) continue;
2835
- const key = keyMatch[1];
2836
- const rawVal = keyMatch[2].trim();
2837
- if (rawVal === "") {
2838
- const items = [];
2839
- while (i + 1 < lines.length) {
2840
- const itemMatch = lines[i + 1].match(/^\s+-\s+(.+)$/);
2841
- if (!itemMatch) break;
2842
- items.push(itemMatch[1].trim());
2843
- i++;
2844
- }
2845
- meta[key] = items.length > 0 ? items : "";
2846
- } else {
2847
- const wasQuoted = /^(['"])(.*)\1$/.test(rawVal);
2848
- const value = unquote(rawVal);
2849
- const num = Number(value);
2850
- meta[key] = !wasQuoted && value !== "" && !isNaN(num) ? num : value;
2851
- }
2852
- }
2853
- return meta;
2854
- }
2855
- function parseFrontmatterBody(innerContent) {
2856
- return parseMetaLines(innerContent.split(/\r?\n/));
2857
- }
2858
- function quoteYamlScalar(value) {
2859
- if (value === "") return '""';
2860
- const looksNumeric = !isNaN(Number(value));
2861
- const looksKeyword = /^(true|false|null|yes|no|on|off)$/i.test(value);
2862
- if (/[":'\n#{}[\\],&*?|<>=%!@`]/.test(value) || value !== value.trim() || value.startsWith("-") || looksNumeric || looksKeyword) {
2863
- return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
2864
- }
2865
- return value;
2866
- }
2867
- function serializeFrontmatter(meta) {
2868
- const lines = [];
2869
- for (const [key, value] of Object.entries(meta)) {
2870
- if (Array.isArray(value)) {
2871
- if (value.length === 0) {
2872
- lines.push(`${key}: ""`);
2873
- continue;
2874
- }
2875
- lines.push(`${key}:`);
2876
- for (const item of value) {
2877
- lines.push(` - ${quoteYamlScalar(item)}`);
2878
- }
2879
- } else if (typeof value === "number") {
2880
- lines.push(`${key}: ${value}`);
2881
- } else {
2882
- lines.push(`${key}: ${quoteYamlScalar(value)}`);
2883
- }
2884
- }
2885
- return lines.join("\n");
2886
- }
2887
- function stripFrontmatter(raw) {
2888
- return parseFrontmatter(raw).body;
2889
- }
2890
- function getScalar(meta, key) {
2891
- const value = meta[key];
2892
- return typeof value === "string" || typeof value === "number" ? value : void 0;
2893
- }
2894
- function getList(meta, key) {
2895
- const value = meta[key];
2896
- return Array.isArray(value) ? value : [];
2897
- }
2898
- function parseFrontmatterCategories(raw) {
2899
- return getList(parseFrontmatter(raw).meta, "category").map((c) => c.toLowerCase());
2900
- }
2901
- function extractFrontmatterTags(raw) {
2902
- const meta = FRONTMATTER_RE.test(raw) ? parseFrontmatter(raw).meta : parseFrontmatterBody(raw);
2903
- const tags = meta["tags"];
2904
- const list = Array.isArray(tags) ? tags : typeof tags === "string" && tags.trim() ? tags.trim().startsWith("[") && tags.trim().endsWith("]") ? tags.trim().slice(1, -1).split(",") : [tags] : [];
2905
- return [...new Set(list.map((tag) => tag.trim()).filter(Boolean))];
2906
- }
2907
- function parseFlatProperties(innerContent) {
2908
- const props = {};
2909
- for (const line of innerContent.split(/\r?\n/)) {
2910
- const match = line.match(/^([^:]+):\s*(.*)$/);
2911
- if (match) {
2912
- props[match[1].trim()] = match[2].trim();
2913
- }
2914
- }
2915
- return props;
2916
- }
2917
- function parseFrontmatterProps(lines) {
2918
- const props = {};
2919
- for (const line of lines) {
2920
- const match = line.match(/^([^:]+):\s*(.*)$/);
2921
- if (match) {
2922
- props[match[1].trim()] = match[2].trim();
2923
- }
2924
- }
2925
- return props;
2926
- }
2927
-
2928
2817
  // src/selection.ts
2929
2818
  function partitionKey(coverage, effortSlug) {
2930
2819
  const tags = coverage?.groupTags ? Object.entries(coverage.groupTags).sort(([a], [b]) => a < b ? -1 : 1).map(([k, v]) => `${k}=${v}`).join(",") : "";
@@ -3398,7 +3287,6 @@ var QueryService = class {
3398
3287
  noteStore;
3399
3288
  blockStore;
3400
3289
  effortStore;
3401
- staticNoteStore;
3402
3290
  pageStore;
3403
3291
  relationStore;
3404
3292
  datasetStore;
@@ -3413,14 +3301,13 @@ var QueryService = class {
3413
3301
  noteTagsStore = {
3414
3302
  getAllFromIndex: async () => []
3415
3303
  };
3416
- constructor(storesOrEventStore, noteStore, blockStore, effortStore, staticNoteStore) {
3417
- if (storesOrEventStore && typeof storesOrEventStore === "object" && ("eventStore" in storesOrEventStore || "noteStore" in storesOrEventStore || "blockStore" in storesOrEventStore || "effortStore" in storesOrEventStore || "staticNoteStore" in storesOrEventStore || "pageStore" in storesOrEventStore || "relationStore" in storesOrEventStore || "datasetStore" in storesOrEventStore)) {
3304
+ constructor(storesOrEventStore, noteStore, blockStore, effortStore) {
3305
+ if (storesOrEventStore && typeof storesOrEventStore === "object" && ("eventStore" in storesOrEventStore || "noteStore" in storesOrEventStore || "blockStore" in storesOrEventStore || "effortStore" in storesOrEventStore || "pageStore" in storesOrEventStore || "relationStore" in storesOrEventStore || "datasetStore" in storesOrEventStore)) {
3418
3306
  const stores = storesOrEventStore;
3419
3307
  this.store = stores.eventStore ?? defaultEventStore;
3420
3308
  this.noteStore = stores.noteStore ?? defaultNoteStore;
3421
3309
  this.blockStore = stores.blockStore ?? defaultBlockStore;
3422
3310
  this.effortStore = stores.effortStore ?? defaultEffortStore;
3423
- this.staticNoteStore = stores.staticNoteStore;
3424
3311
  this.pageStore = stores.pageStore;
3425
3312
  this.relationStore = stores.relationStore;
3426
3313
  this.datasetStore = stores.datasetStore;
@@ -3432,7 +3319,6 @@ var QueryService = class {
3432
3319
  this.noteStore = noteStore ?? defaultNoteStore;
3433
3320
  this.blockStore = blockStore ?? defaultBlockStore;
3434
3321
  this.effortStore = effortStore ?? defaultEffortStore;
3435
- this.staticNoteStore = staticNoteStore;
3436
3322
  }
3437
3323
  }
3438
3324
  /** Windowed flat-fact fetch — the projected view over the event store. */
@@ -3752,14 +3638,11 @@ var QueryService = class {
3752
3638
  }
3753
3639
  const isCatalogHead = parsed.target === "note" && parsed.filters.some((f) => f.key === "type" && !f.negate && f.values.some((v) => v.value === "feed")) && (parsed.filters.some((f) => f.key === "source" && !f.negate && f.values.some((v) => v.value === "feed" || v.value === "feeds")) || parsed.sourceScope?.includes("feeds"));
3754
3640
  if (isCatalogHead) {
3755
- const notes2 = (await this.noteStore.getAllNotes()).concat(
3756
- this.staticNoteStore ? await this.staticNoteStore.getAllNotes() : []
3757
- );
3758
- return this.runFindCatalog(parsed, options2, notes2);
3641
+ return this.runFindCatalog(parsed, options2, await this.noteStore.getAllNotes());
3759
3642
  }
3760
3643
  const read = compileContentRead(parsed.filters, "note", parsed.sourceScope);
3761
- const paging = domainPaging(parsed, options2, read.residual || !!this.staticNoteStore);
3762
- const fetchedNotes = !this.staticNoteStore && (read.filters.length > 0 || read.selection.length > 0 || paging.paged) && this.noteStore.queryDomain ? await this.noteStore.queryDomain({
3644
+ const paging = domainPaging(parsed, options2, read.residual);
3645
+ const fetchedNotes = (read.filters.length > 0 || read.selection.length > 0 || paging.paged) && this.noteStore.queryDomain ? await this.noteStore.queryDomain({
3763
3646
  plan: "notes",
3764
3647
  selection: read.selection,
3765
3648
  ...read.filters.length ? { filters: read.filters } : {},
@@ -3771,11 +3654,6 @@ var QueryService = class {
3771
3654
  let notes = domainNotes ? domainNotes.rows : await this.noteStore.getAllNotes();
3772
3655
  const selectedFromDomain = domainNotes ? domainNotes.selectedCount : void 0;
3773
3656
  const notesPaged = !!domainNotes && paging.paged;
3774
- if (this.staticNoteStore) {
3775
- const staticRows = applyNoteSelectionStages(await this.staticNoteStore.getAllNotes(), parsed);
3776
- const known = new Set(notes.map((n) => n.id));
3777
- notes = notes.concat(staticRows.filter((n) => !known.has(n.id)));
3778
- }
3779
3657
  notes = applyNoteSelectionStages(notes, parsed);
3780
3658
  const selectedCount = selectedFromDomain ?? notes.length;
3781
3659
  const ctx = runContext(options2);
@@ -3784,16 +3662,13 @@ var QueryService = class {
3784
3662
  const matchingIds = /* @__PURE__ */ new Set();
3785
3663
  for (const v of filter.values) {
3786
3664
  const ids = await this.noteStore.getNoteIdsForTag(v.value);
3787
- const sIds = this.staticNoteStore ? await this.staticNoteStore.getNoteIdsForTag(v.value) : /* @__PURE__ */ new Set();
3788
3665
  ids.forEach((id) => matchingIds.add(id));
3789
- sIds.forEach((id) => matchingIds.add(id));
3790
3666
  }
3791
3667
  notes = notes.filter((n) => matchingIds.has(n.id));
3792
3668
  } else if (filter.key === "tags" && filter.negate) {
3793
3669
  for (const v of filter.values) {
3794
3670
  const ids = await this.noteStore.getNoteIdsForTag(v.value);
3795
- const sIds = this.staticNoteStore ? await this.staticNoteStore.getNoteIdsForTag(v.value) : /* @__PURE__ */ new Set();
3796
- notes = notes.filter((n) => !ids.has(n.id) && !sIds.has(n.id));
3671
+ notes = notes.filter((n) => !ids.has(n.id));
3797
3672
  }
3798
3673
  } else if (filter.key === "effort" && !filter.negate) {
3799
3674
  const matchingIds = /* @__PURE__ */ new Set();
@@ -3812,9 +3687,12 @@ var QueryService = class {
3812
3687
  }
3813
3688
  }
3814
3689
  for (const filter of parsed.filters) {
3815
- if (filter.key === "text" && !filter.negate) {
3690
+ if (filter.key === "text") {
3816
3691
  const search = filter.values.map((v) => v.value).join(" ").toLowerCase();
3817
- notes = notes.filter((n) => n.title.toLowerCase().includes(search));
3692
+ notes = notes.filter((n) => {
3693
+ const hit = n.title.toLowerCase().includes(search) || n.rawContent.toLowerCase().includes(search);
3694
+ return filter.negate ? !hit : hit;
3695
+ });
3818
3696
  }
3819
3697
  }
3820
3698
  for (const filter of parsed.filters) {
@@ -3911,6 +3789,26 @@ var QueryService = class {
3911
3789
  }
3912
3790
  return containers;
3913
3791
  }
3792
+ async hydrateBlocks(rows) {
3793
+ const ids = [...new Set(rows.map((row) => row.noteId))];
3794
+ if (ids.length === 0) return [];
3795
+ const notes = this.noteStore.getNotesByIds ? await this.noteStore.getNotesByIds(ids) : await this.noteStore.getAllNotes();
3796
+ const parents = /* @__PURE__ */ new Map();
3797
+ for (const note of notes) {
3798
+ if (!Number.isSafeInteger(note.sourceRevision) || note.sourceRevision < 1) continue;
3799
+ try {
3800
+ validateNoteBlocks(note.rawContent, note.blocks, note.sourceRevision);
3801
+ parents.set(note.id, note);
3802
+ } catch {
3803
+ }
3804
+ }
3805
+ return rows.flatMap((row) => {
3806
+ const note = parents.get(row.noteId);
3807
+ const block = note?.blocks[row.blockId];
3808
+ if (!note || !block || row.id !== `${note.id}:${block.id}` || block.id !== row.blockId || block.kind !== row.dataType || block.version !== row.blockVersion || block.sourceRevision !== row.sourceRevision || note.sourceRevision !== row.sourceRevision || block.from !== row.from || block.to !== row.to) return [];
3809
+ return [{ ...row, rawContent: note.rawContent.slice(block.from, block.to) }];
3810
+ });
3811
+ }
3914
3812
  /**
3915
3813
  * Execute a :block query against the derived block_index store.
3916
3814
  */
@@ -3926,17 +3824,20 @@ var QueryService = class {
3926
3824
  ...paging.limit !== void 0 ? { limit: paging.limit } : {}
3927
3825
  }) : void 0;
3928
3826
  const domainBlocks = fetchedBlocks && fetchedBlocks.plan === "blocks" ? fetchedBlocks : void 0;
3929
- let blocks = domainBlocks ? domainBlocks.rows : await this.blockStore.getAllBlocks();
3930
- const selectedFromDomain = domainBlocks ? domainBlocks.selectedCount : void 0;
3931
- const blocksPaged = !!domainBlocks && paging.paged;
3827
+ const candidates = domainBlocks ? domainBlocks.rows : await this.blockStore.getAllBlocks();
3828
+ let blocks = await this.hydrateBlocks(candidates);
3829
+ const invalidCandidates = blocks.length !== candidates.length;
3830
+ const selectedFromDomain = domainBlocks && !invalidCandidates ? domainBlocks.selectedCount : void 0;
3831
+ const blocksPaged = !!domainBlocks && paging.paged && !invalidCandidates;
3832
+ if (domainBlocks && invalidCandidates) blocks = await this.hydrateBlocks(await this.blockStore.getAllBlocks());
3932
3833
  blocks = applySourceFilter(blocks, parsed.filters);
3933
3834
  blocks = blocks.filter((b) => !b.sourceId || WQL_SOURCE_VALUES.some((k) => sourceMatches(b, k)));
3934
3835
  const selectedCount = selectedFromDomain ?? blocks.length;
3935
3836
  const ctx = runContext(options2);
3936
3837
  for (const filter of parsed.filters) {
3937
- if (filter.key === "text" && !filter.negate) {
3838
+ if (filter.key === "text") {
3938
3839
  const search = filter.values.map((v) => v.value).join(" ").toLowerCase();
3939
- blocks = blocks.filter((b) => b.rawContent.toLowerCase().includes(search));
3840
+ blocks = blocks.filter((b) => filter.negate !== b.rawContent.toLowerCase().includes(search));
3940
3841
  }
3941
3842
  }
3942
3843
  for (const filter of parsed.filters) {
@@ -3961,18 +3862,16 @@ var QueryService = class {
3961
3862
  const matchingNoteIds = /* @__PURE__ */ new Set();
3962
3863
  for (const v of filter.values) {
3963
3864
  const ids = await this.noteStore.getNoteIdsForTag(v.value);
3964
- const sIds = this.staticNoteStore ? await this.staticNoteStore.getNoteIdsForTag(v.value) : /* @__PURE__ */ new Set();
3965
3865
  ids.forEach((id) => matchingNoteIds.add(id));
3966
- sIds.forEach((id) => matchingNoteIds.add(id));
3967
3866
  }
3968
3867
  blocks = blocks.filter((b) => matchingNoteIds.has(b.noteId));
3969
3868
  } else if (filter.key === "effort" && !filter.negate) {
3970
3869
  const matchingBlockIds = /* @__PURE__ */ new Set();
3971
3870
  for (const v of filter.values) {
3972
3871
  const rows = await this.blockEffortsStore.getAllFromIndex("by-effort", v.value);
3973
- for (const r of rows) matchingBlockIds.add(r.blockContentId);
3872
+ for (const row of rows) matchingBlockIds.add(`${row.noteId}:${row.blockId}`);
3974
3873
  }
3975
- blocks = blocks.filter((b) => b.blockContentId && matchingBlockIds.has(b.blockContentId));
3874
+ blocks = blocks.filter((block) => matchingBlockIds.has(`${block.noteId}:${block.blockId}`));
3976
3875
  }
3977
3876
  }
3978
3877
  for (const filter of parsed.filters) {
@@ -3982,11 +3881,7 @@ var QueryService = class {
3982
3881
  }
3983
3882
  }
3984
3883
  if (parsed.window || options2.range) {
3985
- let allNotes = [];
3986
- allNotes = allNotes.concat(await this.noteStore.getAllNotes());
3987
- if (this.staticNoteStore) {
3988
- allNotes = allNotes.concat(await this.staticNoteStore.getAllNotes());
3989
- }
3884
+ const allNotes = await this.noteStore.getAllNotes();
3990
3885
  const noteTimeMap = new Map(allNotes.map((n) => [n.id, n.date ?? n.createdAt]));
3991
3886
  blocks = blocks.filter((b) => effectiveTimeWindow(noteTimeMap.get(b.noteId) ?? b.createdAt, parsed.window, options2.range, ctx));
3992
3887
  }
@@ -4364,7 +4259,7 @@ var QueryService = class {
4364
4259
  let values;
4365
4260
  try {
4366
4261
  values = members.map(
4367
- (m) => shouldConvert && targetUnit ? convertViaCatalog(m.value, m.unit ?? m.metricUnit, targetUnit) : m.value
4262
+ (m) => shouldConvert && targetUnit && (m.unit ?? m.metricUnit) !== void 0 ? convertViaCatalog(m.value, m.unit ?? m.metricUnit, targetUnit) : m.value
4368
4263
  );
4369
4264
  } catch (e) {
4370
4265
  error = e instanceof Error ? e.message : String(e);
@@ -4486,7 +4381,7 @@ var QueryService = class {
4486
4381
  }
4487
4382
  /** All content blocks — journal and seeded corpus in one store. */
4488
4383
  async allContentBlocks() {
4489
- return this.blockStore.getAllBlocks();
4384
+ return this.hydrateBlocks(await this.blockStore.getAllBlocks());
4490
4385
  }
4491
4386
  /** Map each note id to the wod blockContentIds it owns. */
4492
4387
  async noteContentMap(noteIds) {
@@ -4563,7 +4458,10 @@ var QueryService = class {
4563
4458
  type: "feed",
4564
4459
  sourceId: `page:collection:${cat}`,
4565
4460
  catalog: cat,
4566
- createdAt: n.createdAt ?? 0
4461
+ createdAt: n.createdAt ?? 0,
4462
+ rawContent: "",
4463
+ sourceRevision: 1,
4464
+ blocks: {}
4567
4465
  });
4568
4466
  existingCatIds.add(cat);
4569
4467
  }
@@ -4612,9 +4510,7 @@ var QueryService = class {
4612
4510
  const matchingIds = /* @__PURE__ */ new Set();
4613
4511
  for (const v of filter.values) {
4614
4512
  const ids = await this.noteStore.getNoteIdsForTag(v.value);
4615
- const sIds = this.staticNoteStore ? await this.staticNoteStore.getNoteIdsForTag(v.value) : /* @__PURE__ */ new Set();
4616
4513
  ids.forEach((id) => matchingIds.add(id));
4617
- sIds.forEach((id) => matchingIds.add(id));
4618
4514
  }
4619
4515
  matchingCatalogs = matchingCatalogs.filter((c) => {
4620
4516
  const catId = catalogOfItem(c) ?? c.id;
@@ -4639,7 +4535,7 @@ var QueryService = class {
4639
4535
  matchingCatalogs = matchingCatalogs.filter((c) => {
4640
4536
  const catId = catalogOfItem(c) ?? c.id;
4641
4537
  const members = membersByCat.get(catId) ?? [];
4642
- const hit = members.some((m) => m.title.toLowerCase().includes(search));
4538
+ const hit = members.some((m) => m.title.toLowerCase().includes(search) || m.rawContent.toLowerCase().includes(search));
4643
4539
  return filter.negate ? !hit : hit;
4644
4540
  });
4645
4541
  } else if (filter.key === "note") {
@@ -4679,56 +4575,6 @@ var QueryService = class {
4679
4575
  }
4680
4576
  };
4681
4577
 
4682
- // src/static.ts
4683
- function staticCatalogRoute(block) {
4684
- const sourceId = block.sourceId;
4685
- if (sourceId?.startsWith("page:collection:")) {
4686
- const catalog = sourceId.slice("page:collection:".length);
4687
- if (catalog) return { landing: true, catalog, sourceId };
4688
- } else if (sourceId?.startsWith("collection:")) {
4689
- const route = sourceId.slice("collection:".length);
4690
- if (route) {
4691
- const slash = route.indexOf("/");
4692
- return slash === -1 ? { landing: true, catalog: route, sourceId: `page:collection:${route}` } : { landing: false, catalog: route.slice(0, slash), sourceId };
4693
- }
4694
- } else if (sourceId?.startsWith("feed:")) {
4695
- const route = sourceId.slice("feed:".length);
4696
- if (route) {
4697
- const stem = route.startsWith("feeds/") ? route.slice("feeds/".length) : route;
4698
- return { landing: false, catalog: stem.split("/")[0], sourceId };
4699
- }
4700
- }
4701
- return { landing: false, catalog: catalogOfItem({ noteId: block.noteId, sourceId: block.sourceId }), sourceId };
4702
- }
4703
- function staticNotesFromBlocks(blocks) {
4704
- const map = /* @__PURE__ */ new Map();
4705
- for (const block of blocks) {
4706
- if (map.has(block.noteId)) continue;
4707
- const route = staticCatalogRoute(block);
4708
- map.set(block.noteId, {
4709
- id: block.noteId,
4710
- title: block.noteTitle,
4711
- createdAt: block.createdAt,
4712
- type: route.landing ? "feed" : "note",
4713
- sourceId: route.sourceId,
4714
- catalog: route.catalog
4715
- });
4716
- }
4717
- return Array.from(map.values());
4718
- }
4719
- function staticTagIndexFromBlocks(blocks) {
4720
- const index = /* @__PURE__ */ new Map();
4721
- for (const block of blocks) {
4722
- if (block.dataType !== "frontmatter") continue;
4723
- for (const tag of extractFrontmatterTags(block.rawContent)) {
4724
- const ids = index.get(tag);
4725
- if (ids) ids.add(block.noteId);
4726
- else index.set(tag, /* @__PURE__ */ new Set([block.noteId]));
4727
- }
4728
- }
4729
- return index;
4730
- }
4731
-
4732
4578
  // src/dashboard/model.ts
4733
4579
  var DASHBOARD_WIDGET_TYPES = [
4734
4580
  "table",
@@ -4917,6 +4763,117 @@ function buildDashboardDocument(sections, meta) {
4917
4763
  };
4918
4764
  }
4919
4765
 
4766
+ // src/dashboard/frontmatter.ts
4767
+ var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
4768
+ function unquote(value) {
4769
+ return value.replace(
4770
+ /^(['"])(.*)\1$/,
4771
+ (_match, quote, inner) => quote === '"' ? inner.replace(/\\(["\\])/g, "$1") : inner
4772
+ );
4773
+ }
4774
+ function parseFrontmatter(raw) {
4775
+ const match = raw.match(FRONTMATTER_RE);
4776
+ if (!match) return { meta: {}, body: raw };
4777
+ return { meta: parseMetaLines(match[1].split(/\r?\n/)), body: raw.slice(match[0].length) };
4778
+ }
4779
+ function parseMetaLines(lines) {
4780
+ const meta = {};
4781
+ for (let i = 0; i < lines.length; i++) {
4782
+ const keyMatch = lines[i].match(/^([A-Za-z][\w.-]*)\s*:(.*)$/);
4783
+ if (!keyMatch) continue;
4784
+ const key = keyMatch[1];
4785
+ const rawVal = keyMatch[2].trim();
4786
+ if (rawVal === "") {
4787
+ const items = [];
4788
+ while (i + 1 < lines.length) {
4789
+ const itemMatch = lines[i + 1].match(/^\s+-\s+(.+)$/);
4790
+ if (!itemMatch) break;
4791
+ items.push(itemMatch[1].trim());
4792
+ i++;
4793
+ }
4794
+ meta[key] = items.length > 0 ? items : "";
4795
+ } else {
4796
+ const wasQuoted = /^(['"])(.*)\1$/.test(rawVal);
4797
+ const value = unquote(rawVal);
4798
+ const num = Number(value);
4799
+ meta[key] = !wasQuoted && value !== "" && !isNaN(num) ? num : value;
4800
+ }
4801
+ }
4802
+ return meta;
4803
+ }
4804
+ function parseFrontmatterBody(innerContent) {
4805
+ return parseMetaLines(innerContent.split(/\r?\n/));
4806
+ }
4807
+ function quoteYamlScalar(value) {
4808
+ if (value === "") return '""';
4809
+ const looksNumeric = !isNaN(Number(value));
4810
+ const looksKeyword = /^(true|false|null|yes|no|on|off)$/i.test(value);
4811
+ if (/[":'\n#{}[\\],&*?|<>=%!@`]/.test(value) || value !== value.trim() || value.startsWith("-") || looksNumeric || looksKeyword) {
4812
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
4813
+ }
4814
+ return value;
4815
+ }
4816
+ function serializeFrontmatter(meta) {
4817
+ const lines = [];
4818
+ for (const [key, value] of Object.entries(meta)) {
4819
+ if (Array.isArray(value)) {
4820
+ if (value.length === 0) {
4821
+ lines.push(`${key}: ""`);
4822
+ continue;
4823
+ }
4824
+ lines.push(`${key}:`);
4825
+ for (const item of value) {
4826
+ lines.push(` - ${quoteYamlScalar(item)}`);
4827
+ }
4828
+ } else if (typeof value === "number") {
4829
+ lines.push(`${key}: ${value}`);
4830
+ } else {
4831
+ lines.push(`${key}: ${quoteYamlScalar(value)}`);
4832
+ }
4833
+ }
4834
+ return lines.join("\n");
4835
+ }
4836
+ function stripFrontmatter(raw) {
4837
+ return parseFrontmatter(raw).body;
4838
+ }
4839
+ function getScalar(meta, key) {
4840
+ const value = meta[key];
4841
+ return typeof value === "string" || typeof value === "number" ? value : void 0;
4842
+ }
4843
+ function getList(meta, key) {
4844
+ const value = meta[key];
4845
+ return Array.isArray(value) ? value : [];
4846
+ }
4847
+ function parseFrontmatterCategories(raw) {
4848
+ return getList(parseFrontmatter(raw).meta, "category").map((c) => c.toLowerCase());
4849
+ }
4850
+ function extractFrontmatterTags(raw) {
4851
+ const meta = FRONTMATTER_RE.test(raw) ? parseFrontmatter(raw).meta : parseFrontmatterBody(raw);
4852
+ const tags = meta["tags"];
4853
+ const list = Array.isArray(tags) ? tags : typeof tags === "string" && tags.trim() ? tags.trim().startsWith("[") && tags.trim().endsWith("]") ? tags.trim().slice(1, -1).split(",") : [tags] : [];
4854
+ return [...new Set(list.map((tag) => tag.trim()).filter(Boolean))];
4855
+ }
4856
+ function parseFlatProperties(innerContent) {
4857
+ const props = {};
4858
+ for (const line of innerContent.split(/\r?\n/)) {
4859
+ const match = line.match(/^([^:]+):\s*(.*)$/);
4860
+ if (match) {
4861
+ props[match[1].trim()] = match[2].trim();
4862
+ }
4863
+ }
4864
+ return props;
4865
+ }
4866
+ function parseFrontmatterProps(lines) {
4867
+ const props = {};
4868
+ for (const line of lines) {
4869
+ const match = line.match(/^([^:]+):\s*(.*)$/);
4870
+ if (match) {
4871
+ props[match[1].trim()] = match[2].trim();
4872
+ }
4873
+ }
4874
+ return props;
4875
+ }
4876
+
4920
4877
  // src/dashboard/parser.ts
4921
4878
  function parseDashboardNote(rawContent) {
4922
4879
  const { meta } = parseFrontmatter(rawContent);
@@ -5121,6 +5078,44 @@ function appendWidget(raw, spec) {
5121
5078
  while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
5122
5079
  return [...lines, "", ...group, ""].join("\n");
5123
5080
  }
5081
+ function widgetResult(raw, note, order) {
5082
+ let from = 0;
5083
+ while (from < raw.length && from < note.length && raw[from] === note[from]) from++;
5084
+ let to = raw.length;
5085
+ let nextTo = note.length;
5086
+ while (to > from && nextTo > from && raw[to - 1] === note[nextTo - 1]) {
5087
+ to--;
5088
+ nextTo--;
5089
+ }
5090
+ const queries = parseDashboardNote(raw).sections.filter((section) => section.type === "query");
5091
+ const nextQueries = parseDashboardNote(note).sections.filter((section) => section.type === "query");
5092
+ const oldOffsets = lineOffsets(raw);
5093
+ const nextOffsets = lineOffsets(note);
5094
+ const oldLines = raw.split("\n");
5095
+ const nextLines = note.split("\n");
5096
+ const retainedBlocks = [];
5097
+ for (const [nextIndex, oldIndex] of (order ?? queries.map((_, index) => index)).entries()) {
5098
+ if (oldIndex === null) continue;
5099
+ const block = queries[oldIndex];
5100
+ const next = nextQueries[nextIndex];
5101
+ if (!block || !next) throw new Error("Widget mutation lost its source lineage");
5102
+ retainedBlocks.push({
5103
+ from: oldOffsets[block.startLine],
5104
+ to: oldOffsets[block.endLine] + oldLines[block.endLine].replace(/\r$/, "").length,
5105
+ nextFrom: nextOffsets[next.startLine],
5106
+ nextTo: nextOffsets[next.endLine] + nextLines[next.endLine].replace(/\r$/, "").length
5107
+ });
5108
+ }
5109
+ return { ok: true, note, changes: raw === note ? [] : [{ from, to, insert: note.slice(from, nextTo) }], retainedBlocks };
5110
+ }
5111
+ function lineOffsets(raw) {
5112
+ let offset = 0;
5113
+ return raw.split("\n").map((line) => {
5114
+ const from = offset;
5115
+ offset += line.length + 1;
5116
+ return from;
5117
+ });
5118
+ }
5124
5119
  function updateWidget(raw, key, expectedBody, spec) {
5125
5120
  const { sections } = parseDashboardNote(raw);
5126
5121
  const found = findWidgetGroup(sections, key, expectedBody);
@@ -5128,7 +5123,7 @@ function updateWidget(raw, key, expectedBody, spec) {
5128
5123
  const { group } = found;
5129
5124
  const lines = raw.split("\n");
5130
5125
  lines.splice(group.startLine, group.endLine - group.startLine + 1, ...renderWidgetGroup(spec));
5131
- return { ok: true, note: lines.join("\n") };
5126
+ return widgetResult(raw, lines.join("\n"));
5132
5127
  }
5133
5128
  function duplicateWidget(raw, key, expectedBody) {
5134
5129
  const { sections } = parseDashboardNote(raw);
@@ -5138,7 +5133,9 @@ function duplicateWidget(raw, key, expectedBody) {
5138
5133
  const lines = raw.split("\n");
5139
5134
  const copy = lines.slice(group.startLine, group.endLine + 1);
5140
5135
  lines.splice(group.endLine + 1, 0, "", ...copy);
5141
- return { ok: true, note: lines.join("\n") };
5136
+ const order = sections.filter((section) => section.type === "query").map((_, index) => index);
5137
+ order.splice(group.widgetIndex + 1, 0, null);
5138
+ return widgetResult(raw, lines.join("\n"), order);
5142
5139
  }
5143
5140
  function removeWidget(raw, key, expectedBody) {
5144
5141
  const { sections } = parseDashboardNote(raw);
@@ -5150,7 +5147,9 @@ function removeWidget(raw, key, expectedBody) {
5150
5147
  if (group.startLine > 0 && group.startLine < lines.length && lines[group.startLine - 1] === "" && lines[group.startLine] === "") {
5151
5148
  lines.splice(group.startLine, 1);
5152
5149
  }
5153
- return { ok: true, note: lines.join("\n") };
5150
+ const order = sections.filter((section) => section.type === "query").map((_, index) => index);
5151
+ order.splice(group.widgetIndex, 1);
5152
+ return widgetResult(raw, lines.join("\n"), order);
5154
5153
  }
5155
5154
  function moveWidget(raw, key, expectedBody, delta) {
5156
5155
  const { sections } = parseDashboardNote(raw);
@@ -5159,7 +5158,7 @@ function moveWidget(raw, key, expectedBody, delta) {
5159
5158
  const { group } = found;
5160
5159
  const groups = widgetGroupRanges(sections);
5161
5160
  const neighbor = groups[group.widgetIndex + delta];
5162
- if (!neighbor) return { ok: true, note: raw };
5161
+ if (!neighbor) return widgetResult(raw, raw);
5163
5162
  const lines = raw.split("\n");
5164
5163
  const moving = lines.slice(group.startLine, group.endLine + 1);
5165
5164
  if (delta === -1) {
@@ -5171,7 +5170,10 @@ function moveWidget(raw, key, expectedBody, delta) {
5171
5170
  const insertAt = neighbor.endLine - shift + 1;
5172
5171
  lines.splice(insertAt, 0, ...moving);
5173
5172
  }
5174
- return { ok: true, note: lines.join("\n") };
5173
+ const order = groups.map((_, index) => index);
5174
+ order.splice(group.widgetIndex, 1);
5175
+ order.splice(group.widgetIndex + delta, 0, group.widgetIndex);
5176
+ return widgetResult(raw, lines.join("\n"), order);
5175
5177
  }
5176
5178
  function resizeWidget(raw, key, expectedBody, span) {
5177
5179
  if (span.spanCols != null && (span.spanCols < 1 || span.spanCols > DASHBOARD_GRID_MAX_COLS)) {
@@ -5193,7 +5195,7 @@ function resizeWidget(raw, key, expectedBody, span) {
5193
5195
  attributes: block.attributes,
5194
5196
  attributeOrder: block.attributeOrder
5195
5197
  });
5196
- return { ok: true, note: lines.join("\n") };
5198
+ return widgetResult(raw, lines.join("\n"));
5197
5199
  }
5198
5200
  function migrateLegacyDashboardBodies(raw) {
5199
5201
  let out = raw;
@@ -5214,15 +5216,15 @@ ${query}
5214
5216
  }
5215
5217
 
5216
5218
  // src/version.ts
5217
- var VERSION = "0.35.2116";
5218
- var GIT_SHA = "a4d411f";
5219
- var BUILD_TIME = "2026-10-12T00:14:24.795Z";
5219
+ var VERSION = "0.35.2117";
5220
+ var GIT_SHA = "03d1dfc";
5221
+ var BUILD_TIME = "2026-10-12T20:08:12.374Z";
5220
5222
  var SEMVER = {
5221
- version: "0.35.2116",
5222
- gitSha: "a4d411f",
5223
- buildTime: "2026-10-12T00:14:24.795Z"
5223
+ version: "0.35.2117",
5224
+ gitSha: "03d1dfc",
5225
+ buildTime: "2026-10-12T20:08:12.374Z"
5224
5226
  };
5225
5227
 
5226
- export { BUILD_TIME, CONTENT_GROUPING_DIMENSIONS, DASHBOARD_GRID_MAX_COLS, DASHBOARD_WIDGET_TYPES, DEFAULT_DASHBOARD_TITLE, DISCIPLINE_FACTORS, EFFORT_DISCIPLINES, GIT_SHA, PLANNED_WIDGET_TYPES, PageSourceRegistry, QueryDocumentRunner, QueryService, SEMVER, QueryDocumentRunner2 as SharedQueryDocumentRunner, UNASSIGNED, VERSION, WQL_AGGREGATORS, WQL_CALC_TARGETS, WQL_CHART_HEADS, WQL_COMPARISON_OPS, WQL_CONTENT_FILTER_KEYS, WQL_CONTENT_ONLY_KEYS, WQL_DISPLAY_UNITS, WQL_EFFORT_FILTER_KEYS, WQL_FIND_TARGETS, WQL_FUNCTION_HEADS, WQL_GRAINS, WQL_INTENSITY_TIERS, WQL_KEYWORDS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_NOTE_DEFAULT_SOURCES, WQL_RESULT_PLANES, WQL_ROLLUP_PERIODS, WQL_SOURCES, WQL_SOURCE_HEADS, WQL_SOURCE_HEAD_SCOPES, WQL_SOURCE_VALUES, WQL_STANDARD_DATASETS, WQL_TAG_KEYS, WQL_TYPED_TAG_KEYS, WQL_TYPE_HEADS, WQL_VIRTUAL_DIMS, allowedFilterTypesForTarget, appendSuffixes, appendWidget, buildDashboardDocument, buildDashboardScaffold, buildDrillDownQuery, captureContext, catalogOfItem, civilDateAdd, civilDateDiff, civilDateOf, civilDatesCoveredByRange, civilMonday, consumesRollupFacts, convertDisplay, createBuiltinFormulaEvaluator, dashboardSlug, defaultTokenValues, disciplineFactorFor, duplicateWidget, eventsToStoredLogs, extractDashboardTokens, extractFrontmatterTags, findTargetAdvisories, getList, getScalar, getUnitFamily, inRange, isAggregateQuery, isDashboardMeta, isDashboardWidgetType, isEffortDiscipline, isFindQuery, isPipelineQuery, isProposedMetric, migrateLegacyDashboardBodies, moveWidget, normalizeSummaryFacts, normalizeWql, parseDashboardNote, parseDocument, parseFlatProperties, parseFrontmatter, parseFrontmatterBody, parseFrontmatterCategories, parseFrontmatterProps, parseQuery, parseQueryWidgetSuffix, parseWqlSuffixes, projectEventToFacts, referencedTokens, removeWidget, renderWidgetGroup, resizeWidget, resolveCanonicalMetricKey, resolveOutputUnit, resolveWidgetType, resolveWindowRange, rowsParseError, serialize, serializeDocument, serializeFrontmatter, setDashboardTokenValue, sourceMatches, splitAtWhere, staticCatalogRoute, staticNotesFromBlocks, staticTagIndexFromBlocks, stripFrontmatter, substituteTokens2 as substituteTokens, substituteTokens as substituteTokensRaw, supportsWqlFilterKey, systemTimeZone, toEventRows, toSummaryEventRows, unknownTokensMessage, unknownWidgetTypeMessage, updateWidget, widgetBodyLine, widgetFenceTag, wql, wqlCompletion, wqlCompletionSource, wqlFilterKeys, wqlGroupingDimensions, wqlHighlightStyle, wqlLanguage, parser as wqlParser, wql_parser_terms_exports as wqlTerms, zonedNoon, zonedStartOfDay };
5228
+ export { BUILD_TIME, CONTENT_GROUPING_DIMENSIONS, DASHBOARD_GRID_MAX_COLS, DASHBOARD_WIDGET_TYPES, DEFAULT_DASHBOARD_TITLE, DISCIPLINE_FACTORS, EFFORT_DISCIPLINES, GIT_SHA, PLANNED_WIDGET_TYPES, PageSourceRegistry, QueryDocumentRunner, QueryService, SEMVER, QueryDocumentRunner2 as SharedQueryDocumentRunner, UNASSIGNED, VERSION, WQL_AGGREGATORS, WQL_CALC_TARGETS, WQL_CHART_HEADS, WQL_COMPARISON_OPS, WQL_CONTENT_FILTER_KEYS, WQL_CONTENT_ONLY_KEYS, WQL_DISPLAY_UNITS, WQL_EFFORT_FILTER_KEYS, WQL_FIND_TARGETS, WQL_FUNCTION_HEADS, WQL_GRAINS, WQL_INTENSITY_TIERS, WQL_KEYWORDS, WQL_METRIC_AGGREGATES, WQL_METRIC_FAMILIES, WQL_NOTE_DEFAULT_SOURCES, WQL_RESULT_PLANES, WQL_ROLLUP_PERIODS, WQL_SOURCES, WQL_SOURCE_HEADS, WQL_SOURCE_HEAD_SCOPES, WQL_SOURCE_VALUES, WQL_STANDARD_DATASETS, WQL_TAG_KEYS, WQL_TYPED_TAG_KEYS, WQL_TYPE_HEADS, WQL_VIRTUAL_DIMS, allowedFilterTypesForTarget, appendSuffixes, appendWidget, buildDashboardDocument, buildDashboardScaffold, buildDrillDownQuery, captureContext, catalogOfItem, civilDateAdd, civilDateDiff, civilDateOf, civilDatesCoveredByRange, civilMonday, consumesRollupFacts, convertDisplay, createBuiltinFormulaEvaluator, dashboardSlug, defaultTokenValues, disciplineFactorFor, duplicateWidget, eventsToStoredLogs, extractDashboardTokens, extractFrontmatterTags, findTargetAdvisories, getList, getScalar, getUnitFamily, inRange, isAggregateQuery, isDashboardMeta, isDashboardWidgetType, isEffortDiscipline, isFindQuery, isPipelineQuery, isProposedMetric, migrateLegacyDashboardBodies, moveWidget, normalizeSummaryFacts, normalizeWql, parseDashboardNote, parseDocument, parseFlatProperties, parseFrontmatter, parseFrontmatterBody, parseFrontmatterCategories, parseFrontmatterProps, parseQuery, parseQueryWidgetSuffix, parseWqlSuffixes, projectEventToFacts, referencedTokens, removeWidget, renderWidgetGroup, resizeWidget, resolveCanonicalMetricKey, resolveOutputUnit, resolveWidgetType, resolveWindowRange, rowsParseError, serialize, serializeDocument, serializeFrontmatter, setDashboardTokenValue, sourceMatches, splitAtWhere, stripFrontmatter, substituteTokens2 as substituteTokens, substituteTokens as substituteTokensRaw, supportsWqlFilterKey, systemTimeZone, toEventRows, toSummaryEventRows, unknownTokensMessage, unknownWidgetTypeMessage, updateWidget, widgetBodyLine, widgetFenceTag, wql, wqlCompletion, wqlCompletionSource, wqlFilterKeys, wqlGroupingDimensions, wqlHighlightStyle, wqlLanguage, parser as wqlParser, wql_parser_terms_exports as wqlTerms, zonedNoon, zonedStartOfDay };
5227
5229
  //# sourceMappingURL=index.mjs.map
5228
5230
  //# sourceMappingURL=index.mjs.map