wiki-graph-sdk 0.9.0 → 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 +306 -135
  2. package/dist/archives.cjs.map +1 -1
  3. package/dist/archives.js +315 -140
  4. package/dist/archives.js.map +1 -1
  5. package/dist/conversions.cjs +268 -99
  6. package/dist/conversions.cjs.map +1 -1
  7. package/dist/conversions.js +274 -103
  8. package/dist/conversions.js.map +1 -1
  9. package/dist/default-worker.cjs +258 -101
  10. package/dist/default-worker.cjs.map +1 -1
  11. package/dist/default-worker.js +264 -105
  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 +343 -165
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +357 -174
  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 +264 -97
  31. package/dist/maintenance.cjs.map +1 -1
  32. package/dist/maintenance.js +270 -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 +257 -96
  48. package/dist/worker.cjs.map +1 -1
  49. package/dist/worker.js +263 -100
  50. package/dist/worker.js.map +1 -1
  51. package/package.json +2 -2
package/dist/archives.js CHANGED
@@ -11,7 +11,9 @@ import { Readable as Readable2 } from "stream";
11
11
  import {
12
12
  addChapter,
13
13
  applyChapterTree,
14
- assertArchiveIndexArtifactsReady,
14
+ assertArchiveQueryReady,
15
+ resolveIndexedArchiveChapterScope,
16
+ ensureWikiGraphArchiveSchemaCurrent,
15
17
  assertNoActiveBuildJobConflicts,
16
18
  assertNoActiveBuildJobs,
17
19
  deleteArchiveSearchSessions,
@@ -162,6 +164,15 @@ var NodeFile = class {
162
164
  this.name = name;
163
165
  }
164
166
  path;
167
+ async getPhysicalIdentity() {
168
+ const value = await fsPromises.stat(this.path).catch((error) => {
169
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
170
+ return void 0;
171
+ throw error;
172
+ });
173
+ if (value === void 0) return void 0;
174
+ return `${value.dev}:${value.ino}`;
175
+ }
165
176
  kind = "file";
166
177
  [NODE_RESOURCE_KIND] = "file";
167
178
  identity;
@@ -286,6 +297,15 @@ var NodeDirectory = class _NodeDirectory {
286
297
  this.name = pathModule.basename(path);
287
298
  }
288
299
  path;
300
+ async getPhysicalIdentity() {
301
+ const value = await fsPromises.stat(this.path).catch((error) => {
302
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
303
+ return void 0;
304
+ throw error;
305
+ });
306
+ if (value === void 0) return void 0;
307
+ return `${value.dev}:${value.ino}`;
308
+ }
289
309
  kind = "directory";
290
310
  [NODE_RESOURCE_KIND] = "directory";
291
311
  identity;
@@ -295,8 +315,10 @@ var NodeDirectory = class _NodeDirectory {
295
315
  const file = new NodeFile(pathModule.join(this.path, name));
296
316
  try {
297
317
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
298
- } catch {
299
- return void 0;
318
+ } catch (error) {
319
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
320
+ return void 0;
321
+ throw error;
300
322
  }
301
323
  }
302
324
  async getLastModified() {
@@ -314,8 +336,10 @@ var NodeDirectory = class _NodeDirectory {
314
336
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
315
337
  try {
316
338
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
317
- } catch {
318
- return void 0;
339
+ } catch (error) {
340
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
341
+ return void 0;
342
+ throw error;
319
343
  }
320
344
  }
321
345
  async list() {
@@ -439,14 +463,23 @@ async function openNativeNodeDatabase(file, flags) {
439
463
  });
440
464
  });
441
465
  }
442
- async function openNodeZip(file) {
466
+ async function openNodeZip(file, options = {}) {
443
467
  const path = getBrandedNodeResourcePath(file, "file");
444
468
  if (path === void 0) {
445
469
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
446
470
  }
447
- const zipFile = await new Promise((resolve4, reject) => {
448
- yauzl.open(
449
- path,
471
+ const handle = await fsPromises.open(path, "r");
472
+ let size;
473
+ try {
474
+ size = (await handle.stat()).size;
475
+ } catch (error) {
476
+ await handle.close();
477
+ throw error;
478
+ }
479
+ const open2 = () => new Promise((resolve4, reject) => {
480
+ yauzl.fromRandomAccessReader(
481
+ new NodeZipSource(handle),
482
+ size,
450
483
  { autoClose: false, lazyEntries: true },
451
484
  (error, opened) => {
452
485
  if (error || opened === void 0)
@@ -455,63 +488,192 @@ async function openNodeZip(file) {
455
488
  }
456
489
  );
457
490
  });
458
- const entries = await new Promise(
459
- (resolve4, reject) => {
460
- const discovered = /* @__PURE__ */ new Map();
461
- const rejectAndClose = (error) => {
462
- zipFile.close();
463
- reject(
464
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
465
- );
466
- };
467
- const resolveEntries = () => {
468
- zipFile.off("error", rejectAndClose);
469
- resolve4(discovered);
470
- };
471
- zipFile.on("entry", (entry) => {
472
- if (!entry.fileName.endsWith("/")) {
473
- discovered.set(entry.fileName, entry);
474
- }
475
- zipFile.readEntry();
476
- });
477
- zipFile.once("error", rejectAndClose);
478
- zipFile.once("end", resolveEntries);
479
- zipFile.readEntry();
480
- }
481
- );
491
+ try {
492
+ (await open2()).close();
493
+ } catch (error) {
494
+ await handle.close();
495
+ throw error;
496
+ }
482
497
  let closed = false;
498
+ let pending = Promise.resolve();
499
+ let closing;
500
+ const run = (operation) => {
501
+ if (closed)
502
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
503
+ const result = pending.then(async () => {
504
+ const zip = await open2();
505
+ try {
506
+ return await operation(zip);
507
+ } finally {
508
+ zip.close();
509
+ }
510
+ });
511
+ pending = result.then(
512
+ () => void 0,
513
+ () => void 0
514
+ );
515
+ return result;
516
+ };
517
+ const withEntry = (name, operation) => run(async (zip) => {
518
+ let exact;
519
+ let alias;
520
+ let aliasCollision = false;
521
+ await scanNodeZip(zip, (entry) => {
522
+ if (entry.fileName === name) exact = entry;
523
+ if (normalizeNodeZipName(entry.fileName) === name) {
524
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
525
+ aliasCollision = true;
526
+ alias = entry;
527
+ }
528
+ });
529
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
530
+ let bestPosition = -1;
531
+ const candidates = await open2();
532
+ try {
533
+ await scanNodeZip(candidates, async (entry) => {
534
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
535
+ const probe = await open2();
536
+ let position = 0;
537
+ try {
538
+ await scanNodeZip(probe, (candidate) => {
539
+ if (candidate.fileName === entry.fileName) return false;
540
+ position++;
541
+ });
542
+ } finally {
543
+ probe.close();
544
+ }
545
+ if (position >= bestPosition) {
546
+ bestPosition = position;
547
+ alias = entry;
548
+ }
549
+ });
550
+ return await operation(candidates, alias);
551
+ } finally {
552
+ candidates.close();
553
+ }
554
+ }
555
+ return await operation(
556
+ zip,
557
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
558
+ );
559
+ });
483
560
  return {
484
561
  close: () => {
485
- if (!closed) zipFile.close();
486
562
  closed = true;
487
- return Promise.resolve();
563
+ closing ??= pending.then(() => handle.close());
564
+ return closing;
488
565
  },
489
- copyEntry: async (name, target) => {
490
- if (closed) throw new Error("Cannot read a closed ZIP archive");
491
- const entry = entries.get(name);
566
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
492
567
  if (entry === void 0) return false;
493
- await copyNodeZipEntry(zipFile, entry, target);
568
+ await copyNodeZipEntry(zip, entry, target);
494
569
  return true;
495
- },
496
- getEntrySize: (name) => {
497
- if (closed) throw new Error("Cannot read a closed ZIP archive");
498
- return Promise.resolve(entries.get(name)?.uncompressedSize);
499
- },
500
- listEntries: () => Promise.resolve([...entries.keys()]),
501
- readEntryRange: async (name, offset, length) => {
502
- if (closed) throw new Error("Cannot read a closed ZIP archive");
503
- const entry = entries.get(name);
570
+ }),
571
+ getEntrySize: (name) => withEntry(
572
+ name,
573
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
574
+ ),
575
+ listEntries: () => run(async (zip) => {
576
+ const names = /* @__PURE__ */ new Set();
577
+ await scanNodeZip(zip, (entry) => {
578
+ names.add(entry.fileName);
579
+ });
580
+ return [...names];
581
+ }),
582
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
504
583
  if (entry === void 0) return void 0;
505
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
506
- },
507
- readEntry: async (name) => {
508
- if (closed) throw new Error("Cannot read a closed ZIP archive");
509
- const entry = entries.get(name);
584
+ return await readNodeZipEntryRange(zip, entry, offset, length);
585
+ }),
586
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
510
587
  if (entry === void 0) return void 0;
511
- return await readNodeZipEntry(zipFile, entry);
512
- }
588
+ return await readNodeZipEntry(zip, entry);
589
+ })
513
590
  };
514
591
  }
592
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
593
+ constructor(handle) {
594
+ super();
595
+ this.handle = handle;
596
+ }
597
+ handle;
598
+ read(buffer, offset, length, position, callback) {
599
+ const read2 = (written) => {
600
+ if (written === length) {
601
+ callback(null);
602
+ return;
603
+ }
604
+ fs.read(
605
+ this.handle.fd,
606
+ buffer,
607
+ offset + written,
608
+ length - written,
609
+ position + written,
610
+ (error, bytesRead) => {
611
+ if (error) callback(error);
612
+ else if (bytesRead === 0)
613
+ callback(new Error("Unexpected end of ZIP file"));
614
+ else read2(written + bytesRead);
615
+ }
616
+ );
617
+ };
618
+ read2(0);
619
+ }
620
+ _readStreamForRange(start, end) {
621
+ const handle = this.handle;
622
+ return Readable.from(
623
+ (async function* () {
624
+ for (let position = start; position < end; ) {
625
+ const buffer = Buffer.allocUnsafe(
626
+ Math.min(64 * 1024, end - position)
627
+ );
628
+ const { bytesRead } = await handle.read(
629
+ buffer,
630
+ 0,
631
+ buffer.length,
632
+ position
633
+ );
634
+ if (bytesRead === 0) return;
635
+ position += bytesRead;
636
+ yield buffer.subarray(0, bytesRead);
637
+ }
638
+ })()
639
+ );
640
+ }
641
+ };
642
+ function normalizeNodeZipName(name) {
643
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
644
+ return normalized.split("/").includes("..") ? void 0 : normalized;
645
+ }
646
+ async function scanNodeZip(zip, visit) {
647
+ await new Promise((resolve4, reject) => {
648
+ const cleanup = () => {
649
+ zip.off("entry", onEntry);
650
+ zip.off("end", onEnd);
651
+ zip.off("error", onError);
652
+ };
653
+ const onError = (error) => {
654
+ cleanup();
655
+ reject(error);
656
+ };
657
+ const onEnd = () => {
658
+ cleanup();
659
+ resolve4();
660
+ };
661
+ const onEntry = (entry) => {
662
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
663
+ if (keepReading === false) onEnd();
664
+ else zip.readEntry();
665
+ }).catch(
666
+ (error) => onError(
667
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
668
+ )
669
+ );
670
+ };
671
+ zip.on("entry", onEntry);
672
+ zip.once("end", onEnd);
673
+ zip.once("error", onError);
674
+ zip.readEntry();
675
+ });
676
+ }
515
677
  async function copyNodeZipEntry(zipFile, entry, target) {
516
678
  const writer = await target.openWriter();
517
679
  try {
@@ -1282,17 +1444,29 @@ function resolveWikiGraphRuntimePath(path) {
1282
1444
 
1283
1445
  // src/archive/target.ts
1284
1446
  import { realpath, stat as stat2 } from "fs/promises";
1285
- import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve3 } from "path";
1447
+ import { basename as basename2, dirname as dirname2, join as join3, resolve as resolve3 } from "path";
1286
1448
  import {
1287
1449
  formatLocatedWikiGraphUri,
1288
1450
  formatWikiGraphLibraryUri,
1289
- listWikiGraphLibraries,
1290
- listWikiGraphLibraryArchives,
1451
+ findWikiGraphLibraryByPhysicalIdentity,
1452
+ findWikiGraphLibraryArchivePhysicalAliases,
1291
1453
  parseLocatedWikiGraphUri,
1292
1454
  parseWikiGraphLibraryUri,
1293
1455
  resolveWikiGraphLibrary,
1294
- resolveWikiGraphLibraryArchiveFile
1456
+ resolveWikiGraphLibraryArchiveFile,
1457
+ isWikiGraphSchemaVersionError,
1458
+ WikiGraphSchemaVersionError
1295
1459
  } from "wiki-graph-core";
1460
+ function rethrowArchiveSchemaError(error, target) {
1461
+ if (isWikiGraphSchemaVersionError(error) && error.target.kind === "archive") {
1462
+ throw new WikiGraphSchemaVersionError(
1463
+ target.kind === "standalone" ? { kind: "archive", path: resolveWikiGraphRuntimePath(target.path) } : { kind: "library", uri: target.uri.split("/arc/")[0] },
1464
+ error.actualVersion,
1465
+ error.supportedVersion
1466
+ );
1467
+ }
1468
+ throw error;
1469
+ }
1296
1470
  function requireWikiGraphArchiveFile(location) {
1297
1471
  return location.archiveFile;
1298
1472
  }
@@ -1313,12 +1487,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1313
1487
  const path = resolveWikiGraphRuntimePath(inputPath);
1314
1488
  const candidate = await canonicalPath(path);
1315
1489
  const candidateIdentity = await readFileIdentity(path);
1316
- const libraries = await listWikiGraphLibraries();
1317
- const matches = await findMatchingMembers(
1318
- libraries,
1319
- candidate,
1320
- candidateIdentity
1321
- );
1490
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1322
1491
  if (matches.length > 0) {
1323
1492
  const match = requireUnambiguousManagedMatch(path, matches);
1324
1493
  throw new WikiGraphArchiveOwnershipError(
@@ -1327,15 +1496,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1327
1496
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1328
1497
  );
1329
1498
  }
1330
- for (const library of libraries) {
1331
- const rootPath = getNodeResourcePath(library.folder);
1332
- const root = await canonicalPath(rootPath);
1333
- if (!isWithin(root, candidate)) continue;
1334
- throw new WikiGraphArchiveOwnershipError(
1335
- `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1336
- library.isDefault ? void 0 : library.publicId
1337
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1499
+ const ancestors = /* @__PURE__ */ new Set();
1500
+ for (const start of [candidate, resolve3(path)]) {
1501
+ for (let parent = dirname2(start); ; parent = dirname2(parent)) {
1502
+ ancestors.add(parent);
1503
+ if (dirname2(parent) === parent) break;
1504
+ }
1505
+ }
1506
+ for (const parent of ancestors) {
1507
+ const identity = await readFileIdentity(parent);
1508
+ const library = await findWikiGraphLibraryByPhysicalIdentity(
1509
+ identity,
1510
+ new NodeDirectory(parent).identity
1338
1511
  );
1512
+ if (library !== void 0) {
1513
+ throw new WikiGraphArchiveOwnershipError(
1514
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1515
+ library.isDefault ? void 0 : library.publicId
1516
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1517
+ );
1518
+ }
1339
1519
  }
1340
1520
  return path;
1341
1521
  }
@@ -1393,48 +1573,35 @@ async function resolveLibraryLocation(target) {
1393
1573
  async function canonicalPath(path) {
1394
1574
  try {
1395
1575
  return await realpath(path);
1396
- } catch {
1576
+ } catch (error) {
1577
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1578
+ throw error;
1397
1579
  const absolute = resolve3(path);
1398
1580
  const parent = dirname2(absolute);
1399
1581
  return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
1400
1582
  }
1401
1583
  }
1402
- function isWithin(root, candidate) {
1403
- const child = relative(root, candidate);
1404
- return child === "" || !child.startsWith("..") && !isAbsolute3(child);
1405
- }
1406
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1584
+ async function findMatchingMembers(candidate, candidateIdentity) {
1585
+ if (candidateIdentity === void 0) return [];
1586
+ const archives = await findWikiGraphLibraryArchivePhysicalAliases(candidateIdentity);
1407
1587
  const members = [];
1408
- for (const library of libraries) {
1409
- const archives = await listWikiGraphLibraryArchives({
1410
- isDefault: library.isDefault,
1411
- kind: "scope",
1412
- ...library.isDefault ? {} : { publicId: library.publicId }
1413
- });
1414
- for (const archive of archives) {
1415
- const physical = archive.file ?? archive.directory;
1416
- if (physical === void 0) continue;
1588
+ for (const archive of archives) {
1589
+ const parsed = parseWikiGraphLibraryUri(archive.libraryUri);
1590
+ const library = await resolveWikiGraphLibrary(parsed);
1591
+ const physical = archive.file ?? archive.directory;
1592
+ if (library !== void 0 && physical !== void 0)
1417
1593
  members.push({
1418
1594
  archive,
1419
1595
  library,
1420
1596
  memberPath: getNodeResourcePath(physical)
1421
1597
  });
1422
- }
1423
1598
  }
1424
1599
  const exact = [];
1425
1600
  for (const member of members) {
1426
- if (await canonicalPath(member.memberPath) === candidate) {
1601
+ if (await canonicalPath(member.memberPath) === candidate)
1427
1602
  exact.push(member);
1428
- }
1429
- }
1430
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1431
- const identityMatches = [];
1432
- for (const member of members) {
1433
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1434
- identityMatches.push(member);
1435
- }
1436
1603
  }
1437
- return identityMatches;
1604
+ return exact.length > 0 ? exact : members;
1438
1605
  }
1439
1606
  function requireUnambiguousManagedMatch(path, matches) {
1440
1607
  if (matches.length === 1) return matches[0];
@@ -1446,8 +1613,10 @@ async function readFileIdentity(path) {
1446
1613
  try {
1447
1614
  const value = await stat2(path);
1448
1615
  return `${value.dev}:${value.ino}`;
1449
- } catch {
1450
- return void 0;
1616
+ } catch (error) {
1617
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1618
+ return void 0;
1619
+ throw error;
1451
1620
  }
1452
1621
  }
1453
1622
 
@@ -1509,11 +1678,15 @@ var WikiGraphConversionManager = class {
1509
1678
  if (!("path" in options.input)) {
1510
1679
  throw new Error("wikg input requires a file path.");
1511
1680
  }
1681
+ const inputPath = options.input.path;
1512
1682
  await app.openSession(
1513
- new NodeFile(
1514
- await assertStandaloneWikiGraphArchivePath(options.input.path)
1515
- ),
1683
+ new NodeFile(await assertStandaloneWikiGraphArchivePath(inputPath)),
1516
1684
  write
1685
+ ).catch(
1686
+ (error) => rethrowArchiveSchemaError(error, {
1687
+ kind: "standalone",
1688
+ path: inputPath
1689
+ })
1517
1690
  );
1518
1691
  } else if ("stream" in options.input) {
1519
1692
  await app.digestTextStreamSession(
@@ -1913,6 +2086,12 @@ import {
1913
2086
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1914
2087
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1915
2088
  const write = async (archiveFile = location.archiveFile) => {
2089
+ if (cacheOnly) {
2090
+ return await new WikiGraphArchiveFile(archiveFile).readDocument(
2091
+ operation,
2092
+ { searchIndexWritebackPolicy: "cache" }
2093
+ );
2094
+ }
1916
2095
  const writeOptions = {
1917
2096
  ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1918
2097
  ...options.invalidateContinuationCursors === void 0 ? {} : {
@@ -2230,10 +2409,15 @@ var WikiGraphArchiveManager = class {
2230
2409
  }, options.signal);
2231
2410
  }
2232
2411
  async open(target, options = {}) {
2233
- const location = await this.#runtime.run(
2234
- async () => await resolveWikiGraphArchiveLocation(target),
2235
- options.signal
2236
- );
2412
+ const location = await this.#runtime.run(async () => {
2413
+ const location2 = await resolveWikiGraphArchiveLocation(target);
2414
+ try {
2415
+ await ensureWikiGraphArchiveSchemaCurrent(location2.archiveFile);
2416
+ } catch (error) {
2417
+ rethrowArchiveSchemaError(error, target);
2418
+ }
2419
+ return location2;
2420
+ }, options.signal);
2237
2421
  return new WikiGraphArchiveHandle(this.#runtime, location);
2238
2422
  }
2239
2423
  };
@@ -2284,15 +2468,11 @@ var WikiGraphArchiveHandle = class {
2284
2468
  options.queryMode,
2285
2469
  options.signal
2286
2470
  );
2287
- const result = await this.#writeDocument(
2471
+ const result = await this.#readDocument(
2288
2472
  async (document) => {
2289
2473
  const chapters = await this.#resolveQueryChapters(document, options, {
2290
2474
  embeddingProvider
2291
2475
  });
2292
- await ensureArchiveSearchIndex(document, {
2293
- ...chapters === void 0 ? {} : { chapters },
2294
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2295
- });
2296
2476
  return await findArchiveObjects(document, query, {
2297
2477
  ...withoutOperationAndScope(options),
2298
2478
  archiveKey: this.archiveKey,
@@ -2301,7 +2481,6 @@ var WikiGraphArchiveHandle = class {
2301
2481
  });
2302
2482
  },
2303
2483
  {
2304
- searchIndexWritebackPolicy: "cache",
2305
2484
  ...options.signal === void 0 ? {} : { signal: options.signal }
2306
2485
  }
2307
2486
  );
@@ -2406,6 +2585,7 @@ var WikiGraphArchiveHandle = class {
2406
2585
  async (document) => {
2407
2586
  const chapters = await this.#resolveScope(document, options);
2408
2587
  await ensureArchiveSearchIndex(document, {
2588
+ rebuild: true,
2409
2589
  ...chapters === void 0 ? {} : { chapters },
2410
2590
  ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2411
2591
  });
@@ -2695,15 +2875,11 @@ var WikiGraphArchiveHandle = class {
2695
2875
  options.queryMode,
2696
2876
  options.signal
2697
2877
  );
2698
- const result = await this.#writeDocument(
2878
+ const result = await this.#readDocument(
2699
2879
  async (document) => {
2700
2880
  const chapters = await this.#resolveQueryChapters(document, options, {
2701
2881
  embeddingProvider
2702
2882
  });
2703
- await ensureArchiveSearchIndex(document, {
2704
- ...chapters === void 0 ? {} : { chapters },
2705
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2706
- });
2707
2883
  return await listRelatedArchiveObjects(document, objectUri, {
2708
2884
  ...withoutOperation(options),
2709
2885
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2711,7 +2887,6 @@ var WikiGraphArchiveHandle = class {
2711
2887
  });
2712
2888
  },
2713
2889
  {
2714
- searchIndexWritebackPolicy: "cache",
2715
2890
  ...options.signal === void 0 ? {} : { signal: options.signal }
2716
2891
  }
2717
2892
  );
@@ -2755,15 +2930,11 @@ var WikiGraphArchiveHandle = class {
2755
2930
  options.queryMode,
2756
2931
  options.signal
2757
2932
  );
2758
- const result = await this.#writeDocument(
2933
+ const result = await this.#readDocument(
2759
2934
  async (document) => {
2760
2935
  const chapters = await this.#resolveQueryChapters(document, options, {
2761
2936
  embeddingProvider
2762
2937
  });
2763
- await ensureArchiveSearchIndex(document, {
2764
- ...chapters === void 0 ? {} : { chapters },
2765
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2766
- });
2767
2938
  return await listArchiveEvidence(document, objectUri, {
2768
2939
  ...withoutOperation(options),
2769
2940
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2771,7 +2942,6 @@ var WikiGraphArchiveHandle = class {
2771
2942
  });
2772
2943
  },
2773
2944
  {
2774
- searchIndexWritebackPolicy: "cache",
2775
2945
  ...options.signal === void 0 ? {} : { signal: options.signal }
2776
2946
  }
2777
2947
  );
@@ -2888,6 +3058,8 @@ var WikiGraphArchiveHandle = class {
2888
3058
  this.#location.archiveFile
2889
3059
  ).readDocument(operation),
2890
3060
  options.signal
3061
+ ).catch(
3062
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
2891
3063
  );
2892
3064
  }
2893
3065
  async #writeDocument(operation, options = {}) {
@@ -2899,33 +3071,32 @@ var WikiGraphArchiveHandle = class {
2899
3071
  searchIndexWritebackPolicy: options.searchIndexWritebackPolicy
2900
3072
  }
2901
3073
  });
2902
- }, options.signal);
3074
+ }, options.signal).catch(
3075
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
3076
+ );
2903
3077
  }
2904
3078
  async #resolveScope(document, options) {
2905
3079
  if (options.chapters !== void 0) return options.chapters;
2906
3080
  return (await resolveArchiveScope(document, this.objectUri, options.depth))?.chapterIds;
2907
3081
  }
2908
3082
  async #resolveQueryChapters(document, options, capabilities) {
2909
- const chapters = await this.#resolveScope(document, options);
3083
+ const parsed = parseChapterScopePath(this.objectUri);
3084
+ const chapters = options.chapters ?? (parsed === void 0 || parsed.kind === "collection" && options.depth === void 0 ? void 0 : await resolveIndexedArchiveChapterScope(
3085
+ document,
3086
+ parsed.kind === "chapter" ? parsed.chapterPath : void 0,
3087
+ options.depth
3088
+ ));
2910
3089
  const coverageOptions = {
2911
3090
  ...chapters === void 0 ? {} : { chapters },
2912
3091
  ...capabilities.embeddingProvider === void 0 ? {} : { embeddingProvider: capabilities.embeddingProvider },
2913
3092
  requireEmbeddingProvider: true,
2914
3093
  ...options.queryMode === void 0 ? {} : { queryMode: options.queryMode }
2915
3094
  };
2916
- if (options.skipUnindexed !== true) {
2917
- await assertArchiveIndexArtifactsReady(document, coverageOptions);
2918
- return chapters;
2919
- }
2920
- const queryable = await listArchiveQueryableChapterIds(document, {
2921
- ...coverageOptions
3095
+ await assertArchiveQueryReady(document, {
3096
+ ...coverageOptions,
3097
+ ...options.skipUnindexed === void 0 ? {} : { skipUnindexed: options.skipUnindexed }
2922
3098
  });
2923
- if (queryable.length === 0) {
2924
- throw new Error(
2925
- "Wiki Graph query is not ready. No chapters in this scope have a current FTS artifact or source embedding artifact."
2926
- );
2927
- }
2928
- return queryable;
3099
+ return chapters;
2929
3100
  }
2930
3101
  async #resolveQueryEmbeddingProvider(queryMode, signal) {
2931
3102
  if (queryMode === "fts") return void 0;
@@ -2944,6 +3115,10 @@ var WikiGraphArchiveHandle = class {
2944
3115
  async function ensureArchiveSearchIndex(document, options = {}) {
2945
3116
  const scope = options.chapters === void 0 ? {} : { chapters: options.chapters };
2946
3117
  if (await isArchiveSearchIndexCurrent2(document, scope)) return;
3118
+ if (options.rebuild !== true)
3119
+ throw new Error(
3120
+ "Archive search index is missing or dirty. Run `wg <archive-uri>/index sync` explicitly before querying."
3121
+ );
2947
3122
  await rebuildArchiveSearchIndex(document, void 0, {
2948
3123
  ...scope,
2949
3124
  ...options.embeddingProvider === void 0 ? {} : { embeddingProvider: options.embeddingProvider }