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.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,
@@ -43,6 +45,7 @@ import {
43
45
  getChapterTree,
44
46
  WikiGraph as WikiGraph2,
45
47
  WikiGraphArchiveFile as WikiGraphArchiveFile2,
48
+ WikiGraphDirectoryFile as WikiGraphDirectoryFile2,
46
49
  TOC_FILE_VERSION,
47
50
  writeWikgArchive,
48
51
  ObjectMetadataKind
@@ -161,6 +164,15 @@ var NodeFile = class {
161
164
  this.name = name;
162
165
  }
163
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
+ }
164
176
  kind = "file";
165
177
  [NODE_RESOURCE_KIND] = "file";
166
178
  identity;
@@ -285,6 +297,15 @@ var NodeDirectory = class _NodeDirectory {
285
297
  this.name = pathModule.basename(path);
286
298
  }
287
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
+ }
288
309
  kind = "directory";
289
310
  [NODE_RESOURCE_KIND] = "directory";
290
311
  identity;
@@ -294,8 +315,10 @@ var NodeDirectory = class _NodeDirectory {
294
315
  const file = new NodeFile(pathModule.join(this.path, name));
295
316
  try {
296
317
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
297
- } catch {
298
- 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;
299
322
  }
300
323
  }
301
324
  async getLastModified() {
@@ -313,8 +336,10 @@ var NodeDirectory = class _NodeDirectory {
313
336
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
314
337
  try {
315
338
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
316
- } catch {
317
- 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;
318
343
  }
319
344
  }
320
345
  async list() {
@@ -438,14 +463,23 @@ async function openNativeNodeDatabase(file, flags) {
438
463
  });
439
464
  });
440
465
  }
441
- async function openNodeZip(file) {
466
+ async function openNodeZip(file, options = {}) {
442
467
  const path = getBrandedNodeResourcePath(file, "file");
443
468
  if (path === void 0) {
444
469
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
445
470
  }
446
- const zipFile = await new Promise((resolve4, reject) => {
447
- yauzl.open(
448
- 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,
449
483
  { autoClose: false, lazyEntries: true },
450
484
  (error, opened) => {
451
485
  if (error || opened === void 0)
@@ -454,63 +488,192 @@ async function openNodeZip(file) {
454
488
  }
455
489
  );
456
490
  });
457
- const entries = await new Promise(
458
- (resolve4, reject) => {
459
- const discovered = /* @__PURE__ */ new Map();
460
- const rejectAndClose = (error) => {
461
- zipFile.close();
462
- reject(
463
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
464
- );
465
- };
466
- const resolveEntries = () => {
467
- zipFile.off("error", rejectAndClose);
468
- resolve4(discovered);
469
- };
470
- zipFile.on("entry", (entry) => {
471
- if (!entry.fileName.endsWith("/")) {
472
- discovered.set(entry.fileName, entry);
473
- }
474
- zipFile.readEntry();
475
- });
476
- zipFile.once("error", rejectAndClose);
477
- zipFile.once("end", resolveEntries);
478
- zipFile.readEntry();
479
- }
480
- );
491
+ try {
492
+ (await open2()).close();
493
+ } catch (error) {
494
+ await handle.close();
495
+ throw error;
496
+ }
481
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
+ });
482
560
  return {
483
561
  close: () => {
484
- if (!closed) zipFile.close();
485
562
  closed = true;
486
- return Promise.resolve();
563
+ closing ??= pending.then(() => handle.close());
564
+ return closing;
487
565
  },
488
- copyEntry: async (name, target) => {
489
- if (closed) throw new Error("Cannot read a closed ZIP archive");
490
- const entry = entries.get(name);
566
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
491
567
  if (entry === void 0) return false;
492
- await copyNodeZipEntry(zipFile, entry, target);
568
+ await copyNodeZipEntry(zip, entry, target);
493
569
  return true;
494
- },
495
- getEntrySize: (name) => {
496
- if (closed) throw new Error("Cannot read a closed ZIP archive");
497
- return Promise.resolve(entries.get(name)?.uncompressedSize);
498
- },
499
- listEntries: () => Promise.resolve([...entries.keys()]),
500
- readEntryRange: async (name, offset, length) => {
501
- if (closed) throw new Error("Cannot read a closed ZIP archive");
502
- 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) => {
503
583
  if (entry === void 0) return void 0;
504
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
505
- },
506
- readEntry: async (name) => {
507
- if (closed) throw new Error("Cannot read a closed ZIP archive");
508
- const entry = entries.get(name);
584
+ return await readNodeZipEntryRange(zip, entry, offset, length);
585
+ }),
586
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
509
587
  if (entry === void 0) return void 0;
510
- return await readNodeZipEntry(zipFile, entry);
511
- }
588
+ return await readNodeZipEntry(zip, entry);
589
+ })
512
590
  };
513
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
+ }
514
677
  async function copyNodeZipEntry(zipFile, entry, target) {
515
678
  const writer = await target.openWriter();
516
679
  try {
@@ -1281,17 +1444,32 @@ function resolveWikiGraphRuntimePath(path) {
1281
1444
 
1282
1445
  // src/archive/target.ts
1283
1446
  import { realpath, stat as stat2 } from "fs/promises";
1284
- 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";
1285
1448
  import {
1286
1449
  formatLocatedWikiGraphUri,
1287
1450
  formatWikiGraphLibraryUri,
1288
- listWikiGraphLibraries,
1289
- listWikiGraphLibraryArchives,
1451
+ findWikiGraphLibraryByPhysicalIdentity,
1452
+ findWikiGraphLibraryArchivePhysicalAliases,
1290
1453
  parseLocatedWikiGraphUri,
1291
1454
  parseWikiGraphLibraryUri,
1292
1455
  resolveWikiGraphLibrary,
1293
- resolveWikiGraphLibraryArchiveFile
1456
+ resolveWikiGraphLibraryArchiveFile,
1457
+ isWikiGraphSchemaVersionError,
1458
+ WikiGraphSchemaVersionError
1294
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
+ }
1470
+ function requireWikiGraphArchiveFile(location) {
1471
+ return location.archiveFile;
1472
+ }
1295
1473
  var WikiGraphArchiveOwnershipError = class extends Error {
1296
1474
  code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1297
1475
  constructor(message) {
@@ -1309,12 +1487,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1309
1487
  const path = resolveWikiGraphRuntimePath(inputPath);
1310
1488
  const candidate = await canonicalPath(path);
1311
1489
  const candidateIdentity = await readFileIdentity(path);
1312
- const libraries = await listWikiGraphLibraries();
1313
- const matches = await findMatchingMembers(
1314
- libraries,
1315
- candidate,
1316
- candidateIdentity
1317
- );
1490
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1318
1491
  if (matches.length > 0) {
1319
1492
  const match = requireUnambiguousManagedMatch(path, matches);
1320
1493
  throw new WikiGraphArchiveOwnershipError(
@@ -1323,15 +1496,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1323
1496
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1324
1497
  );
1325
1498
  }
1326
- for (const library of libraries) {
1327
- const rootPath = getNodeResourcePath(library.folder);
1328
- const root = await canonicalPath(rootPath);
1329
- if (!isWithin(root, candidate)) continue;
1330
- throw new WikiGraphArchiveOwnershipError(
1331
- `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1332
- library.isDefault ? void 0 : library.publicId
1333
- )}: ${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
1334
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
+ }
1335
1519
  }
1336
1520
  return path;
1337
1521
  }
@@ -1389,47 +1573,35 @@ async function resolveLibraryLocation(target) {
1389
1573
  async function canonicalPath(path) {
1390
1574
  try {
1391
1575
  return await realpath(path);
1392
- } catch {
1576
+ } catch (error) {
1577
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1578
+ throw error;
1393
1579
  const absolute = resolve3(path);
1394
1580
  const parent = dirname2(absolute);
1395
1581
  return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
1396
1582
  }
1397
1583
  }
1398
- function isWithin(root, candidate) {
1399
- const child = relative(root, candidate);
1400
- return child === "" || !child.startsWith("..") && !isAbsolute3(child);
1401
- }
1402
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1584
+ async function findMatchingMembers(candidate, candidateIdentity) {
1585
+ if (candidateIdentity === void 0) return [];
1586
+ const archives = await findWikiGraphLibraryArchivePhysicalAliases(candidateIdentity);
1403
1587
  const members = [];
1404
- for (const library of libraries) {
1405
- const archives = await listWikiGraphLibraryArchives({
1406
- isDefault: library.isDefault,
1407
- kind: "scope",
1408
- ...library.isDefault ? {} : { publicId: library.publicId }
1409
- });
1410
- for (const archive of archives) {
1411
- if (archive.file === 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)
1412
1593
  members.push({
1413
1594
  archive,
1414
1595
  library,
1415
- memberPath: getNodeResourcePath(archive.file)
1596
+ memberPath: getNodeResourcePath(physical)
1416
1597
  });
1417
- }
1418
1598
  }
1419
1599
  const exact = [];
1420
1600
  for (const member of members) {
1421
- if (await canonicalPath(member.memberPath) === candidate) {
1601
+ if (await canonicalPath(member.memberPath) === candidate)
1422
1602
  exact.push(member);
1423
- }
1424
- }
1425
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1426
- const identityMatches = [];
1427
- for (const member of members) {
1428
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1429
- identityMatches.push(member);
1430
- }
1431
1603
  }
1432
- return identityMatches;
1604
+ return exact.length > 0 ? exact : members;
1433
1605
  }
1434
1606
  function requireUnambiguousManagedMatch(path, matches) {
1435
1607
  if (matches.length === 1) return matches[0];
@@ -1441,8 +1613,10 @@ async function readFileIdentity(path) {
1441
1613
  try {
1442
1614
  const value = await stat2(path);
1443
1615
  return `${value.dev}:${value.ino}`;
1444
- } catch {
1445
- 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;
1446
1620
  }
1447
1621
  }
1448
1622
 
@@ -1504,11 +1678,15 @@ var WikiGraphConversionManager = class {
1504
1678
  if (!("path" in options.input)) {
1505
1679
  throw new Error("wikg input requires a file path.");
1506
1680
  }
1681
+ const inputPath = options.input.path;
1507
1682
  await app.openSession(
1508
- new NodeFile(
1509
- await assertStandaloneWikiGraphArchivePath(options.input.path)
1510
- ),
1683
+ new NodeFile(await assertStandaloneWikiGraphArchivePath(inputPath)),
1511
1684
  write
1685
+ ).catch(
1686
+ (error) => rethrowArchiveSchemaError(error, {
1687
+ kind: "standalone",
1688
+ path: inputPath
1689
+ })
1512
1690
  );
1513
1691
  } else if ("stream" in options.input) {
1514
1692
  await app.digestTextStreamSession(
@@ -1902,17 +2080,36 @@ function formatLocatedChapterUri(archiveUri, chapter) {
1902
2080
  // src/archive/write.ts
1903
2081
  import {
1904
2082
  WikiGraphArchiveFile,
2083
+ WikiGraphDirectoryFile,
1905
2084
  writeWikiGraphLibraryArchive
1906
2085
  } from "wiki-graph-core";
1907
2086
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1908
2087
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1909
- const write = async (archiveFile = location.archiveFile) => await new WikiGraphArchiveFile(archiveFile).write(operation, {
1910
- ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1911
- ...options.invalidateContinuationCursors === void 0 ? {} : {
1912
- invalidateContinuationCursors: options.invalidateContinuationCursors
1913
- },
1914
- ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
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
+ }
2095
+ const writeOptions = {
2096
+ ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
2097
+ ...options.invalidateContinuationCursors === void 0 ? {} : {
2098
+ invalidateContinuationCursors: options.invalidateContinuationCursors
2099
+ },
2100
+ ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
2101
+ };
2102
+ if (archiveFile.kind === "directory") {
2103
+ return await new WikiGraphDirectoryFile(archiveFile).write(
2104
+ operation,
2105
+ writeOptions
2106
+ );
2107
+ }
2108
+ return await new WikiGraphArchiveFile(archiveFile).write(
2109
+ operation,
2110
+ writeOptions
2111
+ );
2112
+ };
1916
2113
  if (location.libraryArchiveTarget === void 0 || cacheOnly || options.refreshLibraryIndex === false) {
1917
2114
  return await write();
1918
2115
  }
@@ -2212,10 +2409,15 @@ var WikiGraphArchiveManager = class {
2212
2409
  }, options.signal);
2213
2410
  }
2214
2411
  async open(target, options = {}) {
2215
- const location = await this.#runtime.run(
2216
- async () => await resolveWikiGraphArchiveLocation(target),
2217
- options.signal
2218
- );
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);
2219
2421
  return new WikiGraphArchiveHandle(this.#runtime, location);
2220
2422
  }
2221
2423
  };
@@ -2266,15 +2468,11 @@ var WikiGraphArchiveHandle = class {
2266
2468
  options.queryMode,
2267
2469
  options.signal
2268
2470
  );
2269
- const result = await this.#writeDocument(
2471
+ const result = await this.#readDocument(
2270
2472
  async (document) => {
2271
2473
  const chapters = await this.#resolveQueryChapters(document, options, {
2272
2474
  embeddingProvider
2273
2475
  });
2274
- await ensureArchiveSearchIndex(document, {
2275
- ...chapters === void 0 ? {} : { chapters },
2276
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2277
- });
2278
2476
  return await findArchiveObjects(document, query, {
2279
2477
  ...withoutOperationAndScope(options),
2280
2478
  archiveKey: this.archiveKey,
@@ -2283,7 +2481,6 @@ var WikiGraphArchiveHandle = class {
2283
2481
  });
2284
2482
  },
2285
2483
  {
2286
- searchIndexWritebackPolicy: "cache",
2287
2484
  ...options.signal === void 0 ? {} : { signal: options.signal }
2288
2485
  }
2289
2486
  );
@@ -2388,6 +2585,7 @@ var WikiGraphArchiveHandle = class {
2388
2585
  async (document) => {
2389
2586
  const chapters = await this.#resolveScope(document, options);
2390
2587
  await ensureArchiveSearchIndex(document, {
2588
+ rebuild: true,
2391
2589
  ...chapters === void 0 ? {} : { chapters },
2392
2590
  ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2393
2591
  });
@@ -2566,7 +2764,7 @@ var WikiGraphArchiveHandle = class {
2566
2764
  return await this.#writeDocument(async (document) => {
2567
2765
  const chapterId = await resolveChapterPathReadonly(document, path);
2568
2766
  await assertChapterResetAllowed(
2569
- this.#location.archiveFile,
2767
+ requireWikiGraphArchiveFile(this.#location),
2570
2768
  chapterId,
2571
2769
  stage
2572
2770
  );
@@ -2677,15 +2875,11 @@ var WikiGraphArchiveHandle = class {
2677
2875
  options.queryMode,
2678
2876
  options.signal
2679
2877
  );
2680
- const result = await this.#writeDocument(
2878
+ const result = await this.#readDocument(
2681
2879
  async (document) => {
2682
2880
  const chapters = await this.#resolveQueryChapters(document, options, {
2683
2881
  embeddingProvider
2684
2882
  });
2685
- await ensureArchiveSearchIndex(document, {
2686
- ...chapters === void 0 ? {} : { chapters },
2687
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2688
- });
2689
2883
  return await listRelatedArchiveObjects(document, objectUri, {
2690
2884
  ...withoutOperation(options),
2691
2885
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2693,7 +2887,6 @@ var WikiGraphArchiveHandle = class {
2693
2887
  });
2694
2888
  },
2695
2889
  {
2696
- searchIndexWritebackPolicy: "cache",
2697
2890
  ...options.signal === void 0 ? {} : { signal: options.signal }
2698
2891
  }
2699
2892
  );
@@ -2737,15 +2930,11 @@ var WikiGraphArchiveHandle = class {
2737
2930
  options.queryMode,
2738
2931
  options.signal
2739
2932
  );
2740
- const result = await this.#writeDocument(
2933
+ const result = await this.#readDocument(
2741
2934
  async (document) => {
2742
2935
  const chapters = await this.#resolveQueryChapters(document, options, {
2743
2936
  embeddingProvider
2744
2937
  });
2745
- await ensureArchiveSearchIndex(document, {
2746
- ...chapters === void 0 ? {} : { chapters },
2747
- ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2748
- });
2749
2938
  return await listArchiveEvidence(document, objectUri, {
2750
2939
  ...withoutOperation(options),
2751
2940
  ...embeddingProvider === void 0 ? {} : { embeddingProvider },
@@ -2753,7 +2942,6 @@ var WikiGraphArchiveHandle = class {
2753
2942
  });
2754
2943
  },
2755
2944
  {
2756
- searchIndexWritebackPolicy: "cache",
2757
2945
  ...options.signal === void 0 ? {} : { signal: options.signal }
2758
2946
  }
2759
2947
  );
@@ -2812,14 +3000,18 @@ var WikiGraphArchiveHandle = class {
2812
3000
  }, options);
2813
3001
  }
2814
3002
  async readCover(options = {}) {
2815
- const app = new WikiGraph2({});
2816
- return await this.#runtime.run(
2817
- async () => await app.openSession(
3003
+ return await this.#runtime.run(async () => {
3004
+ if (this.#location.archiveFile.kind === "directory") {
3005
+ return await this.#readDocument(
3006
+ async (document) => await document.readCover()
3007
+ );
3008
+ }
3009
+ const app = new WikiGraph2({});
3010
+ return await app.openSession(
2818
3011
  this.#location.archiveFile,
2819
3012
  async (archive) => await archive.readCover()
2820
- ),
2821
- options.signal
2822
- );
3013
+ );
3014
+ }, options.signal);
2823
3015
  }
2824
3016
  async replaceMetadata(objectPath, value, options = {}) {
2825
3017
  return await this.#writeDocument(async (document) => {
@@ -2860,10 +3052,14 @@ var WikiGraphArchiveHandle = class {
2860
3052
  }
2861
3053
  async #readDocument(operation, options = {}) {
2862
3054
  return await this.#runtime.run(
2863
- async () => await new WikiGraphArchiveFile2(this.#location.archiveFile).readDocument(
2864
- operation
2865
- ),
3055
+ async () => this.#location.archiveFile.kind === "directory" ? await new WikiGraphDirectoryFile2(
3056
+ this.#location.archiveFile
3057
+ ).readDocument(operation) : await new WikiGraphArchiveFile2(
3058
+ this.#location.archiveFile
3059
+ ).readDocument(operation),
2866
3060
  options.signal
3061
+ ).catch(
3062
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
2867
3063
  );
2868
3064
  }
2869
3065
  async #writeDocument(operation, options = {}) {
@@ -2875,33 +3071,32 @@ var WikiGraphArchiveHandle = class {
2875
3071
  searchIndexWritebackPolicy: options.searchIndexWritebackPolicy
2876
3072
  }
2877
3073
  });
2878
- }, options.signal);
3074
+ }, options.signal).catch(
3075
+ (error) => rethrowArchiveSchemaError(error, this.#location.target)
3076
+ );
2879
3077
  }
2880
3078
  async #resolveScope(document, options) {
2881
3079
  if (options.chapters !== void 0) return options.chapters;
2882
3080
  return (await resolveArchiveScope(document, this.objectUri, options.depth))?.chapterIds;
2883
3081
  }
2884
3082
  async #resolveQueryChapters(document, options, capabilities) {
2885
- 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
+ ));
2886
3089
  const coverageOptions = {
2887
3090
  ...chapters === void 0 ? {} : { chapters },
2888
3091
  ...capabilities.embeddingProvider === void 0 ? {} : { embeddingProvider: capabilities.embeddingProvider },
2889
3092
  requireEmbeddingProvider: true,
2890
3093
  ...options.queryMode === void 0 ? {} : { queryMode: options.queryMode }
2891
3094
  };
2892
- if (options.skipUnindexed !== true) {
2893
- await assertArchiveIndexArtifactsReady(document, coverageOptions);
2894
- return chapters;
2895
- }
2896
- const queryable = await listArchiveQueryableChapterIds(document, {
2897
- ...coverageOptions
3095
+ await assertArchiveQueryReady(document, {
3096
+ ...coverageOptions,
3097
+ ...options.skipUnindexed === void 0 ? {} : { skipUnindexed: options.skipUnindexed }
2898
3098
  });
2899
- if (queryable.length === 0) {
2900
- throw new Error(
2901
- "Wiki Graph query is not ready. No chapters in this scope have a current FTS artifact or source embedding artifact."
2902
- );
2903
- }
2904
- return queryable;
3099
+ return chapters;
2905
3100
  }
2906
3101
  async #resolveQueryEmbeddingProvider(queryMode, signal) {
2907
3102
  if (queryMode === "fts") return void 0;
@@ -2920,6 +3115,10 @@ var WikiGraphArchiveHandle = class {
2920
3115
  async function ensureArchiveSearchIndex(document, options = {}) {
2921
3116
  const scope = options.chapters === void 0 ? {} : { chapters: options.chapters };
2922
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
+ );
2923
3122
  await rebuildArchiveSearchIndex(document, void 0, {
2924
3123
  ...scope,
2925
3124
  ...options.embeddingProvider === void 0 ? {} : { embeddingProvider: options.embeddingProvider }