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
@@ -137,6 +137,15 @@ var NodeFile = class {
137
137
  this.name = name;
138
138
  }
139
139
  path;
140
+ async getPhysicalIdentity() {
141
+ const value = await fsPromises.stat(this.path).catch((error) => {
142
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
143
+ return void 0;
144
+ throw error;
145
+ });
146
+ if (value === void 0) return void 0;
147
+ return `${value.dev}:${value.ino}`;
148
+ }
140
149
  kind = "file";
141
150
  [NODE_RESOURCE_KIND] = "file";
142
151
  identity;
@@ -261,6 +270,15 @@ var NodeDirectory = class _NodeDirectory {
261
270
  this.name = pathModule.basename(path);
262
271
  }
263
272
  path;
273
+ async getPhysicalIdentity() {
274
+ const value = await fsPromises.stat(this.path).catch((error) => {
275
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
276
+ return void 0;
277
+ throw error;
278
+ });
279
+ if (value === void 0) return void 0;
280
+ return `${value.dev}:${value.ino}`;
281
+ }
264
282
  kind = "directory";
265
283
  [NODE_RESOURCE_KIND] = "directory";
266
284
  identity;
@@ -270,8 +288,10 @@ var NodeDirectory = class _NodeDirectory {
270
288
  const file = new NodeFile(pathModule.join(this.path, name));
271
289
  try {
272
290
  return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
273
- } catch {
274
- return void 0;
291
+ } catch (error) {
292
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
293
+ return void 0;
294
+ throw error;
275
295
  }
276
296
  }
277
297
  async getLastModified() {
@@ -289,8 +309,10 @@ var NodeDirectory = class _NodeDirectory {
289
309
  const directory = new _NodeDirectory(pathModule.join(this.path, name));
290
310
  try {
291
311
  return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
292
- } catch {
293
- return void 0;
312
+ } catch (error) {
313
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
314
+ return void 0;
315
+ throw error;
294
316
  }
295
317
  }
296
318
  async list() {
@@ -414,14 +436,23 @@ async function openNativeNodeDatabase(file, flags) {
414
436
  });
415
437
  });
416
438
  }
417
- async function openNodeZip(file) {
439
+ async function openNodeZip(file, options = {}) {
418
440
  const path = getBrandedNodeResourcePath(file, "file");
419
441
  if (path === void 0) {
420
442
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
421
443
  }
422
- const zipFile = await new Promise((resolve4, reject) => {
423
- yauzl.open(
424
- path,
444
+ const handle = await fsPromises.open(path, "r");
445
+ let size;
446
+ try {
447
+ size = (await handle.stat()).size;
448
+ } catch (error) {
449
+ await handle.close();
450
+ throw error;
451
+ }
452
+ const open2 = () => new Promise((resolve4, reject) => {
453
+ yauzl.fromRandomAccessReader(
454
+ new NodeZipSource(handle),
455
+ size,
425
456
  { autoClose: false, lazyEntries: true },
426
457
  (error, opened) => {
427
458
  if (error || opened === void 0)
@@ -430,63 +461,192 @@ async function openNodeZip(file) {
430
461
  }
431
462
  );
432
463
  });
433
- const entries = await new Promise(
434
- (resolve4, reject) => {
435
- const discovered = /* @__PURE__ */ new Map();
436
- const rejectAndClose = (error) => {
437
- zipFile.close();
438
- reject(
439
- error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
440
- );
441
- };
442
- const resolveEntries = () => {
443
- zipFile.off("error", rejectAndClose);
444
- resolve4(discovered);
445
- };
446
- zipFile.on("entry", (entry) => {
447
- if (!entry.fileName.endsWith("/")) {
448
- discovered.set(entry.fileName, entry);
449
- }
450
- zipFile.readEntry();
451
- });
452
- zipFile.once("error", rejectAndClose);
453
- zipFile.once("end", resolveEntries);
454
- zipFile.readEntry();
455
- }
456
- );
464
+ try {
465
+ (await open2()).close();
466
+ } catch (error) {
467
+ await handle.close();
468
+ throw error;
469
+ }
457
470
  let closed = false;
471
+ let pending = Promise.resolve();
472
+ let closing;
473
+ const run = (operation) => {
474
+ if (closed)
475
+ return Promise.reject(new Error("Cannot read a closed ZIP archive"));
476
+ const result = pending.then(async () => {
477
+ const zip = await open2();
478
+ try {
479
+ return await operation(zip);
480
+ } finally {
481
+ zip.close();
482
+ }
483
+ });
484
+ pending = result.then(
485
+ () => void 0,
486
+ () => void 0
487
+ );
488
+ return result;
489
+ };
490
+ const withEntry = (name, operation) => run(async (zip) => {
491
+ let exact;
492
+ let alias;
493
+ let aliasCollision = false;
494
+ await scanNodeZip(zip, (entry) => {
495
+ if (entry.fileName === name) exact = entry;
496
+ if (normalizeNodeZipName(entry.fileName) === name) {
497
+ if (alias !== void 0 && alias.fileName !== entry.fileName)
498
+ aliasCollision = true;
499
+ alias = entry;
500
+ }
501
+ });
502
+ if ((exact === void 0 || options.entryNameMode === "normalized") && aliasCollision) {
503
+ let bestPosition = -1;
504
+ const candidates = await open2();
505
+ try {
506
+ await scanNodeZip(candidates, async (entry) => {
507
+ if (normalizeNodeZipName(entry.fileName) !== name) return;
508
+ const probe = await open2();
509
+ let position = 0;
510
+ try {
511
+ await scanNodeZip(probe, (candidate) => {
512
+ if (candidate.fileName === entry.fileName) return false;
513
+ position++;
514
+ });
515
+ } finally {
516
+ probe.close();
517
+ }
518
+ if (position >= bestPosition) {
519
+ bestPosition = position;
520
+ alias = entry;
521
+ }
522
+ });
523
+ return await operation(candidates, alias);
524
+ } finally {
525
+ candidates.close();
526
+ }
527
+ }
528
+ return await operation(
529
+ zip,
530
+ options.entryNameMode === "normalized" ? alias : exact ?? alias
531
+ );
532
+ });
458
533
  return {
459
534
  close: () => {
460
- if (!closed) zipFile.close();
461
535
  closed = true;
462
- return Promise.resolve();
536
+ closing ??= pending.then(() => handle.close());
537
+ return closing;
463
538
  },
464
- copyEntry: async (name, target) => {
465
- if (closed) throw new Error("Cannot read a closed ZIP archive");
466
- const entry = entries.get(name);
539
+ copyEntry: (name, target) => withEntry(name, async (zip, entry) => {
467
540
  if (entry === void 0) return false;
468
- await copyNodeZipEntry(zipFile, entry, target);
541
+ await copyNodeZipEntry(zip, entry, target);
469
542
  return true;
470
- },
471
- getEntrySize: (name) => {
472
- if (closed) throw new Error("Cannot read a closed ZIP archive");
473
- return Promise.resolve(entries.get(name)?.uncompressedSize);
474
- },
475
- listEntries: () => Promise.resolve([...entries.keys()]),
476
- readEntryRange: async (name, offset, length) => {
477
- if (closed) throw new Error("Cannot read a closed ZIP archive");
478
- const entry = entries.get(name);
543
+ }),
544
+ getEntrySize: (name) => withEntry(
545
+ name,
546
+ (_zip, entry) => Promise.resolve(entry?.uncompressedSize)
547
+ ),
548
+ listEntries: () => run(async (zip) => {
549
+ const names = /* @__PURE__ */ new Set();
550
+ await scanNodeZip(zip, (entry) => {
551
+ names.add(entry.fileName);
552
+ });
553
+ return [...names];
554
+ }),
555
+ readEntryRange: (name, offset, length) => withEntry(name, async (zip, entry) => {
479
556
  if (entry === void 0) return void 0;
480
- return await readNodeZipEntryRange(zipFile, entry, offset, length);
481
- },
482
- readEntry: async (name) => {
483
- if (closed) throw new Error("Cannot read a closed ZIP archive");
484
- const entry = entries.get(name);
557
+ return await readNodeZipEntryRange(zip, entry, offset, length);
558
+ }),
559
+ readEntry: (name) => withEntry(name, async (zip, entry) => {
485
560
  if (entry === void 0) return void 0;
486
- return await readNodeZipEntry(zipFile, entry);
487
- }
561
+ return await readNodeZipEntry(zip, entry);
562
+ })
488
563
  };
489
564
  }
565
+ var NodeZipSource = class extends yauzl.RandomAccessReader {
566
+ constructor(handle) {
567
+ super();
568
+ this.handle = handle;
569
+ }
570
+ handle;
571
+ read(buffer, offset, length, position, callback) {
572
+ const read2 = (written) => {
573
+ if (written === length) {
574
+ callback(null);
575
+ return;
576
+ }
577
+ fs.read(
578
+ this.handle.fd,
579
+ buffer,
580
+ offset + written,
581
+ length - written,
582
+ position + written,
583
+ (error, bytesRead) => {
584
+ if (error) callback(error);
585
+ else if (bytesRead === 0)
586
+ callback(new Error("Unexpected end of ZIP file"));
587
+ else read2(written + bytesRead);
588
+ }
589
+ );
590
+ };
591
+ read2(0);
592
+ }
593
+ _readStreamForRange(start, end) {
594
+ const handle = this.handle;
595
+ return import_node_stream.Readable.from(
596
+ (async function* () {
597
+ for (let position = start; position < end; ) {
598
+ const buffer = Buffer.allocUnsafe(
599
+ Math.min(64 * 1024, end - position)
600
+ );
601
+ const { bytesRead } = await handle.read(
602
+ buffer,
603
+ 0,
604
+ buffer.length,
605
+ position
606
+ );
607
+ if (bytesRead === 0) return;
608
+ position += bytesRead;
609
+ yield buffer.subarray(0, bytesRead);
610
+ }
611
+ })()
612
+ );
613
+ }
614
+ };
615
+ function normalizeNodeZipName(name) {
616
+ const normalized = name.replaceAll("\\", "/").trim().split("/").filter((part) => part !== "" && part !== ".").join("/");
617
+ return normalized.split("/").includes("..") ? void 0 : normalized;
618
+ }
619
+ async function scanNodeZip(zip, visit) {
620
+ await new Promise((resolve4, reject) => {
621
+ const cleanup = () => {
622
+ zip.off("entry", onEntry);
623
+ zip.off("end", onEnd);
624
+ zip.off("error", onError);
625
+ };
626
+ const onError = (error) => {
627
+ cleanup();
628
+ reject(error);
629
+ };
630
+ const onEnd = () => {
631
+ cleanup();
632
+ resolve4();
633
+ };
634
+ const onEntry = (entry) => {
635
+ void Promise.resolve().then(() => entry.fileName.endsWith("/") ? void 0 : visit(entry)).then((keepReading) => {
636
+ if (keepReading === false) onEnd();
637
+ else zip.readEntry();
638
+ }).catch(
639
+ (error) => onError(
640
+ error instanceof Error ? error : new Error("Cannot scan ZIP", { cause: error })
641
+ )
642
+ );
643
+ };
644
+ zip.on("entry", onEntry);
645
+ zip.once("end", onEnd);
646
+ zip.once("error", onError);
647
+ zip.readEntry();
648
+ });
649
+ }
490
650
  async function copyNodeZipEntry(zipFile, entry, target) {
491
651
  const writer = await target.openWriter();
492
652
  try {
@@ -1259,6 +1419,16 @@ function resolveWikiGraphRuntimePath(path) {
1259
1419
  var import_promises3 = require("fs/promises");
1260
1420
  var import_path3 = require("path");
1261
1421
  var import_wiki_graph_core2 = require("wiki-graph-core");
1422
+ function rethrowArchiveSchemaError(error, target) {
1423
+ if ((0, import_wiki_graph_core2.isWikiGraphSchemaVersionError)(error) && error.target.kind === "archive") {
1424
+ throw new import_wiki_graph_core2.WikiGraphSchemaVersionError(
1425
+ target.kind === "standalone" ? { kind: "archive", path: resolveWikiGraphRuntimePath(target.path) } : { kind: "library", uri: target.uri.split("/arc/")[0] },
1426
+ error.actualVersion,
1427
+ error.supportedVersion
1428
+ );
1429
+ }
1430
+ throw error;
1431
+ }
1262
1432
  var WikiGraphArchiveOwnershipError = class extends Error {
1263
1433
  code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1264
1434
  constructor(message) {
@@ -1270,12 +1440,7 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1270
1440
  const path = resolveWikiGraphRuntimePath(inputPath);
1271
1441
  const candidate = await canonicalPath(path);
1272
1442
  const candidateIdentity = await readFileIdentity(path);
1273
- const libraries = await (0, import_wiki_graph_core2.listWikiGraphLibraries)();
1274
- const matches = await findMatchingMembers(
1275
- libraries,
1276
- candidate,
1277
- candidateIdentity
1278
- );
1443
+ const matches = await findMatchingMembers(candidate, candidateIdentity);
1279
1444
  if (matches.length > 0) {
1280
1445
  const match = requireUnambiguousManagedMatch(path, matches);
1281
1446
  throw new WikiGraphArchiveOwnershipError(
@@ -1284,63 +1449,61 @@ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1284
1449
  )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1285
1450
  );
1286
1451
  }
1287
- for (const library of libraries) {
1288
- const rootPath = getNodeResourcePath(library.folder);
1289
- const root = await canonicalPath(rootPath);
1290
- if (!isWithin(root, candidate)) continue;
1291
- throw new WikiGraphArchiveOwnershipError(
1292
- `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1293
- library.isDefault ? void 0 : library.publicId
1294
- )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1452
+ const ancestors = /* @__PURE__ */ new Set();
1453
+ for (const start of [candidate, (0, import_path3.resolve)(path)]) {
1454
+ for (let parent = (0, import_path3.dirname)(start); ; parent = (0, import_path3.dirname)(parent)) {
1455
+ ancestors.add(parent);
1456
+ if ((0, import_path3.dirname)(parent) === parent) break;
1457
+ }
1458
+ }
1459
+ for (const parent of ancestors) {
1460
+ const identity = await readFileIdentity(parent);
1461
+ const library = await (0, import_wiki_graph_core2.findWikiGraphLibraryByPhysicalIdentity)(
1462
+ identity,
1463
+ new NodeDirectory(parent).identity
1295
1464
  );
1465
+ if (library !== void 0) {
1466
+ throw new WikiGraphArchiveOwnershipError(
1467
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1468
+ library.isDefault ? void 0 : library.publicId
1469
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1470
+ );
1471
+ }
1296
1472
  }
1297
1473
  return path;
1298
1474
  }
1299
1475
  async function canonicalPath(path) {
1300
1476
  try {
1301
1477
  return await (0, import_promises3.realpath)(path);
1302
- } catch {
1478
+ } catch (error) {
1479
+ if (!(typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")))
1480
+ throw error;
1303
1481
  const absolute = (0, import_path3.resolve)(path);
1304
1482
  const parent = (0, import_path3.dirname)(absolute);
1305
1483
  return parent === absolute ? absolute : (0, import_path3.join)(await canonicalPath(parent), (0, import_path3.basename)(absolute));
1306
1484
  }
1307
1485
  }
1308
- function isWithin(root, candidate) {
1309
- const child = (0, import_path3.relative)(root, candidate);
1310
- return child === "" || !child.startsWith("..") && !(0, import_path3.isAbsolute)(child);
1311
- }
1312
- async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1486
+ async function findMatchingMembers(candidate, candidateIdentity) {
1487
+ if (candidateIdentity === void 0) return [];
1488
+ const archives = await (0, import_wiki_graph_core2.findWikiGraphLibraryArchivePhysicalAliases)(candidateIdentity);
1313
1489
  const members = [];
1314
- for (const library of libraries) {
1315
- const archives = await (0, import_wiki_graph_core2.listWikiGraphLibraryArchives)({
1316
- isDefault: library.isDefault,
1317
- kind: "scope",
1318
- ...library.isDefault ? {} : { publicId: library.publicId }
1319
- });
1320
- for (const archive of archives) {
1321
- const physical = archive.file ?? archive.directory;
1322
- if (physical === void 0) continue;
1490
+ for (const archive of archives) {
1491
+ const parsed = (0, import_wiki_graph_core2.parseWikiGraphLibraryUri)(archive.libraryUri);
1492
+ const library = await (0, import_wiki_graph_core2.resolveWikiGraphLibrary)(parsed);
1493
+ const physical = archive.file ?? archive.directory;
1494
+ if (library !== void 0 && physical !== void 0)
1323
1495
  members.push({
1324
1496
  archive,
1325
1497
  library,
1326
1498
  memberPath: getNodeResourcePath(physical)
1327
1499
  });
1328
- }
1329
1500
  }
1330
1501
  const exact = [];
1331
1502
  for (const member of members) {
1332
- if (await canonicalPath(member.memberPath) === candidate) {
1503
+ if (await canonicalPath(member.memberPath) === candidate)
1333
1504
  exact.push(member);
1334
- }
1335
- }
1336
- if (exact.length > 0 || candidateIdentity === void 0) return exact;
1337
- const identityMatches = [];
1338
- for (const member of members) {
1339
- if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1340
- identityMatches.push(member);
1341
- }
1342
1505
  }
1343
- return identityMatches;
1506
+ return exact.length > 0 ? exact : members;
1344
1507
  }
1345
1508
  function requireUnambiguousManagedMatch(path, matches) {
1346
1509
  if (matches.length === 1) return matches[0];
@@ -1352,8 +1515,10 @@ async function readFileIdentity(path) {
1352
1515
  try {
1353
1516
  const value = await (0, import_promises3.stat)(path);
1354
1517
  return `${value.dev}:${value.ino}`;
1355
- } catch {
1356
- return void 0;
1518
+ } catch (error) {
1519
+ if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
1520
+ return void 0;
1521
+ throw error;
1357
1522
  }
1358
1523
  }
1359
1524
 
@@ -1415,11 +1580,15 @@ var WikiGraphConversionManager = class {
1415
1580
  if (!("path" in options.input)) {
1416
1581
  throw new Error("wikg input requires a file path.");
1417
1582
  }
1583
+ const inputPath = options.input.path;
1418
1584
  await app.openSession(
1419
- new NodeFile(
1420
- await assertStandaloneWikiGraphArchivePath(options.input.path)
1421
- ),
1585
+ new NodeFile(await assertStandaloneWikiGraphArchivePath(inputPath)),
1422
1586
  write
1587
+ ).catch(
1588
+ (error) => rethrowArchiveSchemaError(error, {
1589
+ kind: "standalone",
1590
+ path: inputPath
1591
+ })
1423
1592
  );
1424
1593
  } else if ("stream" in options.input) {
1425
1594
  await app.digestTextStreamSession(