wiki-graph-sdk 0.7.0 → 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.
- package/README.md +16 -0
- package/dist/archives.cjs +470 -642
- package/dist/archives.cjs.map +1 -1
- package/dist/archives.d.ts +42 -80
- package/dist/archives.js +405 -570
- package/dist/archives.js.map +1 -1
- package/dist/conversions.cjs +223 -27
- package/dist/conversions.cjs.map +1 -1
- package/dist/conversions.d.ts +16 -4
- package/dist/conversions.js +228 -23
- package/dist/conversions.js.map +1 -1
- package/dist/default-worker.cjs +293 -55
- package/dist/default-worker.cjs.map +1 -1
- package/dist/default-worker.js +309 -54
- package/dist/default-worker.js.map +1 -1
- package/dist/index.cjs +1044 -923
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +36 -19
- package/dist/index.js +915 -780
- package/dist/index.js.map +1 -1
- package/dist/{jobs-Dd5PMSId.d.ts → jobs-xIm_kshM.d.ts} +22 -14
- package/dist/maintenance.cjs +138 -29
- package/dist/maintenance.cjs.map +1 -1
- package/dist/maintenance.d.ts +2 -1
- package/dist/maintenance.js +143 -25
- package/dist/maintenance.js.map +1 -1
- package/dist/target-CZ7elwmj.d.ts +14 -0
- package/dist/wikispine.cjs +14 -10
- package/dist/wikispine.cjs.map +1 -1
- package/dist/wikispine.js +14 -10
- package/dist/wikispine.js.map +1 -1
- package/dist/worker.cjs +328 -22
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.d.ts +43 -4
- package/dist/worker.js +348 -27
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
package/dist/conversions.cjs
CHANGED
|
@@ -34,8 +34,11 @@ __export(conversions_exports, {
|
|
|
34
34
|
WikiGraphConversionManager: () => WikiGraphConversionManager
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(conversions_exports);
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var import_crypto = require("crypto");
|
|
38
|
+
var import_fs = require("fs");
|
|
39
|
+
var import_promises3 = require("fs/promises");
|
|
40
|
+
var import_path4 = require("path");
|
|
41
|
+
var import_wiki_graph_core3 = require("wiki-graph-core");
|
|
39
42
|
|
|
40
43
|
// src/llm.ts
|
|
41
44
|
var import_anthropic = require("@ai-sdk/anthropic");
|
|
@@ -336,6 +339,14 @@ function decodeNodeResourceIdentity(identity, kind) {
|
|
|
336
339
|
return void 0;
|
|
337
340
|
}
|
|
338
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
|
+
}
|
|
339
350
|
function assertChildName(name) {
|
|
340
351
|
if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
|
|
341
352
|
throw new TypeError(`Directory child must be a relative name: ${name}`);
|
|
@@ -347,42 +358,42 @@ var NodeDatabaseConnection = class {
|
|
|
347
358
|
}
|
|
348
359
|
database;
|
|
349
360
|
async close() {
|
|
350
|
-
await new Promise((
|
|
361
|
+
await new Promise((resolve4, reject) => {
|
|
351
362
|
this.database.close((error) => {
|
|
352
363
|
if (error) reject(error);
|
|
353
|
-
else
|
|
364
|
+
else resolve4();
|
|
354
365
|
});
|
|
355
366
|
});
|
|
356
367
|
}
|
|
357
368
|
async execute(sql) {
|
|
358
|
-
await new Promise((
|
|
369
|
+
await new Promise((resolve4, reject) => {
|
|
359
370
|
this.database.exec(sql, (error) => {
|
|
360
371
|
if (error) reject(error);
|
|
361
|
-
else
|
|
372
|
+
else resolve4();
|
|
362
373
|
});
|
|
363
374
|
});
|
|
364
375
|
}
|
|
365
376
|
async queryAll(sql, params = []) {
|
|
366
|
-
return await new Promise((
|
|
377
|
+
return await new Promise((resolve4, reject) => {
|
|
367
378
|
this.database.all(sql, [...params], (error, rows) => {
|
|
368
379
|
if (error) reject(error);
|
|
369
|
-
else
|
|
380
|
+
else resolve4(rows);
|
|
370
381
|
});
|
|
371
382
|
});
|
|
372
383
|
}
|
|
373
384
|
async queryOne(sql, params = []) {
|
|
374
|
-
return await new Promise((
|
|
385
|
+
return await new Promise((resolve4, reject) => {
|
|
375
386
|
this.database.get(sql, [...params], (error, row) => {
|
|
376
387
|
if (error) reject(error);
|
|
377
|
-
else
|
|
388
|
+
else resolve4(row);
|
|
378
389
|
});
|
|
379
390
|
});
|
|
380
391
|
}
|
|
381
392
|
async run(sql, params = []) {
|
|
382
|
-
await new Promise((
|
|
393
|
+
await new Promise((resolve4, reject) => {
|
|
383
394
|
this.database.run(sql, [...params], (error) => {
|
|
384
395
|
if (error) reject(error);
|
|
385
|
-
else
|
|
396
|
+
else resolve4();
|
|
386
397
|
});
|
|
387
398
|
});
|
|
388
399
|
}
|
|
@@ -396,10 +407,10 @@ async function openNativeNodeDatabase(file, flags) {
|
|
|
396
407
|
if (path === void 0) {
|
|
397
408
|
throw new TypeError("The Node database adapter requires a NodeFile");
|
|
398
409
|
}
|
|
399
|
-
return await new Promise((
|
|
410
|
+
return await new Promise((resolve4, reject) => {
|
|
400
411
|
const database = new nodeSqlite3.Database(path, flags, (error) => {
|
|
401
412
|
if (error) reject(error);
|
|
402
|
-
else
|
|
413
|
+
else resolve4(database);
|
|
403
414
|
});
|
|
404
415
|
});
|
|
405
416
|
}
|
|
@@ -408,19 +419,19 @@ async function openNodeZip(file) {
|
|
|
408
419
|
if (path === void 0) {
|
|
409
420
|
throw new TypeError("The Node ZIP adapter requires a NodeFile");
|
|
410
421
|
}
|
|
411
|
-
const zipFile = await new Promise((
|
|
422
|
+
const zipFile = await new Promise((resolve4, reject) => {
|
|
412
423
|
yauzl.open(
|
|
413
424
|
path,
|
|
414
425
|
{ autoClose: false, lazyEntries: true },
|
|
415
426
|
(error, opened) => {
|
|
416
427
|
if (error || opened === void 0)
|
|
417
428
|
reject(error ?? new Error("Cannot open ZIP"));
|
|
418
|
-
else
|
|
429
|
+
else resolve4(opened);
|
|
419
430
|
}
|
|
420
431
|
);
|
|
421
432
|
});
|
|
422
433
|
const entries = await new Promise(
|
|
423
|
-
(
|
|
434
|
+
(resolve4, reject) => {
|
|
424
435
|
const discovered = /* @__PURE__ */ new Map();
|
|
425
436
|
const rejectAndClose = (error) => {
|
|
426
437
|
zipFile.close();
|
|
@@ -430,7 +441,7 @@ async function openNodeZip(file) {
|
|
|
430
441
|
};
|
|
431
442
|
const resolveEntries = () => {
|
|
432
443
|
zipFile.off("error", rejectAndClose);
|
|
433
|
-
|
|
444
|
+
resolve4(discovered);
|
|
434
445
|
};
|
|
435
446
|
zipFile.on("entry", (entry) => {
|
|
436
447
|
if (!entry.fileName.endsWith("/")) {
|
|
@@ -540,12 +551,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
|
|
|
540
551
|
return Buffer.concat(chunks, collected);
|
|
541
552
|
}
|
|
542
553
|
async function openNodeZipEntryStream(zipFile, entry, options) {
|
|
543
|
-
return await new Promise((
|
|
554
|
+
return await new Promise((resolve4, reject) => {
|
|
544
555
|
const callback = (error, opened) => {
|
|
545
556
|
if (error || opened === void 0) {
|
|
546
557
|
reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
|
|
547
558
|
} else {
|
|
548
|
-
|
|
559
|
+
resolve4(opened);
|
|
549
560
|
}
|
|
550
561
|
};
|
|
551
562
|
if (options === void 0) zipFile.openReadStream(entry, callback);
|
|
@@ -676,11 +687,11 @@ async function writeNodeStream(input, writer) {
|
|
|
676
687
|
);
|
|
677
688
|
}
|
|
678
689
|
async function collectNodeStream(input) {
|
|
679
|
-
return await new Promise((
|
|
690
|
+
return await new Promise((resolve4, reject) => {
|
|
680
691
|
const chunks = [];
|
|
681
692
|
input.on("data", (chunk) => chunks.push(chunk));
|
|
682
693
|
input.once("error", reject);
|
|
683
|
-
input.once("end", () =>
|
|
694
|
+
input.once("end", () => resolve4(Buffer.concat(chunks)));
|
|
684
695
|
});
|
|
685
696
|
}
|
|
686
697
|
var nodeWikiGraphPlatform = {
|
|
@@ -1233,6 +1244,107 @@ function resolveWikiGraphRuntimePath(path) {
|
|
|
1233
1244
|
return (0, import_path2.isAbsolute)(path) ? path : (0, import_path2.resolve)(context.cwd, path);
|
|
1234
1245
|
}
|
|
1235
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
|
+
|
|
1236
1348
|
// src/conversions.ts
|
|
1237
1349
|
var WikiGraphConversionManager = class {
|
|
1238
1350
|
#runtime;
|
|
@@ -1247,6 +1359,9 @@ var WikiGraphConversionManager = class {
|
|
|
1247
1359
|
if (options.targetStage !== void 0 && options.output.format !== "wikg") {
|
|
1248
1360
|
throw new Error("targetStage is only supported for wikg output.");
|
|
1249
1361
|
}
|
|
1362
|
+
if (options.output.format === "pcex") {
|
|
1363
|
+
throw new Error("pcex is supported only as a conversion input.");
|
|
1364
|
+
}
|
|
1250
1365
|
if (options.targetStage !== void 0 && options.input.format === "wikg") {
|
|
1251
1366
|
throw new Error(
|
|
1252
1367
|
"targetStage is only supported when creating a wikg archive."
|
|
@@ -1257,7 +1372,7 @@ var WikiGraphConversionManager = class {
|
|
|
1257
1372
|
const config = await loadWikiGraphRuntimeConfig({
|
|
1258
1373
|
...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
|
|
1259
1374
|
});
|
|
1260
|
-
const app = new
|
|
1375
|
+
const app = new import_wiki_graph_core3.WikiGraph({
|
|
1261
1376
|
...options.verbose === true ? { verbose: true } : {},
|
|
1262
1377
|
...requiresLLM ? { llm: buildWikiGraphLLMOptions(config) } : {}
|
|
1263
1378
|
});
|
|
@@ -1265,7 +1380,17 @@ var WikiGraphConversionManager = class {
|
|
|
1265
1380
|
options.digestDirectory,
|
|
1266
1381
|
requiresDigest
|
|
1267
1382
|
);
|
|
1383
|
+
let chapterCount;
|
|
1384
|
+
let chapterPaths;
|
|
1268
1385
|
const write = async (archive) => {
|
|
1386
|
+
if (requiresDigest) await options.onSourceImported?.();
|
|
1387
|
+
if (typeof archive.readToc === "function") {
|
|
1388
|
+
const toc = await archive.readToc();
|
|
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
|
+
}
|
|
1393
|
+
}
|
|
1269
1394
|
await writeArchive(archive, options.output.path, options.output.format);
|
|
1270
1395
|
};
|
|
1271
1396
|
if (options.input.format === "wikg") {
|
|
@@ -1273,7 +1398,9 @@ var WikiGraphConversionManager = class {
|
|
|
1273
1398
|
throw new Error("wikg input requires a file path.");
|
|
1274
1399
|
}
|
|
1275
1400
|
await app.openSession(
|
|
1276
|
-
new NodeFile(
|
|
1401
|
+
new NodeFile(
|
|
1402
|
+
await assertStandaloneWikiGraphArchivePath(options.input.path)
|
|
1403
|
+
),
|
|
1277
1404
|
write
|
|
1278
1405
|
);
|
|
1279
1406
|
} else if ("stream" in options.input) {
|
|
@@ -1298,16 +1425,29 @@ var WikiGraphConversionManager = class {
|
|
|
1298
1425
|
};
|
|
1299
1426
|
if (options.input.format === "epub") {
|
|
1300
1427
|
await app.digestEpubSession(digestOptions, write);
|
|
1428
|
+
} else if (options.input.format === "pcex") {
|
|
1429
|
+
await app.digestPcexSession(
|
|
1430
|
+
{
|
|
1431
|
+
...digestOptions,
|
|
1432
|
+
pdfDigest: options.input.sourcePdf.digest,
|
|
1433
|
+
...options.input.sourcePdf.name === void 0 ? {} : { pdfName: options.input.sourcePdf.name }
|
|
1434
|
+
},
|
|
1435
|
+
write
|
|
1436
|
+
);
|
|
1301
1437
|
} else if (options.input.format === "markdown") {
|
|
1302
1438
|
await app.digestMarkdownSession(digestOptions, write);
|
|
1303
1439
|
} else {
|
|
1304
1440
|
await app.digestTxtSession(digestOptions, write);
|
|
1305
1441
|
}
|
|
1306
1442
|
}
|
|
1443
|
+
const sourceArtifact = await resolveSourceArtifact(options.input);
|
|
1307
1444
|
return {
|
|
1445
|
+
...chapterCount === void 0 ? {} : { chapterCount },
|
|
1446
|
+
...chapterPaths === void 0 ? {} : { chapterPaths },
|
|
1308
1447
|
inputFormat: options.input.format,
|
|
1309
1448
|
outputFormat: options.output.format,
|
|
1310
|
-
outputPath: options.output.path
|
|
1449
|
+
outputPath: options.output.path,
|
|
1450
|
+
...sourceArtifact === void 0 ? {} : { sourceArtifact }
|
|
1311
1451
|
};
|
|
1312
1452
|
}
|
|
1313
1453
|
};
|
|
@@ -1316,15 +1456,71 @@ async function prepareDigestDirectory(path, required) {
|
|
|
1316
1456
|
if (!required || normalized === void 0 || normalized === "")
|
|
1317
1457
|
return void 0;
|
|
1318
1458
|
const resolved = resolveWikiGraphRuntimePath(normalized);
|
|
1319
|
-
await (0,
|
|
1459
|
+
await (0, import_promises3.rm)(resolved, { force: true, recursive: true });
|
|
1320
1460
|
return new NodeDirectory(resolved);
|
|
1321
1461
|
}
|
|
1322
1462
|
async function writeArchive(archive, path, format) {
|
|
1323
|
-
const
|
|
1463
|
+
const outputPath = format === "wikg" ? await assertStandaloneWikiGraphArchivePath(path) : resolveWikiGraphRuntimePath(path);
|
|
1464
|
+
const file = new NodeFile(outputPath);
|
|
1324
1465
|
if (format === "epub") await archive.exportEpub(file);
|
|
1325
1466
|
else if (format === "wikg") await archive.saveAs(file);
|
|
1467
|
+
else if (format === "pcex") throw new Error("pcex output is not supported.");
|
|
1326
1468
|
else await archive.exportText(file);
|
|
1327
1469
|
}
|
|
1470
|
+
async function resolveSourceArtifact(input) {
|
|
1471
|
+
if (!("path" in input) || input.format === "wikg") return void 0;
|
|
1472
|
+
if (input.format === "pcex") {
|
|
1473
|
+
return {
|
|
1474
|
+
digest: input.sourcePdf.digest.trim().toLowerCase(),
|
|
1475
|
+
mediaType: "application/pdf",
|
|
1476
|
+
...input.sourcePdf.name === void 0 ? {} : { name: input.sourcePdf.name }
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
if (input.format !== "epub") return void 0;
|
|
1480
|
+
const path = resolveWikiGraphRuntimePath(input.path);
|
|
1481
|
+
const hash = (0, import_crypto.createHash)("sha256");
|
|
1482
|
+
try {
|
|
1483
|
+
for await (const chunk of (0, import_fs.createReadStream)(path)) {
|
|
1484
|
+
hash.update(chunk);
|
|
1485
|
+
}
|
|
1486
|
+
} catch (error) {
|
|
1487
|
+
if (isNodeErrorCode(error, "ENOENT")) return void 0;
|
|
1488
|
+
throw error;
|
|
1489
|
+
}
|
|
1490
|
+
return {
|
|
1491
|
+
digest: hash.digest("hex"),
|
|
1492
|
+
mediaType: "application/epub+zip",
|
|
1493
|
+
name: (0, import_path4.basename)(path)
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function isNodeErrorCode(error, code) {
|
|
1497
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1498
|
+
}
|
|
1499
|
+
function countTocChapters(items) {
|
|
1500
|
+
let count = 0;
|
|
1501
|
+
for (const item of items) {
|
|
1502
|
+
if (item.serialId !== void 0) count += 1;
|
|
1503
|
+
count += countTocChapters(
|
|
1504
|
+
item.children
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
return count;
|
|
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
|
+
}
|
|
1328
1524
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1329
1525
|
0 && (module.exports = {
|
|
1330
1526
|
WikiGraphConversionManager
|