wiki-graph-sdk 0.8.4 → 0.9.1

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.
Files changed (51) hide show
  1. package/dist/archives.cjs +345 -152
  2. package/dist/archives.cjs.map +1 -1
  3. package/dist/archives.js +356 -157
  4. package/dist/archives.js.map +1 -1
  5. package/dist/conversions.cjs +269 -99
  6. package/dist/conversions.cjs.map +1 -1
  7. package/dist/conversions.js +275 -103
  8. package/dist/conversions.js.map +1 -1
  9. package/dist/default-worker.cjs +278 -108
  10. package/dist/default-worker.cjs.map +1 -1
  11. package/dist/default-worker.js +285 -112
  12. package/dist/default-worker.js.map +1 -1
  13. package/dist/embedding.cjs +214 -54
  14. package/dist/embedding.cjs.map +1 -1
  15. package/dist/embedding.js +214 -54
  16. package/dist/embedding.js.map +1 -1
  17. package/dist/gc.cjs +214 -54
  18. package/dist/gc.cjs.map +1 -1
  19. package/dist/gc.js +214 -54
  20. package/dist/gc.js.map +1 -1
  21. package/dist/index.cjs +442 -226
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +461 -236
  25. package/dist/index.js.map +1 -1
  26. package/dist/local-config.cjs +214 -54
  27. package/dist/local-config.cjs.map +1 -1
  28. package/dist/local-config.js +214 -54
  29. package/dist/local-config.js.map +1 -1
  30. package/dist/maintenance.cjs +265 -97
  31. package/dist/maintenance.cjs.map +1 -1
  32. package/dist/maintenance.js +271 -101
  33. package/dist/maintenance.js.map +1 -1
  34. package/dist/node-platform.cjs +214 -54
  35. package/dist/node-platform.cjs.map +1 -1
  36. package/dist/node-platform.d.ts +2 -0
  37. package/dist/node-platform.js +214 -54
  38. package/dist/node-platform.js.map +1 -1
  39. package/dist/runtime-config.cjs +214 -54
  40. package/dist/runtime-config.cjs.map +1 -1
  41. package/dist/runtime-config.js +214 -54
  42. package/dist/runtime-config.js.map +1 -1
  43. package/dist/stage.cjs +214 -54
  44. package/dist/stage.cjs.map +1 -1
  45. package/dist/stage.js +214 -54
  46. package/dist/stage.js.map +1 -1
  47. package/dist/worker.cjs +277 -103
  48. package/dist/worker.cjs.map +1 -1
  49. package/dist/worker.js +284 -107
  50. package/dist/worker.js.map +1 -1
  51. package/package.json +2 -2
package/dist/archives.cjs CHANGED
@@ -151,6 +151,15 @@ var NodeFile = class {
151
151
  this.name = name;
152
152
  }
153
153
  path;
154
+ async getPhysicalIdentity() {
155
+ const value = await fsPromises.stat(this.path).catch((error) => {
156
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
157
+ return void 0;
158
+ throw error;
159
+ });
160
+ if (value === void 0) return void 0;
161
+ return `${value.dev}:${value.ino}`;
162
+ }
154
163
  kind = "file";
155
164
  [NODE_RESOURCE_KIND] = "file";
156
165
  identity;
@@ -275,6 +284,15 @@ var NodeDirectory = class _NodeDirectory {
275
284
  this.name = pathModule.basename(path);
276
285
  }
277
286
  path;
287
+ async getPhysicalIdentity() {
288
+ const value = await fsPromises.stat(this.path).catch((error) => {
289
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
290
+ return void 0;
291
+ throw error;
292
+ });
293
+ if (value === void 0) return void 0;
294
+ return `${value.dev}:${value.ino}`;
295
+ }
278
296
  kind = "directory";
279
297
  [NODE_RESOURCE_KIND] = "directory";
280
298
  identity;
@@ -284,8 +302,10 @@ var NodeDirectory = class _NodeDirectory {
284
302
  const file = new NodeFile(pathModule.join(this.path, name));
285
303
  try {
286
304
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
287
- } catch {
288
- return void 0;
305
+ } catch (error) {
306
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
307
+ return void 0;
308
+ throw error;
289
309
  }
290
310
  }
291
311
  async getLastModified() {
@@ -303,8 +323,10 @@ var NodeDirectory = class _NodeDirectory {
303
323
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
304
324
  try {
305
325
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
306
- } catch {
307
- return void 0;
326
+ } catch (error) {
327
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
328
+ return void 0;
329
+ throw error;
308
330
  }
309
331
  }
310
332
  async list() {
@@ -428,14 +450,23 @@ async function openNativeNodeDatabase(file, flags) {
428
450
  });
429
451
  });
430
452
  }
431
- async function openNodeZip(file) {
453
+ async function openNodeZip(file, options = {}) {
432
454
  const path = getBrandedNodeResourcePath(file, "file");
433
455
  if (path === void 0) {
434
456
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
435
457
  }
436
- const zipFile = await new Promise((resolve4, reject) => {
437
- yauzl.open(
438
- path,
458
+ const handle = await fsPromises.open(path, "r");
459
+ let size;
460
+ try {
461
+ size = (await handle.stat()).size;
462
+ } catch (error) {
463
+ await handle.close();
464
+ throw error;
465
+ }
466
+ const open2 = () => new Promise((resolve4, reject) => {
467
+ yauzl.fromRandomAccessReader(
468
+ new NodeZipSource(handle),
469
+ size,
439
470
  { autoClose: false, lazyEntries: true },
440
471
  (error, opened) => {
441
472
  if (error || opened === void 0)
@@ -444,63 +475,192 @@ async function openNodeZip(file) {
444
475
  }
445
476
  );
446
477
  });
447
- const entries = await new Promise(
448
- (resolve4, reject) => {
449
- const discovered = /* @__PURE__ */ new Map();
450
- const rejectAndClose = (error) => {
451
- zipFile.close();
452
- reject(
453
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
454
- );
455
- };
456
- const resolveEntries = () => {
457
- zipFile.off("error", rejectAndClose);
458
- resolve4(discovered);
459
- };
460
- zipFile.on("entry", (entry) => {
461
- if (!entry.fileName.endsWith("/")) {
462
- discovered.set(entry.fileName, entry);
463
- }
464
- zipFile.readEntry();
465
- });
466
- zipFile.once("error", rejectAndClose);
467
- zipFile.once("end", resolveEntries);
468
- zipFile.readEntry();
469
- }
470
- );
478
+ try {
479
+ (await open2()).close();
480
+ } catch (error) {
481
+ await handle.close();
482
+ throw error;
483
+ }
471
484
  let closed = false;
485
+ let pending = Promise.resolve();
486
+ let closing;
487
+ const run = (operation) => {
488
+ if (closed)
489
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
490
+ const result = pending.then(async () => {
491
+ const zip = await open2();
492
+ try {
493
+ return await operation(zip);
494
+ } finally {
495
+ zip.close();
496
+ }
497
+ });
498
+ pending = result.then(
499
+ () => void 0,
500
+ () => void 0
501
+ );
502
+ return result;
503
+ };
504
+ const withEntry = (name, operation) => run(async (zip) => {
505
+ let exact;
506
+ let alias;
507
+ let aliasCollision = false;
508
+ await scanNodeZip(zip, (entry) => {
509
+ if (entry.fileName === name) exact = entry;
510
+ if (normalizeNodeZipName(entry.fileName) === name) {
511
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
512
+ aliasCollision = true;
513
+ alias = entry;
514
+ }
515
+ });
516
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
517
+ let bestPosition = -1;
518
+ const candidates = await open2();
519
+ try {
520
+ await scanNodeZip(candidates, async (entry) => {
521
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
522
+ const probe = await open2();
523
+ let position = 0;
524
+ try {
525
+ await scanNodeZip(probe, (candidate) => {
526
+ if (candidate.fileName === entry.fileName) return false;
527
+ position++;
528
+ });
529
+ } finally {
530
+ probe.close();
531
+ }
532
+ if (position >= bestPosition) {
533
+ bestPosition = position;
534
+ alias = entry;
535
+ }
536
+ });
537
+ return await operation(candidates, alias);
538
+ } finally {
539
+ candidates.close();
540
+ }
541
+ }
542
+ return await operation(
543
+ zip,
544
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
545
+ );
546
+ });
472
547
  return {
473
548
  close: () => {
474
- if (!closed) zipFile.close();
475
549
  closed = true;
476
- return Promise.resolve();
550
+ closing ??= pending.then(() => handle.close());
551
+ return closing;
477
552
  },
478
- copyEntry: async (name, target) => {
479
- if (closed) throw new Error("Cannot read a closed ZIP archive");
480
- const entry = entries.get(name);
553
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
481
554
  if (entry === void 0) return false;
482
- await copyNodeZipEntry(zipFile, entry, target);
555
+ await copyNodeZipEntry(zip, entry, target);
483
556
  return true;
484
- },
485
- getEntrySize: (name) => {
486
- if (closed) throw new Error("Cannot read a closed ZIP archive");
487
- return Promise.resolve(entries.get(name)?.uncompressedSize);
488
- },
489
- listEntries: () => Promise.resolve([...entries.keys()]),
490
- readEntryRange: async (name, offset, length) => {
491
- if (closed) throw new Error("Cannot read a closed ZIP archive");
492
- const entry = entries.get(name);
557
+ }),
558
+ getEntrySize: (name) => withEntry(
559
+ name,
560
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
561
+ ),
562
+ listEntries: () => run(async (zip) => {
563
+ const names = /* @__PURE__ */ new Set();
564
+ await scanNodeZip(zip, (entry) => {
565
+ names.add(entry.fileName);
566
+ });
567
+ return [...names];
568
+ }),
569
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
493
570
  if (entry === void 0) return void 0;
494
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
495
- },
496
- readEntry: async (name) => {
497
- if (closed) throw new Error("Cannot read a closed ZIP archive");
498
- const entry = entries.get(name);
571
+ return await readNodeZipEntryRange(zip, entry, offset, length);
572
+ }),
573
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
499
574
  if (entry === void 0) return void 0;
500
- return await readNodeZipEntry(zipFile, entry);
501
- }
575
+ return await readNodeZipEntry(zip, entry);
576
+ })
502
577
  };
503
578
  }
579
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
580
+ constructor(handle) {
581
+ super();
582
+ this.handle = handle;
583
+ }
584
+ handle;
585
+ read(buffer, offset, length, position, callback) {
586
+ const read2 = (written) => {
587
+ if (written === length) {
588
+ callback(null);
589
+ return;
590
+ }
591
+ fs.read(
592
+ this.handle.fd,
593
+ buffer,
594
+ offset + written,
595
+ length - written,
596
+ position + written,
597
+ (error, bytesRead) => {
598
+ if (error) callback(error);
599
+ else if (bytesRead === 0)
600
+ callback(new Error("Unexpected end of ZIP file"));
601
+ else read2(written + bytesRead);
602
+ }
603
+ );
604
+ };
605
+ read2(0);
606
+ }
607
+ _readStreamForRange(start, end) {
608
+ const handle = this.handle;
609
+ return import_node_stream.Readable.from(
610
+ (async function* () {
611
+ for (let position = start; position < end; ) {
612
+ const buffer = Buffer.allocUnsafe(
613
+ Math.min(64 * 1024, end - position)
614
+ );
615
+ const { bytesRead } = await handle.read(
616
+ buffer,
617
+ 0,
618
+ buffer.length,
619
+ position
620
+ );
621
+ if (bytesRead === 0) return;
622
+ position += bytesRead;
623
+ yield buffer.subarray(0, bytesRead);
624
+ }
625
+ })()
626
+ );
627
+ }
628
+ };
629
+ function normalizeNodeZipName(name) {
630
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
631
+ return normalized.split("/").includes("..") ? void 0 : normalized;
632
+ }
633
+ async function scanNodeZip(zip, visit) {
634
+ await new Promise((resolve4, reject) => {
635
+ const cleanup = () => {
636
+ zip.off("entry", onEntry);
637
+ zip.off("end", onEnd);
638
+ zip.off("error", onError);
639
+ };
640
+ const onError = (error) => {
641
+ cleanup();
642
+ reject(error);
643
+ };
644
+ const onEnd = () => {
645
+ cleanup();
646
+ resolve4();
647
+ };
648
+ const onEntry = (entry) => {
649
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
650
+ if (keepReading === false) onEnd();
651
+ else zip.readEntry();
652
+ }).catch(
653
+ (error) => onError(
654
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
655
+ )
656
+ );
657
+ };
658
+ zip.on("entry", onEntry);
659
+ zip.once("end", onEnd);
660
+ zip.once("error", onError);
661
+ zip.readEntry();
662
+ });
663
+ }
504
664
  async function copyNodeZipEntry(zipFile, entry, target) {
505
665
  const writer = await target.openWriter();
506
666
  try {
@@ -1273,6 +1433,19 @@ function resolveWikiGraphRuntimePath(path) {
1273
1433
  var import_promises3 = require("fs/promises");
1274
1434
  var import_path3 = require("path");
1275
1435
  var import_wiki_graph_core2 = require("wiki-graph-core");
1436
+ function rethrowArchiveSchemaError(error, target) {
1437
+ if ((0, import_wiki_graph_core2.isWikiGraphSchemaVersionError)(error) && error.target.kind === "archive") {
1438
+ throw new import_wiki_graph_core2.WikiGraphSchemaVersionError(
1439
+ target.kind === "standalone" ? { kind: "archive", path: resolveWikiGraphRuntimePath(target.path) } : { kind: "library", uri: target.uri.split("/arc/")[0] },
1440
+ error.actualVersion,
1441
+ error.supportedVersion
1442
+ );
1443
+ }
1444
+ throw error;
1445
+ }
1446
+ function requireWikiGraphArchiveFile(location) {
1447
+ return location.archiveFile;
1448
+ }
1276
1449
  var WikiGraphArchiveOwnershipError = class extends Error {
1277
1450
  code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1278
1451
  constructor(message) {
@@ -1290,12 +1463,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1290
1463
  const path = resolveWikiGraphRuntimePath(inputPath);
1291
1464
  const candidate = await canonicalPath(path);
1292
1465
  const candidateIdentity = await readFileIdentity(path);
1293
- const libraries = await (0, import_wiki_graph_core2.listWikiGraphLibraries)();
1294
- const matches = await findMatchingMembers(
1295
- libraries,
1296
- candidate,
1297
- candidateIdentity
1298
- );
1466
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1299
1467
  if (matches.length > 0) {
1300
1468
  const match = requireUnambiguousManagedMatch(path, matches);
1301
1469
  throw new WikiGraphArchiveOwnershipError(
@@ -1304,15 +1472,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1304
1472
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1305
1473
  );
1306
1474
  }
1307
- for (const library of libraries) {
1308
- const rootPath = getNodeResourcePath(library.folder);
1309
- const root = await canonicalPath(rootPath);
1310
- if (!isWithin(root, candidate)) continue;
1311
- throw new WikiGraphArchiveOwnershipError(
1312
- `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1313
- library.isDefault ? void 0 : library.publicId
1314
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1475
+ const ancestors = /* @__PURE__ */ new Set();
1476
+ for (const start of [candidate, (0, import_path3.resolve)(path)]) {
1477
+ for (let parent = (0, import_path3.dirname)(start); ; parent = (0, import_path3.dirname)(parent)) {
1478
+ ancestors.add(parent);
1479
+ if ((0, import_path3.dirname)(parent) === parent) break;
1480
+ }
1481
+ }
1482
+ for (const parent of ancestors) {
1483
+ const identity = await readFileIdentity(parent);
1484
+ const library = await (0, import_wiki_graph_core2.findWikiGraphLibraryByPhysicalIdentity)(
1485
+ identity,
1486
+ new NodeDirectory(parent).identity
1315
1487
  );
1488
+ if (library !== void 0) {
1489
+ throw new WikiGraphArchiveOwnershipError(
1490
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1491
+ library.isDefault ? void 0 : library.publicId
1492
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1493
+ );
1494
+ }
1316
1495
  }
1317
1496
  return path;
1318
1497
  }
@@ -1370,47 +1549,35 @@ async function resolveLibraryLocation(target) {
1370
1549
  async function canonicalPath(path) {
1371
1550
  try {
1372
1551
  return await (0, import_promises3.realpath)(path);
1373
- } catch {
1552
+ } catch (error) {
1553
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1554
+ throw error;
1374
1555
  const absolute = (0, import_path3.resolve)(path);
1375
1556
  const parent = (0, import_path3.dirname)(absolute);
1376
1557
  return parent === absolute ? absolute : (0, import_path3.join)(await canonicalPath(parent), (0, import_path3.basename)(absolute));
1377
1558
  }
1378
1559
  }
1379
- function isWithin(root, candidate) {
1380
- const child = (0, import_path3.relative)(root, candidate);
1381
- return child === "" || !child.startsWith("..") && !(0, import_path3.isAbsolute)(child);
1382
- }
1383
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1560
+ async function findMatchingMembers(candidate, candidateIdentity) {
1561
+ if (candidateIdentity === void 0) return [];
1562
+ const archives = await (0, import_wiki_graph_core2.findWikiGraphLibraryArchivePhysicalAliases)(candidateIdentity);
1384
1563
  const members = [];
1385
- for (const library of libraries) {
1386
- const archives = await (0, import_wiki_graph_core2.listWikiGraphLibraryArchives)({
1387
- isDefault: library.isDefault,
1388
- kind: "scope",
1389
- ...library.isDefault ? {} : { publicId: library.publicId }
1390
- });
1391
- for (const archive of archives) {
1392
- if (archive.file === void 0) continue;
1564
+ for (const archive of archives) {
1565
+ const parsed = (0, import_wiki_graph_core2.parseWikiGraphLibraryUri)(archive.libraryUri);
1566
+ const library = await (0, import_wiki_graph_core2.resolveWikiGraphLibrary)(parsed);
1567
+ const physical = archive.file ?? archive.directory;
1568
+ if (library !== void 0 && physical !== void 0)
1393
1569
  members.push({
1394
1570
  archive,
1395
1571
  library,
1396
- memberPath: getNodeResourcePath(archive.file)
1572
+ memberPath: getNodeResourcePath(physical)
1397
1573
  });
1398
- }
1399
1574
  }
1400
1575
  const exact = [];
1401
1576
  for (const member of members) {
1402
- if (await canonicalPath(member.memberPath) === candidate) {
1577
+ if (await canonicalPath(member.memberPath) === candidate)
1403
1578
  exact.push(member);
1404
- }
1405
- }
1406
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1407
- const identityMatches = [];
1408
- for (const member of members) {
1409
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1410
- identityMatches.push(member);
1411
- }
1412
1579
  }
1413
- return identityMatches;
1580
+ return exact.length > 0 ? exact : members;
1414
1581
  }
1415
1582
  function requireUnambiguousManagedMatch(path, matches) {
1416
1583
  if (matches.length === 1) return matches[0];
@@ -1422,8 +1589,10 @@ async function readFileIdentity(path) {
1422
1589
  try {
1423
1590
  const value = await (0, import_promises3.stat)(path);
1424
1591
  return `${value.dev}:${value.ino}`;
1425
- } catch {
1426
- return void 0;
1592
+ } catch (error) {
1593
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1594
+ return void 0;
1595
+ throw error;
1427
1596
  }
1428
1597
  }
1429
1598
 
@@ -1485,11 +1654,15 @@ var WikiGraphConversionManager = class {
1485
1654
  if (!("path" in options.input)) {
1486
1655
  throw new Error("wikg input requires a file path.");
1487
1656
  }
1657
+ const inputPath = options.input.path;
1488
1658
  await app.openSession(
1489
- new NodeFile(
1490
- await assertStandaloneWikiGraphArchivePath(options.input.path)
1491
- ),
1659
+ new NodeFile(await assertStandaloneWikiGraphArchivePath(inputPath)),
1492
1660
  write
1661
+ ).catch(
1662
+ (error) => rethrowArchiveSchemaError(error, {
1663
+ kind: "standalone",
1664
+ path: inputPath
1665
+ })
1493
1666
  );
1494
1667
  } else if ("stream" in options.input) {
1495
1668
  await app.digestTextStreamSession(
@@ -1879,13 +2052,31 @@ function formatLocatedChapterUri(archiveUri, chapter) {
1879
2052
  var import_wiki_graph_core6 = require("wiki-graph-core");
1880
2053
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1881
2054
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1882
- const write = async (archiveFile = location.archiveFile) => await new import_wiki_graph_core6.WikiGraphArchiveFile(archiveFile).write(operation, {
1883
- ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1884
- ...options.invalidateContinuationCursors === void 0 ? {} : {
1885
- invalidateContinuationCursors: options.invalidateContinuationCursors
1886
- },
1887
- ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
1888
- });
2055
+ const write = async (archiveFile = location.archiveFile) => {
2056
+ if (cacheOnly) {
2057
+ return await new import_wiki_graph_core6.WikiGraphArchiveFile(archiveFile).readDocument(
2058
+ operation,
2059
+ { searchIndexWritebackPolicy: "cache" }
2060
+ );
2061
+ }
2062
+ const writeOptions = {
2063
+ ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
2064
+ ...options.invalidateContinuationCursors === void 0 ? {} : {
2065
+ invalidateContinuationCursors: options.invalidateContinuationCursors
2066
+ },
2067
+ ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
2068
+ };
2069
+ if (archiveFile.kind === "directory") {
2070
+ return await new import_wiki_graph_core6.WikiGraphDirectoryFile(archiveFile).write(
2071
+ operation,
2072
+ writeOptions
2073
+ );
2074
+ }
2075
+ return await new import_wiki_graph_core6.WikiGraphArchiveFile(archiveFile).write(
2076
+ operation,
2077
+ writeOptions
2078
+ );
2079
+ };
1889
2080
  if (location.libraryArchiveTarget === void 0 || cacheOnly || options.refreshLibraryIndex === false) {
1890
2081
  return await write();
1891
2082
  }
@@ -2185,10 +2376,15 @@ var WikiGraphArchiveManager = class {
2185
2376
  }, options.signal);
2186
2377
  }
2187
2378
  async open(target, options = {}) {
2188
- const location = await this.#runtime.run(
2189
- async () => await resolveWikiGraphArchiveLocation(target),
2190
- options.signal
2191
- );
2379
+ const location = await this.#runtime.run(async () => {
2380
+ const location2 = await resolveWikiGraphArchiveLocation(target);
2381
+ try {
2382
+ await (0, import_wiki_graph_core7.ensureWikiGraphArchiveSchemaCurrent)(location2.archiveFile);
2383
+ } catch (error) {
2384
+ rethrowArchiveSchemaError(error, target);
2385
+ }
2386
+ return location2;
2387
+ }, options.signal);
2192
2388
  return new WikiGraphArchiveHandle(this.#runtime, location);
2193
2389
  }
2194
2390
  };
@@ -2239,15 +2435,11 @@ var WikiGraphArchiveHandle = class {
2239
2435
  options.queryMode,
2240
2436
  options.signal
2241
2437
  );
2242
- const result = await this.#writeDocument(
2438
+ const result = await this.#readDocument(
2243
2439
  async (document) => {
2244
2440
  const chapters = await this.#resolveQueryChapters(document, options, {
2245
2441
  embeddingProvider
2246
2442
  });
2247
- await ensureArchiveSearchIndex(document, {
2248
- ...chapters === void 0 ? {} : { chapters },
2249
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2250
- });
2251
2443
  return await (0, import_wiki_graph_core7.findArchiveObjects)(document, query, {
2252
2444
  ...withoutOperationAndScope(options),
2253
2445
  archiveKey: this.archiveKey,
@@ -2256,7 +2448,6 @@ var WikiGraphArchiveHandle = class {
2256
2448
  });
2257
2449
  },
2258
2450
  {
2259
- searchIndexWritebackPolicy: "cache",
2260
2451
  ...options.signal === void 0 ? {} : { signal: options.signal }
2261
2452
  }
2262
2453
  );
@@ -2361,6 +2552,7 @@ var WikiGraphArchiveHandle = class {
2361
2552
  async (document) => {
2362
2553
  const chapters = await this.#resolveScope(document, options);
2363
2554
  await ensureArchiveSearchIndex(document, {
2555
+ rebuild: true,
2364
2556
  ...chapters === void 0 ? {} : { chapters },
2365
2557
  ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2366
2558
  });
@@ -2539,7 +2731,7 @@ var WikiGraphArchiveHandle = class {
2539
2731
  return await this.#writeDocument(async (document) => {
2540
2732
  const chapterId = await (0, import_wiki_graph_core7.resolveChapterPathReadonly)(document, path);
2541
2733
  await assertChapterResetAllowed(
2542
- this.#location.archiveFile,
2734
+ requireWikiGraphArchiveFile(this.#location),
2543
2735
  chapterId,
2544
2736
  stage
2545
2737
  );
@@ -2650,15 +2842,11 @@ var WikiGraphArchiveHandle = class {
2650
2842
  options.queryMode,
2651
2843
  options.signal
2652
2844
  );
2653
- const result = await this.#writeDocument(
2845
+ const result = await this.#readDocument(
2654
2846
  async (document) => {
2655
2847
  const chapters = await this.#resolveQueryChapters(document, options, {
2656
2848
  embeddingProvider
2657
2849
  });
2658
- await ensureArchiveSearchIndex(document, {
2659
- ...chapters === void 0 ? {} : { chapters },
2660
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2661
- });
2662
2850
  return await (0, import_wiki_graph_core7.listRelatedArchiveObjects)(document, objectUri, {
2663
2851
  ...withoutOperation(options),
2664
2852
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2666,7 +2854,6 @@ var WikiGraphArchiveHandle = class {
2666
2854
  });
2667
2855
  },
2668
2856
  {
2669
- searchIndexWritebackPolicy: "cache",
2670
2857
  ...options.signal === void 0 ? {} : { signal: options.signal }
2671
2858
  }
2672
2859
  );
@@ -2710,15 +2897,11 @@ var WikiGraphArchiveHandle = class {
2710
2897
  options.queryMode,
2711
2898
  options.signal
2712
2899
  );
2713
- const result = await this.#writeDocument(
2900
+ const result = await this.#readDocument(
2714
2901
  async (document) => {
2715
2902
  const chapters = await this.#resolveQueryChapters(document, options, {
2716
2903
  embeddingProvider
2717
2904
  });
2718
- await ensureArchiveSearchIndex(document, {
2719
- ...chapters === void 0 ? {} : { chapters },
2720
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2721
- });
2722
2905
  return await (0, import_wiki_graph_core7.listArchiveEvidence)(document, objectUri, {
2723
2906
  ...withoutOperation(options),
2724
2907
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2726,7 +2909,6 @@ var WikiGraphArchiveHandle = class {
2726
2909
  });
2727
2910
  },
2728
2911
  {
2729
- searchIndexWritebackPolicy: "cache",
2730
2912
  ...options.signal === void 0 ? {} : { signal: options.signal }
2731
2913
  }
2732
2914
  );
@@ -2785,14 +2967,18 @@ var WikiGraphArchiveHandle = class {
2785
2967
  }, options);
2786
2968
  }
2787
2969
  async readCover(options = {}) {
2788
- const app = new import_wiki_graph_core7.WikiGraph({});
2789
- return await this.#runtime.run(
2790
- async () => await app.openSession(
2970
+ return await this.#runtime.run(async () => {
2971
+ if (this.#location.archiveFile.kind === "directory") {
2972
+ return await this.#readDocument(
2973
+ async (document) => await document.readCover()
2974
+ );
2975
+ }
2976
+ const app = new import_wiki_graph_core7.WikiGraph({});
2977
+ return await app.openSession(
2791
2978
  this.#location.archiveFile,
2792
2979
  async (archive) => await archive.readCover()
2793
- ),
2794
- options.signal
2795
- );
2980
+ );
2981
+ }, options.signal);
2796
2982
  }
2797
2983
  async replaceMetadata(objectPath, value, options = {}) {
2798
2984
  return await this.#writeDocument(async (document) => {
@@ -2833,10 +3019,14 @@ var WikiGraphArchiveHandle = class {
2833
3019
  }
2834
3020
  async #readDocument(operation, options = {}) {
2835
3021
  return await this.#runtime.run(
2836
- async () => await new import_wiki_graph_core7.WikiGraphArchiveFile(this.#location.archiveFile).readDocument(
2837
- operation
2838
- ),
3022
+ async () => this.#location.archiveFile.kind === "directory" ? await new import_wiki_graph_core7.WikiGraphDirectoryFile(
3023
+ this.#location.archiveFile
3024
+ ).readDocument(operation) : await new import_wiki_graph_core7.WikiGraphArchiveFile(
3025
+ this.#location.archiveFile
3026
+ ).readDocument(operation),
2839
3027
  options.signal
3028
+ ).catch(
3029
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
2840
3030
  );
2841
3031
  }
2842
3032
  async #writeDocument(operation, options = {}) {
@@ -2848,33 +3038,32 @@ var WikiGraphArchiveHandle = class {
2848
3038
  searchIndexWritebackPolicy: options.searchIndexWritebackPolicy
2849
3039
  }
2850
3040
  });
2851
- }, options.signal);
3041
+ }, options.signal).catch(
3042
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
3043
+ );
2852
3044
  }
2853
3045
  async #resolveScope(document, options) {
2854
3046
  if (options.chapters !== void 0) return options.chapters;
2855
3047
  return (await resolveArchiveScope(document, this.objectUri, options.depth))?.chapterIds;
2856
3048
  }
2857
3049
  async #resolveQueryChapters(document, options, capabilities) {
2858
- const chapters = await this.#resolveScope(document, options);
3050
+ const parsed = parseChapterScopePath(this.objectUri);
3051
+ const chapters = options.chapters ?? (parsed === void 0 || parsed.kind === "collection" && options.depth === void 0 ? void 0 : await (0, import_wiki_graph_core7.resolveIndexedArchiveChapterScope)(
3052
+ document,
3053
+ parsed.kind === "chapter" ? parsed.chapterPath : void 0,
3054
+ options.depth
3055
+ ));
2859
3056
  const coverageOptions = {
2860
3057
  ...chapters === void 0 ? {} : { chapters },
2861
3058
  ...capabilities.embeddingProvider === void 0 ? {} : { embeddingProvider: capabilities.embeddingProvider },
2862
3059
  requireEmbeddingProvider: true,
2863
3060
  ...options.queryMode === void 0 ? {} : { queryMode: options.queryMode }
2864
3061
  };
2865
- if (options.skipUnindexed !== true) {
2866
- await (0, import_wiki_graph_core7.assertArchiveIndexArtifactsReady)(document, coverageOptions);
2867
- return chapters;
2868
- }
2869
- const queryable = await (0, import_wiki_graph_core7.listArchiveQueryableChapterIds)(document, {
2870
- ...coverageOptions
3062
+ await (0, import_wiki_graph_core7.assertArchiveQueryReady)(document, {
3063
+ ...coverageOptions,
3064
+ ...options.skipUnindexed === void 0 ? {} : { skipUnindexed: options.skipUnindexed }
2871
3065
  });
2872
- if (queryable.length === 0) {
2873
- throw new Error(
2874
- "Wiki Graph query is not ready. No chapters in this scope have a current FTS artifact or source embedding artifact."
2875
- );
2876
- }
2877
- return queryable;
3066
+ return chapters;
2878
3067
  }
2879
3068
  async #resolveQueryEmbeddingProvider(queryMode, signal) {
2880
3069
  if (queryMode === "fts") return void 0;
@@ -2893,6 +3082,10 @@ var WikiGraphArchiveHandle = class {
2893
3082
  async function ensureArchiveSearchIndex(document, options = {}) {
2894
3083
  const scope = options.chapters === void 0 ? {} : { chapters: options.chapters };
2895
3084
  if (await (0, import_wiki_graph_core7.isArchiveSearchIndexCurrent)(document, scope)) return;
3085
+ if (options.rebuild !== true)
3086
+ throw new Error(
3087
+ "Archive search index is missing or dirty. Run `wg <archive-uri>/index sync` explicitly before querying."
3088
+ );
2896
3089
  await (0, import_wiki_graph_core7.rebuildArchiveSearchIndex)(document, void 0, {
2897
3090
  ...scope,
2898
3091
  ...options.embeddingProvider === void 0 ? {} : { embeddingProvider: options.embeddingProvider }