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
@@ -109,6 +109,15 @@ var NodeFile = class {
109
109
  this.name = name;
110
110
  }
111
111
  path;
112
+ async getPhysicalIdentity() {
113
+ const value = await fsPromises.stat(this.path).catch((error) => {
114
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
115
+ return void 0;
116
+ throw error;
117
+ });
118
+ if (value === void 0) return void 0;
119
+ return `${value.dev}:${value.ino}`;
120
+ }
112
121
  kind = "file";
113
122
  [NODE_RESOURCE_KIND] = "file";
114
123
  identity;
@@ -233,6 +242,15 @@ var NodeDirectory = class _NodeDirectory {
233
242
  this.name = pathModule.basename(path);
234
243
  }
235
244
  path;
245
+ async getPhysicalIdentity() {
246
+ const value = await fsPromises.stat(this.path).catch((error) => {
247
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
248
+ return void 0;
249
+ throw error;
250
+ });
251
+ if (value === void 0) return void 0;
252
+ return `${value.dev}:${value.ino}`;
253
+ }
236
254
  kind = "directory";
237
255
  [NODE_RESOURCE_KIND] = "directory";
238
256
  identity;
@@ -242,8 +260,10 @@ var NodeDirectory = class _NodeDirectory {
242
260
  const file = new NodeFile(pathModule.join(this.path, name));
243
261
  try {
244
262
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
245
- } catch {
246
- return void 0;
263
+ } catch (error) {
264
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
265
+ return void 0;
266
+ throw error;
247
267
  }
248
268
  }
249
269
  async getLastModified() {
@@ -261,8 +281,10 @@ var NodeDirectory = class _NodeDirectory {
261
281
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
262
282
  try {
263
283
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
264
- } catch {
265
- return void 0;
284
+ } catch (error) {
285
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
286
+ return void 0;
287
+ throw error;
266
288
  }
267
289
  }
268
290
  async list() {
@@ -386,14 +408,23 @@ async function openNativeNodeDatabase(file, flags) {
386
408
  });
387
409
  });
388
410
  }
389
- async function openNodeZip(file) {
411
+ async function openNodeZip(file, options = {}) {
390
412
  const path = getBrandedNodeResourcePath(file, "file");
391
413
  if (path === void 0) {
392
414
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
393
415
  }
394
- const zipFile = await new Promise((resolve4, reject) => {
395
- yauzl.open(
396
- path,
416
+ const handle = await fsPromises.open(path, "r");
417
+ let size;
418
+ try {
419
+ size = (await handle.stat()).size;
420
+ } catch (error) {
421
+ await handle.close();
422
+ throw error;
423
+ }
424
+ const open2 = () => new Promise((resolve4, reject) => {
425
+ yauzl.fromRandomAccessReader(
426
+ new NodeZipSource(handle),
427
+ size,
397
428
  { autoClose: false, lazyEntries: true },
398
429
  (error, opened) => {
399
430
  if (error || opened === void 0)
@@ -402,63 +433,192 @@ async function openNodeZip(file) {
402
433
  }
403
434
  );
404
435
  });
405
- const entries = await new Promise(
406
- (resolve4, reject) => {
407
- const discovered = /* @__PURE__ */ new Map();
408
- const rejectAndClose = (error) => {
409
- zipFile.close();
410
- reject(
411
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
412
- );
413
- };
414
- const resolveEntries = () => {
415
- zipFile.off("error", rejectAndClose);
416
- resolve4(discovered);
417
- };
418
- zipFile.on("entry", (entry) => {
419
- if (!entry.fileName.endsWith("/")) {
420
- discovered.set(entry.fileName, entry);
421
- }
422
- zipFile.readEntry();
423
- });
424
- zipFile.once("error", rejectAndClose);
425
- zipFile.once("end", resolveEntries);
426
- zipFile.readEntry();
427
- }
428
- );
436
+ try {
437
+ (await open2()).close();
438
+ } catch (error) {
439
+ await handle.close();
440
+ throw error;
441
+ }
429
442
  let closed = false;
443
+ let pending = Promise.resolve();
444
+ let closing;
445
+ const run = (operation) => {
446
+ if (closed)
447
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
448
+ const result = pending.then(async () => {
449
+ const zip = await open2();
450
+ try {
451
+ return await operation(zip);
452
+ } finally {
453
+ zip.close();
454
+ }
455
+ });
456
+ pending = result.then(
457
+ () => void 0,
458
+ () => void 0
459
+ );
460
+ return result;
461
+ };
462
+ const withEntry = (name, operation) => run(async (zip) => {
463
+ let exact;
464
+ let alias;
465
+ let aliasCollision = false;
466
+ await scanNodeZip(zip, (entry) => {
467
+ if (entry.fileName === name) exact = entry;
468
+ if (normalizeNodeZipName(entry.fileName) === name) {
469
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
470
+ aliasCollision = true;
471
+ alias = entry;
472
+ }
473
+ });
474
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
475
+ let bestPosition = -1;
476
+ const candidates = await open2();
477
+ try {
478
+ await scanNodeZip(candidates, async (entry) => {
479
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
480
+ const probe = await open2();
481
+ let position = 0;
482
+ try {
483
+ await scanNodeZip(probe, (candidate) => {
484
+ if (candidate.fileName === entry.fileName) return false;
485
+ position++;
486
+ });
487
+ } finally {
488
+ probe.close();
489
+ }
490
+ if (position >= bestPosition) {
491
+ bestPosition = position;
492
+ alias = entry;
493
+ }
494
+ });
495
+ return await operation(candidates, alias);
496
+ } finally {
497
+ candidates.close();
498
+ }
499
+ }
500
+ return await operation(
501
+ zip,
502
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
503
+ );
504
+ });
430
505
  return {
431
506
  close: () => {
432
- if (!closed) zipFile.close();
433
507
  closed = true;
434
- return Promise.resolve();
508
+ closing ??= pending.then(() => handle.close());
509
+ return closing;
435
510
  },
436
- copyEntry: async (name, target) => {
437
- if (closed) throw new Error("Cannot read a closed ZIP archive");
438
- const entry = entries.get(name);
511
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
439
512
  if (entry === void 0) return false;
440
- await copyNodeZipEntry(zipFile, entry, target);
513
+ await copyNodeZipEntry(zip, entry, target);
441
514
  return true;
442
- },
443
- getEntrySize: (name) => {
444
- if (closed) throw new Error("Cannot read a closed ZIP archive");
445
- return Promise.resolve(entries.get(name)?.uncompressedSize);
446
- },
447
- listEntries: () => Promise.resolve([...entries.keys()]),
448
- readEntryRange: async (name, offset, length) => {
449
- if (closed) throw new Error("Cannot read a closed ZIP archive");
450
- const entry = entries.get(name);
515
+ }),
516
+ getEntrySize: (name) => withEntry(
517
+ name,
518
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
519
+ ),
520
+ listEntries: () => run(async (zip) => {
521
+ const names = /* @__PURE__ */ new Set();
522
+ await scanNodeZip(zip, (entry) => {
523
+ names.add(entry.fileName);
524
+ });
525
+ return [...names];
526
+ }),
527
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
451
528
  if (entry === void 0) return void 0;
452
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
453
- },
454
- readEntry: async (name) => {
455
- if (closed) throw new Error("Cannot read a closed ZIP archive");
456
- const entry = entries.get(name);
529
+ return await readNodeZipEntryRange(zip, entry, offset, length);
530
+ }),
531
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
457
532
  if (entry === void 0) return void 0;
458
- return await readNodeZipEntry(zipFile, entry);
459
- }
533
+ return await readNodeZipEntry(zip, entry);
534
+ })
460
535
  };
461
536
  }
537
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
538
+ constructor(handle) {
539
+ super();
540
+ this.handle = handle;
541
+ }
542
+ handle;
543
+ read(buffer, offset, length, position, callback) {
544
+ const read2 = (written) => {
545
+ if (written === length) {
546
+ callback(null);
547
+ return;
548
+ }
549
+ fs.read(
550
+ this.handle.fd,
551
+ buffer,
552
+ offset + written,
553
+ length - written,
554
+ position + written,
555
+ (error, bytesRead) => {
556
+ if (error) callback(error);
557
+ else if (bytesRead === 0)
558
+ callback(new Error("Unexpected end of ZIP file"));
559
+ else read2(written + bytesRead);
560
+ }
561
+ );
562
+ };
563
+ read2(0);
564
+ }
565
+ _readStreamForRange(start, end) {
566
+ const handle = this.handle;
567
+ return import_node_stream.Readable.from(
568
+ (async function* () {
569
+ for (let position = start; position < end; ) {
570
+ const buffer = Buffer.allocUnsafe(
571
+ Math.min(64 * 1024, end - position)
572
+ );
573
+ const { bytesRead } = await handle.read(
574
+ buffer,
575
+ 0,
576
+ buffer.length,
577
+ position
578
+ );
579
+ if (bytesRead === 0) return;
580
+ position += bytesRead;
581
+ yield buffer.subarray(0, bytesRead);
582
+ }
583
+ })()
584
+ );
585
+ }
586
+ };
587
+ function normalizeNodeZipName(name) {
588
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
589
+ return normalized.split("/").includes("..") ? void 0 : normalized;
590
+ }
591
+ async function scanNodeZip(zip, visit) {
592
+ await new Promise((resolve4, reject) => {
593
+ const cleanup = () => {
594
+ zip.off("entry", onEntry);
595
+ zip.off("end", onEnd);
596
+ zip.off("error", onError);
597
+ };
598
+ const onError = (error) => {
599
+ cleanup();
600
+ reject(error);
601
+ };
602
+ const onEnd = () => {
603
+ cleanup();
604
+ resolve4();
605
+ };
606
+ const onEntry = (entry) => {
607
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
608
+ if (keepReading === false) onEnd();
609
+ else zip.readEntry();
610
+ }).catch(
611
+ (error) => onError(
612
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
613
+ )
614
+ );
615
+ };
616
+ zip.on("entry", onEntry);
617
+ zip.once("end", onEnd);
618
+ zip.once("error", onError);
619
+ zip.readEntry();
620
+ });
621
+ }
462
622
  async function copyNodeZipEntry(zipFile, entry, target) {
463
623
  const writer = await target.openWriter();
464
624
  try {
@@ -1564,12 +1724,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1564
1724
  const path = resolveWikiGraphRuntimePath(inputPath);
1565
1725
  const candidate = await canonicalPath(path);
1566
1726
  const candidateIdentity = await readFileIdentity(path);
1567
- const libraries = await (0, import_wiki_graph_core5.listWikiGraphLibraries)();
1568
- const matches = await findMatchingMembers(
1569
- libraries,
1570
- candidate,
1571
- candidateIdentity
1572
- );
1727
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1573
1728
  if (matches.length > 0) {
1574
1729
  const match = requireUnambiguousManagedMatch(path, matches);
1575
1730
  throw new WikiGraphArchiveOwnershipError(
@@ -1578,15 +1733,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1578
1733
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1579
1734
  );
1580
1735
  }
1581
- for (const library of libraries) {
1582
- const rootPath = getNodeResourcePath(library.folder);
1583
- const root = await canonicalPath(rootPath);
1584
- if (!isWithin(root, candidate)) continue;
1585
- throw new WikiGraphArchiveOwnershipError(
1586
- `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core5.formatWikiGraphLibraryUri)(
1587
- library.isDefault ? void 0 : library.publicId
1588
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1736
+ const ancestors = /* @__PURE__ */ new Set();
1737
+ for (const start of [candidate, (0, import_path3.resolve)(path)]) {
1738
+ for (let parent = (0, import_path3.dirname)(start); ; parent = (0, import_path3.dirname)(parent)) {
1739
+ ancestors.add(parent);
1740
+ if ((0, import_path3.dirname)(parent) === parent) break;
1741
+ }
1742
+ }
1743
+ for (const parent of ancestors) {
1744
+ const identity = await readFileIdentity(parent);
1745
+ const library = await (0, import_wiki_graph_core5.findWikiGraphLibraryByPhysicalIdentity)(
1746
+ identity,
1747
+ new NodeDirectory(parent).identity
1589
1748
  );
1749
+ if (library !== void 0) {
1750
+ throw new WikiGraphArchiveOwnershipError(
1751
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core5.formatWikiGraphLibraryUri)(
1752
+ library.isDefault ? void 0 : library.publicId
1753
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1754
+ );
1755
+ }
1590
1756
  }
1591
1757
  return path;
1592
1758
  }
@@ -1594,11 +1760,7 @@ async function resolveWikiGraphArchiveTarget(file) {
1594
1760
  const path = getNodeResourcePath(file);
1595
1761
  const candidate = await canonicalPath(path);
1596
1762
  const candidateIdentity = await readFileIdentity(path);
1597
- const matches = await findMatchingMembers(
1598
- await (0, import_wiki_graph_core5.listWikiGraphLibraries)(),
1599
- candidate,
1600
- candidateIdentity
1601
- );
1763
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1602
1764
  if (matches.length > 0) {
1603
1765
  const match = requireUnambiguousManagedMatch(path, matches);
1604
1766
  return { kind: "library", uri: match.archive.uri };
@@ -1659,48 +1821,35 @@ async function resolveLibraryLocation(target) {
1659
1821
  async function canonicalPath(path) {
1660
1822
  try {
1661
1823
  return await (0, import_promises3.realpath)(path);
1662
- } catch {
1824
+ } catch (error) {
1825
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1826
+ throw error;
1663
1827
  const absolute = (0, import_path3.resolve)(path);
1664
1828
  const parent = (0, import_path3.dirname)(absolute);
1665
1829
  return parent === absolute ? absolute : (0, import_path3.join)(await canonicalPath(parent), (0, import_path3.basename)(absolute));
1666
1830
  }
1667
1831
  }
1668
- function isWithin(root, candidate) {
1669
- const child = (0, import_path3.relative)(root, candidate);
1670
- return child === "" || !child.startsWith("..") && !(0, import_path3.isAbsolute)(child);
1671
- }
1672
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1832
+ async function findMatchingMembers(candidate, candidateIdentity) {
1833
+ if (candidateIdentity === void 0) return [];
1834
+ const archives = await (0, import_wiki_graph_core5.findWikiGraphLibraryArchivePhysicalAliases)(candidateIdentity);
1673
1835
  const members = [];
1674
- for (const library of libraries) {
1675
- const archives = await (0, import_wiki_graph_core5.listWikiGraphLibraryArchives)({
1676
- isDefault: library.isDefault,
1677
- kind: "scope",
1678
- ...library.isDefault ? {} : { publicId: library.publicId }
1679
- });
1680
- for (const archive of archives) {
1681
- const physical = archive.file ?? archive.directory;
1682
- if (physical === void 0) continue;
1836
+ for (const archive of archives) {
1837
+ const parsed = (0, import_wiki_graph_core5.parseWikiGraphLibraryUri)(archive.libraryUri);
1838
+ const library = await (0, import_wiki_graph_core5.resolveWikiGraphLibrary)(parsed);
1839
+ const physical = archive.file ?? archive.directory;
1840
+ if (library !== void 0 && physical !== void 0)
1683
1841
  members.push({
1684
1842
  archive,
1685
1843
  library,
1686
1844
  memberPath: getNodeResourcePath(physical)
1687
1845
  });
1688
- }
1689
1846
  }
1690
1847
  const exact = [];
1691
1848
  for (const member of members) {
1692
- if (await canonicalPath(member.memberPath) === candidate) {
1849
+ if (await canonicalPath(member.memberPath) === candidate)
1693
1850
  exact.push(member);
1694
- }
1695
1851
  }
1696
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1697
- const identityMatches = [];
1698
- for (const member of members) {
1699
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1700
- identityMatches.push(member);
1701
- }
1702
- }
1703
- return identityMatches;
1852
+ return exact.length > 0 ? exact : members;
1704
1853
  }
1705
1854
  function requireUnambiguousManagedMatch(path, matches) {
1706
1855
  if (matches.length === 1) return matches[0];
@@ -1712,8 +1861,10 @@ async function readFileIdentity(path) {
1712
1861
  try {
1713
1862
  const value = await (0, import_promises3.stat)(path);
1714
1863
  return `${value.dev}:${value.ino}`;
1715
- } catch {
1716
- return void 0;
1864
+ } catch (error) {
1865
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1866
+ return void 0;
1867
+ throw error;
1717
1868
  }
1718
1869
  }
1719
1870
 
@@ -1722,6 +1873,12 @@ var import_wiki_graph_core6 = require("wiki-graph-core");
1722
1873
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1723
1874
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1724
1875
  const write = async (archiveFile = location.archiveFile) => {
1876
+ if (cacheOnly) {
1877
+ return await new import_wiki_graph_core6.WikiGraphArchiveFile(archiveFile).readDocument(
1878
+ operation,
1879
+ { searchIndexWritebackPolicy: "cache" }
1880
+ );
1881
+ }
1725
1882
  const writeOptions = {
1726
1883
  ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1727
1884
  ...options.invalidateContinuationCursors === void 0 ? {} : {