yamlover 0.3.55 → 0.3.56

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/server.js CHANGED
@@ -63317,6 +63317,22 @@ var Store = class {
63317
63317
  get stale() {
63318
63318
  return this._stale;
63319
63319
  }
63320
+ /** PREPARED-STATEMENT CACHE — one StatementSync per distinct SQL, reused for the life of the
63321
+ * handle. The read methods below run once per NODE during a projection (the server's
63322
+ * envelopeDeps asks `node`/`entries`/`unrealizedRefs` about every member it serializes), so a
63323
+ * fresh `db.prepare()` per call meant ~200k compilations to answer ONE request over a 29k-node
63324
+ * document — measured at ~97% of a 4.5s /api/content response, against 0.16s of actual row
63325
+ * reading. Statements survive DML (the DELETE/INSERT churn of a re-index); the only DDL this
63326
+ * class runs is in the constructor, before anything can be cached. */
63327
+ stmts = /* @__PURE__ */ new Map();
63328
+ stmt(sql) {
63329
+ let s2 = this.stmts.get(sql);
63330
+ if (!s2) {
63331
+ s2 = this.db.prepare(sql);
63332
+ this.stmts.set(sql, s2);
63333
+ }
63334
+ return s2;
63335
+ }
63320
63336
  constructor(dbPath) {
63321
63337
  this.db = new DatabaseSync(dbPath);
63322
63338
  this.db.exec("PRAGMA journal_mode = WAL;");
@@ -63329,6 +63345,7 @@ var Store = class {
63329
63345
  this._stale = ver !== SCHEMA_VERSION;
63330
63346
  }
63331
63347
  close() {
63348
+ this.stmts.clear();
63332
63349
  this.db.close();
63333
63350
  }
63334
63351
  /** Rebuild the whole index from one resolved document: clear, then insert nodes + edges in
@@ -63543,7 +63560,7 @@ var Store = class {
63543
63560
  * whose on-disk (size, mtime) still matches the manifest has not changed in any way a
63544
63561
  * reindex could see — Windows reports mere READS as changes via last-access-time updates). */
63545
63562
  file(relPath) {
63546
- const r2 = this.db.prepare("SELECT * FROM file WHERE path = ?").get(relPath);
63563
+ const r2 = this.stmt("SELECT * FROM file WHERE path = ?").get(relPath);
63547
63564
  return r2 ? { path: r2.path, hash: r2.hash ?? null, size: r2.size, mtimeMs: r2.mtime_ms } : null;
63548
63565
  }
63549
63566
  /** Manifest entries still lacking a content hash (large blobs the walk never read), smallest
@@ -63583,7 +63600,7 @@ var Store = class {
63583
63600
  * authority links — in source order. These are the entries {@link entries} cannot list (no
63584
63601
  * edge exists), returned so a projection can still show the authored pointer in place. */
63585
63602
  unrealizedRefs(holder) {
63586
- return this.db.prepare("SELECT label, pos, raw, edge, external FROM dangling WHERE holder = ? ORDER BY pos").all(holder).map((r2) => ({
63603
+ return this.stmt("SELECT label, pos, raw, edge, external FROM dangling WHERE holder = ? ORDER BY pos").all(holder).map((r2) => ({
63587
63604
  label: r2.label ?? null,
63588
63605
  pos: r2.pos ?? null,
63589
63606
  raw: r2.raw,
@@ -63593,23 +63610,23 @@ var Store = class {
63593
63610
  }
63594
63611
  /** A node's attributes (null if no such path). */
63595
63612
  node(path12) {
63596
- const row = this.db.prepare("SELECT * FROM node WHERE path = ?").get(path12);
63613
+ const row = this.stmt("SELECT * FROM node WHERE path = ?").get(path12);
63597
63614
  return row ? rowToNode(row) : null;
63598
63615
  }
63599
63616
  /** Direct containment children of a node, in source order (by `pos`). */
63600
63617
  children(path12) {
63601
- return this.db.prepare("SELECT to_path, label, pos FROM edge WHERE from_path = ? AND kind = 'contain' ORDER BY pos").all(path12).map((r2) => ({ to: r2.to_path, label: r2.label ?? null, pos: r2.pos ?? null }));
63618
+ return this.stmt("SELECT to_path, label, pos FROM edge WHERE from_path = ? AND kind = 'contain' ORDER BY pos").all(path12).map((r2) => ({ to: r2.to_path, label: r2.label ?? null, pos: r2.pos ?? null }));
63602
63619
  }
63603
63620
  /** ALL of a node's entries in source order — containment children AND `*`/`~` pointer
63604
63621
  * entries (ref/back). Unlike {@link children} this keeps positional pointers (`- *file`)
63605
63622
  * in place, so an array that mixes inline values and pointers (e.g. a chapter's `chunks`)
63606
63623
  * projects in full. `kind` says whether `to` is an owned child (contain) or a pointer target. */
63607
63624
  entries(path12) {
63608
- return this.db.prepare("SELECT to_path, label, pos, kind FROM edge WHERE from_path = ? AND kind IN ('contain','ref','back') ORDER BY pos").all(path12).map((r2) => ({ to: r2.to_path, label: r2.label ?? null, pos: r2.pos ?? null, kind: r2.kind }));
63625
+ return this.stmt("SELECT to_path, label, pos, kind FROM edge WHERE from_path = ? AND kind IN ('contain','ref','back') ORDER BY pos").all(path12).map((r2) => ({ to: r2.to_path, label: r2.label ?? null, pos: r2.pos ?? null, kind: r2.kind }));
63609
63626
  }
63610
63627
  /** Whether a node has any containment children (expandable in the TOC). */
63611
63628
  hasChildren(path12) {
63612
- const r2 = this.db.prepare("SELECT 1 FROM edge WHERE from_path = ? AND kind = 'contain' LIMIT 1").get(path12);
63629
+ const r2 = this.stmt("SELECT 1 FROM edge WHERE from_path = ? AND kind = 'contain' LIMIT 1").get(path12);
63613
63630
  return r2 != null;
63614
63631
  }
63615
63632
  /** The containment subtree rooted at `path`, up to `depth` levels (TOC). A recursive CTE
@@ -63618,7 +63635,7 @@ var Store = class {
63618
63635
  * not contents: they are pruned here with their whole subtrees, so every TOC consumer gets the
63619
63636
  * same view without re-filtering. */
63620
63637
  toc(path12 = ":", depth = Infinity) {
63621
- const rows = this.db.prepare(
63638
+ const rows = this.stmt(
63622
63639
  `WITH RECURSIVE sub(from_path, to_path, label, pos, lvl) AS (
63623
63640
  SELECT e.from_path, e.to_path, e.label, e.pos, 1 FROM edge e
63624
63641
  JOIN node t ON t.path = e.to_path
@@ -63654,8 +63671,8 @@ var Store = class {
63654
63671
  /** Edges in/out of a node, by kind. `derived` inverses are computed here on demand (never
63655
63672
  * stored) per ENGINE.md — for each ref/back into/out of the node, the reverse direction. */
63656
63673
  relationships(path12) {
63657
- const out = this.db.prepare("SELECT * FROM edge WHERE from_path = ? AND kind != 'contain'").all(path12).map(rowToEdge);
63658
- const inc = this.db.prepare("SELECT * FROM edge WHERE to_path = ? AND kind != 'contain'").all(path12).map(rowToEdge);
63674
+ const out = this.stmt("SELECT * FROM edge WHERE from_path = ? AND kind != 'contain'").all(path12).map(rowToEdge);
63675
+ const inc = this.stmt("SELECT * FROM edge WHERE to_path = ? AND kind != 'contain'").all(path12).map(rowToEdge);
63659
63676
  const derivedIn = inc.filter((e3) => e3.kind === "ref" || e3.kind === "back").map((e3) => ({ from: e3.to, to: e3.from, label: e3.label, kind: "derived", pos: null }));
63660
63677
  return { out, in: [...inc, ...derivedIn] };
63661
63678
  }
@@ -63760,6 +63777,11 @@ var LossyError = class extends Error {
63760
63777
  function dq(s2) {
63761
63778
  return JSON.stringify(s2);
63762
63779
  }
63780
+ function hexValue(tok) {
63781
+ const signed = tok[0] === "-" || tok[0] === "+";
63782
+ const mag = Number(signed ? tok.slice(1) : tok);
63783
+ return tok[0] === "-" ? -mag : mag;
63784
+ }
63763
63785
  function seqMarkLen(text, yaml = false) {
63764
63786
  if (text === "-" || text.startsWith("- ")) return 1;
63765
63787
  if (!yaml && (text === "-:" || text.startsWith("-: "))) return 2;
@@ -65007,7 +65029,7 @@ function plainScalar(text) {
65007
65029
  if (/^[-+]?\.(?:inf|Inf|INF)$/.test(t3)) return { kind: "scalar", value: t3[0] === "-" ? -Infinity : Infinity, raw: text };
65008
65030
  if (/^\.(?:nan|NaN|NAN)$/.test(t3)) return { kind: "scalar", value: NaN, raw: text };
65009
65031
  if (/^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(t3)) return { kind: "scalar", value: Number(t3), raw: text };
65010
- if (/^[-+]?0x[0-9a-fA-F]+$/.test(t3)) return { kind: "scalar", value: Number(t3), raw: text };
65032
+ if (/^[-+]?0x[0-9a-fA-F]+$/.test(t3)) return { kind: "scalar", value: hexValue(t3), raw: text };
65011
65033
  return { kind: "scalar", value: t3, raw: text };
65012
65034
  }
65013
65035
  function backslashUnescape(s2) {
@@ -65314,13 +65336,25 @@ function isDelim(c3) {
65314
65336
  return WS.has(c3) || c3 === "," || c3 === ":" || c3 === "{" || c3 === "}" || c3 === "[" || c3 === "]" || c3 === '"' || c3 === "'" || c3 === "/";
65315
65337
  }
65316
65338
  function json5number(tok) {
65317
- if (/^[+-]?0[xX][0-9a-fA-F]+$/.test(tok)) return Number(tok);
65339
+ if (/^[+-]?0[xX][0-9a-fA-F]+$/.test(tok)) return hexValue(tok);
65318
65340
  if (/^[+-]?Infinity$/.test(tok)) return tok[0] === "-" ? -Infinity : Infinity;
65319
65341
  if (/^[+-]?NaN$/.test(tok)) return NaN;
65320
65342
  if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(tok)) return Number(tok);
65321
65343
  return void 0;
65322
65344
  }
65323
65345
 
65346
+ // ../parser/ts/src/overlay-keys.ts
65347
+ var OVERLAY_KEY = ".yo";
65348
+ var LEGACY_OVERLAY_KEY = "yo";
65349
+ var overlayKeyAlts = [OVERLAY_KEY, LEGACY_OVERLAY_KEY];
65350
+ var FRAGMENTS_SUBKEY = "fragments";
65351
+ var THUMBNAILS_SUBKEY = "thumbnails";
65352
+ var LEGACY_THUMBNAILS_KEY = "yamlover-thumbnails";
65353
+ var LEGACY_ANNOTATIONS_KEY = "yamlover-annotations";
65354
+ var isOverlayKey = (k) => k === OVERLAY_KEY || k === LEGACY_OVERLAY_KEY;
65355
+ var isHiddenEntryKey = (k) => typeof k === "string" && (k.startsWith(".") || k === LEGACY_OVERLAY_KEY || k === LEGACY_THUMBNAILS_KEY || k === LEGACY_ANNOTATIONS_KEY);
65356
+ var isFragmentSegs = (segs) => segs.length >= 3 && isOverlayKey(segs[segs.length - 3]) && segs[segs.length - 2] === FRAGMENTS_SUBKEY;
65357
+
65324
65358
  // ../engine/ts/src/mounts.ts
65325
65359
  import fs from "node:fs";
65326
65360
  import path from "node:path";
@@ -65489,11 +65523,21 @@ function* walkTreeGen(absDir, opts = {}) {
65489
65523
  builtinDefs = (graftTaxonomy() ?? builtinYamloverGraft()).defs;
65490
65524
  }
65491
65525
  applySchemas(root, defsRoot, builtinDefs);
65526
+ stampHiddenKeys(root);
65492
65527
  return {
65493
65528
  doc: { root, source: { concrete: "dir", uri: absDir } },
65494
65529
  files: [...ctx.files.values()]
65495
65530
  };
65496
65531
  }
65532
+ function stampHiddenKeys(root) {
65533
+ for (const e3 of root.entries ?? []) {
65534
+ if (isPointer(e3.value)) continue;
65535
+ if (isHiddenEntryKey(e3.key) && e3.value.meta?.hidden !== true) {
65536
+ e3.value.meta = { ...e3.value.meta, hidden: true };
65537
+ }
65538
+ stampHiddenKeys(e3.value);
65539
+ }
65540
+ }
65497
65541
  function reindex(store, absDir, opts = {}) {
65498
65542
  const prev = store.stale ? /* @__PURE__ */ new Map() : store.manifest();
65499
65543
  const { doc, files } = walkTree(absDir, { ...opts, cache: opts.cache ?? manifestCache(prev) });
@@ -65525,6 +65569,7 @@ async function reindexAsyncDoc(store, absDir, opts = {}) {
65525
65569
  const t0 = Date.now();
65526
65570
  try {
65527
65571
  snap.root.meta = { ...snap.root.meta, documentRoot: true };
65572
+ stampHiddenKeys(snap.root);
65528
65573
  store.indexDocument({ root: snap.root, source: { concrete: "dir", uri: absDir } });
65529
65574
  minInterval = Math.max(partialMs, 5 * (Date.now() - t0));
65530
65575
  const added = snap.filePaths.slice(reported);
@@ -65573,6 +65618,7 @@ async function reindexPathAsync(store, absDir, cachedDoc, changedRel, opts = {})
65573
65618
  while (!r2.done) r2 = gen.next();
65574
65619
  target.arr[target.i].value = r2.value;
65575
65620
  applySchemas(cachedDoc.root, findDefsRoot(absDir), graftDefs(cachedDoc.root));
65621
+ stampHiddenKeys(cachedDoc.root);
65576
65622
  const relPrefix = dirSegs.join("/") + "/";
65577
65623
  const P = pathOfSegs(dirSegs);
65578
65624
  const prevSub = new Map([...prev].filter(([k]) => k.startsWith(relPrefix)));
@@ -65609,7 +65655,7 @@ async function countChildren(absRoot, opts, onCount) {
65609
65655
  }
65610
65656
  let top = 0;
65611
65657
  for (const e3 of entries) {
65612
- if (skipInYamloverDir(e3.name)) continue;
65658
+ if (skipInYamloverDir(e3.name) && e3.name !== BODY_FILE && e3.name !== "meta.yo") continue;
65613
65659
  const abs = path2.join(dir, e3.name);
65614
65660
  if (opts.ignore?.(abs)) continue;
65615
65661
  n2++;
@@ -65791,8 +65837,9 @@ function* dirNode(dir, ctx, inherited) {
65791
65837
  }
65792
65838
  function* yamloverDirNode(absYamlover, ctx) {
65793
65839
  let names3;
65840
+ const isRawOverlay = (n2) => n2 === BODY_FILE || n2 === "meta.yo";
65794
65841
  try {
65795
- names3 = fs2.readdirSync(absYamlover).filter((n2) => !skipInYamloverDir(n2)).filter((n2) => !ctx.opts.ignore?.(path2.join(absYamlover, n2))).sort();
65842
+ names3 = fs2.readdirSync(absYamlover).filter((n2) => !skipInYamloverDir(n2) || isRawOverlay(n2)).filter((n2) => !ctx.opts.ignore?.(path2.join(absYamlover, n2))).sort();
65796
65843
  } catch {
65797
65844
  return null;
65798
65845
  }
@@ -65800,6 +65847,16 @@ function* yamloverDirNode(absYamlover, ctx) {
65800
65847
  ctx.open?.push({ name: path2.basename(absYamlover), entries, meta: { hidden: true } });
65801
65848
  for (const name of names3) {
65802
65849
  const abs = path2.join(absYamlover, name);
65850
+ if (isRawOverlay(name)) {
65851
+ try {
65852
+ if (fs2.statSync(abs).size > MAX_TEXT_BYTES) continue;
65853
+ const text = readTracked(ctx, abs).toString("utf8");
65854
+ entries.push({ key: name, edge: "contain", value: { kind: "scalar", value: text, raw: text, meta: { derivedFormat: "text/plain", span: { uri: abs, start: 0, end: text.length } } } });
65855
+ yield { done: ++ctx.count, path: rel(ctx, abs) };
65856
+ } catch {
65857
+ }
65858
+ continue;
65859
+ }
65803
65860
  const child = yield* childNode(abs, void 0, ctx);
65804
65861
  entries.push({ key: name, edge: "contain", value: child });
65805
65862
  yield { done: ++ctx.count, path: rel(ctx, abs) };
@@ -65824,10 +65881,12 @@ function* childNode(abs, m, ctx) {
65824
65881
  if (fmt && (DOC_FORMATS[fmt] || TEXT_FORMATS.has(fmt))) {
65825
65882
  if (stat.size > MAX_DOC_BYTES) return blob(abs, fmt, ctx);
65826
65883
  if (DOC_FORMATS[fmt]) return parsedDoc(abs, DOC_FORMATS[fmt], ctx);
65884
+ if (stat.size > MAX_TEXT_BYTES) return blob(abs, fmt, ctx);
65827
65885
  return textScalar(abs, fmt, ctx);
65828
65886
  }
65829
65887
  if (fmt) return blob(abs, fmt, ctx);
65830
65888
  if (looksBinary(abs)) return blob(abs, "application/octet-stream", ctx);
65889
+ if (!PARSED_EXTS.has(ext) || path2.basename(abs).startsWith(".")) return blob(abs, "text/plain", ctx);
65831
65890
  return parsedScalar(abs, ext, ctx);
65832
65891
  }
65833
65892
  function decodeConcrete(abs, concrete, format, stat, ctx) {
@@ -65966,8 +66025,7 @@ function applySchemas(root, defsRoot, builtinDefs) {
65966
66025
  return { name: null, node: v };
65967
66026
  };
65968
66027
  const isContainerSchema = (n2) => !!field(n2, "members") || !!field(n2, "others") || !!field(n2, "properties") || !!field(n2, "items") || !!field(n2, "allOf") || ["object", "variant", "omni", "mixed", "kseq", "array"].includes(str(n2, "type") ?? "");
65969
- const OVERLAY_KEYS = /* @__PURE__ */ new Set(["yo"]);
65970
- const elemIsContainer = (el) => el.kind === "mapping" || el.meta?.dirBacked === true || (el.entries ?? []).some((e3) => e3.key == null || !OVERLAY_KEYS.has(e3.key));
66028
+ const elemIsContainer = (el) => el.kind === "mapping" || el.meta?.dirBacked === true || (el.entries ?? []).some((e3) => e3.key == null || !isHiddenEntryKey(e3.key));
65971
66029
  const ordinalElems = (inst) => {
65972
66030
  const anchoredKeys = new Set(inst.meta?.anchored ?? []);
65973
66031
  return (inst.entries ?? []).filter((e3) => (e3.key == null || anchoredKeys.has(e3.key)) && !isPointer(e3.value) && e3.value.meta?.yo !== true && !(e3.value.meta?.schema && isPointer(e3.value.meta.schema)));
@@ -66007,7 +66065,7 @@ function applySchemas(root, defsRoot, builtinDefs) {
66007
66065
  if (e3.value.meta?.schema && isPointer(e3.value.meta.schema)) continue;
66008
66066
  if (e3.value.meta?.yo === true) continue;
66009
66067
  const declared = (membersNode ? field(membersNode, e3.key) : null) ?? (props && !isPointer(props) ? field(props, e3.key) : null);
66010
- const sub = declared ?? (others && !OVERLAY_KEYS.has(e3.key) ? others : null) ?? addl;
66068
+ const sub = declared ?? (isHiddenEntryKey(e3.key) ? null : others ?? addl);
66011
66069
  if (sub) apply(e3.value, sub, depth + 1);
66012
66070
  }
66013
66071
  if (keylessClauses.length) {
@@ -66089,14 +66147,14 @@ function applyBody(file, node, ctx) {
66089
66147
  }
66090
66148
  const ci = childAt.get(e3.key);
66091
66149
  if (ci !== void 0 && children2[ci]) {
66092
- children2[ci] = augmentEntry(children2[ci], e3);
66150
+ children2[ci] = e3.key === YAMLOVER_DIR ? mergeOverlayEntry(children2[ci], e3) : augmentEntry(children2[ci], e3);
66093
66151
  continue;
66094
66152
  }
66095
66153
  bodyAt.set(e3.key, bodyOrder.length);
66096
66154
  bodyOrder.push(e3);
66097
66155
  }
66098
66156
  const unlisted = children2.filter((e3) => e3 !== null);
66099
- const remainder = unlisted.length > 0;
66157
+ const remainder = unlisted.some((e3) => isPointer(e3.value) || e3.value.meta?.hidden !== true);
66100
66158
  const meta = anchored.length > 0 ? { ...meta0, anchored } : meta0;
66101
66159
  const entries = [...bodyOrder, ...unlisted];
66102
66160
  if (body.kind === "mapping" && (body.array || bodyEntries.length > 0 && bodyEntries.every((e3) => e3.key === null))) {
@@ -66104,7 +66162,8 @@ function applyBody(file, node, ctx) {
66104
66162
  }
66105
66163
  if (body.kind === "scalar") {
66106
66164
  const emptySelf = body.value === null && (body.raw == null || body.raw.trim() === "");
66107
- if (!(emptySelf && entries.length > 0)) {
66165
+ const visible = entries.some((e3) => isPointer(e3.value) || e3.value.meta?.hidden !== true);
66166
+ if (!(emptySelf && visible)) {
66108
66167
  return { kind: "scalar", value: body.value, raw: body.raw, entries, array: false, meta };
66109
66168
  }
66110
66169
  }
@@ -66121,6 +66180,29 @@ function augmentEntry(base, overlay2) {
66121
66180
  };
66122
66181
  return { key: base.key, edge: "contain", value };
66123
66182
  }
66183
+ function mergeOverlayEntry(dirE, bodyE) {
66184
+ if (isPointer(dirE.value) || isPointer(bodyE.value)) return bodyE;
66185
+ return { key: dirE.key, edge: "contain", value: mergeOverlayNode(dirE.value, bodyE.value) };
66186
+ }
66187
+ function mergeOverlayNode(disk, body) {
66188
+ const out = (body.entries ?? []).slice();
66189
+ const at = /* @__PURE__ */ new Map();
66190
+ out.forEach((e3, i2) => {
66191
+ if (typeof e3.key === "string") at.set(e3.key, i2);
66192
+ });
66193
+ for (const d2 of disk.entries ?? []) {
66194
+ const i2 = typeof d2.key === "string" ? at.get(d2.key) : void 0;
66195
+ if (i2 === void 0) {
66196
+ out.push(d2);
66197
+ continue;
66198
+ }
66199
+ const b = out[i2];
66200
+ if (!isPointer(b.value) && !isPointer(d2.value)) {
66201
+ out[i2] = { ...b, value: mergeOverlayNode(d2.value, b.value) };
66202
+ }
66203
+ }
66204
+ return { ...body, entries: out, meta: { ...disk.meta, ...body.meta, hidden: true } };
66205
+ }
66124
66206
  function pointerLeafKey(v) {
66125
66207
  if (!isPointer(v)) return null;
66126
66208
  const last = v.steps[v.steps.length - 1];
@@ -66164,6 +66246,10 @@ var EXT_FORMAT = {
66164
66246
  ".heif": "image/heic",
66165
66247
  ".fb2": "application/x-fictionbook+xml",
66166
66248
  ".epub": "application/epub+zip",
66249
+ // A stored mail message. Opaque bytes, never inline text: its own headers name its charset
66250
+ // (koi8-r and windows-1251 are the norm in an old archive) and its parts carry their own
66251
+ // transfer encodings, so decoding belongs to the renderer, not to the walk.
66252
+ ".eml": "message/rfc822",
66167
66253
  ".html": "text/html",
66168
66254
  ".htm": "text/html",
66169
66255
  ".md": "text/markdown",
@@ -66190,9 +66276,42 @@ var EXT_FORMAT = {
66190
66276
  ".txt": "text/plain",
66191
66277
  ".text": "text/plain",
66192
66278
  ".log": "text/plain",
66193
- ".ini": "text/plain"
66279
+ ".ini": "text/plain",
66280
+ // FOREIGN SOURCE CODE — text this project does not speak, NAMED so it stops looking like
66281
+ // a broken yamlover file. Held inline as UTF-8 (they are, in practice) and shown verbatim;
66282
+ // without these a saved web page's minified `.js` reached the yamlover parser and either
66283
+ // degraded loudly or parsed into garbage (a 4 KiB key), both indistinguishable from a `.yo`
66284
+ // its author must fix.
66285
+ ".js": "text/javascript",
66286
+ ".mjs": "text/javascript",
66287
+ ".cjs": "text/javascript",
66288
+ ".ts": "text/typescript",
66289
+ ".tsx": "text/typescript",
66290
+ ".jsx": "text/javascript",
66291
+ ".css": "text/css",
66292
+ ".py": "text/x-python",
66293
+ ".sh": "text/x-shellscript",
66294
+ ".sql": "text/x-sql",
66295
+ ".xml": "text/xml",
66296
+ ".jsonl": "application/x-ndjson"
66194
66297
  };
66195
- var TEXT_FORMATS = /* @__PURE__ */ new Set(["text/markdown", "text/asciidoc", "text/x-plantuml", "text/csv", "text/tab-separated-values"]);
66298
+ var TEXT_FORMATS = /* @__PURE__ */ new Set([
66299
+ "text/markdown",
66300
+ "text/asciidoc",
66301
+ "text/x-plantuml",
66302
+ "text/csv",
66303
+ "text/tab-separated-values",
66304
+ // foreign source code: a string scalar carrying its own bytes, never parsed
66305
+ "text/javascript",
66306
+ "text/typescript",
66307
+ "text/css",
66308
+ "text/x-python",
66309
+ "text/x-shellscript",
66310
+ "text/x-sql",
66311
+ "text/xml",
66312
+ "application/x-ndjson"
66313
+ ]);
66314
+ var PARSED_EXTS = /* @__PURE__ */ new Set(["", ".yo", ".yamlover", ".yaml", ".yml", ".json", ".json5", ".json5p"]);
66196
66315
  var DOC_FORMATS = {
66197
66316
  "yamlover": "yamlover",
66198
66317
  "yaml": "yaml",
@@ -66322,19 +66441,12 @@ function scalarTok(s2) {
66322
66441
  if (typeof v === "boolean") return v ? "true" : "false";
66323
66442
  if (typeof v === "string") return JSON.stringify(v);
66324
66443
  const raw = s2.raw.trim();
66325
- if (raw !== "" && Object.is(json5number2(raw), v)) return raw;
66444
+ if (raw !== "" && Object.is(json5number(raw), v)) return raw;
66326
66445
  if (Number.isNaN(v)) return "NaN";
66327
66446
  if (v === Infinity) return "Infinity";
66328
66447
  if (v === -Infinity) return "-Infinity";
66329
66448
  return String(v);
66330
66449
  }
66331
- function json5number2(tok) {
66332
- if (/^[+-]?0[xX][0-9a-fA-F]+$/.test(tok)) return Number(tok);
66333
- if (/^[+-]?Infinity$/.test(tok)) return tok[0] === "-" ? -Infinity : Infinity;
66334
- if (/^[+-]?NaN$/.test(tok)) return NaN;
66335
- if (/^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(tok)) return Number(tok);
66336
- return void 0;
66337
- }
66338
66450
 
66339
66451
  // ../parser/ts/src/serialize-yamlover.ts
66340
66452
  var STEP2 = 2;
@@ -66766,7 +66878,8 @@ function rawBlock(s2) {
66766
66878
  const header = nl < 0 ? raw : raw.slice(0, nl);
66767
66879
  if (!/^[|>][+-]?$/.test(header)) return null;
66768
66880
  const lines = nl < 0 ? [] : raw.slice(nl + 1).split("\n");
66769
- if (lines.length > 0 && /^[ \t]/.test(lines[0])) return null;
66881
+ const firstRawContent = lines.find((l2) => l2 !== "");
66882
+ if (firstRawContent !== void 0 && /^[ \t]/.test(firstRawContent)) return null;
66770
66883
  if (lines.some((l2) => l2 !== "" && /^ +$/.test(l2))) return null;
66771
66884
  const folded = header[0] === ">";
66772
66885
  const chomp = header.includes("-") ? "strip" : header.includes("+") ? "keep" : "clip";
@@ -66841,7 +66954,8 @@ function blockLines(v) {
66841
66954
  const body = v.slice(0, end);
66842
66955
  if (body === "") return null;
66843
66956
  const lines = body.split("\n");
66844
- if (/^[ \t]/.test(lines[0])) return null;
66957
+ const firstContent = lines.find((l2) => l2 !== "");
66958
+ if (firstContent !== void 0 && /^[ \t]/.test(firstContent)) return null;
66845
66959
  if (lines.some((l2) => l2 !== "" && /^ +$/.test(l2))) return null;
66846
66960
  const header = trailing === 0 ? "|-" : trailing === 1 ? "|" : "|+";
66847
66961
  for (let i2 = 1; i2 < trailing; i2++) lines.push("");
@@ -67656,6 +67770,15 @@ function keyName(portion) {
67656
67770
  if (steps.length !== 1 || steps[0].sel !== "key") throw new SyntaxError(`query: bad name segment "${portion}"`);
67657
67771
  return steps[0].name;
67658
67772
  }
67773
+ function isPointerShapedQuery(text) {
67774
+ try {
67775
+ return parseQuery(text).portions.every(
67776
+ (p2) => p2.kind === "key" || p2.kind === "index" || p2.kind === "nullkey" || p2.kind === "up"
67777
+ );
67778
+ } catch {
67779
+ return false;
67780
+ }
67781
+ }
67659
67782
  var YAMLOVER_AUTHORITY3 = "yamlover.inthemoon.net";
67660
67783
  function evalQuery(s2, text, from = ":") {
67661
67784
  const q = parseQuery(text);
@@ -67893,11 +68016,11 @@ function doubledFragmentDiagnostics(doc) {
67893
68016
  const entries = node.entries ?? [];
67894
68017
  entries.forEach((e3, i2) => {
67895
68018
  const key = typeof e3.key === "string" ? e3.key : null;
67896
- if (key === "fragments" && segs[segs.length - 1] === "fragments" && segs[segs.length - 2] === "yo") {
68019
+ if (key === "fragments" && segs[segs.length - 1] === "fragments" && isOverlayKey(segs[segs.length - 2])) {
67897
68020
  out.push({
67898
68021
  code: "annotations/doubled-fragments",
67899
68022
  severity: "warning",
67900
- message: `a \`fragments:\` mapping nested inside \`yo: fragments:\` \u2014 left by a pre-fix annotate over the flat spelling`,
68023
+ message: `a \`fragments:\` mapping nested inside \`${segs[segs.length - 2]}: fragments:\` \u2014 left by a pre-fix annotate over the flat spelling`,
67901
68024
  path: ":" + [...segs, key].join(":"),
67902
68025
  hint: "move its child fragments up one level, into the enclosing fragments mapping"
67903
68026
  });
@@ -67908,6 +68031,27 @@ function doubledFragmentDiagnostics(doc) {
67908
68031
  walk(doc.root, []);
67909
68032
  return out;
67910
68033
  }
68034
+ function legacyOverlaySpellingDiagnostics(doc) {
68035
+ const out = [];
68036
+ const walk = (node, segs) => {
68037
+ const entries = node.entries ?? [];
68038
+ entries.forEach((e3, i2) => {
68039
+ const key = typeof e3.key === "string" ? e3.key : null;
68040
+ if (key === LEGACY_OVERLAY_KEY || key === LEGACY_THUMBNAILS_KEY) {
68041
+ out.push({
68042
+ code: "annotations/legacy-overlay-spelling",
68043
+ severity: "info",
68044
+ message: `the legacy \`${key}:\` spelling \u2014 the canonical key is ${key === LEGACY_OVERLAY_KEY ? "`.yo:`" : "`.yo: thumbnails:`"}`,
68045
+ path: ":" + [...segs, key].join(":"),
68046
+ hint: "migrate the tree with tools/migrate-dot-yo.mjs (legacy spellings stay readable forever)"
68047
+ });
68048
+ }
68049
+ if (e3.value !== null && typeof e3.value === "object") walk(e3.value, [...segs, key ?? String(i2)]);
68050
+ });
68051
+ };
68052
+ walk(doc.root, []);
68053
+ return out;
68054
+ }
67911
68055
  function deadLinkTargets(doc, store) {
67912
68056
  const out = /* @__PURE__ */ new Set();
67913
68057
  for (const l2 of scanTextLinks(doc)) {
@@ -67924,10 +68068,8 @@ function logDeadLinks(found, log, max = 8) {
67924
68068
  }
67925
68069
 
67926
68070
  // src/server/embed.ts
67927
- var ANNOTATIONS_KEY = "yamlover-annotations";
67928
- var YO_KEY = "yo";
67929
- var FRAGMENTS_SUBKEY = "fragments";
67930
- var THUMBNAILS_KEY = "yamlover-thumbnails";
68071
+ var ANNOTATIONS_KEY = LEGACY_ANNOTATIONS_KEY;
68072
+ var stepAlts = (s2) => typeof s2 === "string" ? [s2] : s2;
67931
68073
  var indentOf = (line) => {
67932
68074
  let i2 = 0;
67933
68075
  while (line[i2] === " ") i2++;
@@ -67942,7 +68084,7 @@ function firstContentIndent(lines) {
67942
68084
  return 0;
67943
68085
  }
67944
68086
  function keyToken(key) {
67945
- return /^[A-Za-z0-9_][A-Za-z0-9_-]*$/.test(key) && !/^\d+$/.test(key) ? key : JSON.stringify(key);
68087
+ return /^\.?[A-Za-z0-9_][A-Za-z0-9_-]*$/.test(key) && !/^\d+$/.test(key) ? key : JSON.stringify(key);
67946
68088
  }
67947
68089
  function findKeyLine(lines, lo, hi, indent, key) {
67948
68090
  const tok = keyToken(key);
@@ -67960,14 +68102,20 @@ function flatRun(line, keys, k) {
67960
68102
  let rest = line.trim();
67961
68103
  let n2 = 0;
67962
68104
  while (k + n2 < keys.length && rest !== "") {
67963
- const key = keys[k + n2];
67964
- const tok = [key, keyToken(key)].find((t3) => rest === `${t3}:` || rest.startsWith(`${t3}: `));
68105
+ const tok = stepAlts(keys[k + n2]).flatMap((key) => [key, keyToken(key)]).find((t3) => rest === `${t3}:` || rest.startsWith(`${t3}: `));
67965
68106
  if (tok === void 0) break;
67966
68107
  rest = rest === `${tok}:` ? "" : rest.slice(tok.length + 2);
67967
68108
  n2++;
67968
68109
  }
67969
68110
  return Math.max(1, n2);
67970
68111
  }
68112
+ function findStepLine(lines, lo, hi, indent, step2) {
68113
+ for (const alt of stepAlts(step2)) {
68114
+ const L2 = findKeyLine(lines, lo, hi, indent, alt);
68115
+ if (L2 >= 0) return L2;
68116
+ }
68117
+ return -1;
68118
+ }
67971
68119
  function trimBack(lines, floor, end) {
67972
68120
  let e3 = end;
67973
68121
  while (e3 > floor + 1 && !isContentLine(lines[e3 - 1])) e3--;
@@ -68005,11 +68153,10 @@ function bodyUnder(lines, L2, hi, indent) {
68005
68153
  function reachBodyAt(lines, start, within) {
68006
68154
  let { lo, hi, indent } = start;
68007
68155
  for (let k = 0; k < within.length; ) {
68008
- const key = within[k];
68009
- const L2 = findKeyLine(lines, lo, hi, indent, key);
68156
+ const L2 = findStepLine(lines, lo, hi, indent, within[k]);
68010
68157
  if (L2 < 0) {
68011
68158
  const at = trimBack(lines, lo - 1, hi);
68012
- lines.splice(at, 0, `${" ".repeat(indent)}${keyToken(key)}:`);
68159
+ lines.splice(at, 0, `${" ".repeat(indent)}${keyToken(stepAlts(within[k])[0])}:`);
68013
68160
  lo = at + 1;
68014
68161
  hi = at + 1;
68015
68162
  indent += 2;
@@ -68022,10 +68169,21 @@ function reachBodyAt(lines, start, within) {
68022
68169
  }
68023
68170
  return { lo, hi, indent };
68024
68171
  }
68172
+ function presentStepAlt(lines, region, step2) {
68173
+ for (const alt of stepAlts(step2)) {
68174
+ if (findKeyLine(lines, region.lo, region.hi, region.indent, alt) >= 0) return alt;
68175
+ }
68176
+ return stepAlts(step2)[0];
68177
+ }
68178
+ function overlaySpellingAt(text, within) {
68179
+ const lines = text.replace(/\n$/, "").split("\n");
68180
+ const region = within.length === 0 ? { lo: 0, hi: lines.length, indent: firstContentIndent(lines) } : findBody(lines, within);
68181
+ return region ? presentStepAlt(lines, region, overlayKeyAlts) : overlayKeyAlts[0];
68182
+ }
68025
68183
  function findBody(lines, within) {
68026
68184
  let region = { lo: 0, hi: lines.length, indent: firstContentIndent(lines) };
68027
68185
  for (let k = 0; k < within.length; ) {
68028
- const L2 = findKeyLine(lines, region.lo, region.hi, region.indent, within[k]);
68186
+ const L2 = findStepLine(lines, region.lo, region.hi, region.indent, within[k]);
68029
68187
  if (L2 < 0) return null;
68030
68188
  const consumed = flatRun(lines[L2], within, k);
68031
68189
  region = bodyUnder(lines, L2, region.hi, region.indent);
@@ -68088,17 +68246,20 @@ function replaceSeqEntryAt(lines, region, key, render) {
68088
68246
  lines.splice(keyLine, trimBack(lines, keyLine, end) - keyLine, ...render(region.indent));
68089
68247
  }
68090
68248
  function upsertFragment(text, within, slug, render) {
68091
- return upsertMapEntry(text, within, [YO_KEY, FRAGMENTS_SUBKEY], slug, render);
68249
+ return upsertMapEntry(text, within, [overlayKeyAlts, FRAGMENTS_SUBKEY], slug, render);
68092
68250
  }
68093
68251
  function pruneEmptyKeyAt(lines, region, key) {
68094
- const L2 = findKeyLine(lines, region.lo, region.hi, region.indent, key);
68095
- if (L2 < 0) return false;
68096
- const t3 = lines[L2].trim();
68097
- if (t3 !== `${key}:` && t3 !== `${keyToken(key)}:`) return false;
68098
- const body = bodyUnder(lines, L2, region.hi, region.indent);
68099
- for (let i2 = body.lo; i2 < body.hi; i2++) if (isContentLine(lines[i2])) return false;
68100
- lines.splice(L2, Math.max(1, body.hi - L2));
68101
- return true;
68252
+ for (const alt of stepAlts(key)) {
68253
+ const L2 = findKeyLine(lines, region.lo, region.hi, region.indent, alt);
68254
+ if (L2 < 0) continue;
68255
+ const t3 = lines[L2].trim();
68256
+ if (t3 !== `${alt}:` && t3 !== `${keyToken(alt)}:`) return false;
68257
+ const body = bodyUnder(lines, L2, region.hi, region.indent);
68258
+ for (let i2 = body.lo; i2 < body.hi; i2++) if (isContentLine(lines[i2])) return false;
68259
+ lines.splice(L2, Math.max(1, body.hi - L2));
68260
+ return true;
68261
+ }
68262
+ return false;
68102
68263
  }
68103
68264
  var isBookmarkLine = (line) => line.trim().startsWith("&");
68104
68265
  function appendBookmarkAt(lines, region, tokens) {
@@ -68148,10 +68309,13 @@ function pruneEmptyYo(text, within) {
68148
68309
  const lines = text.replace(/\n$/, "").split("\n");
68149
68310
  const region = findBody(lines, within);
68150
68311
  if (!region) return text;
68151
- return pruneEmptyKeyAt(lines, region, YO_KEY) ? lines.join("\n") + "\n" : text;
68312
+ return pruneEmptyKeyAt(lines, region, overlayKeyAlts) ? lines.join("\n") + "\n" : text;
68152
68313
  }
68153
68314
  function upsertThumbnail(text, within, resKey, render) {
68154
- return upsertMapEntry(text, within, [THUMBNAILS_KEY], resKey, render);
68315
+ const probe = text.replace(/\n$/, "").split("\n");
68316
+ const legacy = findBody(probe, [...within, LEGACY_THUMBNAILS_KEY]) !== null;
68317
+ const mapPath = legacy ? [LEGACY_THUMBNAILS_KEY] : [overlayKeyAlts, THUMBNAILS_SUBKEY];
68318
+ return upsertMapEntry(text, within, mapPath, resKey, render);
68155
68319
  }
68156
68320
  function annotationsRemainAt(lines, region) {
68157
68321
  const seq = seqItemLines(lines, region, ANNOTATIONS_KEY);
@@ -68168,10 +68332,10 @@ function pruneEmptyKeys(lines, within) {
68168
68332
  for (let d2 = within.length; d2 > 0; d2--) {
68169
68333
  const parent = findBody(lines, within.slice(0, d2 - 1));
68170
68334
  if (!parent) break;
68171
- const L2 = findKeyLine(lines, parent.lo, parent.hi, parent.indent, within[d2 - 1]);
68335
+ const L2 = findStepLine(lines, parent.lo, parent.hi, parent.indent, within[d2 - 1]);
68172
68336
  if (L2 < 0) break;
68173
68337
  const t3 = lines[L2].trim();
68174
- if (t3 !== `${within[d2 - 1]}:` && t3 !== `${keyToken(within[d2 - 1])}:`) break;
68338
+ if (!stepAlts(within[d2 - 1]).some((alt) => t3 === `${alt}:` || t3 === `${keyToken(alt)}:`)) break;
68175
68339
  const body = bodyUnder(lines, L2, parent.hi, parent.indent);
68176
68340
  let empty = true;
68177
68341
  for (let i2 = body.lo; i2 < body.hi; i2++) if (isContentLine(lines[i2])) {
@@ -68196,7 +68360,7 @@ function findMapping(lines, region, mapPath) {
68196
68360
  let r2 = region;
68197
68361
  let line = -1;
68198
68362
  for (let k = 0; k < mapPath.length; ) {
68199
- const L2 = findKeyLine(lines, r2.lo, r2.hi, r2.indent, mapPath[k]);
68363
+ const L2 = findStepLine(lines, r2.lo, r2.hi, r2.indent, mapPath[k]);
68200
68364
  if (L2 < 0) return null;
68201
68365
  const consumed = flatRun(lines[L2], mapPath, k);
68202
68366
  line = L2;
@@ -68725,7 +68889,7 @@ function pruneClone(node, segs, frag, depthLeft, side, deps) {
68725
68889
  const out = [];
68726
68890
  let renderedIdx = 0;
68727
68891
  (node.entries ?? []).forEach((e3, srcIdx) => {
68728
- if (hiddenNode(e3.value)) return;
68892
+ if (hiddenNode(e3.value) && (typeof e3.key !== "string" || !isHiddenEntryKey(e3.key) || deps.memberExists([...segs, e3.key]))) return;
68729
68893
  const idx = renderedIdx;
68730
68894
  if (e3.edge !== "back") renderedIdx++;
68731
68895
  if (isPointer(e3.value)) {
@@ -69151,8 +69315,9 @@ function enforce(v, mode) {
69151
69315
  if (mode === "off") return [];
69152
69316
  if (mode === "report") return v.diagnostics;
69153
69317
  if (mode === "throw") {
69154
- if (v.diagnostics.length) throw new ValidationError(v.allowed ? v.diagnostics[0].message : v.reason, v.diagnostics);
69155
- return [];
69318
+ const fatal = v.diagnostics.filter((d2) => d2.severity !== "info");
69319
+ if (fatal.length) throw new ValidationError(v.allowed ? fatal[0].message : v.reason, v.diagnostics);
69320
+ return v.diagnostics;
69156
69321
  }
69157
69322
  if (!v.allowed) throw new ValidationError(v.reason, v.diagnostics);
69158
69323
  return v.diagnostics;
@@ -69531,13 +69696,16 @@ function createHandlers(dataRoot, opts = {}) {
69531
69696
  cachedDoc = doc;
69532
69697
  return diff2;
69533
69698
  };
69699
+ let filePatched = false;
69534
69700
  const doReindexFile = async (absFile) => {
69701
+ filePatched = false;
69535
69702
  if (cachedDoc) {
69536
69703
  const rel2 = path11.relative(dataRoot, absFile).split(path11.sep).join("/");
69537
69704
  try {
69538
69705
  const res = await reindexPathAsync(store0, dataRoot, cachedDoc, rel2, walkOpts);
69539
69706
  if (res) {
69540
69707
  cachedDoc = res.doc;
69708
+ filePatched = true;
69541
69709
  return res.diff;
69542
69710
  }
69543
69711
  } catch {
@@ -69582,35 +69750,46 @@ function createHandlers(dataRoot, opts = {}) {
69582
69750
  }
69583
69751
  };
69584
69752
  let reconcilePending = null;
69753
+ const settle = (task, label, produce) => enqueue(async () => {
69754
+ const h = tasks.start(task);
69755
+ try {
69756
+ const diff2 = await produce();
69757
+ if (diff2.moved.length > 0 && !readOnly) {
69758
+ const r2 = relinkMoved(dataRoot, diff2.moved, { ignore: ignore2 });
69759
+ if (r2.editedFiles.length > 0) {
69760
+ const follow = reindex(store0, dataRoot, walkOpts);
69761
+ diff2.changed = [.../* @__PURE__ */ new Set([...diff2.changed, ...follow.changed])];
69762
+ cachedDoc = null;
69763
+ }
69764
+ }
69765
+ h.done();
69766
+ log(`${label()}: +${diff2.added.length} ~${diff2.changed.length} \u2212${diff2.removed.length} \u2192${diff2.moved.length}`);
69767
+ if (cachedDoc) logDeadLinks(deadLinkDiagnostics(cachedDoc, store0, dataRoot), log);
69768
+ broadcast(diff2);
69769
+ scheduleHasher();
69770
+ return diff2;
69771
+ } catch (e3) {
69772
+ h.fail(e3);
69773
+ throw e3;
69774
+ }
69775
+ });
69585
69776
  const reconcile2 = () => {
69586
69777
  if (reconcilePending) return reconcilePending;
69587
- const p2 = enqueue(async () => {
69778
+ const p2 = settle("reconciling", () => "reconcile", () => {
69588
69779
  reconcilePending = null;
69589
- const h = tasks.start("reconciling");
69590
- try {
69591
- const diff2 = await doReindex();
69592
- if (diff2.moved.length > 0 && !readOnly) {
69593
- const r2 = relinkMoved(dataRoot, diff2.moved, { ignore: ignore2 });
69594
- if (r2.editedFiles.length > 0) {
69595
- const follow = reindex(store0, dataRoot, walkOpts);
69596
- diff2.changed = [.../* @__PURE__ */ new Set([...diff2.changed, ...follow.changed])];
69597
- cachedDoc = null;
69598
- }
69599
- }
69600
- h.done();
69601
- log(`reconcile: +${diff2.added.length} ~${diff2.changed.length} \u2212${diff2.removed.length} \u2192${diff2.moved.length}`);
69602
- if (cachedDoc) logDeadLinks(deadLinkDiagnostics(cachedDoc, store0, dataRoot), log);
69603
- broadcast(diff2);
69604
- scheduleHasher();
69605
- return diff2;
69606
- } catch (e3) {
69607
- h.fail(e3);
69608
- throw e3;
69609
- }
69780
+ return doReindex();
69610
69781
  });
69611
69782
  reconcilePending = p2;
69612
69783
  return p2;
69613
69784
  };
69785
+ const patchOne = (rel2) => settle(`patching ${rel2}`, () => filePatched ? "patch" : "patch \u2192 full walk", () => doReindexFile(path11.join(dataRoot, ...rel2.split("/"))));
69786
+ const patchable = (batch) => {
69787
+ if (batch.length !== 1) return null;
69788
+ const rel2 = batch[0];
69789
+ if (!store0.file(rel2)) return null;
69790
+ const st = fs10.statSync(path11.join(dataRoot, ...rel2.split("/")), { throwIfNoEntry: false });
69791
+ return st?.isFile() ? rel2 : null;
69792
+ };
69614
69793
  const spuriousEvent = (rel2) => {
69615
69794
  const rec = store0.file(rel2);
69616
69795
  if (!rec) return false;
@@ -69627,7 +69806,8 @@ function createHandlers(dataRoot, opts = {}) {
69627
69806
  return;
69628
69807
  }
69629
69808
  log(`watch: ${external.length} change(s) \u2014 ${external.slice(0, 5).join(", ")}${external.length > 5 ? ", \u2026" : ""}`);
69630
- reconcile2().catch(() => {
69809
+ const one = reconcilePending ? null : patchable(external);
69810
+ (one ? patchOne(one) : reconcile2()).catch(() => {
69631
69811
  });
69632
69812
  }, { ignore: ignore2 }) : null;
69633
69813
  const handler = (req, res, url) => {
@@ -69658,7 +69838,7 @@ function createHandlers(dataRoot, opts = {}) {
69658
69838
  try {
69659
69839
  const v = validateTree(scanTree(dataRoot, ignore2));
69660
69840
  const doc = cachedDoc ?? walkTree(dataRoot, walkOpts).doc;
69661
- sendJson(res, 200, { allowed: v.allowed, diagnostics: [...v.diagnostics, ...deadLinkDiagnostics(doc, s2, dataRoot), ...doubledFragmentDiagnostics(doc)] });
69841
+ sendJson(res, 200, { allowed: v.allowed, diagnostics: [...v.diagnostics, ...deadLinkDiagnostics(doc, s2, dataRoot), ...doubledFragmentDiagnostics(doc), ...legacyOverlaySpellingDiagnostics(doc)] });
69662
69842
  } catch (e3) {
69663
69843
  sendJson(res, 500, { error: String(e3.message || e3) });
69664
69844
  }
@@ -69684,7 +69864,8 @@ function createHandlers(dataRoot, opts = {}) {
69684
69864
  try {
69685
69865
  const paths = evalQuery(s2, q, at);
69686
69866
  const projectScoped = /^\s*::/.test(q);
69687
- const dropHidden = (p3) => projectScoped ? queryHidden(s2, p3) : inHiddenSubtree(s2, p3);
69867
+ const explicit = isPointerShapedQuery(q);
69868
+ const dropHidden = (p3) => !explicit && (projectScoped ? queryHidden(s2, p3) : inHiddenSubtree(s2, p3));
69688
69869
  if (shape === "tree") {
69689
69870
  const results = paths.filter((p3) => !dropHidden(p3)).map((p3) => {
69690
69871
  const segs2 = storePathToSegs(p3);
@@ -69945,7 +70126,10 @@ function createHandlers(dataRoot, opts = {}) {
69945
70126
  ...row2.type === "blob" ? { size: row2.size } : {},
69946
70127
  // a DEGRADED node (walk.ts: its source failed to parse) declares it, so the client
69947
70128
  // shows the failure instead of a merely-empty page and keeps its editor shut
69948
- ...row2.meta?.parseError ? { parseError: row2.meta.parseError } : {}
70129
+ ...row2.meta?.parseError ? { parseError: row2.meta.parseError } : {},
70130
+ // THE OVERLAY LAW: engine-managed `.yo` subtree entries browsed directly are DUMB —
70131
+ // the client forces the generic data view and greys the editor (settings.yo excepted)
70132
+ ...inYoSubtree(s2, segs2) && !isSettingsPath(segs2) ? { special: "overlay" } : {}
69949
70133
  };
69950
70134
  const text = buildEnvelope(
69951
70135
  { segs: segs2, subtree, docDepth, header, relations: buildRelations(dataRoot, s2, segs2) },
@@ -69981,6 +70165,7 @@ function createHandlers(dataRoot, opts = {}) {
69981
70165
  const segs2 = strToSegs(p3);
69982
70166
  if (segs2.length === 0) throw new Error(`mv: ${what} must name a file or directory`);
69983
70167
  if (segs2.some((g) => typeof g === "number")) throw new Error(`mv: ${what} must be a file/directory path (no positions)`);
70168
+ if (inYoSubtree(s2, segs2)) throw new Error(`mv: ${what} is inside the engine-managed .yo subtree`);
69984
70169
  return segs2.join("/");
69985
70170
  };
69986
70171
  const report = mv(dataRoot, rel2(from ?? "", "from"), rel2(to2 ?? "", "to"), { ignore: ignore2 });
@@ -69997,6 +70182,7 @@ function createHandlers(dataRoot, opts = {}) {
69997
70182
  const { path: reqPath, key: newKey } = data;
69998
70183
  const segs2 = canonSegs(s2, strToSegs(reqPath ?? ""), false);
69999
70184
  if (segs2.length === 0 || typeof segs2[segs2.length - 1] !== "string") throw new Error("rekey needs a keyed node path");
70185
+ if (inYoSubtree(s2, segs2) && !isSettingsPath(segs2)) throw new Error("entries under .yo are engine-managed and read-only");
70000
70186
  if (typeof newKey !== "string" || newKey === "" || newKey !== newKey.trim()) throw new Error("a key must be a non-empty, un-padded string");
70001
70187
  const parentSegs = segs2.slice(0, -1);
70002
70188
  const oldKey = String(segs2[segs2.length - 1]);
@@ -70247,11 +70433,21 @@ var inHiddenSubtree = (s2, p2) => {
70247
70433
  return false;
70248
70434
  };
70249
70435
  var visibleHasChildren = (s2, p2) => s2.children(p2).some((c3) => !isHidden(s2, c3.to));
70436
+ var inYoSubtree = (s2, segs) => {
70437
+ for (let i2 = 1; i2 <= segs.length; i2++) {
70438
+ const seg = segs[i2 - 1];
70439
+ if (seg === OVERLAY_KEY || seg === LEGACY_OVERLAY_KEY || seg === LEGACY_THUMBNAILS_KEY) {
70440
+ if (isHidden(s2, storePath(segs.slice(0, i2)))) return true;
70441
+ }
70442
+ }
70443
+ return false;
70444
+ };
70445
+ var isSettingsPath = (segs) => segs.some((g, i2) => g === OVERLAY_KEY && segs[i2 + 1] === "settings.yo");
70250
70446
  var queryHidden = (s2, p2) => {
70251
70447
  if (isHidden(s2, p2)) return true;
70252
70448
  for (let segs = storePathToSegs(p2); segs.length; segs = segs.slice(0, -1)) {
70253
70449
  const sp = storePath(segs);
70254
- if (isHidden(s2, sp) && String(segs[segs.length - 1]).startsWith(".")) return true;
70450
+ if (isHidden(s2, sp) && isHiddenEntryKey(segs[segs.length - 1])) return true;
70255
70451
  }
70256
70452
  return false;
70257
70453
  };
@@ -70321,6 +70517,21 @@ function projectSchema(dataRoot, s2, segs, depth, top) {
70321
70517
  if (t3) schema.title = t3;
70322
70518
  return schema;
70323
70519
  }
70520
+ var STUB_VALUE_MAX = 64 << 10;
70521
+ function isWholeFileBody(row, v) {
70522
+ const span = row.meta?.span;
70523
+ return !!span && span.start === 0 && span.end === v.length;
70524
+ }
70525
+ function stubValue(info, row) {
70526
+ const v = row.value;
70527
+ if (typeof v === "string" && v.length > STUB_VALUE_MAX && isWholeFileBody(row, v)) {
70528
+ info.value = v.slice(0, STUB_VALUE_MAX);
70529
+ info.valueTruncated = true;
70530
+ info.valueLength = v.length;
70531
+ return;
70532
+ }
70533
+ info.value = wireScalar(v);
70534
+ }
70324
70535
  function linkMarker(dataRoot, s2, segs) {
70325
70536
  const p2 = storePath(segs);
70326
70537
  const row = s2.node(p2);
@@ -70332,10 +70543,10 @@ function linkMarker(dataRoot, s2, segs) {
70332
70543
  const title = titleOf(s2, p2);
70333
70544
  if (title) info.title = title;
70334
70545
  if (k === "binary") info.size = row.size;
70335
- else if (k === "scalar") info.value = wireScalar(row.value);
70546
+ else if (k === "scalar") stubValue(info, row);
70336
70547
  else if (k === "omni" || k === "mix") {
70337
70548
  info.count = ownedEntries(s2, p2).length;
70338
- if (k === "omni") info.value = wireScalar(row.value);
70549
+ if (k === "omni") stubValue(info, row);
70339
70550
  } else {
70340
70551
  info.count = ownedEntries(s2, p2).filter((e3) => !(e3.kind === "contain" && isHidden(s2, e3.to))).length;
70341
70552
  }
@@ -70460,11 +70671,17 @@ function firstChunkText(s2, p2) {
70460
70671
  }
70461
70672
  var ONTO_FORMAT = "x-yamlover-onto";
70462
70673
  var FRAGMENT_FORMAT = "x-yamlover-fragment";
70463
- var YO_KEY2 = "yo";
70464
- var FRAGS_SUBKEY = "fragments";
70674
+ var FRAGS_SUBKEY = FRAGMENTS_SUBKEY;
70465
70675
  var CROP_SUBDIR = "fragments";
70466
70676
  var THUMB_SUBDIR = "thumbnails";
70467
70677
  var childPath = (parent, key) => (parent === ":" ? "" : parent) + ":" + key;
70678
+ var overlaySubPath = (s2, hostStore, sub) => {
70679
+ for (const k of overlayKeyAlts) {
70680
+ const p2 = childPath(childPath(hostStore, k), sub);
70681
+ if (s2.node(p2)) return p2;
70682
+ }
70683
+ return null;
70684
+ };
70468
70685
  function projectTag(s2, tagStore) {
70469
70686
  if (!s2.node(tagStore)) return null;
70470
70687
  const segs = storePathToSegs(tagStore);
@@ -70487,8 +70704,8 @@ function readAnnotations(s2, hostStore) {
70487
70704
  return out;
70488
70705
  }
70489
70706
  function readFragments(s2, hostStore) {
70490
- const frags = childPath(childPath(hostStore, YO_KEY2), FRAGS_SUBKEY);
70491
- if (!s2.node(frags)) return [];
70707
+ const frags = overlaySubPath(s2, hostStore, FRAGS_SUBKEY);
70708
+ if (!frags) return [];
70492
70709
  const out = [];
70493
70710
  for (const fc of s2.children(frags)) {
70494
70711
  if (!fc.label) continue;
@@ -70541,6 +70758,9 @@ function annotationsFor(dataRoot, s2, segs) {
70541
70758
  node: nodeClient,
70542
70759
  selector: f2.selector,
70543
70760
  fragmentSlug: f2.slug,
70761
+ // the fragment node's REAL client path — the delete target must carry the overlay
70762
+ // spelling the file actually uses (`.yo` or the legacy `yo`), never re-mint it
70763
+ fragmentPath: segsToStr(storePathToSegs(f2.node)),
70544
70764
  ...f2.description ? { description: f2.description } : {},
70545
70765
  ...f2.imageUrl ? { imageUrl: f2.imageUrl } : {}
70546
70766
  });
@@ -70579,7 +70799,7 @@ function pointerRaw(clientPath) {
70579
70799
  return "::" + out;
70580
70800
  }
70581
70801
  var LANES_SUBKEY = "lanes";
70582
- var BOARD_CONFIG_KEYS = /* @__PURE__ */ new Set(["workflow", "lanes", "yamlover", "ontos", YO_KEY2]);
70802
+ var BOARD_CONFIG_KEYS = /* @__PURE__ */ new Set(["workflow", "lanes", "yamlover", "ontos", ...overlayKeyAlts]);
70583
70803
  var TAGISH_FORMATS = /* @__PURE__ */ new Set([ONTO_FORMAT, "x-yamlover-workflow", "x-yamlover-board"]);
70584
70804
  function writeBoardStructure(src, structure) {
70585
70805
  let lines = src.replace(/\n+$/, "").split("\n");
@@ -70601,15 +70821,15 @@ function writeBoardStructure(src, structure) {
70601
70821
  }
70602
70822
  return out;
70603
70823
  };
70604
- replaceSeqEntryAt(lines, reachBody(lines, [YO_KEY2]), LANES_SUBKEY, render);
70824
+ replaceSeqEntryAt(lines, reachBody(lines, [overlayKeyAlts]), LANES_SUBKEY, render);
70605
70825
  return lines.join("\n") + "\n";
70606
70826
  }
70607
70827
  function membershipPathsOf(s2, store) {
70608
70828
  return s2.relationships(store).out.filter((e3) => e3.kind === "back" && e3.label == null).map((e3) => segsToStr(storePathToSegs(e3.to)));
70609
70829
  }
70610
70830
  function readBoardStructure(s2, boardStore) {
70611
- const lanesStore = childPath(childPath(boardStore, YO_KEY2), LANES_SUBKEY);
70612
- if (!s2.node(lanesStore)) return null;
70831
+ const lanesStore = overlaySubPath(s2, boardStore, LANES_SUBKEY);
70832
+ if (!lanesStore) return null;
70613
70833
  return s2.children(lanesStore).map(
70614
70834
  (lane) => s2.children(lane.to).map((comp) => ({
70615
70835
  tags: membershipPathsOf(s2, comp.to),
@@ -70761,7 +70981,7 @@ function fragmentBlockLines(slug, selector, imagePtr, indent) {
70761
70981
  lines.push(`${pad} created: ${(/* @__PURE__ */ new Date()).toISOString()}`);
70762
70982
  return lines;
70763
70983
  }
70764
- var isFragmentSegs = (segs) => segs.length >= 3 && segs[segs.length - 3] === YO_KEY2 && segs[segs.length - 2] === FRAGS_SUBKEY;
70984
+ var isFragmentSegs2 = (segs) => isFragmentSegs(segs);
70765
70985
  var labelToken = (v) => /[:\[\]()&'"\n]|^\s|\s$/.test(v) ? "'" + v.replace(/'/g, "''") + "'" : v;
70766
70986
  function locateExact(text, sel) {
70767
70987
  const needle = String(sel.exact ?? "");
@@ -70842,7 +71062,7 @@ function embedAnnotation(dataRoot, s2, mode, a2) {
70842
71062
  const made = embedFragment(dataRoot, s2, mode, { target: a2.target, selector: sel });
70843
71063
  return embedAnnotation(dataRoot, s2, mode, { target: made.fragmentPath, tag: a2.tag });
70844
71064
  }
70845
- if (Object.keys(params).length && !isFragmentSegs(segs)) {
71065
+ if (Object.keys(params).length && !isFragmentSegs2(segs)) {
70846
71066
  throw new Error("annotation parameters live on a fragment (docs/annotations/applications)");
70847
71067
  }
70848
71068
  const tokens = [`&${pointerRaw(a2.tag)}:-`, ...Object.entries(params).map(([k, v]) => `${keyToken(k)}: ${yScalar(v)}`)];
@@ -70884,7 +71104,7 @@ function ownDocFile(dataRoot, segs) {
70884
71104
  function embedFragment(dataRoot, s2, mode, f2) {
70885
71105
  const segs = strToSegs(f2.target || ":");
70886
71106
  const chosen = typeof f2.slug === "string" ? f2.slug.trim() : "";
70887
- if (chosen !== "" && s2.node(storePath([...segs, YO_KEY2, FRAGS_SUBKEY, chosen]))) {
71107
+ if (chosen !== "" && overlayKeyAlts.some((yo) => s2.node(storePath([...segs, yo, FRAGS_SUBKEY, chosen])))) {
70888
71108
  throw new Error(`a fragment \`${chosen}\` already exists on this node`);
70889
71109
  }
70890
71110
  const slug = chosen !== "" ? chosen : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -70900,9 +71120,10 @@ function embedFragment(dataRoot, s2, mode, f2) {
70900
71120
  /*ensureOmni*/
70901
71121
  true
70902
71122
  );
70903
- upsertMapEntryAt(lines, reachBodyAt(lines, region, [YO_KEY2, FRAGS_SUBKEY]), slug, (indent) => fragmentBlockLines(slug, f2.selector, null, indent));
71123
+ const yoKey2 = presentStepAlt(lines, region, overlayKeyAlts);
71124
+ upsertMapEntryAt(lines, reachBodyAt(lines, region, [overlayKeyAlts, FRAGS_SUBKEY]), slug, (indent) => fragmentBlockLines(slug, f2.selector, null, indent));
70904
71125
  writeBody(dataRoot, s2, bodyFile2, lines.join("\n") + "\n");
70905
- return { slug, fragmentPath: segsToStr([...segs, YO_KEY2, FRAGS_SUBKEY, slug]) };
71126
+ return { slug, fragmentPath: segsToStr([...segs, yoKey2, FRAGS_SUBKEY, slug]) };
70906
71127
  }
70907
71128
  const { bodyFile, within } = hostFor(dataRoot, s2, segs);
70908
71129
  let imagePtr = null;
@@ -70918,8 +71139,9 @@ function embedFragment(dataRoot, s2, mode, f2) {
70918
71139
  }
70919
71140
  mkdirInside(dataRoot, path11.dirname(bodyFile), { recursive: true });
70920
71141
  const src = fs10.existsSync(bodyFile) ? fs10.readFileSync(bodyFile, "utf8") : "";
71142
+ const yoKey = overlaySpellingAt(src, within);
70921
71143
  writeBody(dataRoot, s2, bodyFile, upsertFragment(src, within, slug, (indent) => fragmentBlockLines(slug, f2.selector, imagePtr, indent)));
70922
- return { slug, fragmentPath: segsToStr([...segs, YO_KEY2, FRAGS_SUBKEY, slug]) };
71144
+ return { slug, fragmentPath: segsToStr([...segs, yoKey, FRAGS_SUBKEY, slug]) };
70923
71145
  }
70924
71146
  var thumbResKey = (w, h) => `[${w}, ${h}]`;
70925
71147
  var thumbName = (hash, w, h) => `${hash.replace(/:/g, "-")}-${w}x${h}.jpg`;
@@ -70984,9 +71206,9 @@ function unembedAnnotation(dataRoot, s2, target, tag, sel) {
70984
71206
  false
70985
71207
  ), keys);
70986
71208
  removeBookmarkAt(lines, fragRegion, pred);
70987
- if (isFragmentSegs(keys) && !bookmarksRemainAt(lines, fragRegion())) {
71209
+ if (isFragmentSegs2(keys) && !bookmarksRemainAt(lines, fragRegion())) {
70988
71210
  removeMapEntryAt(lines, chunkFieldRegion(lines, indices, false), keys.slice(0, -1), keys[keys.length - 1]);
70989
- pruneEmptyKeyAt(lines, chunkFieldRegion(lines, indices, false), YO_KEY2);
71211
+ pruneEmptyKeyAt(lines, chunkFieldRegion(lines, indices, false), overlayKeyAlts);
70990
71212
  collapseChunkOmni(lines, indices);
70991
71213
  } else if (keys.length === 0 && !bookmarksRemainAt(lines, fragRegion())) {
70992
71214
  collapseChunkOmni(lines, indices);
@@ -71007,8 +71229,8 @@ function unembedAnnotation(dataRoot, s2, target, tag, sel) {
71007
71229
  if (!fs10.existsSync(bodyFile)) return bodyFile;
71008
71230
  let src = removeBookmark(fs10.readFileSync(bodyFile, "utf8"), within, pred);
71009
71231
  const overlay2 = overlaidDir(bodyFile) !== null;
71010
- if (isFragmentSegs(within) && !bookmarksRemain(src, within)) {
71011
- src = removeMapEntry(src, within.slice(0, -3), [YO_KEY2, FRAGS_SUBKEY], within[within.length - 1]);
71232
+ if (isFragmentSegs2(within) && !bookmarksRemain(src, within)) {
71233
+ src = removeMapEntry(src, within.slice(0, -3), within.slice(-3, -1), within[within.length - 1]);
71012
71234
  src = pruneEmptyYo(src, within.slice(0, -3));
71013
71235
  src = pruneEmptyAnnotations(src, within.slice(0, -3), overlay2);
71014
71236
  } else {
@@ -72360,6 +72582,9 @@ function applyEdits2(dataRoot, s2, edits) {
72360
72582
  for (const e3 of edits) {
72361
72583
  const editSegs = applyRemap(canonSegs(s2, strToSegs(e3.path ?? ""), true));
72362
72584
  const op = String(e3.op ?? "");
72585
+ if (inYoSubtree(s2, editSegs) && !isSettingsPath(editSegs)) {
72586
+ throw new Error("entries under .yo are engine-managed and read-only (settings.yo excepted)");
72587
+ }
72363
72588
  if (op === "insert" && (e3.concrete === "file/yamlover" || isOverlayDirConcrete(e3.concrete)) && isPlainDir(dataRoot, s2, editSegs)) {
72364
72589
  const dir = path11.resolve(dataRoot, ...editSegs.map(String));
72365
72590
  const src = [...metaTag(e3.meta, void 0, false) ? [metaTag(e3.meta, void 0, false)] : [], String(e3.yamlover ?? ""), ""].join("\n");
@@ -72718,6 +72943,7 @@ function envelopeDeps(s2, dataRoot, membersOnDisk) {
72718
72943
  function previewEnvelope(source) {
72719
72944
  const doc = parseYamlover(source, "<preview>");
72720
72945
  doc.root.meta = { ...doc.root.meta, documentRoot: true, ...doc.head?.length ? { head: doc.head } : {} };
72946
+ stampHiddenKeys(doc.root);
72721
72947
  const s2 = new Store(":memory:");
72722
72948
  try {
72723
72949
  s2.indexDocument(doc);
@@ -72868,7 +73094,19 @@ var EXT_CT = {
72868
73094
  ".html": "text/html",
72869
73095
  ".md": "text/markdown",
72870
73096
  ".csv": "text/csv",
72871
- ".epub": "application/epub+zip"
73097
+ ".epub": "application/epub+zip",
73098
+ ".js": "text/javascript",
73099
+ ".mjs": "text/javascript",
73100
+ ".cjs": "text/javascript",
73101
+ ".ts": "text/typescript",
73102
+ ".tsx": "text/typescript",
73103
+ ".jsx": "text/javascript",
73104
+ ".css": "text/css",
73105
+ ".py": "text/x-python",
73106
+ ".sh": "text/x-shellscript",
73107
+ ".sql": "text/x-sql",
73108
+ ".xml": "text/xml",
73109
+ ".jsonl": "application/x-ndjson"
72872
73110
  };
72873
73111
  function formatFromExt(file) {
72874
73112
  return EXT_CT[path11.extname(file).toLowerCase()] ?? null;
@@ -72880,7 +73118,15 @@ var UTF8_CT = /* @__PURE__ */ new Set([
72880
73118
  "text/asciidoc",
72881
73119
  "text/x-plantuml",
72882
73120
  "text/csv",
72883
- "text/tab-separated-values"
73121
+ "text/tab-separated-values",
73122
+ "text/javascript",
73123
+ "text/typescript",
73124
+ "text/css",
73125
+ "text/x-python",
73126
+ "text/x-shellscript",
73127
+ "text/x-sql",
73128
+ "text/xml",
73129
+ "application/x-ndjson"
72884
73130
  ]);
72885
73131
  function blobContentType(format) {
72886
73132
  if (format == null) return "application/octet-stream";