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.js
CHANGED
|
@@ -3,7 +3,10 @@ import { resolve as __WIKIGRAPH_RESOLVE__ } from "path";
|
|
|
3
3
|
globalThis.__WIKIGRAPH_DATA_DIR__ ??= __WIKIGRAPH_RESOLVE__(__WIKIGRAPH_FILE_URL_TO_PATH__(new URL("./data", import.meta.url)));
|
|
4
4
|
|
|
5
5
|
// src/conversions.ts
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
import { createReadStream } from "fs";
|
|
6
8
|
import { rm as rm2 } from "fs/promises";
|
|
9
|
+
import { basename as basename3 } from "path";
|
|
7
10
|
import {
|
|
8
11
|
WikiGraph
|
|
9
12
|
} from "wiki-graph-core";
|
|
@@ -313,6 +316,14 @@ function decodeNodeResourceIdentity(identity, kind) {
|
|
|
313
316
|
return void 0;
|
|
314
317
|
}
|
|
315
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
|
+
}
|
|
316
327
|
function assertChildName(name) {
|
|
317
328
|
if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
|
|
318
329
|
throw new TypeError(`Directory child must be a relative name: ${name}`);
|
|
@@ -324,42 +335,42 @@ var NodeDatabaseConnection = class {
|
|
|
324
335
|
}
|
|
325
336
|
database;
|
|
326
337
|
async close() {
|
|
327
|
-
await new Promise((
|
|
338
|
+
await new Promise((resolve4, reject) => {
|
|
328
339
|
this.database.close((error) => {
|
|
329
340
|
if (error) reject(error);
|
|
330
|
-
else
|
|
341
|
+
else resolve4();
|
|
331
342
|
});
|
|
332
343
|
});
|
|
333
344
|
}
|
|
334
345
|
async execute(sql) {
|
|
335
|
-
await new Promise((
|
|
346
|
+
await new Promise((resolve4, reject) => {
|
|
336
347
|
this.database.exec(sql, (error) => {
|
|
337
348
|
if (error) reject(error);
|
|
338
|
-
else
|
|
349
|
+
else resolve4();
|
|
339
350
|
});
|
|
340
351
|
});
|
|
341
352
|
}
|
|
342
353
|
async queryAll(sql, params = []) {
|
|
343
|
-
return await new Promise((
|
|
354
|
+
return await new Promise((resolve4, reject) => {
|
|
344
355
|
this.database.all(sql, [...params], (error, rows) => {
|
|
345
356
|
if (error) reject(error);
|
|
346
|
-
else
|
|
357
|
+
else resolve4(rows);
|
|
347
358
|
});
|
|
348
359
|
});
|
|
349
360
|
}
|
|
350
361
|
async queryOne(sql, params = []) {
|
|
351
|
-
return await new Promise((
|
|
362
|
+
return await new Promise((resolve4, reject) => {
|
|
352
363
|
this.database.get(sql, [...params], (error, row) => {
|
|
353
364
|
if (error) reject(error);
|
|
354
|
-
else
|
|
365
|
+
else resolve4(row);
|
|
355
366
|
});
|
|
356
367
|
});
|
|
357
368
|
}
|
|
358
369
|
async run(sql, params = []) {
|
|
359
|
-
await new Promise((
|
|
370
|
+
await new Promise((resolve4, reject) => {
|
|
360
371
|
this.database.run(sql, [...params], (error) => {
|
|
361
372
|
if (error) reject(error);
|
|
362
|
-
else
|
|
373
|
+
else resolve4();
|
|
363
374
|
});
|
|
364
375
|
});
|
|
365
376
|
}
|
|
@@ -373,10 +384,10 @@ async function openNativeNodeDatabase(file, flags) {
|
|
|
373
384
|
if (path === void 0) {
|
|
374
385
|
throw new TypeError("The Node database adapter requires a NodeFile");
|
|
375
386
|
}
|
|
376
|
-
return await new Promise((
|
|
387
|
+
return await new Promise((resolve4, reject) => {
|
|
377
388
|
const database = new nodeSqlite3.Database(path, flags, (error) => {
|
|
378
389
|
if (error) reject(error);
|
|
379
|
-
else
|
|
390
|
+
else resolve4(database);
|
|
380
391
|
});
|
|
381
392
|
});
|
|
382
393
|
}
|
|
@@ -385,19 +396,19 @@ async function openNodeZip(file) {
|
|
|
385
396
|
if (path === void 0) {
|
|
386
397
|
throw new TypeError("The Node ZIP adapter requires a NodeFile");
|
|
387
398
|
}
|
|
388
|
-
const zipFile = await new Promise((
|
|
399
|
+
const zipFile = await new Promise((resolve4, reject) => {
|
|
389
400
|
yauzl.open(
|
|
390
401
|
path,
|
|
391
402
|
{ autoClose: false, lazyEntries: true },
|
|
392
403
|
(error, opened) => {
|
|
393
404
|
if (error || opened === void 0)
|
|
394
405
|
reject(error ?? new Error("Cannot open ZIP"));
|
|
395
|
-
else
|
|
406
|
+
else resolve4(opened);
|
|
396
407
|
}
|
|
397
408
|
);
|
|
398
409
|
});
|
|
399
410
|
const entries = await new Promise(
|
|
400
|
-
(
|
|
411
|
+
(resolve4, reject) => {
|
|
401
412
|
const discovered = /* @__PURE__ */ new Map();
|
|
402
413
|
const rejectAndClose = (error) => {
|
|
403
414
|
zipFile.close();
|
|
@@ -407,7 +418,7 @@ async function openNodeZip(file) {
|
|
|
407
418
|
};
|
|
408
419
|
const resolveEntries = () => {
|
|
409
420
|
zipFile.off("error", rejectAndClose);
|
|
410
|
-
|
|
421
|
+
resolve4(discovered);
|
|
411
422
|
};
|
|
412
423
|
zipFile.on("entry", (entry) => {
|
|
413
424
|
if (!entry.fileName.endsWith("/")) {
|
|
@@ -517,12 +528,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
|
|
|
517
528
|
return Buffer.concat(chunks, collected);
|
|
518
529
|
}
|
|
519
530
|
async function openNodeZipEntryStream(zipFile, entry, options) {
|
|
520
|
-
return await new Promise((
|
|
531
|
+
return await new Promise((resolve4, reject) => {
|
|
521
532
|
const callback = (error, opened) => {
|
|
522
533
|
if (error || opened === void 0) {
|
|
523
534
|
reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
|
|
524
535
|
} else {
|
|
525
|
-
|
|
536
|
+
resolve4(opened);
|
|
526
537
|
}
|
|
527
538
|
};
|
|
528
539
|
if (options === void 0) zipFile.openReadStream(entry, callback);
|
|
@@ -653,11 +664,11 @@ async function writeNodeStream(input, writer) {
|
|
|
653
664
|
);
|
|
654
665
|
}
|
|
655
666
|
async function collectNodeStream(input) {
|
|
656
|
-
return await new Promise((
|
|
667
|
+
return await new Promise((resolve4, reject) => {
|
|
657
668
|
const chunks = [];
|
|
658
669
|
input.on("data", (chunk) => chunks.push(chunk));
|
|
659
670
|
input.once("error", reject);
|
|
660
|
-
input.once("end", () =>
|
|
671
|
+
input.once("end", () => resolve4(Buffer.concat(chunks)));
|
|
661
672
|
});
|
|
662
673
|
}
|
|
663
674
|
var nodeWikiGraphPlatform = {
|
|
@@ -1210,6 +1221,116 @@ function resolveWikiGraphRuntimePath(path) {
|
|
|
1210
1221
|
return isAbsolute2(path) ? path : resolve2(context.cwd, path);
|
|
1211
1222
|
}
|
|
1212
1223
|
|
|
1224
|
+
// src/archive/target.ts
|
|
1225
|
+
import { realpath, stat as stat2 } from "fs/promises";
|
|
1226
|
+
import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve3 } from "path";
|
|
1227
|
+
import {
|
|
1228
|
+
formatLocatedWikiGraphUri,
|
|
1229
|
+
formatWikiGraphLibraryUri,
|
|
1230
|
+
listWikiGraphLibraries,
|
|
1231
|
+
listWikiGraphLibraryArchives,
|
|
1232
|
+
parseLocatedWikiGraphUri,
|
|
1233
|
+
parseWikiGraphLibraryUri,
|
|
1234
|
+
resolveWikiGraphLibrary,
|
|
1235
|
+
resolveWikiGraphLibraryArchiveFile
|
|
1236
|
+
} from "wiki-graph-core";
|
|
1237
|
+
var WikiGraphArchiveOwnershipError = class extends Error {
|
|
1238
|
+
code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
|
|
1239
|
+
constructor(message) {
|
|
1240
|
+
super(message);
|
|
1241
|
+
this.name = "WikiGraphArchiveOwnershipError";
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
async function assertStandaloneWikiGraphArchivePath(inputPath) {
|
|
1245
|
+
const path = resolveWikiGraphRuntimePath(inputPath);
|
|
1246
|
+
const candidate = await canonicalPath(path);
|
|
1247
|
+
const candidateIdentity = await readFileIdentity(path);
|
|
1248
|
+
const libraries = await listWikiGraphLibraries();
|
|
1249
|
+
const matches = await findMatchingMembers(
|
|
1250
|
+
libraries,
|
|
1251
|
+
candidate,
|
|
1252
|
+
candidateIdentity
|
|
1253
|
+
);
|
|
1254
|
+
if (matches.length > 0) {
|
|
1255
|
+
const match = requireUnambiguousManagedMatch(path, matches);
|
|
1256
|
+
throw new WikiGraphArchiveOwnershipError(
|
|
1257
|
+
`Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
|
|
1258
|
+
match.library.isDefault ? void 0 : match.library.publicId
|
|
1259
|
+
)}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
for (const library of libraries) {
|
|
1263
|
+
const rootPath = getNodeResourcePath(library.folder);
|
|
1264
|
+
const root = await canonicalPath(rootPath);
|
|
1265
|
+
if (!isWithin(root, candidate)) continue;
|
|
1266
|
+
throw new WikiGraphArchiveOwnershipError(
|
|
1267
|
+
`Standalone archive path belongs to registered library ${formatWikiGraphLibraryUri(
|
|
1268
|
+
library.isDefault ? void 0 : library.publicId
|
|
1269
|
+
)}: ${path}. Scan the library, then address the archive by its library UUID URI.`
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
return path;
|
|
1273
|
+
}
|
|
1274
|
+
async function canonicalPath(path) {
|
|
1275
|
+
try {
|
|
1276
|
+
return await realpath(path);
|
|
1277
|
+
} catch {
|
|
1278
|
+
const absolute = resolve3(path);
|
|
1279
|
+
const parent = dirname2(absolute);
|
|
1280
|
+
return parent === absolute ? absolute : join3(await canonicalPath(parent), basename2(absolute));
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function isWithin(root, candidate) {
|
|
1284
|
+
const child = relative(root, candidate);
|
|
1285
|
+
return child === "" || !child.startsWith("..") && !isAbsolute3(child);
|
|
1286
|
+
}
|
|
1287
|
+
async function findMatchingMembers(libraries, candidate, candidateIdentity) {
|
|
1288
|
+
const members = [];
|
|
1289
|
+
for (const library of libraries) {
|
|
1290
|
+
const archives = await listWikiGraphLibraryArchives({
|
|
1291
|
+
isDefault: library.isDefault,
|
|
1292
|
+
kind: "scope",
|
|
1293
|
+
...library.isDefault ? {} : { publicId: library.publicId }
|
|
1294
|
+
});
|
|
1295
|
+
for (const archive of archives) {
|
|
1296
|
+
if (archive.file === void 0) continue;
|
|
1297
|
+
members.push({
|
|
1298
|
+
archive,
|
|
1299
|
+
library,
|
|
1300
|
+
memberPath: getNodeResourcePath(archive.file)
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const exact = [];
|
|
1305
|
+
for (const member of members) {
|
|
1306
|
+
if (await canonicalPath(member.memberPath) === candidate) {
|
|
1307
|
+
exact.push(member);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (exact.length > 0 || candidateIdentity === void 0) return exact;
|
|
1311
|
+
const identityMatches = [];
|
|
1312
|
+
for (const member of members) {
|
|
1313
|
+
if (await readFileIdentity(member.memberPath) === candidateIdentity) {
|
|
1314
|
+
identityMatches.push(member);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
return identityMatches;
|
|
1318
|
+
}
|
|
1319
|
+
function requireUnambiguousManagedMatch(path, matches) {
|
|
1320
|
+
if (matches.length === 1) return matches[0];
|
|
1321
|
+
throw new WikiGraphArchiveOwnershipError(
|
|
1322
|
+
`Archive file identity is ambiguous across managed archives: ${path}. Use an explicit library UUID URI (${matches.map((match) => match.archive.uri).join(", ")}).`
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
async function readFileIdentity(path) {
|
|
1326
|
+
try {
|
|
1327
|
+
const value = await stat2(path);
|
|
1328
|
+
return `${value.dev}:${value.ino}`;
|
|
1329
|
+
} catch {
|
|
1330
|
+
return void 0;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1213
1334
|
// src/conversions.ts
|
|
1214
1335
|
var WikiGraphConversionManager = class {
|
|
1215
1336
|
#runtime;
|
|
@@ -1224,6 +1345,9 @@ var WikiGraphConversionManager = class {
|
|
|
1224
1345
|
if (options.targetStage !== void 0 && options.output.format !== "wikg") {
|
|
1225
1346
|
throw new Error("targetStage is only supported for wikg output.");
|
|
1226
1347
|
}
|
|
1348
|
+
if (options.output.format === "pcex") {
|
|
1349
|
+
throw new Error("pcex is supported only as a conversion input.");
|
|
1350
|
+
}
|
|
1227
1351
|
if (options.targetStage !== void 0 && options.input.format === "wikg") {
|
|
1228
1352
|
throw new Error(
|
|
1229
1353
|
"targetStage is only supported when creating a wikg archive."
|
|
@@ -1242,7 +1366,17 @@ var WikiGraphConversionManager = class {
|
|
|
1242
1366
|
options.digestDirectory,
|
|
1243
1367
|
requiresDigest
|
|
1244
1368
|
);
|
|
1369
|
+
let chapterCount;
|
|
1370
|
+
let chapterPaths;
|
|
1245
1371
|
const write = async (archive) => {
|
|
1372
|
+
if (requiresDigest) await options.onSourceImported?.();
|
|
1373
|
+
if (typeof archive.readToc === "function") {
|
|
1374
|
+
const toc = await archive.readToc();
|
|
1375
|
+
chapterCount = toc === void 0 ? void 0 : countTocChapters(toc.items);
|
|
1376
|
+
if (options.output.format === "wikg") {
|
|
1377
|
+
chapterPaths = toc === void 0 ? [] : collectTocChapterPaths(toc.items);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1246
1380
|
await writeArchive(archive, options.output.path, options.output.format);
|
|
1247
1381
|
};
|
|
1248
1382
|
if (options.input.format === "wikg") {
|
|
@@ -1250,7 +1384,9 @@ var WikiGraphConversionManager = class {
|
|
|
1250
1384
|
throw new Error("wikg input requires a file path.");
|
|
1251
1385
|
}
|
|
1252
1386
|
await app.openSession(
|
|
1253
|
-
new NodeFile(
|
|
1387
|
+
new NodeFile(
|
|
1388
|
+
await assertStandaloneWikiGraphArchivePath(options.input.path)
|
|
1389
|
+
),
|
|
1254
1390
|
write
|
|
1255
1391
|
);
|
|
1256
1392
|
} else if ("stream" in options.input) {
|
|
@@ -1275,16 +1411,29 @@ var WikiGraphConversionManager = class {
|
|
|
1275
1411
|
};
|
|
1276
1412
|
if (options.input.format === "epub") {
|
|
1277
1413
|
await app.digestEpubSession(digestOptions, write);
|
|
1414
|
+
} else if (options.input.format === "pcex") {
|
|
1415
|
+
await app.digestPcexSession(
|
|
1416
|
+
{
|
|
1417
|
+
...digestOptions,
|
|
1418
|
+
pdfDigest: options.input.sourcePdf.digest,
|
|
1419
|
+
...options.input.sourcePdf.name === void 0 ? {} : { pdfName: options.input.sourcePdf.name }
|
|
1420
|
+
},
|
|
1421
|
+
write
|
|
1422
|
+
);
|
|
1278
1423
|
} else if (options.input.format === "markdown") {
|
|
1279
1424
|
await app.digestMarkdownSession(digestOptions, write);
|
|
1280
1425
|
} else {
|
|
1281
1426
|
await app.digestTxtSession(digestOptions, write);
|
|
1282
1427
|
}
|
|
1283
1428
|
}
|
|
1429
|
+
const sourceArtifact = await resolveSourceArtifact(options.input);
|
|
1284
1430
|
return {
|
|
1431
|
+
...chapterCount === void 0 ? {} : { chapterCount },
|
|
1432
|
+
...chapterPaths === void 0 ? {} : { chapterPaths },
|
|
1285
1433
|
inputFormat: options.input.format,
|
|
1286
1434
|
outputFormat: options.output.format,
|
|
1287
|
-
outputPath: options.output.path
|
|
1435
|
+
outputPath: options.output.path,
|
|
1436
|
+
...sourceArtifact === void 0 ? {} : { sourceArtifact }
|
|
1288
1437
|
};
|
|
1289
1438
|
}
|
|
1290
1439
|
};
|
|
@@ -1297,11 +1446,67 @@ async function prepareDigestDirectory(path, required) {
|
|
|
1297
1446
|
return new NodeDirectory(resolved);
|
|
1298
1447
|
}
|
|
1299
1448
|
async function writeArchive(archive, path, format) {
|
|
1300
|
-
const
|
|
1449
|
+
const outputPath = format === "wikg" ? await assertStandaloneWikiGraphArchivePath(path) : resolveWikiGraphRuntimePath(path);
|
|
1450
|
+
const file = new NodeFile(outputPath);
|
|
1301
1451
|
if (format === "epub") await archive.exportEpub(file);
|
|
1302
1452
|
else if (format === "wikg") await archive.saveAs(file);
|
|
1453
|
+
else if (format === "pcex") throw new Error("pcex output is not supported.");
|
|
1303
1454
|
else await archive.exportText(file);
|
|
1304
1455
|
}
|
|
1456
|
+
async function resolveSourceArtifact(input) {
|
|
1457
|
+
if (!("path" in input) || input.format === "wikg") return void 0;
|
|
1458
|
+
if (input.format === "pcex") {
|
|
1459
|
+
return {
|
|
1460
|
+
digest: input.sourcePdf.digest.trim().toLowerCase(),
|
|
1461
|
+
mediaType: "application/pdf",
|
|
1462
|
+
...input.sourcePdf.name === void 0 ? {} : { name: input.sourcePdf.name }
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
if (input.format !== "epub") return void 0;
|
|
1466
|
+
const path = resolveWikiGraphRuntimePath(input.path);
|
|
1467
|
+
const hash = createHash("sha256");
|
|
1468
|
+
try {
|
|
1469
|
+
for await (const chunk of createReadStream(path)) {
|
|
1470
|
+
hash.update(chunk);
|
|
1471
|
+
}
|
|
1472
|
+
} catch (error) {
|
|
1473
|
+
if (isNodeErrorCode(error, "ENOENT")) return void 0;
|
|
1474
|
+
throw error;
|
|
1475
|
+
}
|
|
1476
|
+
return {
|
|
1477
|
+
digest: hash.digest("hex"),
|
|
1478
|
+
mediaType: "application/epub+zip",
|
|
1479
|
+
name: basename3(path)
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
function isNodeErrorCode(error, code) {
|
|
1483
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
1484
|
+
}
|
|
1485
|
+
function countTocChapters(items) {
|
|
1486
|
+
let count = 0;
|
|
1487
|
+
for (const item of items) {
|
|
1488
|
+
if (item.serialId !== void 0) count += 1;
|
|
1489
|
+
count += countTocChapters(
|
|
1490
|
+
item.children
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
return count;
|
|
1494
|
+
}
|
|
1495
|
+
function collectTocChapterPaths(items, ancestorKeys = []) {
|
|
1496
|
+
const paths = [];
|
|
1497
|
+
for (const item of items) {
|
|
1498
|
+
const key = item.key ?? `chapter-${item.serialId ?? "group"}`;
|
|
1499
|
+
const chapterPath = [...ancestorKeys, key];
|
|
1500
|
+
if (item.serialId !== void 0) paths.push(chapterPath.join("/"));
|
|
1501
|
+
paths.push(
|
|
1502
|
+
...collectTocChapterPaths(
|
|
1503
|
+
item.children,
|
|
1504
|
+
chapterPath
|
|
1505
|
+
)
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
return paths;
|
|
1509
|
+
}
|
|
1305
1510
|
export {
|
|
1306
1511
|
WikiGraphConversionManager
|
|
1307
1512
|
};
|