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
@@ -42,6 +42,15 @@ var NodeFile = class {
42
42
  this.name = name;
43
43
  }
44
44
  path;
45
+ async getPhysicalIdentity() {
46
+ const value = await fsPromises.stat(this.path).catch((error) => {
47
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
48
+ return void 0;
49
+ throw error;
50
+ });
51
+ if (value === void 0) return void 0;
52
+ return `${value.dev}:${value.ino}`;
53
+ }
45
54
  kind = "file";
46
55
  [NODE_RESOURCE_KIND] = "file";
47
56
  identity;
@@ -166,6 +175,15 @@ var NodeDirectory = class _NodeDirectory {
166
175
  this.name = pathModule.basename(path);
167
176
  }
168
177
  path;
178
+ async getPhysicalIdentity() {
179
+ const value = await fsPromises.stat(this.path).catch((error) => {
180
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
181
+ return void 0;
182
+ throw error;
183
+ });
184
+ if (value === void 0) return void 0;
185
+ return `${value.dev}:${value.ino}`;
186
+ }
169
187
  kind = "directory";
170
188
  [NODE_RESOURCE_KIND] = "directory";
171
189
  identity;
@@ -175,8 +193,10 @@ var NodeDirectory = class _NodeDirectory {
175
193
  const file = new NodeFile(pathModule.join(this.path, name));
176
194
  try {
177
195
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
178
- } catch {
179
- return void 0;
196
+ } catch (error) {
197
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
198
+ return void 0;
199
+ throw error;
180
200
  }
181
201
  }
182
202
  async getLastModified() {
@@ -194,8 +214,10 @@ var NodeDirectory = class _NodeDirectory {
194
214
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
195
215
  try {
196
216
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
197
- } catch {
198
- return void 0;
217
+ } catch (error) {
218
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
219
+ return void 0;
220
+ throw error;
199
221
  }
200
222
  }
201
223
  async list() {
@@ -319,14 +341,23 @@ async function openNativeNodeDatabase(file, flags) {
319
341
  });
320
342
  });
321
343
  }
322
- async function openNodeZip(file) {
344
+ async function openNodeZip(file, options = {}) {
323
345
  const path = getBrandedNodeResourcePath(file, "file");
324
346
  if (path === void 0) {
325
347
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
326
348
  }
327
- const zipFile = await new Promise((resolve4, reject) => {
328
- yauzl.open(
329
- path,
349
+ const handle = await fsPromises.open(path, "r");
350
+ let size;
351
+ try {
352
+ size = (await handle.stat()).size;
353
+ } catch (error) {
354
+ await handle.close();
355
+ throw error;
356
+ }
357
+ const open2 = () => new Promise((resolve4, reject) => {
358
+ yauzl.fromRandomAccessReader(
359
+ new NodeZipSource(handle),
360
+ size,
330
361
  { autoClose: false, lazyEntries: true },
331
362
  (error, opened) => {
332
363
  if (error || opened === void 0)
@@ -335,63 +366,192 @@ async function openNodeZip(file) {
335
366
  }
336
367
  );
337
368
  });
338
- const entries = await new Promise(
339
- (resolve4, reject) => {
340
- const discovered = /* @__PURE__ */ new Map();
341
- const rejectAndClose = (error) => {
342
- zipFile.close();
343
- reject(
344
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
345
- );
346
- };
347
- const resolveEntries = () => {
348
- zipFile.off("error", rejectAndClose);
349
- resolve4(discovered);
350
- };
351
- zipFile.on("entry", (entry) => {
352
- if (!entry.fileName.endsWith("/")) {
353
- discovered.set(entry.fileName, entry);
354
- }
355
- zipFile.readEntry();
356
- });
357
- zipFile.once("error", rejectAndClose);
358
- zipFile.once("end", resolveEntries);
359
- zipFile.readEntry();
360
- }
361
- );
369
+ try {
370
+ (await open2()).close();
371
+ } catch (error) {
372
+ await handle.close();
373
+ throw error;
374
+ }
362
375
  let closed = false;
376
+ let pending = Promise.resolve();
377
+ let closing;
378
+ const run = (operation) => {
379
+ if (closed)
380
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
381
+ const result = pending.then(async () => {
382
+ const zip = await open2();
383
+ try {
384
+ return await operation(zip);
385
+ } finally {
386
+ zip.close();
387
+ }
388
+ });
389
+ pending = result.then(
390
+ () => void 0,
391
+ () => void 0
392
+ );
393
+ return result;
394
+ };
395
+ const withEntry = (name, operation) => run(async (zip) => {
396
+ let exact;
397
+ let alias;
398
+ let aliasCollision = false;
399
+ await scanNodeZip(zip, (entry) => {
400
+ if (entry.fileName === name) exact = entry;
401
+ if (normalizeNodeZipName(entry.fileName) === name) {
402
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
403
+ aliasCollision = true;
404
+ alias = entry;
405
+ }
406
+ });
407
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
408
+ let bestPosition = -1;
409
+ const candidates = await open2();
410
+ try {
411
+ await scanNodeZip(candidates, async (entry) => {
412
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
413
+ const probe = await open2();
414
+ let position = 0;
415
+ try {
416
+ await scanNodeZip(probe, (candidate) => {
417
+ if (candidate.fileName === entry.fileName) return false;
418
+ position++;
419
+ });
420
+ } finally {
421
+ probe.close();
422
+ }
423
+ if (position >= bestPosition) {
424
+ bestPosition = position;
425
+ alias = entry;
426
+ }
427
+ });
428
+ return await operation(candidates, alias);
429
+ } finally {
430
+ candidates.close();
431
+ }
432
+ }
433
+ return await operation(
434
+ zip,
435
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
436
+ );
437
+ });
363
438
  return {
364
439
  close: () => {
365
- if (!closed) zipFile.close();
366
440
  closed = true;
367
- return Promise.resolve();
441
+ closing ??= pending.then(() => handle.close());
442
+ return closing;
368
443
  },
369
- copyEntry: async (name, target) => {
370
- if (closed) throw new Error("Cannot read a closed ZIP archive");
371
- const entry = entries.get(name);
444
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
372
445
  if (entry === void 0) return false;
373
- await copyNodeZipEntry(zipFile, entry, target);
446
+ await copyNodeZipEntry(zip, entry, target);
374
447
  return true;
375
- },
376
- getEntrySize: (name) => {
377
- if (closed) throw new Error("Cannot read a closed ZIP archive");
378
- return Promise.resolve(entries.get(name)?.uncompressedSize);
379
- },
380
- listEntries: () => Promise.resolve([...entries.keys()]),
381
- readEntryRange: async (name, offset, length) => {
382
- if (closed) throw new Error("Cannot read a closed ZIP archive");
383
- const entry = entries.get(name);
448
+ }),
449
+ getEntrySize: (name) => withEntry(
450
+ name,
451
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
452
+ ),
453
+ listEntries: () => run(async (zip) => {
454
+ const names = /* @__PURE__ */ new Set();
455
+ await scanNodeZip(zip, (entry) => {
456
+ names.add(entry.fileName);
457
+ });
458
+ return [...names];
459
+ }),
460
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
384
461
  if (entry === void 0) return void 0;
385
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
386
- },
387
- readEntry: async (name) => {
388
- if (closed) throw new Error("Cannot read a closed ZIP archive");
389
- const entry = entries.get(name);
462
+ return await readNodeZipEntryRange(zip, entry, offset, length);
463
+ }),
464
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
390
465
  if (entry === void 0) return void 0;
391
- return await readNodeZipEntry(zipFile, entry);
392
- }
466
+ return await readNodeZipEntry(zip, entry);
467
+ })
393
468
  };
394
469
  }
470
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
471
+ constructor(handle) {
472
+ super();
473
+ this.handle = handle;
474
+ }
475
+ handle;
476
+ read(buffer, offset, length, position, callback) {
477
+ const read2 = (written) => {
478
+ if (written === length) {
479
+ callback(null);
480
+ return;
481
+ }
482
+ fs.read(
483
+ this.handle.fd,
484
+ buffer,
485
+ offset + written,
486
+ length - written,
487
+ position + written,
488
+ (error, bytesRead) => {
489
+ if (error) callback(error);
490
+ else if (bytesRead === 0)
491
+ callback(new Error("Unexpected end of ZIP file"));
492
+ else read2(written + bytesRead);
493
+ }
494
+ );
495
+ };
496
+ read2(0);
497
+ }
498
+ _readStreamForRange(start, end) {
499
+ const handle = this.handle;
500
+ return Readable.from(
501
+ (async function* () {
502
+ for (let position = start; position < end; ) {
503
+ const buffer = Buffer.allocUnsafe(
504
+ Math.min(64 * 1024, end - position)
505
+ );
506
+ const { bytesRead } = await handle.read(
507
+ buffer,
508
+ 0,
509
+ buffer.length,
510
+ position
511
+ );
512
+ if (bytesRead === 0) return;
513
+ position += bytesRead;
514
+ yield buffer.subarray(0, bytesRead);
515
+ }
516
+ })()
517
+ );
518
+ }
519
+ };
520
+ function normalizeNodeZipName(name) {
521
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
522
+ return normalized.split("/").includes("..") ? void 0 : normalized;
523
+ }
524
+ async function scanNodeZip(zip, visit) {
525
+ await new Promise((resolve4, reject) => {
526
+ const cleanup = () => {
527
+ zip.off("entry", onEntry);
528
+ zip.off("end", onEnd);
529
+ zip.off("error", onError);
530
+ };
531
+ const onError = (error) => {
532
+ cleanup();
533
+ reject(error);
534
+ };
535
+ const onEnd = () => {
536
+ cleanup();
537
+ resolve4();
538
+ };
539
+ const onEntry = (entry) => {
540
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
541
+ if (keepReading === false) onEnd();
542
+ else zip.readEntry();
543
+ }).catch(
544
+ (error) => onError(
545
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
546
+ )
547
+ );
548
+ };
549
+ zip.on("entry", onEntry);
550
+ zip.once("end", onEnd);
551
+ zip.once("error", onError);
552
+ zip.readEntry();
553
+ });
554
+ }
395
555
  async function copyNodeZipEntry(zipFile, entry, target) {
396
556
  const writer = await target.openWriter();
397
557
  try {
@@ -732,17 +892,29 @@ function resolveWikiGraphRuntimePath(path) {
732
892
 
733
893
  // src/archive/target.ts
734
894
  import { realpath, stat as stat2 } from "fs/promises";
735
- import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join2, relative, resolve as resolve3 } from "path";
895
+ import { basename as basename2, dirname as dirname2, join as join2, resolve as resolve3 } from "path";
736
896
  import {
737
897
  formatLocatedWikiGraphUri,
738
898
  formatWikiGraphLibraryUri,
739
- listWikiGraphLibraries,
740
- listWikiGraphLibraryArchives,
899
+ findWikiGraphLibraryByPhysicalIdentity,
900
+ findWikiGraphLibraryArchivePhysicalAliases,
741
901
  parseLocatedWikiGraphUri,
742
902
  parseWikiGraphLibraryUri,
743
903
  resolveWikiGraphLibrary,
744
- resolveWikiGraphLibraryArchiveFile
904
+ resolveWikiGraphLibraryArchiveFile,
905
+ isWikiGraphSchemaVersionError,
906
+ WikiGraphSchemaVersionError
745
907
  } from "wiki-graph-core";
908
+ function rethrowArchiveSchemaError(error, target) {
909
+ if (isWikiGraphSchemaVersionError(error) && error.target.kind === "archive") {
910
+ throw new WikiGraphSchemaVersionError(
911
+ target.kind === "standalone" ? { kind: "archive", path: resolveWikiGraphRuntimePath(target.path) } : { kind: "library", uri: target.uri.split("/arc/")[0] },
912
+ error.actualVersion,
913
+ error.supportedVersion
914
+ );
915
+ }
916
+ throw error;
917
+ }
746
918
  var WikiGraphArchiveOwnershipError = class extends Error {
747
919
  code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
748
920
  constructor(message) {
@@ -754,12 +926,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
754
926
  const path = resolveWikiGraphRuntimePath(inputPath);
755
927
  const candidate = await canonicalPath(path);
756
928
  const candidateIdentity = await readFileIdentity(path);
757
- const libraries = await listWikiGraphLibraries();
758
- const matches = await findMatchingMembers(
759
- libraries,
760
- candidate,
761
- candidateIdentity
762
- );
929
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
763
930
  if (matches.length > 0) {
764
931
  const match = requireUnambiguousManagedMatch(path, matches);
765
932
  throw new WikiGraphArchiveOwnershipError(
@@ -768,63 +935,61 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
768
935
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
769
936
  );
770
937
  }
771
- for (const library of libraries) {
772
- const rootPath = getNodeResourcePath(library.folder);
773
- const root = await canonicalPath(rootPath);
774
- if (!isWithin(root, candidate)) continue;
775
- throw new WikiGraphArchiveOwnershipError(
776
- `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
777
- library.isDefault ? void 0 : library.publicId
778
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
938
+ const ancestors = /* @__PURE__ */ new Set();
939
+ for (const start of [candidate, resolve3(path)]) {
940
+ for (let parent = dirname2(start); ; parent = dirname2(parent)) {
941
+ ancestors.add(parent);
942
+ if (dirname2(parent) === parent) break;
943
+ }
944
+ }
945
+ for (const parent of ancestors) {
946
+ const identity = await readFileIdentity(parent);
947
+ const library = await findWikiGraphLibraryByPhysicalIdentity(
948
+ identity,
949
+ new NodeDirectory(parent).identity
779
950
  );
951
+ if (library !== void 0) {
952
+ throw new WikiGraphArchiveOwnershipError(
953
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
954
+ library.isDefault ? void 0 : library.publicId
955
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
956
+ );
957
+ }
780
958
  }
781
959
  return path;
782
960
  }
783
961
  async function canonicalPath(path) {
784
962
  try {
785
963
  return await realpath(path);
786
- } catch {
964
+ } catch (error) {
965
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
966
+ throw error;
787
967
  const absolute = resolve3(path);
788
968
  const parent = dirname2(absolute);
789
969
  return parent === absolute ? absolute : join2(await canonicalPath(parent), basename2(absolute));
790
970
  }
791
971
  }
792
- function isWithin(root, candidate) {
793
- const child = relative(root, candidate);
794
- return child === "" || !child.startsWith("..") && !isAbsolute3(child);
795
- }
796
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
972
+ async function findMatchingMembers(candidate, candidateIdentity) {
973
+ if (candidateIdentity === void 0) return [];
974
+ const archives = await findWikiGraphLibraryArchivePhysicalAliases(candidateIdentity);
797
975
  const members = [];
798
- for (const library of libraries) {
799
- const archives = await listWikiGraphLibraryArchives({
800
- isDefault: library.isDefault,
801
- kind: "scope",
802
- ...library.isDefault ? {} : { publicId: library.publicId }
803
- });
804
- for (const archive of archives) {
805
- const physical = archive.file ?? archive.directory;
806
- if (physical === void 0) continue;
976
+ for (const archive of archives) {
977
+ const parsed = parseWikiGraphLibraryUri(archive.libraryUri);
978
+ const library = await resolveWikiGraphLibrary(parsed);
979
+ const physical = archive.file ?? archive.directory;
980
+ if (library !== void 0 && physical !== void 0)
807
981
  members.push({
808
982
  archive,
809
983
  library,
810
984
  memberPath: getNodeResourcePath(physical)
811
985
  });
812
- }
813
986
  }
814
987
  const exact = [];
815
988
  for (const member of members) {
816
- if (await canonicalPath(member.memberPath) === candidate) {
989
+ if (await canonicalPath(member.memberPath) === candidate)
817
990
  exact.push(member);
818
- }
819
991
  }
820
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
821
- const identityMatches = [];
822
- for (const member of members) {
823
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
824
- identityMatches.push(member);
825
- }
826
- }
827
- return identityMatches;
992
+ return exact.length > 0 ? exact : members;
828
993
  }
829
994
  function requireUnambiguousManagedMatch(path, matches) {
830
995
  if (matches.length === 1) return matches[0];
@@ -836,8 +1001,10 @@ async function readFileIdentity(path) {
836
1001
  try {
837
1002
  const value = await stat2(path);
838
1003
  return `${value.dev}:${value.ino}`;
839
- } catch {
840
- return void 0;
1004
+ } catch (error) {
1005
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1006
+ return void 0;
1007
+ throw error;
841
1008
  }
842
1009
  }
843
1010
 
@@ -869,7 +1036,9 @@ var WikiGraphMaintenanceManager = class {
869
1036
  return await upgradeWikiGraphMaintenanceTarget({
870
1037
  file: new NodeFile(path),
871
1038
  kind: "archive"
872
- });
1039
+ }).catch(
1040
+ (error) => rethrowArchiveSchemaError(error, { kind: "standalone", path })
1041
+ );
873
1042
  },
874
1043
  void 0,
875
1044
  { skipHomeBootstrap: target.kind === "home" }