wiki-graph-sdk 0.9.0 → 0.9.2

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 +316 -142
  2. package/dist/archives.cjs.map +1 -1
  3. package/dist/archives.js +326 -148
  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 +354 -174
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +369 -184
  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
@@ -96,6 +96,15 @@ var NodeFile = class {
96
96
  this.name = name;
97
97
  }
98
98
  path;
99
+ async getPhysicalIdentity() {
100
+ const value = await fsPromises.stat(this.path).catch((error) => {
101
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
102
+ return void 0;
103
+ throw error;
104
+ });
105
+ if (value === void 0) return void 0;
106
+ return `${value.dev}:${value.ino}`;
107
+ }
99
108
  kind = "file";
100
109
  [NODE_RESOURCE_KIND] = "file";
101
110
  identity;
@@ -220,6 +229,15 @@ var NodeDirectory = class _NodeDirectory {
220
229
  this.name = pathModule.basename(path);
221
230
  }
222
231
  path;
232
+ async getPhysicalIdentity() {
233
+ const value = await fsPromises.stat(this.path).catch((error) => {
234
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
235
+ return void 0;
236
+ throw error;
237
+ });
238
+ if (value === void 0) return void 0;
239
+ return `${value.dev}:${value.ino}`;
240
+ }
223
241
  kind = "directory";
224
242
  [NODE_RESOURCE_KIND] = "directory";
225
243
  identity;
@@ -229,8 +247,10 @@ var NodeDirectory = class _NodeDirectory {
229
247
  const file = new NodeFile(pathModule.join(this.path, name));
230
248
  try {
231
249
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
232
- } catch {
233
- return void 0;
250
+ } catch (error) {
251
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
252
+ return void 0;
253
+ throw error;
234
254
  }
235
255
  }
236
256
  async getLastModified() {
@@ -248,8 +268,10 @@ var NodeDirectory = class _NodeDirectory {
248
268
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
249
269
  try {
250
270
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
251
- } catch {
252
- return void 0;
271
+ } catch (error) {
272
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
273
+ return void 0;
274
+ throw error;
253
275
  }
254
276
  }
255
277
  async list() {
@@ -373,14 +395,23 @@ async function openNativeNodeDatabase(file, flags) {
373
395
  });
374
396
  });
375
397
  }
376
- async function openNodeZip(file) {
398
+ async function openNodeZip(file, options = {}) {
377
399
  const path = getBrandedNodeResourcePath(file, "file");
378
400
  if (path === void 0) {
379
401
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
380
402
  }
381
- const zipFile = await new Promise((resolve4, reject) => {
382
- yauzl.open(
383
- path,
403
+ const handle = await fsPromises.open(path, "r");
404
+ let size;
405
+ try {
406
+ size = (await handle.stat()).size;
407
+ } catch (error) {
408
+ await handle.close();
409
+ throw error;
410
+ }
411
+ const open2 = () => new Promise((resolve4, reject) => {
412
+ yauzl.fromRandomAccessReader(
413
+ new NodeZipSource(handle),
414
+ size,
384
415
  { autoClose: false, lazyEntries: true },
385
416
  (error, opened) => {
386
417
  if (error || opened === void 0)
@@ -389,63 +420,192 @@ async function openNodeZip(file) {
389
420
  }
390
421
  );
391
422
  });
392
- const entries = await new Promise(
393
- (resolve4, reject) => {
394
- const discovered = /* @__PURE__ */ new Map();
395
- const rejectAndClose = (error) => {
396
- zipFile.close();
397
- reject(
398
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
399
- );
400
- };
401
- const resolveEntries = () => {
402
- zipFile.off("error", rejectAndClose);
403
- resolve4(discovered);
404
- };
405
- zipFile.on("entry", (entry) => {
406
- if (!entry.fileName.endsWith("/")) {
407
- discovered.set(entry.fileName, entry);
408
- }
409
- zipFile.readEntry();
410
- });
411
- zipFile.once("error", rejectAndClose);
412
- zipFile.once("end", resolveEntries);
413
- zipFile.readEntry();
414
- }
415
- );
423
+ try {
424
+ (await open2()).close();
425
+ } catch (error) {
426
+ await handle.close();
427
+ throw error;
428
+ }
416
429
  let closed = false;
430
+ let pending = Promise.resolve();
431
+ let closing;
432
+ const run = (operation) => {
433
+ if (closed)
434
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
435
+ const result = pending.then(async () => {
436
+ const zip = await open2();
437
+ try {
438
+ return await operation(zip);
439
+ } finally {
440
+ zip.close();
441
+ }
442
+ });
443
+ pending = result.then(
444
+ () => void 0,
445
+ () => void 0
446
+ );
447
+ return result;
448
+ };
449
+ const withEntry = (name, operation) => run(async (zip) => {
450
+ let exact;
451
+ let alias;
452
+ let aliasCollision = false;
453
+ await scanNodeZip(zip, (entry) => {
454
+ if (entry.fileName === name) exact = entry;
455
+ if (normalizeNodeZipName(entry.fileName) === name) {
456
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
457
+ aliasCollision = true;
458
+ alias = entry;
459
+ }
460
+ });
461
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
462
+ let bestPosition = -1;
463
+ const candidates = await open2();
464
+ try {
465
+ await scanNodeZip(candidates, async (entry) => {
466
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
467
+ const probe = await open2();
468
+ let position = 0;
469
+ try {
470
+ await scanNodeZip(probe, (candidate) => {
471
+ if (candidate.fileName === entry.fileName) return false;
472
+ position++;
473
+ });
474
+ } finally {
475
+ probe.close();
476
+ }
477
+ if (position >= bestPosition) {
478
+ bestPosition = position;
479
+ alias = entry;
480
+ }
481
+ });
482
+ return await operation(candidates, alias);
483
+ } finally {
484
+ candidates.close();
485
+ }
486
+ }
487
+ return await operation(
488
+ zip,
489
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
490
+ );
491
+ });
417
492
  return {
418
493
  close: () => {
419
- if (!closed) zipFile.close();
420
494
  closed = true;
421
- return Promise.resolve();
495
+ closing ??= pending.then(() => handle.close());
496
+ return closing;
422
497
  },
423
- copyEntry: async (name, target) => {
424
- if (closed) throw new Error("Cannot read a closed ZIP archive");
425
- const entry = entries.get(name);
498
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
426
499
  if (entry === void 0) return false;
427
- await copyNodeZipEntry(zipFile, entry, target);
500
+ await copyNodeZipEntry(zip, entry, target);
428
501
  return true;
429
- },
430
- getEntrySize: (name) => {
431
- if (closed) throw new Error("Cannot read a closed ZIP archive");
432
- return Promise.resolve(entries.get(name)?.uncompressedSize);
433
- },
434
- listEntries: () => Promise.resolve([...entries.keys()]),
435
- readEntryRange: async (name, offset, length) => {
436
- if (closed) throw new Error("Cannot read a closed ZIP archive");
437
- const entry = entries.get(name);
502
+ }),
503
+ getEntrySize: (name) => withEntry(
504
+ name,
505
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
506
+ ),
507
+ listEntries: () => run(async (zip) => {
508
+ const names = /* @__PURE__ */ new Set();
509
+ await scanNodeZip(zip, (entry) => {
510
+ names.add(entry.fileName);
511
+ });
512
+ return [...names];
513
+ }),
514
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
438
515
  if (entry === void 0) return void 0;
439
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
440
- },
441
- readEntry: async (name) => {
442
- if (closed) throw new Error("Cannot read a closed ZIP archive");
443
- const entry = entries.get(name);
516
+ return await readNodeZipEntryRange(zip, entry, offset, length);
517
+ }),
518
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
444
519
  if (entry === void 0) return void 0;
445
- return await readNodeZipEntry(zipFile, entry);
446
- }
520
+ return await readNodeZipEntry(zip, entry);
521
+ })
447
522
  };
448
523
  }
524
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
525
+ constructor(handle) {
526
+ super();
527
+ this.handle = handle;
528
+ }
529
+ handle;
530
+ read(buffer, offset, length, position, callback) {
531
+ const read2 = (written) => {
532
+ if (written === length) {
533
+ callback(null);
534
+ return;
535
+ }
536
+ fs.read(
537
+ this.handle.fd,
538
+ buffer,
539
+ offset + written,
540
+ length - written,
541
+ position + written,
542
+ (error, bytesRead) => {
543
+ if (error) callback(error);
544
+ else if (bytesRead === 0)
545
+ callback(new Error("Unexpected end of ZIP file"));
546
+ else read2(written + bytesRead);
547
+ }
548
+ );
549
+ };
550
+ read2(0);
551
+ }
552
+ _readStreamForRange(start, end) {
553
+ const handle = this.handle;
554
+ return Readable.from(
555
+ (async function* () {
556
+ for (let position = start; position < end; ) {
557
+ const buffer = Buffer.allocUnsafe(
558
+ Math.min(64 * 1024, end - position)
559
+ );
560
+ const { bytesRead } = await handle.read(
561
+ buffer,
562
+ 0,
563
+ buffer.length,
564
+ position
565
+ );
566
+ if (bytesRead === 0) return;
567
+ position += bytesRead;
568
+ yield buffer.subarray(0, bytesRead);
569
+ }
570
+ })()
571
+ );
572
+ }
573
+ };
574
+ function normalizeNodeZipName(name) {
575
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
576
+ return normalized.split("/").includes("..") ? void 0 : normalized;
577
+ }
578
+ async function scanNodeZip(zip, visit) {
579
+ await new Promise((resolve4, reject) => {
580
+ const cleanup = () => {
581
+ zip.off("entry", onEntry);
582
+ zip.off("end", onEnd);
583
+ zip.off("error", onError);
584
+ };
585
+ const onError = (error) => {
586
+ cleanup();
587
+ reject(error);
588
+ };
589
+ const onEnd = () => {
590
+ cleanup();
591
+ resolve4();
592
+ };
593
+ const onEntry = (entry) => {
594
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
595
+ if (keepReading === false) onEnd();
596
+ else zip.readEntry();
597
+ }).catch(
598
+ (error) => onError(
599
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
600
+ )
601
+ );
602
+ };
603
+ zip.on("entry", onEntry);
604
+ zip.once("end", onEnd);
605
+ zip.once("error", onError);
606
+ zip.readEntry();
607
+ });
608
+ }
449
609
  async function copyNodeZipEntry(zipFile, entry, target) {
450
610
  const writer = await target.openWriter();
451
611
  try {
@@ -1533,16 +1693,18 @@ async function withWikiGraphSDKHost(options, operation) {
1533
1693
 
1534
1694
  // src/archive/target.ts
1535
1695
  import { realpath, stat as stat2 } from "fs/promises";
1536
- import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve3 } from "path";
1696
+ import { basename as basename2, dirname as dirname2, join as join3, resolve as resolve3 } from "path";
1537
1697
  import {
1538
1698
  formatLocatedWikiGraphUri,
1539
1699
  formatWikiGraphLibraryUri,
1540
- listWikiGraphLibraries,
1541
- listWikiGraphLibraryArchives,
1700
+ findWikiGraphLibraryByPhysicalIdentity,
1701
+ findWikiGraphLibraryArchivePhysicalAliases,
1542
1702
  parseLocatedWikiGraphUri,
1543
1703
  parseWikiGraphLibraryUri,
1544
1704
  resolveWikiGraphLibrary,
1545
- resolveWikiGraphLibraryArchiveFile
1705
+ resolveWikiGraphLibraryArchiveFile,
1706
+ isWikiGraphSchemaVersionError,
1707
+ WikiGraphSchemaVersionError
1546
1708
  } from "wiki-graph-core";
1547
1709
 
1548
1710
  // src/runtime-path.ts
@@ -1575,12 +1737,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1575
1737
  const path = resolveWikiGraphRuntimePath(inputPath);
1576
1738
  const candidate = await canonicalPath(path);
1577
1739
  const candidateIdentity = await readFileIdentity(path);
1578
- const libraries = await listWikiGraphLibraries();
1579
- const matches = await findMatchingMembers(
1580
- libraries,
1581
- candidate,
1582
- candidateIdentity
1583
- );
1740
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1584
1741
  if (matches.length > 0) {
1585
1742
  const match = requireUnambiguousManagedMatch(path, matches);
1586
1743
  throw new WikiGraphArchiveOwnershipError(
@@ -1589,15 +1746,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1589
1746
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1590
1747
  );
1591
1748
  }
1592
- for (const library of libraries) {
1593
- const rootPath = getNodeResourcePath(library.folder);
1594
- const root = await canonicalPath(rootPath);
1595
- if (!isWithin(root, candidate)) continue;
1596
- throw new WikiGraphArchiveOwnershipError(
1597
- `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1598
- library.isDefault ? void 0 : library.publicId
1599
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1749
+ const ancestors = /* @__PURE__ */ new Set();
1750
+ for (const start of [candidate, resolve3(path)]) {
1751
+ for (let parent = dirname2(start); ; parent = dirname2(parent)) {
1752
+ ancestors.add(parent);
1753
+ if (dirname2(parent) === parent) break;
1754
+ }
1755
+ }
1756
+ for (const parent of ancestors) {
1757
+ const identity = await readFileIdentity(parent);
1758
+ const library = await findWikiGraphLibraryByPhysicalIdentity(
1759
+ identity,
1760
+ new NodeDirectory(parent).identity
1600
1761
  );
1762
+ if (library !== void 0) {
1763
+ throw new WikiGraphArchiveOwnershipError(
1764
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1765
+ library.isDefault ? void 0 : library.publicId
1766
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1767
+ );
1768
+ }
1601
1769
  }
1602
1770
  return path;
1603
1771
  }
@@ -1605,11 +1773,7 @@ async function resolveWikiGraphArchiveTarget(file) {
1605
1773
  const path = getNodeResourcePath(file);
1606
1774
  const candidate = await canonicalPath(path);
1607
1775
  const candidateIdentity = await readFileIdentity(path);
1608
- const matches = await findMatchingMembers(
1609
- await listWikiGraphLibraries(),
1610
- candidate,
1611
- candidateIdentity
1612
- );
1776
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1613
1777
  if (matches.length > 0) {
1614
1778
  const match = requireUnambiguousManagedMatch(path, matches);
1615
1779
  return { kind: "library", uri: match.archive.uri };
@@ -1670,48 +1834,35 @@ async function resolveLibraryLocation(target) {
1670
1834
  async function canonicalPath(path) {
1671
1835
  try {
1672
1836
  return await realpath(path);
1673
- } catch {
1837
+ } catch (error) {
1838
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1839
+ throw error;
1674
1840
  const absolute = resolve3(path);
1675
1841
  const parent = dirname2(absolute);
1676
1842
  return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
1677
1843
  }
1678
1844
  }
1679
- function isWithin(root, candidate) {
1680
- const child = relative(root, candidate);
1681
- return child === "" || !child.startsWith("..") && !isAbsolute3(child);
1682
- }
1683
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1845
+ async function findMatchingMembers(candidate, candidateIdentity) {
1846
+ if (candidateIdentity === void 0) return [];
1847
+ const archives = await findWikiGraphLibraryArchivePhysicalAliases(candidateIdentity);
1684
1848
  const members = [];
1685
- for (const library of libraries) {
1686
- const archives = await listWikiGraphLibraryArchives({
1687
- isDefault: library.isDefault,
1688
- kind: "scope",
1689
- ...library.isDefault ? {} : { publicId: library.publicId }
1690
- });
1691
- for (const archive of archives) {
1692
- const physical = archive.file ?? archive.directory;
1693
- if (physical === void 0) continue;
1849
+ for (const archive of archives) {
1850
+ const parsed = parseWikiGraphLibraryUri(archive.libraryUri);
1851
+ const library = await resolveWikiGraphLibrary(parsed);
1852
+ const physical = archive.file ?? archive.directory;
1853
+ if (library !== void 0 && physical !== void 0)
1694
1854
  members.push({
1695
1855
  archive,
1696
1856
  library,
1697
1857
  memberPath: getNodeResourcePath(physical)
1698
1858
  });
1699
- }
1700
1859
  }
1701
1860
  const exact = [];
1702
1861
  for (const member of members) {
1703
- if (await canonicalPath(member.memberPath) === candidate) {
1862
+ if (await canonicalPath(member.memberPath) === candidate)
1704
1863
  exact.push(member);
1705
- }
1706
1864
  }
1707
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1708
- const identityMatches = [];
1709
- for (const member of members) {
1710
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1711
- identityMatches.push(member);
1712
- }
1713
- }
1714
- return identityMatches;
1865
+ return exact.length > 0 ? exact : members;
1715
1866
  }
1716
1867
  function requireUnambiguousManagedMatch(path, matches) {
1717
1868
  if (matches.length === 1) return matches[0];
@@ -1723,8 +1874,10 @@ async function readFileIdentity(path) {
1723
1874
  try {
1724
1875
  const value = await stat2(path);
1725
1876
  return `${value.dev}:${value.ino}`;
1726
- } catch {
1727
- return void 0;
1877
+ } catch (error) {
1878
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1879
+ return void 0;
1880
+ throw error;
1728
1881
  }
1729
1882
  }
1730
1883
 
@@ -1737,6 +1890,12 @@ import {
1737
1890
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
1738
1891
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1739
1892
  const write = async (archiveFile = location.archiveFile) => {
1893
+ if (cacheOnly) {
1894
+ return await new WikiGraphArchiveFile(archiveFile).readDocument(
1895
+ operation,
1896
+ { searchIndexWritebackPolicy: "cache" }
1897
+ );
1898
+ }
1740
1899
  const writeOptions = {
1741
1900
  ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1742
1901
  ...options.invalidateContinuationCursors === void 0 ? {} : {