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
@@ -62,6 +62,15 @@ var NodeFile = class {
62
62
  this.name = name;
63
63
  }
64
64
  path;
65
+ async getPhysicalIdentity() {
66
+ const value = await fsPromises.stat(this.path).catch((error) => {
67
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
68
+ return void 0;
69
+ throw error;
70
+ });
71
+ if (value === void 0) return void 0;
72
+ return `${value.dev}:${value.ino}`;
73
+ }
65
74
  kind = "file";
66
75
  [NODE_RESOURCE_KIND] = "file";
67
76
  identity;
@@ -186,6 +195,15 @@ var NodeDirectory = class _NodeDirectory {
186
195
  this.name = pathModule.basename(path);
187
196
  }
188
197
  path;
198
+ async getPhysicalIdentity() {
199
+ const value = await fsPromises.stat(this.path).catch((error) => {
200
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
201
+ return void 0;
202
+ throw error;
203
+ });
204
+ if (value === void 0) return void 0;
205
+ return `${value.dev}:${value.ino}`;
206
+ }
189
207
  kind = "directory";
190
208
  [NODE_RESOURCE_KIND] = "directory";
191
209
  identity;
@@ -195,8 +213,10 @@ var NodeDirectory = class _NodeDirectory {
195
213
  const file = new NodeFile(pathModule.join(this.path, name));
196
214
  try {
197
215
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
198
- } catch {
199
- return void 0;
216
+ } catch (error) {
217
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
218
+ return void 0;
219
+ throw error;
200
220
  }
201
221
  }
202
222
  async getLastModified() {
@@ -214,8 +234,10 @@ var NodeDirectory = class _NodeDirectory {
214
234
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
215
235
  try {
216
236
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
217
- } catch {
218
- return void 0;
237
+ } catch (error) {
238
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
239
+ return void 0;
240
+ throw error;
219
241
  }
220
242
  }
221
243
  async list() {
@@ -339,14 +361,23 @@ async function openNativeNodeDatabase(file, flags) {
339
361
  });
340
362
  });
341
363
  }
342
- async function openNodeZip(file) {
364
+ async function openNodeZip(file, options = {}) {
343
365
  const path = getBrandedNodeResourcePath(file, "file");
344
366
  if (path === void 0) {
345
367
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
346
368
  }
347
- const zipFile = await new Promise((resolve4, reject) => {
348
- yauzl.open(
349
- path,
369
+ const handle = await fsPromises.open(path, "r");
370
+ let size;
371
+ try {
372
+ size = (await handle.stat()).size;
373
+ } catch (error) {
374
+ await handle.close();
375
+ throw error;
376
+ }
377
+ const open2 = () => new Promise((resolve4, reject) => {
378
+ yauzl.fromRandomAccessReader(
379
+ new NodeZipSource(handle),
380
+ size,
350
381
  { autoClose: false, lazyEntries: true },
351
382
  (error, opened) => {
352
383
  if (error || opened === void 0)
@@ -355,63 +386,192 @@ async function openNodeZip(file) {
355
386
  }
356
387
  );
357
388
  });
358
- const entries = await new Promise(
359
- (resolve4, reject) => {
360
- const discovered = /* @__PURE__ */ new Map();
361
- const rejectAndClose = (error) => {
362
- zipFile.close();
363
- reject(
364
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
365
- );
366
- };
367
- const resolveEntries = () => {
368
- zipFile.off("error", rejectAndClose);
369
- resolve4(discovered);
370
- };
371
- zipFile.on("entry", (entry) => {
372
- if (!entry.fileName.endsWith("/")) {
373
- discovered.set(entry.fileName, entry);
374
- }
375
- zipFile.readEntry();
376
- });
377
- zipFile.once("error", rejectAndClose);
378
- zipFile.once("end", resolveEntries);
379
- zipFile.readEntry();
380
- }
381
- );
389
+ try {
390
+ (await open2()).close();
391
+ } catch (error) {
392
+ await handle.close();
393
+ throw error;
394
+ }
382
395
  let closed = false;
396
+ let pending = Promise.resolve();
397
+ let closing;
398
+ const run = (operation) => {
399
+ if (closed)
400
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
401
+ const result = pending.then(async () => {
402
+ const zip = await open2();
403
+ try {
404
+ return await operation(zip);
405
+ } finally {
406
+ zip.close();
407
+ }
408
+ });
409
+ pending = result.then(
410
+ () => void 0,
411
+ () => void 0
412
+ );
413
+ return result;
414
+ };
415
+ const withEntry = (name, operation) => run(async (zip) => {
416
+ let exact;
417
+ let alias;
418
+ let aliasCollision = false;
419
+ await scanNodeZip(zip, (entry) => {
420
+ if (entry.fileName === name) exact = entry;
421
+ if (normalizeNodeZipName(entry.fileName) === name) {
422
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
423
+ aliasCollision = true;
424
+ alias = entry;
425
+ }
426
+ });
427
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
428
+ let bestPosition = -1;
429
+ const candidates = await open2();
430
+ try {
431
+ await scanNodeZip(candidates, async (entry) => {
432
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
433
+ const probe = await open2();
434
+ let position = 0;
435
+ try {
436
+ await scanNodeZip(probe, (candidate) => {
437
+ if (candidate.fileName === entry.fileName) return false;
438
+ position++;
439
+ });
440
+ } finally {
441
+ probe.close();
442
+ }
443
+ if (position >= bestPosition) {
444
+ bestPosition = position;
445
+ alias = entry;
446
+ }
447
+ });
448
+ return await operation(candidates, alias);
449
+ } finally {
450
+ candidates.close();
451
+ }
452
+ }
453
+ return await operation(
454
+ zip,
455
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
456
+ );
457
+ });
383
458
  return {
384
459
  close: () => {
385
- if (!closed) zipFile.close();
386
460
  closed = true;
387
- return Promise.resolve();
461
+ closing ??= pending.then(() => handle.close());
462
+ return closing;
388
463
  },
389
- copyEntry: async (name, target) => {
390
- if (closed) throw new Error("Cannot read a closed ZIP archive");
391
- const entry = entries.get(name);
464
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
392
465
  if (entry === void 0) return false;
393
- await copyNodeZipEntry(zipFile, entry, target);
466
+ await copyNodeZipEntry(zip, entry, target);
394
467
  return true;
395
- },
396
- getEntrySize: (name) => {
397
- if (closed) throw new Error("Cannot read a closed ZIP archive");
398
- return Promise.resolve(entries.get(name)?.uncompressedSize);
399
- },
400
- listEntries: () => Promise.resolve([...entries.keys()]),
401
- readEntryRange: async (name, offset, length) => {
402
- if (closed) throw new Error("Cannot read a closed ZIP archive");
403
- const entry = entries.get(name);
468
+ }),
469
+ getEntrySize: (name) => withEntry(
470
+ name,
471
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
472
+ ),
473
+ listEntries: () => run(async (zip) => {
474
+ const names = /* @__PURE__ */ new Set();
475
+ await scanNodeZip(zip, (entry) => {
476
+ names.add(entry.fileName);
477
+ });
478
+ return [...names];
479
+ }),
480
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
404
481
  if (entry === void 0) return void 0;
405
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
406
- },
407
- readEntry: async (name) => {
408
- if (closed) throw new Error("Cannot read a closed ZIP archive");
409
- const entry = entries.get(name);
482
+ return await readNodeZipEntryRange(zip, entry, offset, length);
483
+ }),
484
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
410
485
  if (entry === void 0) return void 0;
411
- return await readNodeZipEntry(zipFile, entry);
412
- }
486
+ return await readNodeZipEntry(zip, entry);
487
+ })
413
488
  };
414
489
  }
490
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
491
+ constructor(handle) {
492
+ super();
493
+ this.handle = handle;
494
+ }
495
+ handle;
496
+ read(buffer, offset, length, position, callback) {
497
+ const read2 = (written) => {
498
+ if (written === length) {
499
+ callback(null);
500
+ return;
501
+ }
502
+ fs.read(
503
+ this.handle.fd,
504
+ buffer,
505
+ offset + written,
506
+ length - written,
507
+ position + written,
508
+ (error, bytesRead) => {
509
+ if (error) callback(error);
510
+ else if (bytesRead === 0)
511
+ callback(new Error("Unexpected end of ZIP file"));
512
+ else read2(written + bytesRead);
513
+ }
514
+ );
515
+ };
516
+ read2(0);
517
+ }
518
+ _readStreamForRange(start, end) {
519
+ const handle = this.handle;
520
+ return import_node_stream.Readable.from(
521
+ (async function* () {
522
+ for (let position = start; position < end; ) {
523
+ const buffer = Buffer.allocUnsafe(
524
+ Math.min(64 * 1024, end - position)
525
+ );
526
+ const { bytesRead } = await handle.read(
527
+ buffer,
528
+ 0,
529
+ buffer.length,
530
+ position
531
+ );
532
+ if (bytesRead === 0) return;
533
+ position += bytesRead;
534
+ yield buffer.subarray(0, bytesRead);
535
+ }
536
+ })()
537
+ );
538
+ }
539
+ };
540
+ function normalizeNodeZipName(name) {
541
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
542
+ return normalized.split("/").includes("..") ? void 0 : normalized;
543
+ }
544
+ async function scanNodeZip(zip, visit) {
545
+ await new Promise((resolve4, reject) => {
546
+ const cleanup = () => {
547
+ zip.off("entry", onEntry);
548
+ zip.off("end", onEnd);
549
+ zip.off("error", onError);
550
+ };
551
+ const onError = (error) => {
552
+ cleanup();
553
+ reject(error);
554
+ };
555
+ const onEnd = () => {
556
+ cleanup();
557
+ resolve4();
558
+ };
559
+ const onEntry = (entry) => {
560
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
561
+ if (keepReading === false) onEnd();
562
+ else zip.readEntry();
563
+ }).catch(
564
+ (error) => onError(
565
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
566
+ )
567
+ );
568
+ };
569
+ zip.on("entry", onEntry);
570
+ zip.once("end", onEnd);
571
+ zip.once("error", onError);
572
+ zip.readEntry();
573
+ });
574
+ }
415
575
  async function copyNodeZipEntry(zipFile, entry, target) {
416
576
  const writer = await target.openWriter();
417
577
  try {
@@ -754,6 +914,16 @@ function resolveWikiGraphRuntimePath(path) {
754
914
  var import_promises2 = require("fs/promises");
755
915
  var import_path2 = require("path");
756
916
  var import_wiki_graph_core = require("wiki-graph-core");
917
+ function rethrowArchiveSchemaError(error, target) {
918
+ if ((0, import_wiki_graph_core.isWikiGraphSchemaVersionError)(error) && error.target.kind === "archive") {
919
+ throw new import_wiki_graph_core.WikiGraphSchemaVersionError(
920
+ target.kind === "standalone" ? { kind: "archive", path: resolveWikiGraphRuntimePath(target.path) } : { kind: "library", uri: target.uri.split("/arc/")[0] },
921
+ error.actualVersion,
922
+ error.supportedVersion
923
+ );
924
+ }
925
+ throw error;
926
+ }
757
927
  var WikiGraphArchiveOwnershipError = class extends Error {
758
928
  code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
759
929
  constructor(message) {
@@ -765,12 +935,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
765
935
  const path = resolveWikiGraphRuntimePath(inputPath);
766
936
  const candidate = await canonicalPath(path);
767
937
  const candidateIdentity = await readFileIdentity(path);
768
- const libraries = await (0, import_wiki_graph_core.listWikiGraphLibraries)();
769
- const matches = await findMatchingMembers(
770
- libraries,
771
- candidate,
772
- candidateIdentity
773
- );
938
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
774
939
  if (matches.length > 0) {
775
940
  const match = requireUnambiguousManagedMatch(path, matches);
776
941
  throw new WikiGraphArchiveOwnershipError(
@@ -779,62 +944,61 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
779
944
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
780
945
  );
781
946
  }
782
- for (const library of libraries) {
783
- const rootPath = getNodeResourcePath(library.folder);
784
- const root = await canonicalPath(rootPath);
785
- if (!isWithin(root, candidate)) continue;
786
- throw new WikiGraphArchiveOwnershipError(
787
- `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
788
- library.isDefault ? void 0 : library.publicId
789
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
947
+ const ancestors = /* @__PURE__ */ new Set();
948
+ for (const start of [candidate, (0, import_path2.resolve)(path)]) {
949
+ for (let parent = (0, import_path2.dirname)(start); ; parent = (0, import_path2.dirname)(parent)) {
950
+ ancestors.add(parent);
951
+ if ((0, import_path2.dirname)(parent) === parent) break;
952
+ }
953
+ }
954
+ for (const parent of ancestors) {
955
+ const identity = await readFileIdentity(parent);
956
+ const library = await (0, import_wiki_graph_core.findWikiGraphLibraryByPhysicalIdentity)(
957
+ identity,
958
+ new NodeDirectory(parent).identity
790
959
  );
960
+ if (library !== void 0) {
961
+ throw new WikiGraphArchiveOwnershipError(
962
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
963
+ library.isDefault ? void 0 : library.publicId
964
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
965
+ );
966
+ }
791
967
  }
792
968
  return path;
793
969
  }
794
970
  async function canonicalPath(path) {
795
971
  try {
796
972
  return await (0, import_promises2.realpath)(path);
797
- } catch {
973
+ } catch (error) {
974
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
975
+ throw error;
798
976
  const absolute = (0, import_path2.resolve)(path);
799
977
  const parent = (0, import_path2.dirname)(absolute);
800
978
  return parent === absolute ? absolute : (0, import_path2.join)(await canonicalPath(parent), (0, import_path2.basename)(absolute));
801
979
  }
802
980
  }
803
- function isWithin(root, candidate) {
804
- const child = (0, import_path2.relative)(root, candidate);
805
- return child === "" || !child.startsWith("..") && !(0, import_path2.isAbsolute)(child);
806
- }
807
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
981
+ async function findMatchingMembers(candidate, candidateIdentity) {
982
+ if (candidateIdentity === void 0) return [];
983
+ const archives = await (0, import_wiki_graph_core.findWikiGraphLibraryArchivePhysicalAliases)(candidateIdentity);
808
984
  const members = [];
809
- for (const library of libraries) {
810
- const archives = await (0, import_wiki_graph_core.listWikiGraphLibraryArchives)({
811
- isDefault: library.isDefault,
812
- kind: "scope",
813
- ...library.isDefault ? {} : { publicId: library.publicId }
814
- });
815
- for (const archive of archives) {
816
- if (archive.file === void 0) continue;
985
+ for (const archive of archives) {
986
+ const parsed = (0, import_wiki_graph_core.parseWikiGraphLibraryUri)(archive.libraryUri);
987
+ const library = await (0, import_wiki_graph_core.resolveWikiGraphLibrary)(parsed);
988
+ const physical = archive.file ?? archive.directory;
989
+ if (library !== void 0 && physical !== void 0)
817
990
  members.push({
818
991
  archive,
819
992
  library,
820
- memberPath: getNodeResourcePath(archive.file)
993
+ memberPath: getNodeResourcePath(physical)
821
994
  });
822
- }
823
995
  }
824
996
  const exact = [];
825
997
  for (const member of members) {
826
- if (await canonicalPath(member.memberPath) === candidate) {
998
+ if (await canonicalPath(member.memberPath) === candidate)
827
999
  exact.push(member);
828
- }
829
1000
  }
830
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
831
- const identityMatches = [];
832
- for (const member of members) {
833
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
834
- identityMatches.push(member);
835
- }
836
- }
837
- return identityMatches;
1001
+ return exact.length > 0 ? exact : members;
838
1002
  }
839
1003
  function requireUnambiguousManagedMatch(path, matches) {
840
1004
  if (matches.length === 1) return matches[0];
@@ -846,8 +1010,10 @@ async function readFileIdentity(path) {
846
1010
  try {
847
1011
  const value = await (0, import_promises2.stat)(path);
848
1012
  return `${value.dev}:${value.ino}`;
849
- } catch {
850
- return void 0;
1013
+ } catch (error) {
1014
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1015
+ return void 0;
1016
+ throw error;
851
1017
  }
852
1018
  }
853
1019
 
@@ -879,7 +1045,9 @@ var WikiGraphMaintenanceManager = class {
879
1045
  return await (0, import_wiki_graph_core2.upgradeWikiGraphMaintenanceTarget)({
880
1046
  file: new NodeFile(path),
881
1047
  kind: "archive"
882
- });
1048
+ }).catch(
1049
+ (error) => rethrowArchiveSchemaError(error, { kind: "standalone", path })
1050
+ );
883
1051
  },
884
1052
  void 0,
885
1053
  { skipHomeBootstrap: target.kind === "home" }