wiki-graph-sdk 0.7.1 → 0.8.0

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.
@@ -36,9 +36,9 @@ __export(conversions_exports, {
36
36
  module.exports = __toCommonJS(conversions_exports);
37
37
  var import_crypto = require("crypto");
38
38
  var import_fs = require("fs");
39
- var import_promises2 = require("fs/promises");
40
- var import_path3 = require("path");
41
- var import_wiki_graph_core2 = require("wiki-graph-core");
39
+ var import_promises3 = require("fs/promises");
40
+ var import_path4 = require("path");
41
+ var import_wiki_graph_core3 = require("wiki-graph-core");
42
42
 
43
43
  // src/llm.ts
44
44
  var import_anthropic = require("@ai-sdk/anthropic");
@@ -339,6 +339,14 @@ function decodeNodeResourceIdentity(identity, kind) {
339
339
  return void 0;
340
340
  }
341
341
  }
342
+ function getNodeResourcePath(resource) {
343
+ const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
344
+ if (brandedPath !== void 0) return brandedPath;
345
+ if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
346
+ return resource.path;
347
+ }
348
+ throw new TypeError("Expected a Node File or Directory resource");
349
+ }
342
350
  function assertChildName(name) {
343
351
  if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
344
352
  throw new TypeError(`Directory child must be a relative name: ${name}`);
@@ -350,42 +358,42 @@ var NodeDatabaseConnection = class {
350
358
  }
351
359
  database;
352
360
  async close() {
353
- await new Promise((resolve3, reject) => {
361
+ await new Promise((resolve4, reject) => {
354
362
  this.database.close((error) => {
355
363
  if (error) reject(error);
356
- else resolve3();
364
+ else resolve4();
357
365
  });
358
366
  });
359
367
  }
360
368
  async execute(sql) {
361
- await new Promise((resolve3, reject) => {
369
+ await new Promise((resolve4, reject) => {
362
370
  this.database.exec(sql, (error) => {
363
371
  if (error) reject(error);
364
- else resolve3();
372
+ else resolve4();
365
373
  });
366
374
  });
367
375
  }
368
376
  async queryAll(sql, params = []) {
369
- return await new Promise((resolve3, reject) => {
377
+ return await new Promise((resolve4, reject) => {
370
378
  this.database.all(sql, [...params], (error, rows) => {
371
379
  if (error) reject(error);
372
- else resolve3(rows);
380
+ else resolve4(rows);
373
381
  });
374
382
  });
375
383
  }
376
384
  async queryOne(sql, params = []) {
377
- return await new Promise((resolve3, reject) => {
385
+ return await new Promise((resolve4, reject) => {
378
386
  this.database.get(sql, [...params], (error, row) => {
379
387
  if (error) reject(error);
380
- else resolve3(row);
388
+ else resolve4(row);
381
389
  });
382
390
  });
383
391
  }
384
392
  async run(sql, params = []) {
385
- await new Promise((resolve3, reject) => {
393
+ await new Promise((resolve4, reject) => {
386
394
  this.database.run(sql, [...params], (error) => {
387
395
  if (error) reject(error);
388
- else resolve3();
396
+ else resolve4();
389
397
  });
390
398
  });
391
399
  }
@@ -399,10 +407,10 @@ async function openNativeNodeDatabase(file, flags) {
399
407
  if (path === void 0) {
400
408
  throw new TypeError("The Node database adapter requires a NodeFile");
401
409
  }
402
- return await new Promise((resolve3, reject) => {
410
+ return await new Promise((resolve4, reject) => {
403
411
  const database = new nodeSqlite3.Database(path, flags, (error) => {
404
412
  if (error) reject(error);
405
- else resolve3(database);
413
+ else resolve4(database);
406
414
  });
407
415
  });
408
416
  }
@@ -411,19 +419,19 @@ async function openNodeZip(file) {
411
419
  if (path === void 0) {
412
420
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
413
421
  }
414
- const zipFile = await new Promise((resolve3, reject) => {
422
+ const zipFile = await new Promise((resolve4, reject) => {
415
423
  yauzl.open(
416
424
  path,
417
425
  { autoClose: false, lazyEntries: true },
418
426
  (error, opened) => {
419
427
  if (error || opened === void 0)
420
428
  reject(error ?? new Error("Cannot open ZIP"));
421
- else resolve3(opened);
429
+ else resolve4(opened);
422
430
  }
423
431
  );
424
432
  });
425
433
  const entries = await new Promise(
426
- (resolve3, reject) => {
434
+ (resolve4, reject) => {
427
435
  const discovered = /* @__PURE__ */ new Map();
428
436
  const rejectAndClose = (error) => {
429
437
  zipFile.close();
@@ -433,7 +441,7 @@ async function openNodeZip(file) {
433
441
  };
434
442
  const resolveEntries = () => {
435
443
  zipFile.off("error", rejectAndClose);
436
- resolve3(discovered);
444
+ resolve4(discovered);
437
445
  };
438
446
  zipFile.on("entry", (entry) => {
439
447
  if (!entry.fileName.endsWith("/")) {
@@ -543,12 +551,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
543
551
  return Buffer.concat(chunks, collected);
544
552
  }
545
553
  async function openNodeZipEntryStream(zipFile, entry, options) {
546
- return await new Promise((resolve3, reject) => {
554
+ return await new Promise((resolve4, reject) => {
547
555
  const callback = (error, opened) => {
548
556
  if (error || opened === void 0) {
549
557
  reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
550
558
  } else {
551
- resolve3(opened);
559
+ resolve4(opened);
552
560
  }
553
561
  };
554
562
  if (options === void 0) zipFile.openReadStream(entry, callback);
@@ -679,11 +687,11 @@ async function writeNodeStream(input, writer) {
679
687
  );
680
688
  }
681
689
  async function collectNodeStream(input) {
682
- return await new Promise((resolve3, reject) => {
690
+ return await new Promise((resolve4, reject) => {
683
691
  const chunks = [];
684
692
  input.on("data", (chunk) => chunks.push(chunk));
685
693
  input.once("error", reject);
686
- input.once("end", () => resolve3(Buffer.concat(chunks)));
694
+ input.once("end", () => resolve4(Buffer.concat(chunks)));
687
695
  });
688
696
  }
689
697
  var nodeWikiGraphPlatform = {
@@ -1236,6 +1244,107 @@ function resolveWikiGraphRuntimePath(path) {
1236
1244
  return (0, import_path2.isAbsolute)(path) ? path : (0, import_path2.resolve)(context.cwd, path);
1237
1245
  }
1238
1246
 
1247
+ // src/archive/target.ts
1248
+ var import_promises2 = require("fs/promises");
1249
+ var import_path3 = require("path");
1250
+ var import_wiki_graph_core2 = require("wiki-graph-core");
1251
+ var WikiGraphArchiveOwnershipError = class extends Error {
1252
+ code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1253
+ constructor(message) {
1254
+ super(message);
1255
+ this.name = "WikiGraphArchiveOwnershipError";
1256
+ }
1257
+ };
1258
+ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1259
+ const path = resolveWikiGraphRuntimePath(inputPath);
1260
+ const candidate = await canonicalPath(path);
1261
+ const candidateIdentity = await readFileIdentity(path);
1262
+ const libraries = await (0, import_wiki_graph_core2.listWikiGraphLibraries)();
1263
+ const matches = await findMatchingMembers(
1264
+ libraries,
1265
+ candidate,
1266
+ candidateIdentity
1267
+ );
1268
+ if (matches.length > 0) {
1269
+ const match = requireUnambiguousManagedMatch(path, matches);
1270
+ throw new WikiGraphArchiveOwnershipError(
1271
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1272
+ match.library.isDefault ? void 0 : match.library.publicId
1273
+ )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1274
+ );
1275
+ }
1276
+ for (const library of libraries) {
1277
+ const rootPath = getNodeResourcePath(library.folder);
1278
+ const root = await canonicalPath(rootPath);
1279
+ if (!isWithin(root, candidate)) continue;
1280
+ throw new WikiGraphArchiveOwnershipError(
1281
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core2.formatWikiGraphLibraryUri)(
1282
+ library.isDefault ? void 0 : library.publicId
1283
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1284
+ );
1285
+ }
1286
+ return path;
1287
+ }
1288
+ async function canonicalPath(path) {
1289
+ try {
1290
+ return await (0, import_promises2.realpath)(path);
1291
+ } catch {
1292
+ const absolute = (0, import_path3.resolve)(path);
1293
+ const parent = (0, import_path3.dirname)(absolute);
1294
+ return parent === absolute ? absolute : (0, import_path3.join)(await canonicalPath(parent), (0, import_path3.basename)(absolute));
1295
+ }
1296
+ }
1297
+ function isWithin(root, candidate) {
1298
+ const child = (0, import_path3.relative)(root, candidate);
1299
+ return child === "" || !child.startsWith("..") && !(0, import_path3.isAbsolute)(child);
1300
+ }
1301
+ async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1302
+ const members = [];
1303
+ for (const library of libraries) {
1304
+ const archives = await (0, import_wiki_graph_core2.listWikiGraphLibraryArchives)({
1305
+ isDefault: library.isDefault,
1306
+ kind: "scope",
1307
+ ...library.isDefault ? {} : { publicId: library.publicId }
1308
+ });
1309
+ for (const archive of archives) {
1310
+ if (archive.file === void 0) continue;
1311
+ members.push({
1312
+ archive,
1313
+ library,
1314
+ memberPath: getNodeResourcePath(archive.file)
1315
+ });
1316
+ }
1317
+ }
1318
+ const exact = [];
1319
+ for (const member of members) {
1320
+ if (await canonicalPath(member.memberPath) === candidate) {
1321
+ exact.push(member);
1322
+ }
1323
+ }
1324
+ if (exact.length > 0 || candidateIdentity === void 0) return exact;
1325
+ const identityMatches = [];
1326
+ for (const member of members) {
1327
+ if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1328
+ identityMatches.push(member);
1329
+ }
1330
+ }
1331
+ return identityMatches;
1332
+ }
1333
+ function requireUnambiguousManagedMatch(path, matches) {
1334
+ if (matches.length === 1) return matches[0];
1335
+ throw new WikiGraphArchiveOwnershipError(
1336
+ `Archive file identity is ambiguous across managed archives: ${path}. Use an explicit library UUID URI (${matches.map((match) => match.archive.uri).join(", ")}).`
1337
+ );
1338
+ }
1339
+ async function readFileIdentity(path) {
1340
+ try {
1341
+ const value = await (0, import_promises2.stat)(path);
1342
+ return `${value.dev}:${value.ino}`;
1343
+ } catch {
1344
+ return void 0;
1345
+ }
1346
+ }
1347
+
1239
1348
  // src/conversions.ts
1240
1349
  var WikiGraphConversionManager = class {
1241
1350
  #runtime;
@@ -1263,7 +1372,7 @@ var WikiGraphConversionManager = class {
1263
1372
  const config = await loadWikiGraphRuntimeConfig({
1264
1373
  ...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
1265
1374
  });
1266
- const app = new import_wiki_graph_core2.WikiGraph({
1375
+ const app = new import_wiki_graph_core3.WikiGraph({
1267
1376
  ...options.verbose === true ? { verbose: true } : {},
1268
1377
  ...requiresLLM ? { llm: buildWikiGraphLLMOptions(config) } : {}
1269
1378
  });
@@ -1272,11 +1381,15 @@ var WikiGraphConversionManager = class {
1272
1381
  requiresDigest
1273
1382
  );
1274
1383
  let chapterCount;
1384
+ let chapterPaths;
1275
1385
  const write = async (archive) => {
1276
1386
  if (requiresDigest) await options.onSourceImported?.();
1277
1387
  if (typeof archive.readToc === "function") {
1278
1388
  const toc = await archive.readToc();
1279
1389
  chapterCount = toc === void 0 ? void 0 : countTocChapters(toc.items);
1390
+ if (options.output.format === "wikg") {
1391
+ chapterPaths = toc === void 0 ? [] : collectTocChapterPaths(toc.items);
1392
+ }
1280
1393
  }
1281
1394
  await writeArchive(archive, options.output.path, options.output.format);
1282
1395
  };
@@ -1285,7 +1398,9 @@ var WikiGraphConversionManager = class {
1285
1398
  throw new Error("wikg input requires a file path.");
1286
1399
  }
1287
1400
  await app.openSession(
1288
- new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
1401
+ new NodeFile(
1402
+ await assertStandaloneWikiGraphArchivePath(options.input.path)
1403
+ ),
1289
1404
  write
1290
1405
  );
1291
1406
  } else if ("stream" in options.input) {
@@ -1328,6 +1443,7 @@ var WikiGraphConversionManager = class {
1328
1443
  const sourceArtifact = await resolveSourceArtifact(options.input);
1329
1444
  return {
1330
1445
  ...chapterCount === void 0 ? {} : { chapterCount },
1446
+ ...chapterPaths === void 0 ? {} : { chapterPaths },
1331
1447
  inputFormat: options.input.format,
1332
1448
  outputFormat: options.output.format,
1333
1449
  outputPath: options.output.path,
@@ -1340,11 +1456,12 @@ async function prepareDigestDirectory(path, required) {
1340
1456
  if (!required || normalized === void 0 || normalized === "")
1341
1457
  return void 0;
1342
1458
  const resolved = resolveWikiGraphRuntimePath(normalized);
1343
- await (0, import_promises2.rm)(resolved, { force: true, recursive: true });
1459
+ await (0, import_promises3.rm)(resolved, { force: true, recursive: true });
1344
1460
  return new NodeDirectory(resolved);
1345
1461
  }
1346
1462
  async function writeArchive(archive, path, format) {
1347
- const file = new NodeFile(resolveWikiGraphRuntimePath(path));
1463
+ const outputPath = format === "wikg" ? await assertStandaloneWikiGraphArchivePath(path) : resolveWikiGraphRuntimePath(path);
1464
+ const file = new NodeFile(outputPath);
1348
1465
  if (format === "epub") await archive.exportEpub(file);
1349
1466
  else if (format === "wikg") await archive.saveAs(file);
1350
1467
  else if (format === "pcex") throw new Error("pcex output is not supported.");
@@ -1373,7 +1490,7 @@ async function resolveSourceArtifact(input) {
1373
1490
  return {
1374
1491
  digest: hash.digest("hex"),
1375
1492
  mediaType: "application/epub+zip",
1376
- name: (0, import_path3.basename)(path)
1493
+ name: (0, import_path4.basename)(path)
1377
1494
  };
1378
1495
  }
1379
1496
  function isNodeErrorCode(error, code) {
@@ -1389,6 +1506,21 @@ function countTocChapters(items) {
1389
1506
  }
1390
1507
  return count;
1391
1508
  }
1509
+ function collectTocChapterPaths(items, ancestorKeys = []) {
1510
+ const paths = [];
1511
+ for (const item of items) {
1512
+ const key = item.key ?? `chapter-${item.serialId ?? "group"}`;
1513
+ const chapterPath = [...ancestorKeys, key];
1514
+ if (item.serialId !== void 0) paths.push(chapterPath.join("/"));
1515
+ paths.push(
1516
+ ...collectTocChapterPaths(
1517
+ item.children,
1518
+ chapterPath
1519
+ )
1520
+ );
1521
+ }
1522
+ return paths;
1523
+ }
1392
1524
  // Annotate the CommonJS export names for ESM import in node:
1393
1525
  0 && (module.exports = {
1394
1526
  WikiGraphConversionManager