yamlover 0.3.7 → 0.3.11

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
@@ -62331,6 +62331,7 @@ var require_ignore = __commonJS({
62331
62331
  // src/server/engine-api.ts
62332
62332
  import path9 from "node:path";
62333
62333
  import fs9 from "node:fs";
62334
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
62334
62335
 
62335
62336
  // ../parser/ts/src/ir.ts
62336
62337
  function isPointer(v) {
@@ -62441,13 +62442,13 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62441
62442
  let steps = ptr.steps;
62442
62443
  let chain;
62443
62444
  const linkAuthority = ptr.base.scope === "link" ? ptr.base.authority : "";
62444
- let isLink = false;
62445
+ const isWorld = ptr.base.scope === "link" && ptr.base.world === true;
62445
62446
  const external = () => ({ kind: "external", authority: linkAuthority, steps: ptr.steps });
62446
62447
  switch (ptr.base.scope) {
62447
62448
  case "link": {
62448
- isLink = true;
62449
62449
  chain = chains.get(root) ?? [root];
62450
- steps = [{ sel: "key", name: ptr.base.authority }, ...ptr.steps];
62450
+ const selfImport = ptr.base.authority === "yamlover" && !root.entries?.some((e3) => e3.key === "yamlover");
62451
+ steps = selfImport ? ptr.steps : [{ sel: "key", name: ptr.base.authority }, ...ptr.steps];
62451
62452
  break;
62452
62453
  }
62453
62454
  case "document": {
@@ -62470,7 +62471,7 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62470
62471
  }
62471
62472
  for (const st of steps) {
62472
62473
  if (st.sel === "parent") {
62473
- if (chain.length <= 1) return isLink ? external() : { kind: "unresolved", reason: '".." above the document root' };
62474
+ if (chain.length <= 1) return isWorld ? external() : { kind: "unresolved", reason: '".." above the document root' };
62474
62475
  chain = chain.slice(0, -1);
62475
62476
  continue;
62476
62477
  }
@@ -62485,8 +62486,8 @@ function resolve(doc, chains, fromChain, ptr, visited, anchorKeys) {
62485
62486
  continue;
62486
62487
  }
62487
62488
  }
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}]`}` };
62489
+ if (!node.entries) return isWorld ? external() : { kind: "unresolved", reason: "step into a node with no fields" };
62490
+ return isWorld ? external() : { kind: "unresolved", reason: `no ${st.sel === "key" ? `key "${st.name}"` : `index [${st.n}]`}` };
62490
62491
  }
62491
62492
  if (isPointer(entry.value)) {
62492
62493
  if (visited.has(entry.value)) return { kind: "unresolved", reason: "pointer cycle" };
@@ -62631,6 +62632,71 @@ var Store = class {
62631
62632
  throw e3;
62632
62633
  }
62633
62634
  }
62635
+ /** Patch the index for a SINGLE changed subtree instead of rebuilding the whole DB. `doc` is the
62636
+ * FULL, freshly-resolved document (the cached tree with the changed file's subtree spliced back
62637
+ * in and schemas re-applied) — correctness comes from resolving against the whole tree in
62638
+ * memory; speed comes from writing only the rows under `prefix` (the store path P of the changed
62639
+ * subtree). Every node/edge/dangling/file row whose owner lies under P is replaced; rows outside
62640
+ * P are left untouched (their resolution did not change). `files` is the re-walked manifest for
62641
+ * the subtree (POSIX paths under `relPrefix`).
62642
+ *
62643
+ * Returns false WITHOUT writing when the patch is not provably equal to a full rebuild: if any
62644
+ * external `ref`/`back` edge pointing INTO the subtree changed (a referenced node added/removed/
62645
+ * re-resolved), the caller must fall back to a full reindex. The boundary `contain` edge into P
62646
+ * (from P's parent, outside the subtree) is stable and intentionally left in place. */
62647
+ patchSubtree(doc, prefix, files, relPrefix) {
62648
+ const colon = prefix + ":";
62649
+ const brack = prefix + "[";
62650
+ const underP = (p2) => p2 === prefix || p2.startsWith(colon) || p2.startsWith(brack);
62651
+ const edges = resolveDocument(doc);
62652
+ const edgeKey = (from, to2, label, kind, pos) => JSON.stringify([from, to2, label, kind, pos]);
62653
+ 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();
62654
+ 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();
62655
+ if (extInOld.length !== extInNew.length || extInOld.some((k, i2) => k !== extInNew[i2])) return false;
62656
+ const insNode = this.db.prepare(
62657
+ `INSERT INTO node (path, type, format, value, content_hash, size, is_array, meta)
62658
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
62659
+ );
62660
+ const insEdge = this.db.prepare(
62661
+ `INSERT INTO edge (from_path, to_path, label, kind, pos) VALUES (?, ?, ?, ?, ?)`
62662
+ );
62663
+ const insDangling = this.db.prepare("INSERT INTO dangling (from_path, raw, reason) VALUES (?, ?, ?)");
62664
+ const insFile = this.db.prepare("INSERT OR REPLACE INTO file (path, hash, size, mtime_ms) VALUES (?, ?, ?, ?)");
62665
+ this.db.exec("BEGIN");
62666
+ try {
62667
+ const delUnder = (col, table) => {
62668
+ this.db.prepare(`DELETE FROM ${table} WHERE ${col} = ? OR substr(${col},1,?) = ? OR substr(${col},1,?) = ?`).run(prefix, colon.length, colon, brack.length, brack);
62669
+ };
62670
+ delUnder("path", "node");
62671
+ delUnder("from_path", "edge");
62672
+ delUnder("from_path", "dangling");
62673
+ if (relPrefix) this.db.prepare("DELETE FROM file WHERE substr(path,1,?) = ?").run(relPrefix.length, relPrefix);
62674
+ walkNodes(doc.root, ":", (p2, node, parent, label, pos) => {
62675
+ if (!underP(p2)) return;
62676
+ const meta = node.meta ? JSON.stringify(node.meta) : null;
62677
+ const owned = node.entries?.filter((e3) => e3.edge !== "back") ?? [];
62678
+ const isArray = node.array || node.kind === "mapping" && owned.length > 0 && owned.every((e3) => e3.key === null);
62679
+ const value = node.kind === "scalar" ? JSON.stringify(node.value) : null;
62680
+ const format = node.kind === "blob" ? node.format : formatFromMeta(node);
62681
+ const hash = node.kind === "blob" ? node.contentHash : null;
62682
+ const size = node.kind === "blob" ? node.size : null;
62683
+ insNode.run(p2, node.kind, format, value, hash, size, isArray ? 1 : 0, meta);
62684
+ if (parent !== null && underP(parent)) insEdge.run(parent, p2, label, "contain", pos);
62685
+ });
62686
+ for (const r2 of edges) {
62687
+ if (!underP(r2.holder)) continue;
62688
+ if (r2.target.kind === "node") insEdge.run(r2.holder, r2.target.path, r2.label, r2.edge, r2.pos);
62689
+ else if (r2.target.kind === "unresolved") insDangling.run(r2.from, r2.raw, r2.target.reason);
62690
+ }
62691
+ for (const f2 of files) insFile.run(f2.path, f2.hash, f2.size, f2.mtimeMs);
62692
+ this.db.exec("COMMIT");
62693
+ this._stale = false;
62694
+ return true;
62695
+ } catch (e3) {
62696
+ this.db.exec("ROLLBACK");
62697
+ throw e3;
62698
+ }
62699
+ }
62634
62700
  /** Incrementally add ONE annotation document at `annStorePath`, with a forward `target` ref edge
62635
62701
  * to `targetStorePath` and (when given) a keyless `back` edge to `tagStorePath` — the applied
62636
62702
  * tag's `~-` membership. This avoids a full re-walk/rebuild (which re-reads and re-hashes every
@@ -64312,15 +64378,16 @@ function* walkTreeGen(absDir, opts = {}) {
64312
64378
  root.meta = { ...root.meta, documentRoot: true };
64313
64379
  const defsRoot = findDefsRoot(absDir);
64314
64380
  const defsDir = path.join(defsRoot, "$defs");
64381
+ const selfRoot = path.resolve(absDir) === defsRoot;
64315
64382
  const arrayRoot = root.array || (root.entries?.length ? root.entries.every((e3) => e3.key === null) : false);
64316
64383
  let builtinDefs;
64317
64384
  if (!arrayRoot && root.entries && !root.entries.some((e3) => e3.key === "yamlover")) {
64318
- if (fs.existsSync(defsDir)) {
64385
+ if (fs.existsSync(defsDir) && !selfRoot) {
64319
64386
  const shared = [{ key: "$defs", edge: "contain", value: yield* dirNode(defsDir, ctx) }];
64320
64387
  const tagsDir = path.join(defsRoot, "tags");
64321
64388
  if (fs.existsSync(tagsDir)) shared.push({ key: "tags", edge: "contain", value: yield* dirNode(tagsDir, ctx) });
64322
64389
  root.entries.push({ key: "yamlover", edge: "contain", value: { kind: "mapping", entries: shared, array: false } });
64323
- } else {
64390
+ } else if (!fs.existsSync(defsDir)) {
64324
64391
  const built = builtinYamloverGraft();
64325
64392
  root.entries.push({ key: "yamlover", edge: "contain", value: built.node });
64326
64393
  builtinDefs = built.defs;
@@ -64338,7 +64405,7 @@ function reindex(store, absDir, opts = {}) {
64338
64405
  store.indexDocument(doc, files);
64339
64406
  return diffManifest(prev, files);
64340
64407
  }
64341
- async function reindexAsync(store, absDir, opts = {}) {
64408
+ async function reindexAsyncDoc(store, absDir, opts = {}) {
64342
64409
  const prev = store.stale ? /* @__PURE__ */ new Map() : store.manifest();
64343
64410
  const onProgress = opts.onProgress;
64344
64411
  const total = onProgress ? await countChildren(path.resolve(absDir), opts) : void 0;
@@ -64350,7 +64417,49 @@ async function reindexAsync(store, absDir, opts = {}) {
64350
64417
  onProgress?.({ done: total ?? files.length, total, message: "writing index\u2026" });
64351
64418
  await yieldLoop();
64352
64419
  store.indexDocument(doc, files);
64353
- return diffManifest(prev, files);
64420
+ return { diff: diffManifest(prev, files), doc, files };
64421
+ }
64422
+ async function reindexPathAsync(store, absDir, cachedDoc, changedRel, opts = {}) {
64423
+ const root = path.resolve(absDir);
64424
+ const parts = changedRel.split("/");
64425
+ const yi = parts.indexOf(YAMLOVER_DIR);
64426
+ const dirSegs = yi >= 0 ? parts.slice(0, yi) : parts.slice(0, -1);
64427
+ if (dirSegs.length === 0) return null;
64428
+ if (dirSegs[0] === "$defs" || dirSegs[0] === "tags") return null;
64429
+ let entries = cachedDoc.root.entries;
64430
+ let target = null;
64431
+ for (let d = 0; d < dirSegs.length; d++) {
64432
+ if (!entries) return null;
64433
+ const i2 = entries.findIndex((e3) => e3.key === dirSegs[d] && !isPointer(e3.value));
64434
+ if (i2 < 0) return null;
64435
+ if (d === dirSegs.length - 1) target = { arr: entries, i: i2 };
64436
+ else entries = entries[i2].value.entries;
64437
+ }
64438
+ if (!target) return null;
64439
+ const absSpliceDir = path.join(root, ...dirSegs);
64440
+ if (!fs.existsSync(absSpliceDir) || !fs.statSync(absSpliceDir).isDirectory()) return null;
64441
+ const prev = store.stale ? /* @__PURE__ */ new Map() : store.manifest();
64442
+ const ctx = { root, opts: { ...opts, cache: opts.cache ?? manifestCache(prev) }, files: /* @__PURE__ */ new Map(), count: 0 };
64443
+ const gen = dirNode(absSpliceDir, ctx);
64444
+ let r2 = gen.next();
64445
+ while (!r2.done) r2 = gen.next();
64446
+ target.arr[target.i].value = r2.value;
64447
+ applySchemas(cachedDoc.root, findDefsRoot(absDir), graftDefs(cachedDoc.root));
64448
+ const relPrefix = dirSegs.join("/") + "/";
64449
+ const P = ":" + dirSegs.join(":");
64450
+ const prevSub = new Map([...prev].filter(([k]) => k.startsWith(relPrefix)));
64451
+ const files = [...ctx.files.values()];
64452
+ const diff2 = diffManifest(prevSub, files);
64453
+ if (!store.patchSubtree(cachedDoc, P, files, relPrefix)) return null;
64454
+ return { diff: diff2, doc: cachedDoc };
64455
+ }
64456
+ function graftDefs(root) {
64457
+ const yam = root.entries?.find((e3) => e3.key === "yamlover" && !isPointer(e3.value))?.value;
64458
+ const defs = yam?.entries?.find((e3) => e3.key === "$defs" && !isPointer(e3.value))?.value;
64459
+ if (!defs?.entries) return void 0;
64460
+ const m = /* @__PURE__ */ new Map();
64461
+ for (const e3 of defs.entries) if (e3.key && !isPointer(e3.value)) m.set(e3.key, e3.value);
64462
+ return m.size ? m : void 0;
64354
64463
  }
64355
64464
  function manifestCache(prev) {
64356
64465
  return (rel, size, mtimeMs) => {
@@ -64629,6 +64738,7 @@ function applySchemas(root, defsRoot, builtinDefs) {
64629
64738
  const addl = field(s2, "additionalProperties");
64630
64739
  for (const e3 of inst.entries ?? []) {
64631
64740
  if (e3.key == null || isPointer(e3.value)) continue;
64741
+ if (e3.value.meta?.schema && isPointer(e3.value.meta.schema)) continue;
64632
64742
  const declared = props && !isPointer(props) ? field(props, e3.key) : null;
64633
64743
  const sub = declared ?? addl;
64634
64744
  if (sub) apply(e3.value, sub, depth + 1);
@@ -65380,6 +65490,10 @@ function evalQuery(s2, text, from = ":") {
65380
65490
  binds = [docRootOf(s2, from)];
65381
65491
  break;
65382
65492
  case "link": {
65493
+ if (q.base.authority === "yamlover" && childByKey(s2, ":", "yamlover") === null) {
65494
+ binds = [":"];
65495
+ break;
65496
+ }
65383
65497
  const hit = childByKey(s2, ":", q.base.authority);
65384
65498
  binds = hit === null ? [] : [hit];
65385
65499
  break;
@@ -65994,6 +66108,7 @@ function createHandlers(dataRoot, opts = {}) {
65994
66108
  fs9.mkdirSync(path9.dirname(dbPath), { recursive: true });
65995
66109
  const store0 = new Store(dbPath);
65996
66110
  const store = () => store0;
66111
+ let cachedDoc = null;
65997
66112
  const log = opts.log ?? (() => {
65998
66113
  });
65999
66114
  let closed = false;
@@ -66099,14 +66214,32 @@ function createHandlers(dataRoot, opts = {}) {
66099
66214
  }
66100
66215
  })();
66101
66216
  };
66102
- const doReindex = () => reindexAsync(store0, dataRoot, { ignore: ignore2 });
66217
+ const doReindex = async () => {
66218
+ const { diff: diff2, doc } = await reindexAsyncDoc(store0, dataRoot, { ignore: ignore2 });
66219
+ cachedDoc = doc;
66220
+ return diff2;
66221
+ };
66222
+ const doReindexFile = async (absFile) => {
66223
+ if (cachedDoc) {
66224
+ const rel = path9.relative(dataRoot, absFile).split(path9.sep).join("/");
66225
+ try {
66226
+ const res = await reindexPathAsync(store0, dataRoot, cachedDoc, rel, { ignore: ignore2 });
66227
+ if (res) {
66228
+ cachedDoc = res.doc;
66229
+ return res.diff;
66230
+ }
66231
+ } catch {
66232
+ }
66233
+ }
66234
+ return doReindex();
66235
+ };
66103
66236
  const runIndexTask = (label) => enqueue(async () => {
66104
66237
  const h = tasks.start(label);
66105
66238
  const t0 = Date.now();
66106
66239
  let lastLog = 0;
66107
66240
  log(`${label}\u2026`);
66108
66241
  try {
66109
- const diff2 = await reindexAsync(store0, dataRoot, {
66242
+ const { diff: diff2, doc } = await reindexAsyncDoc(store0, dataRoot, {
66110
66243
  ignore: ignore2,
66111
66244
  onProgress: (p2) => {
66112
66245
  h.progress(p2.done, p2.total, p2.message);
@@ -66117,6 +66250,7 @@ function createHandlers(dataRoot, opts = {}) {
66117
66250
  }
66118
66251
  }
66119
66252
  });
66253
+ cachedDoc = doc;
66120
66254
  h.done();
66121
66255
  log(
66122
66256
  `${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 +66273,7 @@ function createHandlers(dataRoot, opts = {}) {
66139
66273
  if (r2.editedFiles.length > 0) {
66140
66274
  const follow = reindex(store0, dataRoot, { ignore: ignore2 });
66141
66275
  diff2.changed = [.../* @__PURE__ */ new Set([...diff2.changed, ...follow.changed])];
66276
+ cachedDoc = null;
66142
66277
  }
66143
66278
  }
66144
66279
  h.done();
@@ -66202,8 +66337,8 @@ function createHandlers(dataRoot, opts = {}) {
66202
66337
  if (!a2?.tag || s2.node(tagStore)?.format !== TAG_FORMAT) {
66203
66338
  throw new Error("annotation needs a `tag` that is an x-yamlover-tag node");
66204
66339
  }
66205
- embedAnnotation(dataRoot, s2, a2);
66206
- broadcast(await doReindex());
66340
+ const bodyFile = embedAnnotation(dataRoot, s2, a2);
66341
+ broadcast(await doReindexFile(bodyFile));
66207
66342
  scheduleHasher();
66208
66343
  return { ok: true };
66209
66344
  })
@@ -66228,8 +66363,8 @@ function createHandlers(dataRoot, opts = {}) {
66228
66363
  const tag = url.searchParams.get("tag") || "";
66229
66364
  enqueue(async () => {
66230
66365
  if (!tag) throw new Error("delete needs a `tag`");
66231
- unembedAnnotation(dataRoot, s2, target, tag);
66232
- broadcast(await doReindex());
66366
+ const bodyFile = unembedAnnotation(dataRoot, s2, target, tag);
66367
+ broadcast(await doReindexFile(bodyFile));
66233
66368
  }).then(() => sendJson(res, 200, { ok: true })).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66234
66369
  return;
66235
66370
  }
@@ -66255,6 +66390,22 @@ function createHandlers(dataRoot, opts = {}) {
66255
66390
  ).then((body) => sendJson(res, 201, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66256
66391
  return;
66257
66392
  }
66393
+ if (req.method === "POST" && url.pathname === "/api/board") {
66394
+ readBody(req).then(
66395
+ (data) => enqueue(async () => {
66396
+ const b = data;
66397
+ const cols = Array.isArray(b?.columns) ? b.columns.map((lane) => Array.isArray(lane) ? lane.map((p3) => String(p3)) : []) : [];
66398
+ const { bodyFile } = hostFor(dataRoot, s2, strToSegs(b?.path || ":"));
66399
+ fs9.mkdirSync(path9.dirname(bodyFile), { recursive: true });
66400
+ const src = fs9.existsSync(bodyFile) ? fs9.readFileSync(bodyFile, "utf8") : "";
66401
+ fs9.writeFileSync(bodyFile, writeBoardColumns(src, cols));
66402
+ broadcast(await doReindex());
66403
+ scheduleHasher();
66404
+ return { ok: true };
66405
+ })
66406
+ ).then((body) => sendJson(res, 201, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66407
+ return;
66408
+ }
66258
66409
  if (req.method === "POST" && url.pathname === "/api/paste") {
66259
66410
  readBody(req).then(
66260
66411
  (data) => enqueue(async () => {
@@ -66284,6 +66435,31 @@ function createHandlers(dataRoot, opts = {}) {
66284
66435
  ).then((body) => sendJson(res, 200, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66285
66436
  return;
66286
66437
  }
66438
+ if (req.method === "POST" && url.pathname === "/api/agent-docs") {
66439
+ readBody(req).then(
66440
+ (data) => enqueue(async () => {
66441
+ const overwrite = !!data?.overwrite;
66442
+ const files = [];
66443
+ let wrote = false;
66444
+ for (const doc of loadAgentDocs()) {
66445
+ const exists = fs9.existsSync(path9.resolve(dataRoot, doc.name));
66446
+ if (exists && !overwrite) {
66447
+ files.push({ name: doc.name, status: "exists" });
66448
+ continue;
66449
+ }
66450
+ writeInside(dataRoot, dataRoot, doc.name, Buffer.from(doc.content, "utf8"));
66451
+ files.push({ name: doc.name, status: exists ? "overwritten" : "created" });
66452
+ wrote = true;
66453
+ }
66454
+ if (wrote) {
66455
+ broadcast(await doReindex());
66456
+ scheduleHasher();
66457
+ }
66458
+ return { files };
66459
+ })
66460
+ ).then((body) => sendJson(res, 201, body)).catch((e3) => sendJson(res, 400, { error: String(e3.message || e3) }));
66461
+ return;
66462
+ }
66287
66463
  const segs = strToSegs(url.searchParams.get("path") || ":");
66288
66464
  const p2 = storePath(segs);
66289
66465
  const depth = parseDepth(url.searchParams.get("depth"));
@@ -66350,7 +66526,7 @@ function createHandlers(dataRoot, opts = {}) {
66350
66526
  const viewDepth = depth ?? 1;
66351
66527
  const kind = displayKind(s2, p2, row);
66352
66528
  if (url.pathname === "/api/json") {
66353
- const wantBytes = kind === "binary" && url.searchParams.get("binary") === "1";
66529
+ const wantBytes = row.type === "blob" && url.searchParams.get("binary") === "1";
66354
66530
  sendJson(res, 200, {
66355
66531
  path: segsToStr(segs),
66356
66532
  type: tocType(s2, p2, row),
@@ -66389,7 +66565,19 @@ function createHandlers(dataRoot, opts = {}) {
66389
66565
  function tocType(s2, p2, row) {
66390
66566
  return typeName(s2, p2, row);
66391
66567
  }
66568
+ var JSON_EXT = { ".json": "json", ".json5": "json5", ".json5p": "json5p" };
66392
66569
  function concreteOf(dataRoot, segs, row) {
66570
+ const last = segs[segs.length - 1];
66571
+ if (typeof last === "string") {
66572
+ const json = JSON_EXT[path9.extname(last).toLowerCase()];
66573
+ if (json) {
66574
+ const abs2 = path9.resolve(dataRoot, ...segs.map(String));
66575
+ try {
66576
+ if (fs9.statSync(abs2).isFile()) return json;
66577
+ } catch {
66578
+ }
66579
+ }
66580
+ }
66393
66581
  if (row.type !== "mapping") return null;
66394
66582
  if (segs.some((g) => typeof g === "number")) return null;
66395
66583
  const abs = path9.resolve(dataRoot, ...segs.map(String));
@@ -66645,6 +66833,21 @@ function pointerRaw(clientPath) {
66645
66833
  }
66646
66834
  return "::" + out;
66647
66835
  }
66836
+ function writeBoardColumns(src, cols) {
66837
+ const laneLine = (lane) => `- [${lane.map((p2) => pointerToken(pointerRaw(p2))).join(", ")}]`;
66838
+ const block = cols.length === 0 ? ["columns: []"] : ["columns:", ...cols.map(laneLine)];
66839
+ let lines = src.replace(/\n+$/, "").split("\n");
66840
+ if (src.trim() === "") lines = ["!!<*yamlover:$defs:board>"];
66841
+ const start = lines.findIndex((l2) => /^columns:/.test(l2));
66842
+ if (start >= 0) {
66843
+ let end = start + 1;
66844
+ while (end < lines.length && (lines[end] === "" || /^[ \t-]/.test(lines[end]))) end++;
66845
+ lines.splice(start, end - start, ...block);
66846
+ } else {
66847
+ lines.push(...block);
66848
+ }
66849
+ return lines.join("\n") + "\n";
66850
+ }
66648
66851
  function sidecarTarget(dataRoot, mode, subdir, bodyFile) {
66649
66852
  const dirOverlay = bodyFile.endsWith(path9.join(".yamlover", "body.yamlover"));
66650
66853
  if (mode === "per-directory" && dirOverlay) {
@@ -66704,6 +66907,7 @@ function embedAnnotation(dataRoot, s2, a2) {
66704
66907
  fs9.mkdirSync(path9.dirname(bodyFile), { recursive: true });
66705
66908
  const src = fs9.existsSync(bodyFile) ? fs9.readFileSync(bodyFile, "utf8") : "";
66706
66909
  fs9.writeFileSync(bodyFile, appendAnnotation(src, within, (indent) => annotationItemLines(a2, indent)));
66910
+ return bodyFile;
66707
66911
  }
66708
66912
  function embedFragment(dataRoot, s2, mode, f2) {
66709
66913
  const segs = strToSegs(f2.target || ":");
@@ -66760,10 +66964,11 @@ async function ensureThumbnail(dataRoot, s2, mode, segs, row, w, h) {
66760
66964
  }
66761
66965
  function unembedAnnotation(dataRoot, s2, target, tag) {
66762
66966
  const { bodyFile, within } = hostFor(dataRoot, s2, strToSegs(target || ":"));
66763
- if (!fs9.existsSync(bodyFile)) return;
66764
- const needle = pointerRaw(tag);
66967
+ if (!fs9.existsSync(bodyFile)) return bodyFile;
66968
+ const needlePath = ":" + pointerRaw(tag).replace(/^:+/, "");
66765
66969
  const src = fs9.readFileSync(bodyFile, "utf8");
66766
- fs9.writeFileSync(bodyFile, removeAnnotation(src, within, (itemText) => itemText.includes(needle)));
66970
+ fs9.writeFileSync(bodyFile, removeAnnotation(src, within, (itemText) => itemText.replace(/\s+/g, "").includes(needlePath)));
66971
+ return bodyFile;
66767
66972
  }
66768
66973
  function writeTag(dataRoot, location, name) {
66769
66974
  if (/[/\\\r\n:]/.test(name)) throw new Error("a tag name cannot contain '/', '\\', ':' or line breaks");
@@ -66980,6 +67185,17 @@ function writeInside(dataRoot, dir, name, bytes) {
66980
67185
  if (target !== root && !target.startsWith(root + path9.sep)) throw new Error("target escapes the data root");
66981
67186
  fs9.writeFileSync(target, bytes);
66982
67187
  }
67188
+ var AGENT_DOCS_DIR = path9.join(path9.dirname(fileURLToPath2(import.meta.url)), "agent-docs");
67189
+ function loadAgentDocs() {
67190
+ let names3;
67191
+ try {
67192
+ names3 = fs9.readdirSync(AGENT_DOCS_DIR).filter((f2) => f2.endsWith(".md")).sort();
67193
+ } catch {
67194
+ throw new Error(`agent-docs resources not found at ${AGENT_DOCS_DIR}`);
67195
+ }
67196
+ if (names3.length === 0) throw new Error(`no agent-docs resources at ${AGENT_DOCS_DIR}`);
67197
+ return names3.map((name) => ({ name, content: fs9.readFileSync(path9.join(AGENT_DOCS_DIR, name), "utf8") }));
67198
+ }
66983
67199
  var indentOf2 = (line) => {
66984
67200
  let i2 = 0;
66985
67201
  while (line[i2] === " ") i2++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yamlover",
3
- "version": "0.3.7",
3
+ "version": "0.3.11",
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",