yamlover 0.3.7 → 0.3.10

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
@@ -62441,13 +62441,13 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62441
62441
  let steps = ptr.steps;
62442
62442
  let chain;
62443
62443
  const linkAuthority = ptr.base.scope === "link" ? ptr.base.authority : "";
62444
- let isLink = false;
62444
+ const isWorld = ptr.base.scope === "link" && ptr.base.world === true;
62445
62445
  const external = () => ({ kind: "external", authority: linkAuthority, steps: ptr.steps });
62446
62446
  switch (ptr.base.scope) {
62447
62447
  case "link": {
62448
- isLink = true;
62449
62448
  chain = chains.get(root) ?? [root];
62450
- steps = [{ sel: "key", name: ptr.base.authority }, ...ptr.steps];
62449
+ const selfImport = ptr.base.authority === "yamlover" && !root.entries?.some((e3) => e3.key === "yamlover");
62450
+ steps = selfImport ? ptr.steps : [{ sel: "key", name: ptr.base.authority }, ...ptr.steps];
62451
62451
  break;
62452
62452
  }
62453
62453
  case "document": {
@@ -62470,7 +62470,7 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62470
62470
  }
62471
62471
  for (const st of steps) {
62472
62472
  if (st.sel === "parent") {
62473
- if (chain.length <= 1) return isLink ? external() : { kind: "unresolved", reason: '".." above the document root' };
62473
+ if (chain.length <= 1) return isWorld ? external() : { kind: "unresolved", reason: '".." above the document root' };
62474
62474
  chain = chain.slice(0, -1);
62475
62475
  continue;
62476
62476
  }
@@ -62485,8 +62485,8 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62485
62485
  continue;
62486
62486
  }
62487
62487
  }
62488
- if (!node.entries) return isLink ? external() : { kind: "unresolved", reason: "step into a node with no fields" };
62489
- return isLink ? external() : { kind: "unresolved", reason: `no ${st.sel === "key" ? `key "${st.name}"` : `index [${st.n}]`}` };
62488
+ if (!node.entries) return isWorld ? external() : { kind: "unresolved", reason: "step into a node with no fields" };
62489
+ return isWorld ? external() : { kind: "unresolved", reason: `no ${st.sel === "key" ? `key "${st.name}"` : `index [${st.n}]`}` };
62490
62490
  }
62491
62491
  if (isPointer(entry.value)) {
62492
62492
  if (visited.has(entry.value)) return { kind: "unresolved", reason: "pointer cycle" };
@@ -62631,6 +62631,71 @@ var Store = class {
62631
62631
  throw e3;
62632
62632
  }
62633
62633
  }
62634
+ /** Patch the index for a SINGLE changed subtree instead of rebuilding the whole DB. `doc` is the
62635
+ * FULL, freshly-resolved document (the cached tree with the changed file's subtree spliced back
62636
+ * in and schemas re-applied) — correctness comes from resolving against the whole tree in
62637
+ * memory; speed comes from writing only the rows under `prefix` (the store path P of the changed
62638
+ * subtree). Every node/edge/dangling/file row whose owner lies under P is replaced; rows outside
62639
+ * P are left untouched (their resolution did not change). `files` is the re-walked manifest for
62640
+ * the subtree (POSIX paths under `relPrefix`).
62641
+ *
62642
+ * Returns false WITHOUT writing when the patch is not provably equal to a full rebuild: if any
62643
+ * external `ref`/`back` edge pointing INTO the subtree changed (a referenced node added/removed/
62644
+ * re-resolved), the caller must fall back to a full reindex. The boundary `contain` edge into P
62645
+ * (from P's parent, outside the subtree) is stable and intentionally left in place. */
62646
+ patchSubtree(doc, prefix, files, relPrefix) {
62647
+ const colon = prefix + ":";
62648
+ const brack = prefix + "[";
62649
+ const underP = (p2) => p2 === prefix || p2.startsWith(colon) || p2.startsWith(brack);
62650
+ const edges = resolveDocument(doc);
62651
+ const edgeKey = (from, to2, label, kind, pos) => JSON.stringify([from, to2, label, kind, pos]);
62652
+ const extInNew = edges.filter((r2) => r2.target.kind === "node" && underP(r2.target.path) && !underP(r2.holder)).map((r2) => edgeKey(r2.holder, r2.target.path, r2.label, r2.edge, r2.pos)).sort();
62653
+ const extInOld = this.db.prepare("SELECT from_path, to_path, label, kind, pos FROM edge WHERE kind IN ('ref','back')").all().filter((r2) => underP(r2.to_path) && !underP(r2.from_path)).map((r2) => edgeKey(r2.from_path, r2.to_path, r2.label ?? null, r2.kind, r2.pos ?? null)).sort();
62654
+ if (extInOld.length !== extInNew.length || extInOld.some((k, i2) => k !== extInNew[i2])) return false;
62655
+ const insNode = this.db.prepare(
62656
+ `INSERT INTO node (path, type, format, value, content_hash, size, is_array, meta)
62657
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
62658
+ );
62659
+ const insEdge = this.db.prepare(
62660
+ `INSERT INTO edge (from_path, to_path, label, kind, pos) VALUES (?, ?, ?, ?, ?)`
62661
+ );
62662
+ const insDangling = this.db.prepare("INSERT INTO dangling (from_path, raw, reason) VALUES (?, ?, ?)");
62663
+ const insFile = this.db.prepare("INSERT OR REPLACE INTO file (path, hash, size, mtime_ms) VALUES (?, ?, ?, ?)");
62664
+ this.db.exec("BEGIN");
62665
+ try {
62666
+ const delUnder = (col, table) => {
62667
+ this.db.prepare(`DELETE FROM ${table} WHERE ${col} = ? OR substr(${col},1,?) = ? OR substr(${col},1,?) = ?`).run(prefix, colon.length, colon, brack.length, brack);
62668
+ };
62669
+ delUnder("path", "node");
62670
+ delUnder("from_path", "edge");
62671
+ delUnder("from_path", "dangling");
62672
+ if (relPrefix) this.db.prepare("DELETE FROM file WHERE substr(path,1,?) = ?").run(relPrefix.length, relPrefix);
62673
+ walkNodes(doc.root, ":", (p2, node, parent, label, pos) => {
62674
+ if (!underP(p2)) return;
62675
+ const meta = node.meta ? JSON.stringify(node.meta) : null;
62676
+ const owned = node.entries?.filter((e3) => e3.edge !== "back") ?? [];
62677
+ const isArray = node.array || node.kind === "mapping" && owned.length > 0 && owned.every((e3) => e3.key === null);
62678
+ const value = node.kind === "scalar" ? JSON.stringify(node.value) : null;
62679
+ const format = node.kind === "blob" ? node.format : formatFromMeta(node);
62680
+ const hash = node.kind === "blob" ? node.contentHash : null;
62681
+ const size = node.kind === "blob" ? node.size : null;
62682
+ insNode.run(p2, node.kind, format, value, hash, size, isArray ? 1 : 0, meta);
62683
+ if (parent !== null && underP(parent)) insEdge.run(parent, p2, label, "contain", pos);
62684
+ });
62685
+ for (const r2 of edges) {
62686
+ if (!underP(r2.holder)) continue;
62687
+ if (r2.target.kind === "node") insEdge.run(r2.holder, r2.target.path, r2.label, r2.edge, r2.pos);
62688
+ else if (r2.target.kind === "unresolved") insDangling.run(r2.from, r2.raw, r2.target.reason);
62689
+ }
62690
+ for (const f2 of files) insFile.run(f2.path, f2.hash, f2.size, f2.mtimeMs);
62691
+ this.db.exec("COMMIT");
62692
+ this._stale = false;
62693
+ return true;
62694
+ } catch (e3) {
62695
+ this.db.exec("ROLLBACK");
62696
+ throw e3;
62697
+ }
62698
+ }
62634
62699
  /** Incrementally add ONE annotation document at `annStorePath`, with a forward `target` ref edge
62635
62700
  * to `targetStorePath` and (when given) a keyless `back` edge to `tagStorePath` — the applied
62636
62701
  * tag's `~-` membership. This avoids a full re-walk/rebuild (which re-reads and re-hashes every
@@ -64312,15 +64377,16 @@ function* walkTreeGen(absDir, opts = {}) {
64312
64377
  root.meta = { ...root.meta, documentRoot: true };
64313
64378
  const defsRoot = findDefsRoot(absDir);
64314
64379
  const defsDir = path.join(defsRoot, "$defs");
64380
+ const selfRoot = path.resolve(absDir) === defsRoot;
64315
64381
  const arrayRoot = root.array || (root.entries?.length ? root.entries.every((e3) => e3.key === null) : false);
64316
64382
  let builtinDefs;
64317
64383
  if (!arrayRoot && root.entries && !root.entries.some((e3) => e3.key === "yamlover")) {
64318
- if (fs.existsSync(defsDir)) {
64384
+ if (fs.existsSync(defsDir) && !selfRoot) {
64319
64385
  const shared = [{ key: "$defs", edge: "contain", value: yield* dirNode(defsDir, ctx) }];
64320
64386
  const tagsDir = path.join(defsRoot, "tags");
64321
64387
  if (fs.existsSync(tagsDir)) shared.push({ key: "tags", edge: "contain", value: yield* dirNode(tagsDir, ctx) });
64322
64388
  root.entries.push({ key: "yamlover", edge: "contain", value: { kind: "mapping", entries: shared, array: false } });
64323
- } else {
64389
+ } else if (!fs.existsSync(defsDir)) {
64324
64390
  const built = builtinYamloverGraft();
64325
64391
  root.entries.push({ key: "yamlover", edge: "contain", value: built.node });
64326
64392
  builtinDefs = built.defs;
@@ -64338,7 +64404,7 @@ function reindex(store, absDir, opts = {}) {
64338
64404
  store.indexDocument(doc, files);
64339
64405
  return diffManifest(prev, files);
64340
64406
  }
64341
- async function reindexAsync(store, absDir, opts = {}) {
64407
+ async function reindexAsyncDoc(store, absDir, opts = {}) {
64342
64408
  const prev = store.stale ? /* @__PURE__ */ new Map() : store.manifest();
64343
64409
  const onProgress = opts.onProgress;
64344
64410
  const total = onProgress ? await countChildren(path.resolve(absDir), opts) : void 0;
@@ -64350,7 +64416,49 @@ async function reindexAsync(store, absDir, opts = {}) {
64350
64416
  onProgress?.({ done: total ?? files.length, total, message: "writing index\u2026" });
64351
64417
  await yieldLoop();
64352
64418
  store.indexDocument(doc, files);
64353
- return diffManifest(prev, files);
64419
+ return { diff: diffManifest(prev, files), doc, files };
64420
+ }
64421
+ async function reindexPathAsync(store, absDir, cachedDoc, changedRel, opts = {}) {
64422
+ const root = path.resolve(absDir);
64423
+ const parts = changedRel.split("/");
64424
+ const yi = parts.indexOf(YAMLOVER_DIR);
64425
+ const dirSegs = yi >= 0 ? parts.slice(0, yi) : parts.slice(0, -1);
64426
+ if (dirSegs.length === 0) return null;
64427
+ if (dirSegs[0] === "$defs" || dirSegs[0] === "tags") return null;
64428
+ let entries = cachedDoc.root.entries;
64429
+ let target = null;
64430
+ for (let d = 0; d < dirSegs.length; d++) {
64431
+ if (!entries) return null;
64432
+ const i2 = entries.findIndex((e3) => e3.key === dirSegs[d] && !isPointer(e3.value));
64433
+ if (i2 < 0) return null;
64434
+ if (d === dirSegs.length - 1) target = { arr: entries, i: i2 };
64435
+ else entries = entries[i2].value.entries;
64436
+ }
64437
+ if (!target) return null;
64438
+ const absSpliceDir = path.join(root, ...dirSegs);
64439
+ if (!fs.existsSync(absSpliceDir) || !fs.statSync(absSpliceDir).isDirectory()) return null;
64440
+ const prev = store.stale ? /* @__PURE__ */ new Map() : store.manifest();
64441
+ const ctx = { root, opts: { ...opts, cache: opts.cache ?? manifestCache(prev) }, files: /* @__PURE__ */ new Map(), count: 0 };
64442
+ const gen = dirNode(absSpliceDir, ctx);
64443
+ let r2 = gen.next();
64444
+ while (!r2.done) r2 = gen.next();
64445
+ target.arr[target.i].value = r2.value;
64446
+ applySchemas(cachedDoc.root, findDefsRoot(absDir), graftDefs(cachedDoc.root));
64447
+ const relPrefix = dirSegs.join("/") + "/";
64448
+ const P = ":" + dirSegs.join(":");
64449
+ const prevSub = new Map([...prev].filter(([k]) => k.startsWith(relPrefix)));
64450
+ const files = [...ctx.files.values()];
64451
+ const diff2 = diffManifest(prevSub, files);
64452
+ if (!store.patchSubtree(cachedDoc, P, files, relPrefix)) return null;
64453
+ return { diff: diff2, doc: cachedDoc };
64454
+ }
64455
+ function graftDefs(root) {
64456
+ const yam = root.entries?.find((e3) => e3.key === "yamlover" && !isPointer(e3.value))?.value;
64457
+ const defs = yam?.entries?.find((e3) => e3.key === "$defs" && !isPointer(e3.value))?.value;
64458
+ if (!defs?.entries) return void 0;
64459
+ const m = /* @__PURE__ */ new Map();
64460
+ for (const e3 of defs.entries) if (e3.key && !isPointer(e3.value)) m.set(e3.key, e3.value);
64461
+ return m.size ? m : void 0;
64354
64462
  }
64355
64463
  function manifestCache(prev) {
64356
64464
  return (rel, size, mtimeMs) => {
@@ -64629,6 +64737,7 @@ function applySchemas(root, defsRoot, builtinDefs) {
64629
64737
  const addl = field(s2, "additionalProperties");
64630
64738
  for (const e3 of inst.entries ?? []) {
64631
64739
  if (e3.key == null || isPointer(e3.value)) continue;
64740
+ if (e3.value.meta?.schema && isPointer(e3.value.meta.schema)) continue;
64632
64741
  const declared = props && !isPointer(props) ? field(props, e3.key) : null;
64633
64742
  const sub = declared ?? addl;
64634
64743
  if (sub) apply(e3.value, sub, depth + 1);
@@ -65380,6 +65489,10 @@ function evalQuery(s2, text, from = ":") {
65380
65489
  binds = [docRootOf(s2, from)];
65381
65490
  break;
65382
65491
  case "link": {
65492
+ if (q.base.authority === "yamlover" && childByKey(s2, ":", "yamlover") === null) {
65493
+ binds = [":"];
65494
+ break;
65495
+ }
65383
65496
  const hit = childByKey(s2, ":", q.base.authority);
65384
65497
  binds = hit === null ? [] : [hit];
65385
65498
  break;
@@ -65994,6 +66107,7 @@ function createHandlers(dataRoot, opts = {}) {
65994
66107
  fs9.mkdirSync(path9.dirname(dbPath), { recursive: true });
65995
66108
  const store0 = new Store(dbPath);
65996
66109
  const store = () => store0;
66110
+ let cachedDoc = null;
65997
66111
  const log = opts.log ?? (() => {
65998
66112
  });
65999
66113
  let closed = false;
@@ -66099,14 +66213,32 @@ function createHandlers(dataRoot, opts = {}) {
66099
66213
  }
66100
66214
  })();
66101
66215
  };
66102
- const doReindex = () => reindexAsync(store0, dataRoot, { ignore: ignore2 });
66216
+ const doReindex = async () => {
66217
+ const { diff: diff2, doc } = await reindexAsyncDoc(store0, dataRoot, { ignore: ignore2 });
66218
+ cachedDoc = doc;
66219
+ return diff2;
66220
+ };
66221
+ const doReindexFile = async (absFile) => {
66222
+ if (cachedDoc) {
66223
+ const rel = path9.relative(dataRoot, absFile).split(path9.sep).join("/");
66224
+ try {
66225
+ const res = await reindexPathAsync(store0, dataRoot, cachedDoc, rel, { ignore: ignore2 });
66226
+ if (res) {
66227
+ cachedDoc = res.doc;
66228
+ return res.diff;
66229
+ }
66230
+ } catch {
66231
+ }
66232
+ }
66233
+ return doReindex();
66234
+ };
66103
66235
  const runIndexTask = (label) => enqueue(async () => {
66104
66236
  const h = tasks.start(label);
66105
66237
  const t0 = Date.now();
66106
66238
  let lastLog = 0;
66107
66239
  log(`${label}\u2026`);
66108
66240
  try {
66109
- const diff2 = await reindexAsync(store0, dataRoot, {
66241
+ const { diff: diff2, doc } = await reindexAsyncDoc(store0, dataRoot, {
66110
66242
  ignore: ignore2,
66111
66243
  onProgress: (p2) => {
66112
66244
  h.progress(p2.done, p2.total, p2.message);
@@ -66117,6 +66249,7 @@ function createHandlers(dataRoot, opts = {}) {
66117
66249
  }
66118
66250
  }
66119
66251
  });
66252
+ cachedDoc = doc;
66120
66253
  h.done();
66121
66254
  log(
66122
66255
  `${label} done in ${((Date.now() - t0) / 1e3).toFixed(1)}s (+${diff2.added.length} ~${diff2.changed.length} \u2212${diff2.removed.length} \u2192${diff2.moved.length})`
@@ -66139,6 +66272,7 @@ function createHandlers(dataRoot, opts = {}) {
66139
66272
  if (r2.editedFiles.length > 0) {
66140
66273
  const follow = reindex(store0, dataRoot, { ignore: ignore2 });
66141
66274
  diff2.changed = [.../* @__PURE__ */ new Set([...diff2.changed, ...follow.changed])];
66275
+ cachedDoc = null;
66142
66276
  }
66143
66277
  }
66144
66278
  h.done();
@@ -66202,8 +66336,8 @@ function createHandlers(dataRoot, opts = {}) {
66202
66336
  if (!a2?.tag || s2.node(tagStore)?.format !== TAG_FORMAT) {
66203
66337
  throw new Error("annotation needs a `tag` that is an x-yamlover-tag node");
66204
66338
  }
66205
- embedAnnotation(dataRoot, s2, a2);
66206
- broadcast(await doReindex());
66339
+ const bodyFile = embedAnnotation(dataRoot, s2, a2);
66340
+ broadcast(await doReindexFile(bodyFile));
66207
66341
  scheduleHasher();
66208
66342
  return { ok: true };
66209
66343
  })
@@ -66228,8 +66362,8 @@ function createHandlers(dataRoot, opts = {}) {
66228
66362
  const tag = url.searchParams.get("tag") || "";
66229
66363
  enqueue(async () => {
66230
66364
  if (!tag) throw new Error("delete needs a `tag`");
66231
- unembedAnnotation(dataRoot, s2, target, tag);
66232
- broadcast(await doReindex());
66365
+ const bodyFile = unembedAnnotation(dataRoot, s2, target, tag);
66366
+ broadcast(await doReindexFile(bodyFile));
66233
66367
  }).then(() => sendJson(res, 200, { ok: true })).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66234
66368
  return;
66235
66369
  }
@@ -66255,6 +66389,22 @@ function createHandlers(dataRoot, opts = {}) {
66255
66389
  ).then((body) => sendJson(res, 201, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66256
66390
  return;
66257
66391
  }
66392
+ if (req.method === "POST" && url.pathname === "/api/board") {
66393
+ readBody(req).then(
66394
+ (data) => enqueue(async () => {
66395
+ const b = data;
66396
+ const cols = Array.isArray(b?.columns) ? b.columns.map((lane) => Array.isArray(lane) ? lane.map((p3) => String(p3)) : []) : [];
66397
+ const { bodyFile } = hostFor(dataRoot, s2, strToSegs(b?.path || ":"));
66398
+ fs9.mkdirSync(path9.dirname(bodyFile), { recursive: true });
66399
+ const src = fs9.existsSync(bodyFile) ? fs9.readFileSync(bodyFile, "utf8") : "";
66400
+ fs9.writeFileSync(bodyFile, writeBoardColumns(src, cols));
66401
+ broadcast(await doReindex());
66402
+ scheduleHasher();
66403
+ return { ok: true };
66404
+ })
66405
+ ).then((body) => sendJson(res, 201, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66406
+ return;
66407
+ }
66258
66408
  if (req.method === "POST" && url.pathname === "/api/paste") {
66259
66409
  readBody(req).then(
66260
66410
  (data) => enqueue(async () => {
@@ -66350,7 +66500,7 @@ function createHandlers(dataRoot, opts = {}) {
66350
66500
  const viewDepth = depth ?? 1;
66351
66501
  const kind = displayKind(s2, p2, row);
66352
66502
  if (url.pathname === "/api/json") {
66353
- const wantBytes = kind === "binary" && url.searchParams.get("binary") === "1";
66503
+ const wantBytes = row.type === "blob" && url.searchParams.get("binary") === "1";
66354
66504
  sendJson(res, 200, {
66355
66505
  path: segsToStr(segs),
66356
66506
  type: tocType(s2, p2, row),
@@ -66389,7 +66539,19 @@ function createHandlers(dataRoot, opts = {}) {
66389
66539
  function tocType(s2, p2, row) {
66390
66540
  return typeName(s2, p2, row);
66391
66541
  }
66542
+ var JSON_EXT = { ".json": "json", ".json5": "json5", ".json5p": "json5p" };
66392
66543
  function concreteOf(dataRoot, segs, row) {
66544
+ const last = segs[segs.length - 1];
66545
+ if (typeof last === "string") {
66546
+ const json = JSON_EXT[path9.extname(last).toLowerCase()];
66547
+ if (json) {
66548
+ const abs2 = path9.resolve(dataRoot, ...segs.map(String));
66549
+ try {
66550
+ if (fs9.statSync(abs2).isFile()) return json;
66551
+ } catch {
66552
+ }
66553
+ }
66554
+ }
66393
66555
  if (row.type !== "mapping") return null;
66394
66556
  if (segs.some((g) => typeof g === "number")) return null;
66395
66557
  const abs = path9.resolve(dataRoot, ...segs.map(String));
@@ -66645,6 +66807,21 @@ function pointerRaw(clientPath) {
66645
66807
  }
66646
66808
  return "::" + out;
66647
66809
  }
66810
+ function writeBoardColumns(src, cols) {
66811
+ const laneLine = (lane) => `- [${lane.map((p2) => pointerToken(pointerRaw(p2))).join(", ")}]`;
66812
+ const block = cols.length === 0 ? ["columns: []"] : ["columns:", ...cols.map(laneLine)];
66813
+ let lines = src.replace(/\n+$/, "").split("\n");
66814
+ if (src.trim() === "") lines = ["!!<*yamlover:$defs:board>"];
66815
+ const start = lines.findIndex((l2) => /^columns:/.test(l2));
66816
+ if (start >= 0) {
66817
+ let end = start + 1;
66818
+ while (end < lines.length && (lines[end] === "" || /^[ \t-]/.test(lines[end]))) end++;
66819
+ lines.splice(start, end - start, ...block);
66820
+ } else {
66821
+ lines.push(...block);
66822
+ }
66823
+ return lines.join("\n") + "\n";
66824
+ }
66648
66825
  function sidecarTarget(dataRoot, mode, subdir, bodyFile) {
66649
66826
  const dirOverlay = bodyFile.endsWith(path9.join(".yamlover", "body.yamlover"));
66650
66827
  if (mode === "per-directory" && dirOverlay) {
@@ -66704,6 +66881,7 @@ function embedAnnotation(dataRoot, s2, a2) {
66704
66881
  fs9.mkdirSync(path9.dirname(bodyFile), { recursive: true });
66705
66882
  const src = fs9.existsSync(bodyFile) ? fs9.readFileSync(bodyFile, "utf8") : "";
66706
66883
  fs9.writeFileSync(bodyFile, appendAnnotation(src, within, (indent) => annotationItemLines(a2, indent)));
66884
+ return bodyFile;
66707
66885
  }
66708
66886
  function embedFragment(dataRoot, s2, mode, f2) {
66709
66887
  const segs = strToSegs(f2.target || ":");
@@ -66760,10 +66938,11 @@ async function ensureThumbnail(dataRoot, s2, mode, segs, row, w, h) {
66760
66938
  }
66761
66939
  function unembedAnnotation(dataRoot, s2, target, tag) {
66762
66940
  const { bodyFile, within } = hostFor(dataRoot, s2, strToSegs(target || ":"));
66763
- if (!fs9.existsSync(bodyFile)) return;
66764
- const needle = pointerRaw(tag);
66941
+ if (!fs9.existsSync(bodyFile)) return bodyFile;
66942
+ const needlePath = ":" + pointerRaw(tag).replace(/^:+/, "");
66765
66943
  const src = fs9.readFileSync(bodyFile, "utf8");
66766
- fs9.writeFileSync(bodyFile, removeAnnotation(src, within, (itemText) => itemText.includes(needle)));
66944
+ fs9.writeFileSync(bodyFile, removeAnnotation(src, within, (itemText) => itemText.replace(/\s+/g, "").includes(needlePath)));
66945
+ return bodyFile;
66767
66946
  }
66768
66947
  function writeTag(dataRoot, location, name) {
66769
66948
  if (/[/\\\r\n:]/.test(name)) throw new Error("a tag name cannot contain '/', '\\', ':' or line breaks");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yamlover",
3
- "version": "0.3.7",
3
+ "version": "0.3.10",
4
4
  "description": "Browse a yamlover tree in the web: npx yamlover <root> serves a React SPA over a directory.",
5
5
  "type": "module",
6
6
  "license": "MIT",