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/worker.cjs CHANGED
@@ -67,6 +67,15 @@ var NodeFile = class {
67
67
  this.name = name;
68
68
  }
69
69
  path;
70
+ async getPhysicalIdentity() {
71
+ const value = await fsPromises.stat(this.path).catch((error) => {
72
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
73
+ return void 0;
74
+ throw error;
75
+ });
76
+ if (value === void 0) return void 0;
77
+ return `${value.dev}:${value.ino}`;
78
+ }
70
79
  kind = "file";
71
80
  [NODE_RESOURCE_KIND] = "file";
72
81
  identity;
@@ -191,6 +200,15 @@ var NodeDirectory = class _NodeDirectory {
191
200
  this.name = pathModule.basename(path);
192
201
  }
193
202
  path;
203
+ async getPhysicalIdentity() {
204
+ const value = await fsPromises.stat(this.path).catch((error) => {
205
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
206
+ return void 0;
207
+ throw error;
208
+ });
209
+ if (value === void 0) return void 0;
210
+ return `${value.dev}:${value.ino}`;
211
+ }
194
212
  kind = "directory";
195
213
  [NODE_RESOURCE_KIND] = "directory";
196
214
  identity;
@@ -200,8 +218,10 @@ var NodeDirectory = class _NodeDirectory {
200
218
  const file = new NodeFile(pathModule.join(this.path, name));
201
219
  try {
202
220
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
203
- } catch {
204
- return void 0;
221
+ } catch (error) {
222
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
223
+ return void 0;
224
+ throw error;
205
225
  }
206
226
  }
207
227
  async getLastModified() {
@@ -219,8 +239,10 @@ var NodeDirectory = class _NodeDirectory {
219
239
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
220
240
  try {
221
241
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
222
- } catch {
223
- return void 0;
242
+ } catch (error) {
243
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
244
+ return void 0;
245
+ throw error;
224
246
  }
225
247
  }
226
248
  async list() {
@@ -344,14 +366,23 @@ async function openNativeNodeDatabase(file, flags) {
344
366
  });
345
367
  });
346
368
  }
347
- async function openNodeZip(file) {
369
+ async function openNodeZip(file, options = {}) {
348
370
  const path = getBrandedNodeResourcePath(file, "file");
349
371
  if (path === void 0) {
350
372
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
351
373
  }
352
- const zipFile = await new Promise((resolve4, reject) => {
353
- yauzl.open(
354
- path,
374
+ const handle = await fsPromises.open(path, "r");
375
+ let size;
376
+ try {
377
+ size = (await handle.stat()).size;
378
+ } catch (error) {
379
+ await handle.close();
380
+ throw error;
381
+ }
382
+ const open2 = () => new Promise((resolve4, reject) => {
383
+ yauzl.fromRandomAccessReader(
384
+ new NodeZipSource(handle),
385
+ size,
355
386
  { autoClose: false, lazyEntries: true },
356
387
  (error, opened) => {
357
388
  if (error || opened === void 0)
@@ -360,63 +391,192 @@ async function openNodeZip(file) {
360
391
  }
361
392
  );
362
393
  });
363
- const entries = await new Promise(
364
- (resolve4, reject) => {
365
- const discovered = /* @__PURE__ */ new Map();
366
- const rejectAndClose = (error) => {
367
- zipFile.close();
368
- reject(
369
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
370
- );
371
- };
372
- const resolveEntries = () => {
373
- zipFile.off("error", rejectAndClose);
374
- resolve4(discovered);
375
- };
376
- zipFile.on("entry", (entry) => {
377
- if (!entry.fileName.endsWith("/")) {
378
- discovered.set(entry.fileName, entry);
379
- }
380
- zipFile.readEntry();
381
- });
382
- zipFile.once("error", rejectAndClose);
383
- zipFile.once("end", resolveEntries);
384
- zipFile.readEntry();
385
- }
386
- );
394
+ try {
395
+ (await open2()).close();
396
+ } catch (error) {
397
+ await handle.close();
398
+ throw error;
399
+ }
387
400
  let closed = false;
401
+ let pending = Promise.resolve();
402
+ let closing;
403
+ const run = (operation) => {
404
+ if (closed)
405
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
406
+ const result = pending.then(async () => {
407
+ const zip = await open2();
408
+ try {
409
+ return await operation(zip);
410
+ } finally {
411
+ zip.close();
412
+ }
413
+ });
414
+ pending = result.then(
415
+ () => void 0,
416
+ () => void 0
417
+ );
418
+ return result;
419
+ };
420
+ const withEntry = (name, operation) => run(async (zip) => {
421
+ let exact;
422
+ let alias;
423
+ let aliasCollision = false;
424
+ await scanNodeZip(zip, (entry) => {
425
+ if (entry.fileName === name) exact = entry;
426
+ if (normalizeNodeZipName(entry.fileName) === name) {
427
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
428
+ aliasCollision = true;
429
+ alias = entry;
430
+ }
431
+ });
432
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
433
+ let bestPosition = -1;
434
+ const candidates = await open2();
435
+ try {
436
+ await scanNodeZip(candidates, async (entry) => {
437
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
438
+ const probe = await open2();
439
+ let position = 0;
440
+ try {
441
+ await scanNodeZip(probe, (candidate) => {
442
+ if (candidate.fileName === entry.fileName) return false;
443
+ position++;
444
+ });
445
+ } finally {
446
+ probe.close();
447
+ }
448
+ if (position >= bestPosition) {
449
+ bestPosition = position;
450
+ alias = entry;
451
+ }
452
+ });
453
+ return await operation(candidates, alias);
454
+ } finally {
455
+ candidates.close();
456
+ }
457
+ }
458
+ return await operation(
459
+ zip,
460
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
461
+ );
462
+ });
388
463
  return {
389
464
  close: () => {
390
- if (!closed) zipFile.close();
391
465
  closed = true;
392
- return Promise.resolve();
466
+ closing ??= pending.then(() => handle.close());
467
+ return closing;
393
468
  },
394
- copyEntry: async (name, target) => {
395
- if (closed) throw new Error("Cannot read a closed ZIP archive");
396
- const entry = entries.get(name);
469
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
397
470
  if (entry === void 0) return false;
398
- await copyNodeZipEntry(zipFile, entry, target);
471
+ await copyNodeZipEntry(zip, entry, target);
399
472
  return true;
400
- },
401
- getEntrySize: (name) => {
402
- if (closed) throw new Error("Cannot read a closed ZIP archive");
403
- return Promise.resolve(entries.get(name)?.uncompressedSize);
404
- },
405
- listEntries: () => Promise.resolve([...entries.keys()]),
406
- readEntryRange: async (name, offset, length) => {
407
- if (closed) throw new Error("Cannot read a closed ZIP archive");
408
- const entry = entries.get(name);
473
+ }),
474
+ getEntrySize: (name) => withEntry(
475
+ name,
476
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
477
+ ),
478
+ listEntries: () => run(async (zip) => {
479
+ const names = /* @__PURE__ */ new Set();
480
+ await scanNodeZip(zip, (entry) => {
481
+ names.add(entry.fileName);
482
+ });
483
+ return [...names];
484
+ }),
485
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
409
486
  if (entry === void 0) return void 0;
410
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
411
- },
412
- readEntry: async (name) => {
413
- if (closed) throw new Error("Cannot read a closed ZIP archive");
414
- const entry = entries.get(name);
487
+ return await readNodeZipEntryRange(zip, entry, offset, length);
488
+ }),
489
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
415
490
  if (entry === void 0) return void 0;
416
- return await readNodeZipEntry(zipFile, entry);
417
- }
491
+ return await readNodeZipEntry(zip, entry);
492
+ })
418
493
  };
419
494
  }
495
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
496
+ constructor(handle) {
497
+ super();
498
+ this.handle = handle;
499
+ }
500
+ handle;
501
+ read(buffer, offset, length, position, callback) {
502
+ const read2 = (written) => {
503
+ if (written === length) {
504
+ callback(null);
505
+ return;
506
+ }
507
+ fs.read(
508
+ this.handle.fd,
509
+ buffer,
510
+ offset + written,
511
+ length - written,
512
+ position + written,
513
+ (error, bytesRead) => {
514
+ if (error) callback(error);
515
+ else if (bytesRead === 0)
516
+ callback(new Error("Unexpected end of ZIP file"));
517
+ else read2(written + bytesRead);
518
+ }
519
+ );
520
+ };
521
+ read2(0);
522
+ }
523
+ _readStreamForRange(start, end) {
524
+ const handle = this.handle;
525
+ return import_node_stream.Readable.from(
526
+ (async function* () {
527
+ for (let position = start; position < end; ) {
528
+ const buffer = Buffer.allocUnsafe(
529
+ Math.min(64 * 1024, end - position)
530
+ );
531
+ const { bytesRead } = await handle.read(
532
+ buffer,
533
+ 0,
534
+ buffer.length,
535
+ position
536
+ );
537
+ if (bytesRead === 0) return;
538
+ position += bytesRead;
539
+ yield buffer.subarray(0, bytesRead);
540
+ }
541
+ })()
542
+ );
543
+ }
544
+ };
545
+ function normalizeNodeZipName(name) {
546
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
547
+ return normalized.split("/").includes("..") ? void 0 : normalized;
548
+ }
549
+ async function scanNodeZip(zip, visit) {
550
+ await new Promise((resolve4, reject) => {
551
+ const cleanup = () => {
552
+ zip.off("entry", onEntry);
553
+ zip.off("end", onEnd);
554
+ zip.off("error", onError);
555
+ };
556
+ const onError = (error) => {
557
+ cleanup();
558
+ reject(error);
559
+ };
560
+ const onEnd = () => {
561
+ cleanup();
562
+ resolve4();
563
+ };
564
+ const onEntry = (entry) => {
565
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
566
+ if (keepReading === false) onEnd();
567
+ else zip.readEntry();
568
+ }).catch(
569
+ (error) => onError(
570
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
571
+ )
572
+ );
573
+ };
574
+ zip.on("entry", onEntry);
575
+ zip.once("end", onEnd);
576
+ zip.once("error", onError);
577
+ zip.readEntry();
578
+ });
579
+ }
420
580
  async function copyNodeZipEntry(zipFile, entry, target) {
421
581
  const writer = await target.openWriter();
422
582
  try {
@@ -856,12 +1016,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
856
1016
  const path = resolveWikiGraphRuntimePath(inputPath);
857
1017
  const candidate = await canonicalPath(path);
858
1018
  const candidateIdentity = await readFileIdentity(path);
859
- const libraries = await (0, import_wiki_graph_core.listWikiGraphLibraries)();
860
- const matches = await findMatchingMembers(
861
- libraries,
862
- candidate,
863
- candidateIdentity
864
- );
1019
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
865
1020
  if (matches.length > 0) {
866
1021
  const match = requireUnambiguousManagedMatch(path, matches);
867
1022
  throw new WikiGraphArchiveOwnershipError(
@@ -870,15 +1025,26 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
870
1025
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
871
1026
  );
872
1027
  }
873
- for (const library of libraries) {
874
- const rootPath = getNodeResourcePath(library.folder);
875
- const root = await canonicalPath(rootPath);
876
- if (!isWithin(root, candidate)) continue;
877
- throw new WikiGraphArchiveOwnershipError(
878
- `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
879
- library.isDefault ? void 0 : library.publicId
880
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1028
+ const ancestors = /* @__PURE__ */ new Set();
1029
+ for (const start of [candidate, (0, import_path2.resolve)(path)]) {
1030
+ for (let parent = (0, import_path2.dirname)(start); ; parent = (0, import_path2.dirname)(parent)) {
1031
+ ancestors.add(parent);
1032
+ if ((0, import_path2.dirname)(parent) === parent) break;
1033
+ }
1034
+ }
1035
+ for (const parent of ancestors) {
1036
+ const identity = await readFileIdentity(parent);
1037
+ const library = await (0, import_wiki_graph_core.findWikiGraphLibraryByPhysicalIdentity)(
1038
+ identity,
1039
+ new NodeDirectory(parent).identity
881
1040
  );
1041
+ if (library !== void 0) {
1042
+ throw new WikiGraphArchiveOwnershipError(
1043
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
1044
+ library.isDefault ? void 0 : library.publicId
1045
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1046
+ );
1047
+ }
882
1048
  }
883
1049
  return path;
884
1050
  }
@@ -936,47 +1102,35 @@ async function resolveLibraryLocation(target) {
936
1102
  async function canonicalPath(path) {
937
1103
  try {
938
1104
  return await (0, import_promises2.realpath)(path);
939
- } catch {
1105
+ } catch (error) {
1106
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1107
+ throw error;
940
1108
  const absolute = (0, import_path2.resolve)(path);
941
1109
  const parent = (0, import_path2.dirname)(absolute);
942
1110
  return parent === absolute ? absolute : (0, import_path2.join)(await canonicalPath(parent), (0, import_path2.basename)(absolute));
943
1111
  }
944
1112
  }
945
- function isWithin(root, candidate) {
946
- const child = (0, import_path2.relative)(root, candidate);
947
- return child === "" || !child.startsWith("..") && !(0, import_path2.isAbsolute)(child);
948
- }
949
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1113
+ async function findMatchingMembers(candidate, candidateIdentity) {
1114
+ if (candidateIdentity === void 0) return [];
1115
+ const archives = await (0, import_wiki_graph_core.findWikiGraphLibraryArchivePhysicalAliases)(candidateIdentity);
950
1116
  const members = [];
951
- for (const library of libraries) {
952
- const archives = await (0, import_wiki_graph_core.listWikiGraphLibraryArchives)({
953
- isDefault: library.isDefault,
954
- kind: "scope",
955
- ...library.isDefault ? {} : { publicId: library.publicId }
956
- });
957
- for (const archive of archives) {
958
- if (archive.file === void 0) continue;
1117
+ for (const archive of archives) {
1118
+ const parsed = (0, import_wiki_graph_core.parseWikiGraphLibraryUri)(archive.libraryUri);
1119
+ const library = await (0, import_wiki_graph_core.resolveWikiGraphLibrary)(parsed);
1120
+ const physical = archive.file ?? archive.directory;
1121
+ if (library !== void 0 && physical !== void 0)
959
1122
  members.push({
960
1123
  archive,
961
1124
  library,
962
- memberPath: getNodeResourcePath(archive.file)
1125
+ memberPath: getNodeResourcePath(physical)
963
1126
  });
964
- }
965
1127
  }
966
1128
  const exact = [];
967
1129
  for (const member of members) {
968
- if (await canonicalPath(member.memberPath) === candidate) {
1130
+ if (await canonicalPath(member.memberPath) === candidate)
969
1131
  exact.push(member);
970
- }
971
1132
  }
972
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
973
- const identityMatches = [];
974
- for (const member of members) {
975
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
976
- identityMatches.push(member);
977
- }
978
- }
979
- return identityMatches;
1133
+ return exact.length > 0 ? exact : members;
980
1134
  }
981
1135
  function requireUnambiguousManagedMatch(path, matches) {
982
1136
  if (matches.length === 1) return matches[0];
@@ -988,8 +1142,10 @@ async function readFileIdentity(path) {
988
1142
  try {
989
1143
  const value = await (0, import_promises2.stat)(path);
990
1144
  return `${value.dev}:${value.ino}`;
991
- } catch {
992
- return void 0;
1145
+ } catch (error) {
1146
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1147
+ return void 0;
1148
+ throw error;
993
1149
  }
994
1150
  }
995
1151
 
@@ -997,13 +1153,31 @@ async function readFileIdentity(path) {
997
1153
  var import_wiki_graph_core2 = require("wiki-graph-core");
998
1154
  async function writeWikiGraphArchiveLocation(location, operation, options = {}) {
999
1155
  const cacheOnly = options.searchIndexWritebackPolicy === "cache";
1000
- const write = async (archiveFile = location.archiveFile) => await new import_wiki_graph_core2.WikiGraphArchiveFile(archiveFile).write(operation, {
1001
- ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1002
- ...options.invalidateContinuationCursors === void 0 ? {} : {
1003
- invalidateContinuationCursors: options.invalidateContinuationCursors
1004
- },
1005
- ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
1006
- });
1156
+ const write = async (archiveFile = location.archiveFile) => {
1157
+ if (cacheOnly) {
1158
+ return await new import_wiki_graph_core2.WikiGraphArchiveFile(archiveFile).readDocument(
1159
+ operation,
1160
+ { searchIndexWritebackPolicy: "cache" }
1161
+ );
1162
+ }
1163
+ const writeOptions = {
1164
+ ...cacheOnly ? {} : { derivedStateKeys: [location.archiveKey, location.archivePath] },
1165
+ ...options.invalidateContinuationCursors === void 0 ? {} : {
1166
+ invalidateContinuationCursors: options.invalidateContinuationCursors
1167
+ },
1168
+ ...options.searchIndexWritebackPolicy === void 0 ? {} : { searchIndexWritebackPolicy: options.searchIndexWritebackPolicy }
1169
+ };
1170
+ if (archiveFile.kind === "directory") {
1171
+ return await new import_wiki_graph_core2.WikiGraphDirectoryFile(archiveFile).write(
1172
+ operation,
1173
+ writeOptions
1174
+ );
1175
+ }
1176
+ return await new import_wiki_graph_core2.WikiGraphArchiveFile(archiveFile).write(
1177
+ operation,
1178
+ writeOptions
1179
+ );
1180
+ };
1007
1181
  if (location.libraryArchiveTarget === void 0 || cacheOnly || options.refreshLibraryIndex === false) {
1008
1182
  return await write();
1009
1183
  }