wiki-graph-sdk 0.7.1 → 0.8.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 (58) hide show
  1. package/README.md +22 -1
  2. package/dist/archives.cjs +494 -656
  3. package/dist/archives.cjs.map +1 -1
  4. package/dist/archives.d.ts +48 -80
  5. package/dist/archives.js +431 -585
  6. package/dist/archives.js.map +1 -1
  7. package/dist/conversions.cjs +183 -33
  8. package/dist/conversions.cjs.map +1 -1
  9. package/dist/conversions.d.ts +8 -1
  10. package/dist/conversions.js +188 -29
  11. package/dist/conversions.js.map +1 -1
  12. package/dist/default-worker.cjs +322 -62
  13. package/dist/default-worker.cjs.map +1 -1
  14. package/dist/default-worker.d.ts +1 -0
  15. package/dist/default-worker.js +336 -60
  16. package/dist/default-worker.js.map +1 -1
  17. package/dist/embedding.cjs +2 -1
  18. package/dist/embedding.cjs.map +1 -1
  19. package/dist/embedding.d.ts +4 -4
  20. package/dist/embedding.js +2 -1
  21. package/dist/embedding.js.map +1 -1
  22. package/dist/index.cjs +1295 -949
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +58 -24
  25. package/dist/index.js +1164 -805
  26. package/dist/index.js.map +1 -1
  27. package/dist/{jobs-Dd5PMSId.d.ts → jobs-CRK-DZ40.d.ts} +27 -14
  28. package/dist/local-config.cjs +2 -1
  29. package/dist/local-config.cjs.map +1 -1
  30. package/dist/local-config.js +2 -1
  31. package/dist/local-config.js.map +1 -1
  32. package/dist/maintenance.cjs +140 -30
  33. package/dist/maintenance.cjs.map +1 -1
  34. package/dist/maintenance.d.ts +5 -1
  35. package/dist/maintenance.js +145 -26
  36. package/dist/maintenance.js.map +1 -1
  37. package/dist/runtime-config.cjs +4 -3
  38. package/dist/runtime-config.cjs.map +1 -1
  39. package/dist/runtime-config.d.ts +4 -1
  40. package/dist/runtime-config.js +4 -3
  41. package/dist/runtime-config.js.map +1 -1
  42. package/dist/stage-BnWI3Rqt.d.ts +33 -0
  43. package/dist/stage.cjs +22 -12
  44. package/dist/stage.cjs.map +1 -1
  45. package/dist/stage.d.ts +2 -14
  46. package/dist/stage.js +22 -12
  47. package/dist/stage.js.map +1 -1
  48. package/dist/target-CZ7elwmj.d.ts +14 -0
  49. package/dist/wikispine.cjs +14 -10
  50. package/dist/wikispine.cjs.map +1 -1
  51. package/dist/wikispine.js +14 -10
  52. package/dist/wikispine.js.map +1 -1
  53. package/dist/worker.cjs +295 -25
  54. package/dist/worker.cjs.map +1 -1
  55. package/dist/worker.d.ts +28 -5
  56. package/dist/worker.js +313 -28
  57. package/dist/worker.js.map +1 -1
  58. package/package.json +2 -2
@@ -6,7 +6,7 @@ globalThis.__WIKIGRAPH_DATA_DIR__ ??= __WIKIGRAPH_RESOLVE__(__WIKIGRAPH_FILE_URL
6
6
  import { createHash } from "crypto";
7
7
  import { createReadStream } from "fs";
8
8
  import { rm as rm2 } from "fs/promises";
9
- import { basename as basename2 } from "path";
9
+ import { basename as basename3 } from "path";
10
10
  import {
11
11
  WikiGraph
12
12
  } from "wiki-graph-core";
@@ -316,6 +316,14 @@ function decodeNodeResourceIdentity(identity, kind) {
316
316
  return void 0;
317
317
  }
318
318
  }
319
+ function getNodeResourcePath(resource) {
320
+ const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
321
+ if (brandedPath !== void 0) return brandedPath;
322
+ if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
323
+ return resource.path;
324
+ }
325
+ throw new TypeError("Expected a Node File or Directory resource");
326
+ }
319
327
  function assertChildName(name) {
320
328
  if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
321
329
  throw new TypeError(`Directory child must be a relative name: ${name}`);
@@ -327,42 +335,42 @@ var NodeDatabaseConnection = class {
327
335
  }
328
336
  database;
329
337
  async close() {
330
- await new Promise((resolve3, reject) => {
338
+ await new Promise((resolve4, reject) => {
331
339
  this.database.close((error) => {
332
340
  if (error) reject(error);
333
- else resolve3();
341
+ else resolve4();
334
342
  });
335
343
  });
336
344
  }
337
345
  async execute(sql) {
338
- await new Promise((resolve3, reject) => {
346
+ await new Promise((resolve4, reject) => {
339
347
  this.database.exec(sql, (error) => {
340
348
  if (error) reject(error);
341
- else resolve3();
349
+ else resolve4();
342
350
  });
343
351
  });
344
352
  }
345
353
  async queryAll(sql, params = []) {
346
- return await new Promise((resolve3, reject) => {
354
+ return await new Promise((resolve4, reject) => {
347
355
  this.database.all(sql, [...params], (error, rows) => {
348
356
  if (error) reject(error);
349
- else resolve3(rows);
357
+ else resolve4(rows);
350
358
  });
351
359
  });
352
360
  }
353
361
  async queryOne(sql, params = []) {
354
- return await new Promise((resolve3, reject) => {
362
+ return await new Promise((resolve4, reject) => {
355
363
  this.database.get(sql, [...params], (error, row) => {
356
364
  if (error) reject(error);
357
- else resolve3(row);
365
+ else resolve4(row);
358
366
  });
359
367
  });
360
368
  }
361
369
  async run(sql, params = []) {
362
- await new Promise((resolve3, reject) => {
370
+ await new Promise((resolve4, reject) => {
363
371
  this.database.run(sql, [...params], (error) => {
364
372
  if (error) reject(error);
365
- else resolve3();
373
+ else resolve4();
366
374
  });
367
375
  });
368
376
  }
@@ -376,10 +384,10 @@ async function openNativeNodeDatabase(file, flags) {
376
384
  if (path === void 0) {
377
385
  throw new TypeError("The Node database adapter requires a NodeFile");
378
386
  }
379
- return await new Promise((resolve3, reject) => {
387
+ return await new Promise((resolve4, reject) => {
380
388
  const database = new nodeSqlite3.Database(path, flags, (error) => {
381
389
  if (error) reject(error);
382
- else resolve3(database);
390
+ else resolve4(database);
383
391
  });
384
392
  });
385
393
  }
@@ -388,19 +396,19 @@ async function openNodeZip(file) {
388
396
  if (path === void 0) {
389
397
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
390
398
  }
391
- const zipFile = await new Promise((resolve3, reject) => {
399
+ const zipFile = await new Promise((resolve4, reject) => {
392
400
  yauzl.open(
393
401
  path,
394
402
  { autoClose: false, lazyEntries: true },
395
403
  (error, opened) => {
396
404
  if (error || opened === void 0)
397
405
  reject(error ?? new Error("Cannot open ZIP"));
398
- else resolve3(opened);
406
+ else resolve4(opened);
399
407
  }
400
408
  );
401
409
  });
402
410
  const entries = await new Promise(
403
- (resolve3, reject) => {
411
+ (resolve4, reject) => {
404
412
  const discovered = /* @__PURE__ */ new Map();
405
413
  const rejectAndClose = (error) => {
406
414
  zipFile.close();
@@ -410,7 +418,7 @@ async function openNodeZip(file) {
410
418
  };
411
419
  const resolveEntries = () => {
412
420
  zipFile.off("error", rejectAndClose);
413
- resolve3(discovered);
421
+ resolve4(discovered);
414
422
  };
415
423
  zipFile.on("entry", (entry) => {
416
424
  if (!entry.fileName.endsWith("/")) {
@@ -520,12 +528,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
520
528
  return Buffer.concat(chunks, collected);
521
529
  }
522
530
  async function openNodeZipEntryStream(zipFile, entry, options) {
523
- return await new Promise((resolve3, reject) => {
531
+ return await new Promise((resolve4, reject) => {
524
532
  const callback = (error, opened) => {
525
533
  if (error || opened === void 0) {
526
534
  reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
527
535
  } else {
528
- resolve3(opened);
536
+ resolve4(opened);
529
537
  }
530
538
  };
531
539
  if (options === void 0) zipFile.openReadStream(entry, callback);
@@ -656,11 +664,11 @@ async function writeNodeStream(input, writer) {
656
664
  );
657
665
  }
658
666
  async function collectNodeStream(input) {
659
- return await new Promise((resolve3, reject) => {
667
+ return await new Promise((resolve4, reject) => {
660
668
  const chunks = [];
661
669
  input.on("data", (chunk) => chunks.push(chunk));
662
670
  input.once("error", reject);
663
- input.once("end", () => resolve3(Buffer.concat(chunks)));
671
+ input.once("end", () => resolve4(Buffer.concat(chunks)));
664
672
  });
665
673
  }
666
674
  var nodeWikiGraphPlatform = {
@@ -781,12 +789,16 @@ function getWikiGraphSDKRuntimeContext() {
781
789
  return runtimeContext.getStore() ?? {
782
790
  cwd: process.cwd(),
783
791
  env: process.env,
784
- envPolicy: "production"
792
+ envPolicy: "production",
793
+ providers: {}
785
794
  };
786
795
  }
787
796
  function getWikiGraphSDKStateDir() {
788
797
  return getWikiGraphSDKRuntimeContext().stateDir;
789
798
  }
799
+ function getWikiGraphSDKProviders() {
800
+ return getWikiGraphSDKRuntimeContext().providers ?? {};
801
+ }
790
802
 
791
803
  // src/local-config.ts
792
804
  import { join as join2 } from "path";
@@ -1004,8 +1016,8 @@ var inlineLLMConfigSchema = z.object({
1004
1016
  });
1005
1017
  async function loadWikiGraphRuntimeConfig(options) {
1006
1018
  const [embedding, localLLM, concurrent, wikimedia, wikispine] = await Promise.all([
1007
- readLocalConfigSection("embeddings"),
1008
- readLocalConfigSection("llm"),
1019
+ options?.skipEmbeddingConfig === true ? Promise.resolve({}) : readLocalConfigSection("embeddings"),
1020
+ options?.skipLLMConfig === true ? Promise.resolve({}) : readLocalConfigSection("llm"),
1009
1021
  readLocalConfigSection("concurrent"),
1010
1022
  readLocalConfigSection("wikimedia"),
1011
1023
  readLocalConfigSection("wikispine")
@@ -1201,6 +1213,14 @@ function formatError2(error) {
1201
1213
  return error instanceof Error ? error.message : String(error);
1202
1214
  }
1203
1215
 
1216
+ // src/providers.ts
1217
+ function resolveWikiGraphLLMJSON(options) {
1218
+ if (options.llm !== void 0 && options.llmJSON !== void 0) {
1219
+ throw new Error("Pass either llm or llmJSON, not both.");
1220
+ }
1221
+ return options.llm === void 0 ? options.llmJSON : JSON.stringify(options.llm);
1222
+ }
1223
+
1204
1224
  // src/runtime-path.ts
1205
1225
  import { homedir as homedir3 } from "os";
1206
1226
  import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
@@ -1213,6 +1233,116 @@ function resolveWikiGraphRuntimePath(path) {
1213
1233
  return isAbsolute2(path) ? path : resolve2(context.cwd, path);
1214
1234
  }
1215
1235
 
1236
+ // src/archive/target.ts
1237
+ import { realpath, stat as stat2 } from "fs/promises";
1238
+ import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve3 } from "path";
1239
+ import {
1240
+ formatLocatedWikiGraphUri,
1241
+ formatWikiGraphLibraryUri,
1242
+ listWikiGraphLibraries,
1243
+ listWikiGraphLibraryArchives,
1244
+ parseLocatedWikiGraphUri,
1245
+ parseWikiGraphLibraryUri,
1246
+ resolveWikiGraphLibrary,
1247
+ resolveWikiGraphLibraryArchiveFile
1248
+ } from "wiki-graph-core";
1249
+ var WikiGraphArchiveOwnershipError = class extends Error {
1250
+ code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
1251
+ constructor(message) {
1252
+ super(message);
1253
+ this.name = "WikiGraphArchiveOwnershipError";
1254
+ }
1255
+ };
1256
+ async function assertStandaloneWikiGraphArchivePath(inputPath) {
1257
+ const path = resolveWikiGraphRuntimePath(inputPath);
1258
+ const candidate = await canonicalPath(path);
1259
+ const candidateIdentity = await readFileIdentity(path);
1260
+ const libraries = await listWikiGraphLibraries();
1261
+ const matches = await findMatchingMembers(
1262
+ libraries,
1263
+ candidate,
1264
+ candidateIdentity
1265
+ );
1266
+ if (matches.length > 0) {
1267
+ const match = requireUnambiguousManagedMatch(path, matches);
1268
+ throw new WikiGraphArchiveOwnershipError(
1269
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1270
+ match.library.isDefault ? void 0 : match.library.publicId
1271
+ )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
1272
+ );
1273
+ }
1274
+ for (const library of libraries) {
1275
+ const rootPath = getNodeResourcePath(library.folder);
1276
+ const root = await canonicalPath(rootPath);
1277
+ if (!isWithin(root, candidate)) continue;
1278
+ throw new WikiGraphArchiveOwnershipError(
1279
+ `Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
1280
+ library.isDefault ? void 0 : library.publicId
1281
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
1282
+ );
1283
+ }
1284
+ return path;
1285
+ }
1286
+ async function canonicalPath(path) {
1287
+ try {
1288
+ return await realpath(path);
1289
+ } catch {
1290
+ const absolute = resolve3(path);
1291
+ const parent = dirname2(absolute);
1292
+ return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
1293
+ }
1294
+ }
1295
+ function isWithin(root, candidate) {
1296
+ const child = relative(root, candidate);
1297
+ return child === "" || !child.startsWith("..") && !isAbsolute3(child);
1298
+ }
1299
+ async function findMatchingMembers(libraries, candidate, candidateIdentity) {
1300
+ const members = [];
1301
+ for (const library of libraries) {
1302
+ const archives = await listWikiGraphLibraryArchives({
1303
+ isDefault: library.isDefault,
1304
+ kind: "scope",
1305
+ ...library.isDefault ? {} : { publicId: library.publicId }
1306
+ });
1307
+ for (const archive of archives) {
1308
+ if (archive.file === void 0) continue;
1309
+ members.push({
1310
+ archive,
1311
+ library,
1312
+ memberPath: getNodeResourcePath(archive.file)
1313
+ });
1314
+ }
1315
+ }
1316
+ const exact = [];
1317
+ for (const member of members) {
1318
+ if (await canonicalPath(member.memberPath) === candidate) {
1319
+ exact.push(member);
1320
+ }
1321
+ }
1322
+ if (exact.length > 0 || candidateIdentity === void 0) return exact;
1323
+ const identityMatches = [];
1324
+ for (const member of members) {
1325
+ if (await readFileIdentity(member.memberPath) === candidateIdentity) {
1326
+ identityMatches.push(member);
1327
+ }
1328
+ }
1329
+ return identityMatches;
1330
+ }
1331
+ function requireUnambiguousManagedMatch(path, matches) {
1332
+ if (matches.length === 1) return matches[0];
1333
+ throw new WikiGraphArchiveOwnershipError(
1334
+ `Archive file identity is ambiguous across managed archives: ${path}. Use an explicit library UUID URI (${matches.map((match) => match.archive.uri).join(", ")}).`
1335
+ );
1336
+ }
1337
+ async function readFileIdentity(path) {
1338
+ try {
1339
+ const value = await stat2(path);
1340
+ return `${value.dev}:${value.ino}`;
1341
+ } catch {
1342
+ return void 0;
1343
+ }
1344
+ }
1345
+
1216
1346
  // src/conversions.ts
1217
1347
  var WikiGraphConversionManager = class {
1218
1348
  #runtime;
@@ -1237,23 +1367,33 @@ var WikiGraphConversionManager = class {
1237
1367
  }
1238
1368
  const requiresDigest = options.input.format !== "wikg";
1239
1369
  const requiresLLM = requiresDigest && targetStage !== "planned" && targetStage !== "sourced";
1370
+ const llmJSON = resolveWikiGraphLLMJSON(options);
1371
+ const injectedLLM = llmJSON === void 0 ? getWikiGraphSDKProviders().llm : void 0;
1240
1372
  const config = await loadWikiGraphRuntimeConfig({
1241
- ...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
1373
+ ...llmJSON === void 0 ? {} : { llmJSON },
1374
+ ...injectedLLM === void 0 ? {} : { skipLLMConfig: true },
1375
+ ...getWikiGraphSDKProviders().embedding === void 0 ? {} : { skipEmbeddingConfig: true }
1242
1376
  });
1243
1377
  const app = new WikiGraph({
1244
1378
  ...options.verbose === true ? { verbose: true } : {},
1245
- ...requiresLLM ? { llm: buildWikiGraphLLMOptions(config) } : {}
1379
+ ...requiresLLM ? {
1380
+ llm: injectedLLM === void 0 ? buildWikiGraphLLMOptions(config) : { streamProvider: injectedLLM }
1381
+ } : {}
1246
1382
  });
1247
1383
  const documentDirectory = await prepareDigestDirectory(
1248
1384
  options.digestDirectory,
1249
1385
  requiresDigest
1250
1386
  );
1251
1387
  let chapterCount;
1388
+ let chapterPaths;
1252
1389
  const write = async (archive) => {
1253
1390
  if (requiresDigest) await options.onSourceImported?.();
1254
1391
  if (typeof archive.readToc === "function") {
1255
1392
  const toc = await archive.readToc();
1256
1393
  chapterCount = toc === void 0 ? void 0 : countTocChapters(toc.items);
1394
+ if (options.output.format === "wikg") {
1395
+ chapterPaths = toc === void 0 ? [] : collectTocChapterPaths(toc.items);
1396
+ }
1257
1397
  }
1258
1398
  await writeArchive(archive, options.output.path, options.output.format);
1259
1399
  };
@@ -1262,7 +1402,9 @@ var WikiGraphConversionManager = class {
1262
1402
  throw new Error("wikg input requires a file path.");
1263
1403
  }
1264
1404
  await app.openSession(
1265
- new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
1405
+ new NodeFile(
1406
+ await assertStandaloneWikiGraphArchivePath(options.input.path)
1407
+ ),
1266
1408
  write
1267
1409
  );
1268
1410
  } else if ("stream" in options.input) {
@@ -1305,6 +1447,7 @@ var WikiGraphConversionManager = class {
1305
1447
  const sourceArtifact = await resolveSourceArtifact(options.input);
1306
1448
  return {
1307
1449
  ...chapterCount === void 0 ? {} : { chapterCount },
1450
+ ...chapterPaths === void 0 ? {} : { chapterPaths },
1308
1451
  inputFormat: options.input.format,
1309
1452
  outputFormat: options.output.format,
1310
1453
  outputPath: options.output.path,
@@ -1321,7 +1464,8 @@ async function prepareDigestDirectory(path, required) {
1321
1464
  return new NodeDirectory(resolved);
1322
1465
  }
1323
1466
  async function writeArchive(archive, path, format) {
1324
- const file = new NodeFile(resolveWikiGraphRuntimePath(path));
1467
+ const outputPath = format === "wikg" ? await assertStandaloneWikiGraphArchivePath(path) : resolveWikiGraphRuntimePath(path);
1468
+ const file = new NodeFile(outputPath);
1325
1469
  if (format === "epub") await archive.exportEpub(file);
1326
1470
  else if (format === "wikg") await archive.saveAs(file);
1327
1471
  else if (format === "pcex") throw new Error("pcex output is not supported.");
@@ -1350,7 +1494,7 @@ async function resolveSourceArtifact(input) {
1350
1494
  return {
1351
1495
  digest: hash.digest("hex"),
1352
1496
  mediaType: "application/epub+zip",
1353
- name: basename2(path)
1497
+ name: basename3(path)
1354
1498
  };
1355
1499
  }
1356
1500
  function isNodeErrorCode(error, code) {
@@ -1366,6 +1510,21 @@ function countTocChapters(items) {
1366
1510
  }
1367
1511
  return count;
1368
1512
  }
1513
+ function collectTocChapterPaths(items, ancestorKeys = []) {
1514
+ const paths = [];
1515
+ for (const item of items) {
1516
+ const key = item.key ?? `chapter-${item.serialId ?? "group"}`;
1517
+ const chapterPath = [...ancestorKeys, key];
1518
+ if (item.serialId !== void 0) paths.push(chapterPath.join("/"));
1519
+ paths.push(
1520
+ ...collectTocChapterPaths(
1521
+ item.children,
1522
+ chapterPath
1523
+ )
1524
+ );
1525
+ }
1526
+ return paths;
1527
+ }
1369
1528
  export {
1370
1529
  WikiGraphConversionManager
1371
1530
  };