wiki-graph-sdk 0.7.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.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +32 -0
  3. package/dist/archives.cjs +2799 -0
  4. package/dist/archives.cjs.map +1 -0
  5. package/dist/archives.d.ts +249 -0
  6. package/dist/archives.js +2814 -0
  7. package/dist/archives.js.map +1 -0
  8. package/dist/conversions.cjs +1332 -0
  9. package/dist/conversions.cjs.map +1 -0
  10. package/dist/conversions.d.ts +37 -0
  11. package/dist/conversions.js +1308 -0
  12. package/dist/conversions.js.map +1 -0
  13. package/dist/data/editor/clue_reviewer.jinja +410 -0
  14. package/dist/data/editor/clue_reviewer_generator.jinja +428 -0
  15. package/dist/data/editor/revision_feedback.jinja +5 -0
  16. package/dist/data/editor/text_compressor.jinja +131 -0
  17. package/dist/data/guaranteed/response_intent_classifier.jinja +8 -0
  18. package/dist/data/output/epub/cover.xhtml.jinja +13 -0
  19. package/dist/data/output/epub/nav.xhtml.jinja +12 -0
  20. package/dist/data/output/epub/package.opf.xml.jinja +39 -0
  21. package/dist/data/output/epub/section.xhtml.jinja +12 -0
  22. package/dist/data/topologization/book_coherence_extraction.jinja +223 -0
  23. package/dist/data/topologization/chunk_extraction.jinja +233 -0
  24. package/dist/data/topologization/evidence_choice.jinja +38 -0
  25. package/dist/data/topologization/translate_chunks.jinja +138 -0
  26. package/dist/data/topologization/user_focused_extraction.jinja +378 -0
  27. package/dist/default-worker.cjs +1709 -0
  28. package/dist/default-worker.cjs.map +1 -0
  29. package/dist/default-worker.d.ts +10 -0
  30. package/dist/default-worker.js +1705 -0
  31. package/dist/default-worker.js.map +1 -0
  32. package/dist/embedding.cjs +1091 -0
  33. package/dist/embedding.cjs.map +1 -0
  34. package/dist/embedding.d.ts +26 -0
  35. package/dist/embedding.js +1062 -0
  36. package/dist/embedding.js.map +1 -0
  37. package/dist/entry-context.cjs +91 -0
  38. package/dist/entry-context.cjs.map +1 -0
  39. package/dist/entry-context.d.ts +20 -0
  40. package/dist/entry-context.js +68 -0
  41. package/dist/entry-context.js.map +1 -0
  42. package/dist/gc.cjs +731 -0
  43. package/dist/gc.cjs.map +1 -0
  44. package/dist/gc.d.ts +14 -0
  45. package/dist/gc.js +707 -0
  46. package/dist/gc.js.map +1 -0
  47. package/dist/index.cjs +4760 -0
  48. package/dist/index.cjs.map +1 -0
  49. package/dist/index.d.ts +213 -0
  50. package/dist/index.js +4808 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/jobs-Dd5PMSId.d.ts +98 -0
  53. package/dist/llm.cjs +101 -0
  54. package/dist/llm.cjs.map +1 -0
  55. package/dist/llm.d.ts +7 -0
  56. package/dist/llm.js +79 -0
  57. package/dist/llm.js.map +1 -0
  58. package/dist/local-config.cjs +1024 -0
  59. package/dist/local-config.cjs.map +1 -0
  60. package/dist/local-config.d.ts +14 -0
  61. package/dist/local-config.js +989 -0
  62. package/dist/local-config.js.map +1 -0
  63. package/dist/maintenance.cjs +800 -0
  64. package/dist/maintenance.cjs.map +1 -0
  65. package/dist/maintenance.d.ts +24 -0
  66. package/dist/maintenance.js +778 -0
  67. package/dist/maintenance.js.map +1 -0
  68. package/dist/node-platform.cjs +758 -0
  69. package/dist/node-platform.cjs.map +1 -0
  70. package/dist/node-platform.d.ts +49 -0
  71. package/dist/node-platform.js +723 -0
  72. package/dist/node-platform.js.map +1 -0
  73. package/dist/planning.cjs +164 -0
  74. package/dist/planning.cjs.map +1 -0
  75. package/dist/planning.d.ts +43 -0
  76. package/dist/planning.js +137 -0
  77. package/dist/planning.js.map +1 -0
  78. package/dist/runtime-config.cjs +1155 -0
  79. package/dist/runtime-config.cjs.map +1 -0
  80. package/dist/runtime-config.d.ts +50 -0
  81. package/dist/runtime-config.js +1127 -0
  82. package/dist/runtime-config.js.map +1 -0
  83. package/dist/stage.cjs +1265 -0
  84. package/dist/stage.cjs.map +1 -0
  85. package/dist/stage.d.ts +16 -0
  86. package/dist/stage.js +1239 -0
  87. package/dist/stage.js.map +1 -0
  88. package/dist/wikispine.cjs +85 -0
  89. package/dist/wikispine.cjs.map +1 -0
  90. package/dist/wikispine.d.ts +6 -0
  91. package/dist/wikispine.js +63 -0
  92. package/dist/wikispine.js.map +1 -0
  93. package/dist/worker.cjs +738 -0
  94. package/dist/worker.cjs.map +1 -0
  95. package/dist/worker.d.ts +157 -0
  96. package/dist/worker.js +715 -0
  97. package/dist/worker.js.map +1 -0
  98. package/package.json +151 -0
@@ -0,0 +1,2799 @@
1
+ globalThis.__WIKIGRAPH_DATA_DIR__ ??= require("path").resolve(__dirname, "data");
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/archive/index.ts
32
+ var archive_exports = {};
33
+ __export(archive_exports, {
34
+ WikiGraphArchiveExistsError: () => WikiGraphArchiveExistsError,
35
+ WikiGraphArchiveHandle: () => WikiGraphArchiveHandle,
36
+ WikiGraphArchiveManager: () => WikiGraphArchiveManager,
37
+ createWikiGraphArchiveInspectReport: () => createWikiGraphArchiveInspectReport,
38
+ resolveWikiGraphArchiveLocation: () => resolveWikiGraphArchiveLocation
39
+ });
40
+ module.exports = __toCommonJS(archive_exports);
41
+
42
+ // src/archive/archive.ts
43
+ var import_crypto = require("crypto");
44
+ var import_promises3 = require("fs/promises");
45
+ var import_os3 = require("os");
46
+ var import_path3 = require("path");
47
+ var import_stream = require("stream");
48
+ var import_wiki_graph_core5 = require("wiki-graph-core");
49
+
50
+ // src/conversions.ts
51
+ var import_promises2 = require("fs/promises");
52
+ var import_wiki_graph_core2 = require("wiki-graph-core");
53
+
54
+ // src/llm.ts
55
+ var import_anthropic = require("@ai-sdk/anthropic");
56
+ var import_google = require("@ai-sdk/google");
57
+ var import_openai = require("@ai-sdk/openai");
58
+ var import_openai_compatible = require("@ai-sdk/openai-compatible");
59
+ function buildWikiGraphLLMOptions(config) {
60
+ const llm = config.llm;
61
+ if (llm?.provider === void 0 || llm.model === void 0) {
62
+ throw new Error(
63
+ "Missing LLM configuration. Set --llm for one run, or configure `wikg://local/config/llm` with provider and model."
64
+ );
65
+ }
66
+ return {
67
+ model: createLanguageModel(llm.provider, llm.model, {
68
+ apiKey: llm.apiKey,
69
+ baseURL: llm.baseURL,
70
+ name: llm.name
71
+ }),
72
+ ...config.concurrent?.request === void 0 ? {} : { concurrent: config.concurrent.request }
73
+ };
74
+ }
75
+ function createLanguageModel(provider, model, options) {
76
+ switch (provider) {
77
+ case "anthropic": {
78
+ const anthropic = (0, import_anthropic.createAnthropic)({
79
+ ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
80
+ ...options.name === void 0 ? {} : { name: options.name }
81
+ });
82
+ return anthropic(model);
83
+ }
84
+ case "google": {
85
+ const google = (0, import_google.createGoogleGenerativeAI)({
86
+ ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
87
+ ...options.name === void 0 ? {} : { name: options.name }
88
+ });
89
+ return google(model);
90
+ }
91
+ case "openai": {
92
+ if (options.baseURL !== void 0) {
93
+ throw new Error(
94
+ "openai does not accept llm.baseURL or baseURL in --llm JSON. Use openai-compatible for third-party OpenAI-style APIs."
95
+ );
96
+ }
97
+ const openai = (0, import_openai.createOpenAI)({
98
+ ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
99
+ ...options.name === void 0 ? {} : { name: options.name }
100
+ });
101
+ return openai(model);
102
+ }
103
+ case "openai-compatible": {
104
+ if (options.baseURL === void 0) {
105
+ throw new Error(
106
+ "openai-compatible requires llm.baseURL or baseURL in --llm JSON."
107
+ );
108
+ }
109
+ const openaiCompatible = (0, import_openai_compatible.createOpenAICompatible)({
110
+ baseURL: options.baseURL,
111
+ name: options.name ?? createOpenAICompatibleName(options.baseURL),
112
+ ...options.apiKey === void 0 ? {} : { apiKey: options.apiKey }
113
+ });
114
+ return openaiCompatible(model);
115
+ }
116
+ }
117
+ }
118
+ function createOpenAICompatibleName(baseURL) {
119
+ try {
120
+ return new URL(baseURL).hostname;
121
+ } catch {
122
+ return "openai-compatible";
123
+ }
124
+ }
125
+
126
+ // src/node-platform.ts
127
+ var asyncHooks = __toESM(require("async_hooks"), 1);
128
+ var crypto = __toESM(require("crypto"), 1);
129
+ var fs = __toESM(require("fs"), 1);
130
+ var fsPromises = __toESM(require("fs/promises"), 1);
131
+ var os = __toESM(require("os"), 1);
132
+ var pathModule = __toESM(require("path"), 1);
133
+ var process2 = __toESM(require("process"), 1);
134
+ var import_node_stream = require("stream");
135
+ var import_promises = require("stream/promises");
136
+ var sqlite3 = __toESM(require("sqlite3"), 1);
137
+ var yauzl = __toESM(require("yauzl"), 1);
138
+ var yazl = __toESM(require("yazl"), 1);
139
+ var import_nunjucks = require("nunjucks");
140
+ var import_platform = require("wiki-graph-core/platform");
141
+ var nodeProcess = process2.default ?? process2;
142
+ var nodeSqlite3 = sqlite3.default ?? sqlite3;
143
+ var NODE_RESOURCE_KIND = /* @__PURE__ */ Symbol.for("wiki-graph-sdk.node-resource.v1");
144
+ var NodeFile = class {
145
+ constructor(path, name = pathModule.basename(path)) {
146
+ this.path = path;
147
+ this.identity = encodeNodeResourceIdentity("file", path);
148
+ this.name = name;
149
+ }
150
+ path;
151
+ kind = "file";
152
+ [NODE_RESOURCE_KIND] = "file";
153
+ identity;
154
+ name;
155
+ async openReader() {
156
+ const handle = await fsPromises.open(this.path, "r");
157
+ let closed = false;
158
+ try {
159
+ const stats = await handle.stat();
160
+ if (!stats.isFile())
161
+ throw new Error("File reader requires a regular file");
162
+ const { size } = stats;
163
+ return {
164
+ size,
165
+ close: async () => {
166
+ if (!closed) await handle.close();
167
+ closed = true;
168
+ },
169
+ read: async (offset, length) => {
170
+ if (closed) throw new Error("Cannot read from a closed FileReader");
171
+ assertFileRange(offset, length, size);
172
+ const buffer = Buffer.allocUnsafe(length);
173
+ let bytesRead = 0;
174
+ while (bytesRead < length) {
175
+ const result = await handle.read(
176
+ buffer,
177
+ bytesRead,
178
+ length - bytesRead,
179
+ offset + bytesRead
180
+ );
181
+ if (result.bytesRead === 0) {
182
+ throw new Error("File changed while reading its byte range");
183
+ }
184
+ bytesRead += result.bytesRead;
185
+ }
186
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, bytesRead);
187
+ }
188
+ };
189
+ } catch (error) {
190
+ await handle.close();
191
+ throw error;
192
+ }
193
+ }
194
+ async getLastModified() {
195
+ try {
196
+ return (await fsPromises.stat(this.path)).mtimeMs;
197
+ } catch (error) {
198
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
199
+ return void 0;
200
+ }
201
+ throw error;
202
+ }
203
+ }
204
+ async openWriter() {
205
+ await fsPromises.mkdir(pathModule.dirname(this.path), { recursive: true });
206
+ const temporary = `${this.path}.tmp-${crypto.randomUUID()}`;
207
+ const handle = await fsPromises.open(temporary, "wx");
208
+ let closed = false;
209
+ return {
210
+ write: async (data) => {
211
+ if (closed) throw new Error("Cannot write to a closed FileWriter");
212
+ await writeNodeFileHandle(handle, data);
213
+ },
214
+ writeAt: async (offset, data) => {
215
+ if (closed) throw new Error("Cannot write to a closed FileWriter");
216
+ assertFileOffset(offset);
217
+ await writeNodeFileHandle(handle, data, offset);
218
+ },
219
+ commit: async () => {
220
+ if (!closed) {
221
+ await handle.close();
222
+ await fsPromises.rename(temporary, this.path);
223
+ }
224
+ closed = true;
225
+ },
226
+ abort: async () => {
227
+ if (!closed) {
228
+ await handle.close();
229
+ await fsPromises.rm(temporary, { force: true });
230
+ }
231
+ closed = true;
232
+ }
233
+ };
234
+ }
235
+ };
236
+ async function writeNodeFileHandle(handle, data, position) {
237
+ const buffer = Buffer.from(data);
238
+ let written = 0;
239
+ while (written < buffer.byteLength) {
240
+ const result = await handle.write(
241
+ buffer,
242
+ written,
243
+ buffer.byteLength - written,
244
+ position === void 0 ? null : position + written
245
+ );
246
+ if (result.bytesWritten === 0) {
247
+ throw new Error("File writer made no progress");
248
+ }
249
+ written += result.bytesWritten;
250
+ }
251
+ }
252
+ function assertFileRange(offset, length, size) {
253
+ assertFileOffset(offset);
254
+ if (!Number.isSafeInteger(length) || length < 0) {
255
+ throw new RangeError(
256
+ "File read length must be a non-negative safe integer"
257
+ );
258
+ }
259
+ if (offset > size || length > size - offset) {
260
+ throw new RangeError("File read range exceeds the file size");
261
+ }
262
+ }
263
+ function assertFileOffset(offset) {
264
+ if (!Number.isSafeInteger(offset) || offset < 0) {
265
+ throw new RangeError("File offset must be a non-negative safe integer");
266
+ }
267
+ }
268
+ var NodeDirectory = class _NodeDirectory {
269
+ constructor(path) {
270
+ this.path = path;
271
+ this.identity = encodeNodeResourceIdentity("directory", path);
272
+ this.name = pathModule.basename(path);
273
+ }
274
+ path;
275
+ kind = "directory";
276
+ [NODE_RESOURCE_KIND] = "directory";
277
+ identity;
278
+ name;
279
+ async getFile(name) {
280
+ assertChildName(name);
281
+ const file = new NodeFile(pathModule.join(this.path, name));
282
+ try {
283
+ return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
284
+ } catch {
285
+ return void 0;
286
+ }
287
+ }
288
+ async getLastModified() {
289
+ try {
290
+ return (await fsPromises.stat(this.path)).mtimeMs;
291
+ } catch (error) {
292
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
293
+ return void 0;
294
+ }
295
+ throw error;
296
+ }
297
+ }
298
+ async getDirectory(name) {
299
+ assertChildName(name);
300
+ const directory = new _NodeDirectory(pathModule.join(this.path, name));
301
+ try {
302
+ return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
303
+ } catch {
304
+ return void 0;
305
+ }
306
+ }
307
+ async list() {
308
+ const entries = await fsPromises.readdir(this.path, {
309
+ withFileTypes: true
310
+ });
311
+ return entries.map(
312
+ (entry) => entry.isDirectory() ? new _NodeDirectory(pathModule.join(this.path, entry.name)) : new NodeFile(pathModule.join(this.path, entry.name), entry.name)
313
+ );
314
+ }
315
+ async createFile(name) {
316
+ assertChildName(name);
317
+ await fsPromises.mkdir(this.path, { recursive: true });
318
+ const file = new NodeFile(pathModule.join(this.path, name));
319
+ await fsPromises.writeFile(file.path, "", { flag: "wx" });
320
+ return file;
321
+ }
322
+ async createDirectory(name) {
323
+ assertChildName(name);
324
+ await fsPromises.mkdir(this.path, { recursive: true });
325
+ const directory = new _NodeDirectory(pathModule.join(this.path, name));
326
+ await fsPromises.mkdir(directory.path);
327
+ return directory;
328
+ }
329
+ async remove(name, options) {
330
+ assertChildName(name);
331
+ await fsPromises.rm(pathModule.join(this.path, name), {
332
+ force: true,
333
+ recursive: options?.recursive === true
334
+ });
335
+ }
336
+ };
337
+ function encodeNodeResourceIdentity(kind, path) {
338
+ return `node-${kind}:${Buffer.from(pathModule.resolve(path), "utf8").toString("base64url")}`;
339
+ }
340
+ function decodeNodeResourceIdentity(identity, kind) {
341
+ const prefix = `node-${kind}:`;
342
+ if (!identity.startsWith(prefix)) {
343
+ return pathModule.isAbsolute(identity) ? identity : void 0;
344
+ }
345
+ try {
346
+ return Buffer.from(identity.slice(prefix.length), "base64url").toString(
347
+ "utf8"
348
+ );
349
+ } catch {
350
+ return void 0;
351
+ }
352
+ }
353
+ function getNodeResourcePath(resource) {
354
+ const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
355
+ if (brandedPath !== void 0) return brandedPath;
356
+ if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
357
+ return resource.path;
358
+ }
359
+ throw new TypeError("Expected a Node File or Directory resource");
360
+ }
361
+ function assertChildName(name) {
362
+ if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
363
+ throw new TypeError(`Directory child must be a relative name: ${name}`);
364
+ }
365
+ }
366
+ var NodeDatabaseConnection = class {
367
+ constructor(database) {
368
+ this.database = database;
369
+ }
370
+ database;
371
+ async close() {
372
+ await new Promise((resolve3, reject) => {
373
+ this.database.close((error) => {
374
+ if (error) reject(error);
375
+ else resolve3();
376
+ });
377
+ });
378
+ }
379
+ async execute(sql) {
380
+ await new Promise((resolve3, reject) => {
381
+ this.database.exec(sql, (error) => {
382
+ if (error) reject(error);
383
+ else resolve3();
384
+ });
385
+ });
386
+ }
387
+ async queryAll(sql, params = []) {
388
+ return await new Promise((resolve3, reject) => {
389
+ this.database.all(sql, [...params], (error, rows) => {
390
+ if (error) reject(error);
391
+ else resolve3(rows);
392
+ });
393
+ });
394
+ }
395
+ async queryOne(sql, params = []) {
396
+ return await new Promise((resolve3, reject) => {
397
+ this.database.get(sql, [...params], (error, row) => {
398
+ if (error) reject(error);
399
+ else resolve3(row);
400
+ });
401
+ });
402
+ }
403
+ async run(sql, params = []) {
404
+ await new Promise((resolve3, reject) => {
405
+ this.database.run(sql, [...params], (error) => {
406
+ if (error) reject(error);
407
+ else resolve3();
408
+ });
409
+ });
410
+ }
411
+ };
412
+ async function openNodeDatabase(file, options) {
413
+ const flags = (options.mode === "readonly" ? nodeSqlite3.OPEN_READONLY : nodeSqlite3.OPEN_READWRITE | (options.create ? nodeSqlite3.OPEN_CREATE : 0)) | nodeSqlite3.OPEN_FULLMUTEX;
414
+ return new NodeDatabaseConnection(await openNativeNodeDatabase(file, flags));
415
+ }
416
+ async function openNativeNodeDatabase(file, flags) {
417
+ const path = getBrandedNodeResourcePath(file, "file");
418
+ if (path === void 0) {
419
+ throw new TypeError("The Node database adapter requires a NodeFile");
420
+ }
421
+ return await new Promise((resolve3, reject) => {
422
+ const database = new nodeSqlite3.Database(path, flags, (error) => {
423
+ if (error) reject(error);
424
+ else resolve3(database);
425
+ });
426
+ });
427
+ }
428
+ async function openNodeZip(file) {
429
+ const path = getBrandedNodeResourcePath(file, "file");
430
+ if (path === void 0) {
431
+ throw new TypeError("The Node ZIP adapter requires a NodeFile");
432
+ }
433
+ const zipFile = await new Promise((resolve3, reject) => {
434
+ yauzl.open(
435
+ path,
436
+ { autoClose: false, lazyEntries: true },
437
+ (error, opened) => {
438
+ if (error || opened === void 0)
439
+ reject(error ?? new Error("Cannot open ZIP"));
440
+ else resolve3(opened);
441
+ }
442
+ );
443
+ });
444
+ const entries = await new Promise(
445
+ (resolve3, reject) => {
446
+ const discovered = /* @__PURE__ */ new Map();
447
+ const rejectAndClose = (error) => {
448
+ zipFile.close();
449
+ reject(
450
+ error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
451
+ );
452
+ };
453
+ const resolveEntries = () => {
454
+ zipFile.off("error", rejectAndClose);
455
+ resolve3(discovered);
456
+ };
457
+ zipFile.on("entry", (entry) => {
458
+ if (!entry.fileName.endsWith("/")) {
459
+ discovered.set(entry.fileName, entry);
460
+ }
461
+ zipFile.readEntry();
462
+ });
463
+ zipFile.once("error", rejectAndClose);
464
+ zipFile.once("end", resolveEntries);
465
+ zipFile.readEntry();
466
+ }
467
+ );
468
+ let closed = false;
469
+ return {
470
+ close: () => {
471
+ if (!closed) zipFile.close();
472
+ closed = true;
473
+ return Promise.resolve();
474
+ },
475
+ copyEntry: async (name, target) => {
476
+ if (closed) throw new Error("Cannot read a closed ZIP archive");
477
+ const entry = entries.get(name);
478
+ if (entry === void 0) return false;
479
+ await copyNodeZipEntry(zipFile, entry, target);
480
+ return true;
481
+ },
482
+ getEntrySize: (name) => {
483
+ if (closed) throw new Error("Cannot read a closed ZIP archive");
484
+ return Promise.resolve(entries.get(name)?.uncompressedSize);
485
+ },
486
+ listEntries: () => Promise.resolve([...entries.keys()]),
487
+ readEntryRange: async (name, offset, length) => {
488
+ if (closed) throw new Error("Cannot read a closed ZIP archive");
489
+ const entry = entries.get(name);
490
+ if (entry === void 0) return void 0;
491
+ return await readNodeZipEntryRange(zipFile, entry, offset, length);
492
+ },
493
+ readEntry: async (name) => {
494
+ if (closed) throw new Error("Cannot read a closed ZIP archive");
495
+ const entry = entries.get(name);
496
+ if (entry === void 0) return void 0;
497
+ return await readNodeZipEntry(zipFile, entry);
498
+ }
499
+ };
500
+ }
501
+ async function copyNodeZipEntry(zipFile, entry, target) {
502
+ const writer = await target.openWriter();
503
+ try {
504
+ const stream = await openNodeZipEntryStream(zipFile, entry);
505
+ await writeNodeStream(stream, writer);
506
+ await writer.commit();
507
+ } catch (error) {
508
+ await writer.abort().catch(() => void 0);
509
+ throw error;
510
+ }
511
+ }
512
+ async function readNodeZipEntry(zipFile, entry) {
513
+ return await collectNodeStream(await openNodeZipEntryStream(zipFile, entry));
514
+ }
515
+ async function readNodeZipEntryRange(zipFile, entry, offset, length) {
516
+ assertFileRange(offset, length, entry.uncompressedSize);
517
+ if (length === 0) return new Uint8Array();
518
+ if (entry.compressionMethod !== 0) {
519
+ const content2 = await collectDecodedNodeStreamRange(
520
+ await openNodeZipEntryStream(zipFile, entry),
521
+ offset,
522
+ length
523
+ );
524
+ return new Uint8Array(
525
+ content2.buffer,
526
+ content2.byteOffset,
527
+ content2.byteLength
528
+ );
529
+ }
530
+ const content = await collectNodeStream(
531
+ await openNodeZipEntryStream(zipFile, entry, {
532
+ decompress: null,
533
+ decrypt: null,
534
+ end: offset + length,
535
+ start: offset
536
+ })
537
+ );
538
+ return new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
539
+ }
540
+ async function collectDecodedNodeStreamRange(input, offset, length) {
541
+ const chunks = [];
542
+ const end = offset + length;
543
+ let position = 0;
544
+ let collected = 0;
545
+ for await (const value of input) {
546
+ const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
547
+ const chunkEnd = position + chunk.byteLength;
548
+ if (chunkEnd > offset && position < end) {
549
+ const selected = chunk.subarray(
550
+ Math.max(0, offset - position),
551
+ Math.min(chunk.byteLength, end - position)
552
+ );
553
+ chunks.push(selected);
554
+ collected += selected.byteLength;
555
+ }
556
+ position = chunkEnd;
557
+ if (position >= end) break;
558
+ }
559
+ if (collected !== length) {
560
+ throw new Error("ZIP entry changed while reading its byte range");
561
+ }
562
+ return Buffer.concat(chunks, collected);
563
+ }
564
+ async function openNodeZipEntryStream(zipFile, entry, options) {
565
+ return await new Promise((resolve3, reject) => {
566
+ const callback = (error, opened) => {
567
+ if (error || opened === void 0) {
568
+ reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
569
+ } else {
570
+ resolve3(opened);
571
+ }
572
+ };
573
+ if (options === void 0) zipFile.openReadStream(entry, callback);
574
+ else zipFile.openReadStream(entry, options, callback);
575
+ });
576
+ }
577
+ async function writeNodeZip(file, entries) {
578
+ const zipFile = new yazl.ZipFile();
579
+ const writer = await file.openWriter();
580
+ const outputStream = zipFile.outputStream;
581
+ const outputDone = writeNodeStream(outputStream, writer);
582
+ const entryState = { cancelled: false, complete: false };
583
+ let iterator;
584
+ try {
585
+ iterator = getNodeZipEntryIterator(entries);
586
+ const entriesDone = addNodeZipEntries(zipFile, iterator, entryState);
587
+ await Promise.race([entriesDone, outputDone]);
588
+ if (!entryState.complete) {
589
+ throw new Error("ZIP output ended before entry production completed");
590
+ }
591
+ zipFile.end();
592
+ await outputDone;
593
+ await writer.commit();
594
+ } catch (error) {
595
+ entryState.cancelled = true;
596
+ if (!entryState.complete && iterator !== void 0) {
597
+ closeNodeZipEntryIterator(iterator);
598
+ }
599
+ if (!outputStream.destroyed) {
600
+ outputStream.destroy(
601
+ error instanceof Error ? error : new Error("Cannot write ZIP archive")
602
+ );
603
+ }
604
+ await outputDone.catch(() => void 0);
605
+ await writer.abort().catch(() => void 0);
606
+ throw error;
607
+ }
608
+ }
609
+ async function addNodeZipEntries(zipFile, iterator, state) {
610
+ while (!state.cancelled) {
611
+ const result = await iterator.next();
612
+ if (state.cancelled) return;
613
+ if (result.done) {
614
+ state.complete = true;
615
+ return;
616
+ }
617
+ if ("file" in result.value) {
618
+ zipFile.addReadStream(
619
+ createNodeFileReadStream(result.value.file),
620
+ result.value.name,
621
+ { compress: false }
622
+ );
623
+ } else if ("data" in result.value) {
624
+ zipFile.addBuffer(Buffer.from(result.value.data), result.value.name, {
625
+ compress: false
626
+ });
627
+ } else {
628
+ zipFile.addReadStream(
629
+ createNodeRangeReadStream(result.value),
630
+ result.value.name,
631
+ { compress: false, size: result.value.size }
632
+ );
633
+ }
634
+ }
635
+ }
636
+ function createNodeFileReadStream(file) {
637
+ return import_node_stream.Readable.from(
638
+ (async function* () {
639
+ const reader = await file.openReader();
640
+ try {
641
+ for (let offset = 0; offset < reader.size; ) {
642
+ const chunk = await reader.read(
643
+ offset,
644
+ Math.min(64 * 1024, reader.size - offset)
645
+ );
646
+ offset += chunk.byteLength;
647
+ yield chunk;
648
+ }
649
+ } finally {
650
+ await reader.close();
651
+ }
652
+ })()
653
+ );
654
+ }
655
+ function getBrandedNodeResourcePath(resource, kind) {
656
+ if (typeof resource !== "object" || resource === null) return void 0;
657
+ const candidate = resource;
658
+ return candidate[NODE_RESOURCE_KIND] === kind && typeof candidate.path === "string" ? candidate.path : void 0;
659
+ }
660
+ function createNodeRangeReadStream(entry) {
661
+ return import_node_stream.Readable.from(
662
+ (async function* () {
663
+ for (let offset = 0; offset < entry.size; ) {
664
+ const chunk = await entry.read(
665
+ offset,
666
+ Math.min(64 * 1024, entry.size - offset)
667
+ );
668
+ offset += chunk.byteLength;
669
+ yield chunk;
670
+ }
671
+ })()
672
+ );
673
+ }
674
+ function getNodeZipEntryIterator(entries) {
675
+ const asyncIterator = entries[Symbol.asyncIterator];
676
+ return asyncIterator === void 0 ? entries[Symbol.iterator]() : asyncIterator.call(entries);
677
+ }
678
+ function closeNodeZipEntryIterator(iterator) {
679
+ if (iterator.return === void 0) return;
680
+ try {
681
+ void Promise.resolve(iterator.return()).catch(() => void 0);
682
+ } catch {
683
+ }
684
+ }
685
+ async function writeNodeStream(input, writer) {
686
+ await (0, import_promises.pipeline)(
687
+ input,
688
+ new import_node_stream.Writable({
689
+ write: (chunk, _encoding, callback) => {
690
+ void writer.write(chunk).then(
691
+ () => callback(),
692
+ (error) => callback(
693
+ error instanceof Error ? error : new Error("Cannot stream ZIP archive")
694
+ )
695
+ );
696
+ }
697
+ })
698
+ );
699
+ }
700
+ async function collectNodeStream(input) {
701
+ return await new Promise((resolve3, reject) => {
702
+ const chunks = [];
703
+ input.on("data", (chunk) => chunks.push(chunk));
704
+ input.once("error", reject);
705
+ input.once("end", () => resolve3(Buffer.concat(chunks)));
706
+ });
707
+ }
708
+ var nodeWikiGraphPlatform = {
709
+ asyncContext: {
710
+ create: () => new asyncHooks.AsyncLocalStorage()
711
+ },
712
+ database: { open: openNodeDatabase },
713
+ lifecycle: {
714
+ instanceId: `node-process:${nodeProcess.pid}`,
715
+ isInstanceAlive: (instanceId) => {
716
+ const match = /^node-process:(\d+)$/u.exec(instanceId);
717
+ if (match === null) return Promise.resolve(void 0);
718
+ try {
719
+ nodeProcess.kill(Number(match[1]), 0);
720
+ return Promise.resolve(true);
721
+ } catch (error) {
722
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH") {
723
+ return Promise.resolve(false);
724
+ }
725
+ return Promise.resolve(true);
726
+ }
727
+ }
728
+ },
729
+ resources: {
730
+ getDirectory: (identity) => {
731
+ const path = decodeNodeResourceIdentity(identity, "directory");
732
+ return Promise.resolve(
733
+ path === void 0 ? void 0 : new NodeDirectory(path)
734
+ );
735
+ },
736
+ getFile: (identity) => {
737
+ const path = decodeNodeResourceIdentity(identity, "file");
738
+ return Promise.resolve(
739
+ path === void 0 ? void 0 : new NodeFile(path)
740
+ );
741
+ },
742
+ resolveLegacyDirectory: (reference) => {
743
+ const path = decodeNodeResourceIdentity(reference, "directory");
744
+ return Promise.resolve(
745
+ path === void 0 ? void 0 : new NodeDirectory(path)
746
+ );
747
+ }
748
+ },
749
+ templates: {
750
+ createEnvironment: (options) => new import_nunjucks.Environment(new NodeTemplateLoader(), options)
751
+ },
752
+ zip: {
753
+ open: openNodeZip,
754
+ write: writeNodeZip
755
+ }
756
+ };
757
+ var NodeTemplateLoader = class extends import_nunjucks.Loader {
758
+ #dataDirectory;
759
+ constructor(dataDirectory) {
760
+ super();
761
+ this.#dataDirectory = dataDirectory;
762
+ }
763
+ getSource(templateName) {
764
+ const name = (0, import_platform.normalizeTemplateName)(templateName);
765
+ const filePath = pathModule.join(
766
+ this.#dataDirectory ?? resolveNodeDataDirectory(),
767
+ name
768
+ );
769
+ return {
770
+ noCache: false,
771
+ path: name,
772
+ src: fs.readFileSync(filePath, "utf8")
773
+ };
774
+ }
775
+ };
776
+ function resolveNodeDataDirectory() {
777
+ const injected = globalThis.__WIKIGRAPH_DATA_DIR__;
778
+ if (typeof injected === "string" && injected !== "") return injected;
779
+ let directory = nodeProcess.cwd();
780
+ while (true) {
781
+ for (const candidate of [
782
+ pathModule.join(directory, "data"),
783
+ pathModule.join(directory, "packages", "core", "data")
784
+ ]) {
785
+ try {
786
+ if (fs.statSync(candidate).isDirectory()) return candidate;
787
+ } catch {
788
+ }
789
+ }
790
+ const parent = pathModule.dirname(directory);
791
+ if (parent === directory)
792
+ throw new Error("Could not locate data directory");
793
+ directory = parent;
794
+ }
795
+ }
796
+
797
+ // src/runtime-config.ts
798
+ var import_zod = require("zod");
799
+
800
+ // src/local-config.ts
801
+ var import_wiki_graph_core = require("wiki-graph-core");
802
+
803
+ // src/runtime-context.ts
804
+ var import_async_hooks = require("async_hooks");
805
+ var RUNTIME_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("wiki-graph-sdk.runtime-context.v1");
806
+ var runtimeContext = getRuntimeContextStorage();
807
+ function getRuntimeContextStorage() {
808
+ const runtimeGlobal = globalThis;
809
+ const existing = runtimeGlobal[RUNTIME_CONTEXT_KEY];
810
+ if (existing !== void 0) {
811
+ return existing;
812
+ }
813
+ const storage = new import_async_hooks.AsyncLocalStorage();
814
+ Object.defineProperty(runtimeGlobal, RUNTIME_CONTEXT_KEY, {
815
+ configurable: false,
816
+ enumerable: false,
817
+ value: storage,
818
+ writable: false
819
+ });
820
+ return storage;
821
+ }
822
+ function getWikiGraphSDKRuntimeContext() {
823
+ return runtimeContext.getStore() ?? {
824
+ cwd: process.cwd(),
825
+ env: process.env,
826
+ envPolicy: "production"
827
+ };
828
+ }
829
+ function getWikiGraphSDKStateDir() {
830
+ return getWikiGraphSDKRuntimeContext().stateDir;
831
+ }
832
+
833
+ // src/local-config.ts
834
+ var import_path = require("path");
835
+ var import_os = require("os");
836
+ var LOCAL_CONFIG_SCHEMA_SQL = `
837
+ CREATE TABLE IF NOT EXISTS config_sections (
838
+ section TEXT PRIMARY KEY,
839
+ value_json TEXT NOT NULL,
840
+ updated_at INTEGER NOT NULL
841
+ );
842
+ `;
843
+ async function readLocalConfigSection(section) {
844
+ return await withLocalConfigDatabase(async (database) => {
845
+ return await readLocalConfigSectionInDatabase(database, section);
846
+ });
847
+ }
848
+ function validateLocalConfigSection(section, value) {
849
+ switch (section) {
850
+ case "embeddings":
851
+ return validateEmbeddingsConfig(value);
852
+ case "llm":
853
+ return validateLLMConfig(value);
854
+ case "concurrent":
855
+ return validateConcurrentConfig(value);
856
+ case "wikimedia":
857
+ return validateWikimediaConfig(value);
858
+ case "wikispine":
859
+ return validateWikispineConfig(value);
860
+ }
861
+ }
862
+ async function withLocalConfigDatabase(operation) {
863
+ const database = await (0, import_wiki_graph_core.openSharedStateDatabase)(
864
+ new NodeFile(
865
+ (0, import_path.join)(
866
+ getWikiGraphSDKStateDir() ?? (0, import_path.join)((0, import_os.homedir)(), ".wikigraph"),
867
+ "core.sqlite"
868
+ )
869
+ ),
870
+ LOCAL_CONFIG_SCHEMA_SQL
871
+ );
872
+ try {
873
+ return await operation(database);
874
+ } finally {
875
+ await database.close();
876
+ }
877
+ }
878
+ async function readLocalConfigSectionInDatabase(database, section) {
879
+ const value = await database.queryOne(
880
+ "SELECT value_json FROM config_sections WHERE section = ?",
881
+ [section],
882
+ (row) => String(row.value_json)
883
+ );
884
+ return value === void 0 ? {} : parseSectionJSON(section, value);
885
+ }
886
+ function parseSectionJSON(section, value) {
887
+ let parsed;
888
+ try {
889
+ parsed = JSON.parse(value);
890
+ } catch (error) {
891
+ throw new Error(
892
+ `Invalid local config JSON for ${section}: ${formatError(error)}`
893
+ );
894
+ }
895
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
896
+ throw new Error(
897
+ `Invalid local config section ${section}: expected object.`
898
+ );
899
+ }
900
+ return validateLocalConfigSection(section, parsed);
901
+ }
902
+ function validateEmbeddingsConfig(value) {
903
+ const allowedKeys = /* @__PURE__ */ new Set([
904
+ "apiKey",
905
+ "baseURL",
906
+ "dimensions",
907
+ "model",
908
+ "name",
909
+ "provider"
910
+ ]);
911
+ const allowedProviders = /* @__PURE__ */ new Set(["openai", "openai-compatible"]);
912
+ const next = {};
913
+ for (const [key, entry] of Object.entries(value)) {
914
+ if (!allowedKeys.has(key)) {
915
+ throw new Error(`Unknown embeddings config key: ${key}`);
916
+ }
917
+ if (key === "dimensions") {
918
+ const parsed = typeof entry === "number" ? entry : typeof entry === "string" ? Number(entry) : NaN;
919
+ if (!Number.isInteger(parsed) || parsed <= 0) {
920
+ throw new Error("embeddings.dimensions must be a positive integer.");
921
+ }
922
+ next[key] = parsed;
923
+ continue;
924
+ }
925
+ if (typeof entry !== "string" || entry.trim() === "") {
926
+ throw new Error(`embeddings.${key} must be a non-empty string.`);
927
+ }
928
+ const normalized = entry.trim();
929
+ if (key === "provider" && !allowedProviders.has(normalized)) {
930
+ throw new Error(`Unknown embeddings.provider: ${normalized}`);
931
+ }
932
+ next[key] = normalized;
933
+ }
934
+ return next;
935
+ }
936
+ function validateLLMConfig(value) {
937
+ const allowedKeys = /* @__PURE__ */ new Set([
938
+ "apiKey",
939
+ "baseURL",
940
+ "model",
941
+ "name",
942
+ "provider"
943
+ ]);
944
+ const allowedProviders = /* @__PURE__ */ new Set([
945
+ "anthropic",
946
+ "google",
947
+ "openai",
948
+ "openai-compatible"
949
+ ]);
950
+ const next = {};
951
+ for (const [key, entry] of Object.entries(value)) {
952
+ if (!allowedKeys.has(key)) {
953
+ throw new Error(`Unknown llm config key: ${key}`);
954
+ }
955
+ if (typeof entry !== "string" || entry.trim() === "") {
956
+ throw new Error(`llm.${key} must be a non-empty string.`);
957
+ }
958
+ const normalized = entry.trim();
959
+ if (key === "provider" && !allowedProviders.has(normalized)) {
960
+ throw new Error(`Unknown llm.provider: ${normalized}`);
961
+ }
962
+ next[key] = normalized;
963
+ }
964
+ return next;
965
+ }
966
+ function validateConcurrentConfig(value) {
967
+ const allowedKeys = /* @__PURE__ */ new Set(["job", "request"]);
968
+ const next = {};
969
+ for (const [key, entry] of Object.entries(value)) {
970
+ if (!allowedKeys.has(key)) {
971
+ throw new Error(`Unknown concurrent config key: ${key}`);
972
+ }
973
+ const parsed = typeof entry === "number" ? entry : typeof entry === "string" ? Number(entry) : NaN;
974
+ if (!Number.isInteger(parsed) || parsed <= 0) {
975
+ throw new Error(`concurrent.${key} must be a positive integer.`);
976
+ }
977
+ next[key] = parsed;
978
+ }
979
+ return next;
980
+ }
981
+ function validateWikispineConfig(value) {
982
+ const allowedKeys = /* @__PURE__ */ new Set(["endpoint", "provider", "token"]);
983
+ const allowedProviders = /* @__PURE__ */ new Set(["cli", "fetch"]);
984
+ const next = {};
985
+ for (const [key, entry] of Object.entries(value)) {
986
+ if (!allowedKeys.has(key)) {
987
+ throw new Error(`Unknown wikispine config key: ${key}`);
988
+ }
989
+ if (typeof entry !== "string" || entry.trim() === "") {
990
+ throw new Error(`wikispine.${key} must be a non-empty string.`);
991
+ }
992
+ const normalized = entry.trim();
993
+ if (key === "provider" && !allowedProviders.has(normalized)) {
994
+ throw new Error(`Unknown wikispine.provider: ${normalized}`);
995
+ }
996
+ next[key] = normalized;
997
+ }
998
+ return next;
999
+ }
1000
+ function validateWikimediaConfig(value) {
1001
+ return validateEndpointConfig("wikimedia", value);
1002
+ }
1003
+ function validateEndpointConfig(section, value) {
1004
+ const allowedKeys = /* @__PURE__ */ new Set(["endpoint", "token"]);
1005
+ const next = {};
1006
+ for (const [key, entry] of Object.entries(value)) {
1007
+ if (!allowedKeys.has(key)) {
1008
+ throw new Error(`Unknown ${section} config key: ${key}`);
1009
+ }
1010
+ if (typeof entry !== "string" || entry.trim() === "") {
1011
+ throw new Error(`${section}.${key} must be a non-empty string.`);
1012
+ }
1013
+ next[key] = entry.trim();
1014
+ }
1015
+ return next;
1016
+ }
1017
+ function formatError(error) {
1018
+ return error instanceof Error ? error.message : String(error);
1019
+ }
1020
+
1021
+ // src/runtime-config.ts
1022
+ var WIKI_GRAPH_PROVIDER_VALUES = [
1023
+ "anthropic",
1024
+ "google",
1025
+ "openai",
1026
+ "openai-compatible"
1027
+ ];
1028
+ var wikiGraphProviderSchema = import_zod.z.enum(WIKI_GRAPH_PROVIDER_VALUES);
1029
+ var inlineLLMConfigSchema = import_zod.z.object({
1030
+ apiKey: import_zod.z.string().min(1).optional(),
1031
+ baseURL: import_zod.z.string().min(1).optional(),
1032
+ baseUrl: import_zod.z.string().min(1).optional(),
1033
+ chatCompletionsUrl: import_zod.z.string().min(1).optional(),
1034
+ llm: import_zod.z.object({
1035
+ apiKey: import_zod.z.string().min(1).optional(),
1036
+ baseURL: import_zod.z.string().min(1).optional(),
1037
+ baseUrl: import_zod.z.string().min(1).optional(),
1038
+ chatCompletionsUrl: import_zod.z.string().min(1).optional(),
1039
+ model: import_zod.z.string().min(1).optional(),
1040
+ name: import_zod.z.string().min(1).optional(),
1041
+ provider: wikiGraphProviderSchema.optional()
1042
+ }).optional(),
1043
+ model: import_zod.z.string().min(1).optional(),
1044
+ name: import_zod.z.string().min(1).optional(),
1045
+ provider: wikiGraphProviderSchema.optional()
1046
+ });
1047
+ async function loadWikiGraphRuntimeConfig(options) {
1048
+ const [embedding, localLLM, concurrent, wikimedia, wikispine] = await Promise.all([
1049
+ readLocalConfigSection("embeddings"),
1050
+ readLocalConfigSection("llm"),
1051
+ readLocalConfigSection("concurrent"),
1052
+ readLocalConfigSection("wikimedia"),
1053
+ readLocalConfigSection("wikispine")
1054
+ ]);
1055
+ const inlineLLMConfig = options?.llmJSON === void 0 ? void 0 : parseInlineLLMConfig(options.llmJSON);
1056
+ const llm = createLLMConfig({
1057
+ apiKey: firstDefined(inlineLLMConfig?.apiKey, readString(localLLM.apiKey)),
1058
+ baseURL: firstDefined(
1059
+ inlineLLMConfig?.baseURL,
1060
+ readString(localLLM.baseURL)
1061
+ ),
1062
+ model: firstDefined(inlineLLMConfig?.model, readString(localLLM.model)),
1063
+ name: firstDefined(inlineLLMConfig?.name, readString(localLLM.name)),
1064
+ provider: firstDefined(
1065
+ inlineLLMConfig?.provider,
1066
+ readProvider(localLLM.provider)
1067
+ )
1068
+ });
1069
+ const requestConcurrent = readPositiveInteger(concurrent.request);
1070
+ const jobConcurrent = readPositiveInteger(concurrent.job);
1071
+ const envPolicy = getWikiGraphSDKRuntimeContext().envPolicy;
1072
+ const wikispineConfig = resolveWikispineConfig(wikispine, envPolicy);
1073
+ const wikimediaConfig = createWikimediaConfig(wikimedia, envPolicy);
1074
+ const embeddingConfig = createEmbeddingConfig(embedding);
1075
+ return {
1076
+ ...jobConcurrent === void 0 && requestConcurrent === void 0 ? {} : {
1077
+ concurrent: {
1078
+ ...jobConcurrent === void 0 ? {} : { job: jobConcurrent },
1079
+ ...requestConcurrent === void 0 ? {} : { request: requestConcurrent }
1080
+ }
1081
+ },
1082
+ ...embeddingConfig === void 0 ? {} : { embedding: embeddingConfig },
1083
+ ...llm === void 0 ? {} : { llm },
1084
+ ...wikimediaConfig === void 0 ? {} : { wikimedia: wikimediaConfig },
1085
+ ...wikispineConfig === void 0 ? {} : { wikispine: wikispineConfig }
1086
+ };
1087
+ }
1088
+ function createWikimediaConfig(input, envPolicy) {
1089
+ const endpoint = readString(input?.endpoint);
1090
+ const token = readString(input?.token);
1091
+ if (endpoint === void 0 && token === void 0) return void 0;
1092
+ if (token === void 0) {
1093
+ throw new Error(
1094
+ "Remote Wikimedia access requires an API key. Configure `wikg://local/config/wikimedia` token with `put token --secret`."
1095
+ );
1096
+ }
1097
+ return {
1098
+ endpoint: endpoint ?? resolveHostedProviderEndpoint("wg-wikimedia", envPolicy),
1099
+ token
1100
+ };
1101
+ }
1102
+ function createEmbeddingConfig(input) {
1103
+ const value = input ?? {};
1104
+ const provider = readEmbeddingProvider(value.provider);
1105
+ const apiKey = readString(value.apiKey);
1106
+ const baseURL = readString(value.baseURL);
1107
+ const dimensions = typeof value.dimensions === "number" && Number.isInteger(value.dimensions) && value.dimensions > 0 ? value.dimensions : void 0;
1108
+ const model = readString(value.model);
1109
+ const name = readString(value.name);
1110
+ if (provider === void 0 || model === void 0 || provider === "openai-compatible" && baseURL === void 0 || provider === "openai" && baseURL !== void 0) {
1111
+ return void 0;
1112
+ }
1113
+ return {
1114
+ ...apiKey === void 0 ? {} : { apiKey },
1115
+ ...baseURL === void 0 ? {} : { baseURL },
1116
+ ...dimensions === void 0 ? {} : { dimensions },
1117
+ ...model === void 0 ? {} : { model },
1118
+ ...name === void 0 ? {} : { name },
1119
+ ...provider === void 0 ? {} : { provider }
1120
+ };
1121
+ }
1122
+ function parseInlineLLMConfig(value) {
1123
+ const normalized = normalizeString(value);
1124
+ if (normalized === void 0) {
1125
+ throw new Error("--llm must be a non-empty JSON object.");
1126
+ }
1127
+ let parsedJson;
1128
+ try {
1129
+ parsedJson = JSON.parse(normalized);
1130
+ } catch (error) {
1131
+ throw new Error(`Invalid --llm JSON: ${formatError2(error)}`);
1132
+ }
1133
+ const parsed = inlineLLMConfigSchema.safeParse(parsedJson);
1134
+ if (!parsed.success) {
1135
+ throw new Error(
1136
+ `Invalid --llm config: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`
1137
+ );
1138
+ }
1139
+ const input = parsed.data.llm ?? parsed.data;
1140
+ const provider = input.provider ?? inferInlineProvider(input);
1141
+ const baseURL = resolveInlineBaseURL(input);
1142
+ const config = createLLMConfig({
1143
+ apiKey: input.apiKey,
1144
+ baseURL,
1145
+ model: input.model,
1146
+ name: input.name,
1147
+ provider
1148
+ });
1149
+ if (config === void 0) {
1150
+ throw new Error("--llm must contain at least one supported LLM field.");
1151
+ }
1152
+ return config;
1153
+ }
1154
+ function inferInlineProvider(input) {
1155
+ if (input.provider === void 0 && (input.baseURL !== void 0 || input.baseUrl !== void 0 || input.chatCompletionsUrl !== void 0)) {
1156
+ return "openai-compatible";
1157
+ }
1158
+ return input.provider;
1159
+ }
1160
+ function inferBaseURLFromChatCompletionsURL(input) {
1161
+ if (input.chatCompletionsUrl === void 0) {
1162
+ return void 0;
1163
+ }
1164
+ const suffix = "/chat/completions";
1165
+ if (!input.chatCompletionsUrl.endsWith(suffix)) {
1166
+ throw new Error(
1167
+ "--llm chatCompletionsUrl must end with /chat/completions when baseURL is not provided."
1168
+ );
1169
+ }
1170
+ return input.chatCompletionsUrl.slice(0, -suffix.length);
1171
+ }
1172
+ function resolveInlineBaseURL(input) {
1173
+ if (input.baseURL !== void 0) {
1174
+ return input.baseURL;
1175
+ }
1176
+ if (input.baseUrl !== void 0) {
1177
+ return input.baseUrl;
1178
+ }
1179
+ return inferBaseURLFromChatCompletionsURL(input);
1180
+ }
1181
+ function createLLMConfig(input) {
1182
+ if (input.apiKey === void 0 && input.baseURL === void 0 && input.model === void 0 && input.name === void 0 && input.provider === void 0) {
1183
+ return void 0;
1184
+ }
1185
+ return {
1186
+ ...input.apiKey === void 0 ? {} : { apiKey: input.apiKey },
1187
+ ...input.baseURL === void 0 ? {} : { baseURL: input.baseURL },
1188
+ ...input.model === void 0 ? {} : { model: input.model },
1189
+ ...input.name === void 0 ? {} : { name: input.name },
1190
+ ...input.provider === void 0 ? {} : { provider: input.provider }
1191
+ };
1192
+ }
1193
+ function readProvider(value) {
1194
+ if (typeof value !== "string") {
1195
+ return void 0;
1196
+ }
1197
+ const parsed = wikiGraphProviderSchema.safeParse(value);
1198
+ return parsed.success ? parsed.data : void 0;
1199
+ }
1200
+ function resolveWikispineConfig(value, envPolicy = getWikiGraphSDKRuntimeContext().envPolicy) {
1201
+ const provider = readWikispineProvider(value.provider);
1202
+ const endpoint = readString(value.endpoint);
1203
+ const token = readString(value.token);
1204
+ if (provider === void 0) {
1205
+ return void 0;
1206
+ }
1207
+ if (provider === "cli") return { provider };
1208
+ if (token === void 0) {
1209
+ throw new Error(
1210
+ "Remote WikiSpine access requires an API key. Configure `wikg://local/config/wikispine` token with `put token --secret`."
1211
+ );
1212
+ }
1213
+ return {
1214
+ endpoint: endpoint ?? resolveHostedProviderEndpoint("wikispine", envPolicy),
1215
+ provider,
1216
+ token
1217
+ };
1218
+ }
1219
+ function resolveHostedProviderEndpoint(scope, envPolicy = getWikiGraphSDKRuntimeContext().envPolicy) {
1220
+ const origin = envPolicy === "development" ? "https://pdf-craft-api.oomol.dev" : "https://api.pdfcraft.ai";
1221
+ return `${origin}/v1/${scope}`;
1222
+ }
1223
+ function readWikispineProvider(value) {
1224
+ return value === "cli" || value === "fetch" ? value : void 0;
1225
+ }
1226
+ function readEmbeddingProvider(value) {
1227
+ return value === "openai" || value === "openai-compatible" ? value : void 0;
1228
+ }
1229
+ function readString(value) {
1230
+ return typeof value === "string" ? value : void 0;
1231
+ }
1232
+ function readPositiveInteger(value) {
1233
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
1234
+ }
1235
+ function firstDefined(first, second) {
1236
+ return first ?? second;
1237
+ }
1238
+ function normalizeString(value) {
1239
+ const normalized = value?.trim();
1240
+ return normalized === void 0 || normalized === "" ? void 0 : normalized;
1241
+ }
1242
+ function formatError2(error) {
1243
+ return error instanceof Error ? error.message : String(error);
1244
+ }
1245
+
1246
+ // src/runtime-path.ts
1247
+ var import_os2 = require("os");
1248
+ var import_path2 = require("path");
1249
+ function resolveWikiGraphRuntimePath(path) {
1250
+ const context = getWikiGraphSDKRuntimeContext();
1251
+ const environmentHome = context.env.HOME?.trim();
1252
+ const home = environmentHome === void 0 || environmentHome === "" ? (0, import_os2.homedir)() : environmentHome;
1253
+ if (path === "~") return home;
1254
+ if (path.startsWith("~/")) return (0, import_path2.resolve)(home, path.slice(2));
1255
+ return (0, import_path2.isAbsolute)(path) ? path : (0, import_path2.resolve)(context.cwd, path);
1256
+ }
1257
+
1258
+ // src/conversions.ts
1259
+ var WikiGraphConversionManager = class {
1260
+ #runtime;
1261
+ constructor(runtime) {
1262
+ this.#runtime = runtime;
1263
+ }
1264
+ async convert(options) {
1265
+ return await this.#runtime.run(async () => await this.#convert(options));
1266
+ }
1267
+ async #convert(options) {
1268
+ const targetStage = options.targetStage ?? "summarized";
1269
+ if (options.targetStage !== void 0 && options.output.format !== "wikg") {
1270
+ throw new Error("targetStage is only supported for wikg output.");
1271
+ }
1272
+ if (options.targetStage !== void 0 && options.input.format === "wikg") {
1273
+ throw new Error(
1274
+ "targetStage is only supported when creating a wikg archive."
1275
+ );
1276
+ }
1277
+ const requiresDigest = options.input.format !== "wikg";
1278
+ const requiresLLM = requiresDigest && targetStage !== "planned" && targetStage !== "sourced";
1279
+ const config = await loadWikiGraphRuntimeConfig({
1280
+ ...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
1281
+ });
1282
+ const app = new import_wiki_graph_core2.WikiGraph({
1283
+ ...options.verbose === true ? { verbose: true } : {},
1284
+ ...requiresLLM ? { llm: buildWikiGraphLLMOptions(config) } : {}
1285
+ });
1286
+ const documentDirectory = await prepareDigestDirectory(
1287
+ options.digestDirectory,
1288
+ requiresDigest
1289
+ );
1290
+ const write = async (archive) => {
1291
+ await writeArchive(archive, options.output.path, options.output.format);
1292
+ };
1293
+ if (options.input.format === "wikg") {
1294
+ if (!("path" in options.input)) {
1295
+ throw new Error("wikg input requires a file path.");
1296
+ }
1297
+ await app.openSession(
1298
+ new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
1299
+ write
1300
+ );
1301
+ } else if ("stream" in options.input) {
1302
+ await app.digestTextStreamSession(
1303
+ {
1304
+ ...documentDirectory === void 0 ? {} : { documentDirectory },
1305
+ ...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
1306
+ sourceFormat: options.input.format,
1307
+ stream: options.input.stream,
1308
+ ...(options.prompt ?? config.prompt) === void 0 ? {} : { extractionPrompt: options.prompt ?? config.prompt },
1309
+ targetStage
1310
+ },
1311
+ write
1312
+ );
1313
+ } else {
1314
+ const digestOptions = {
1315
+ ...documentDirectory === void 0 ? {} : { documentDirectory },
1316
+ file: new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
1317
+ ...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
1318
+ ...(options.prompt ?? config.prompt) === void 0 ? {} : { extractionPrompt: options.prompt ?? config.prompt },
1319
+ targetStage
1320
+ };
1321
+ if (options.input.format === "epub") {
1322
+ await app.digestEpubSession(digestOptions, write);
1323
+ } else if (options.input.format === "markdown") {
1324
+ await app.digestMarkdownSession(digestOptions, write);
1325
+ } else {
1326
+ await app.digestTxtSession(digestOptions, write);
1327
+ }
1328
+ }
1329
+ return {
1330
+ inputFormat: options.input.format,
1331
+ outputFormat: options.output.format,
1332
+ outputPath: options.output.path
1333
+ };
1334
+ }
1335
+ };
1336
+ async function prepareDigestDirectory(path, required) {
1337
+ const normalized = path?.trim();
1338
+ if (!required || normalized === void 0 || normalized === "")
1339
+ return void 0;
1340
+ const resolved = resolveWikiGraphRuntimePath(normalized);
1341
+ await (0, import_promises2.rm)(resolved, { force: true, recursive: true });
1342
+ return new NodeDirectory(resolved);
1343
+ }
1344
+ async function writeArchive(archive, path, format) {
1345
+ const file = new NodeFile(resolveWikiGraphRuntimePath(path));
1346
+ if (format === "epub") await archive.exportEpub(file);
1347
+ else if (format === "wikg") await archive.saveAs(file);
1348
+ else await archive.exportText(file);
1349
+ }
1350
+
1351
+ // src/query-runtime.ts
1352
+ var import_wiki_graph_core3 = require("wiki-graph-core");
1353
+
1354
+ // src/embedding.ts
1355
+ var import_ai = require("ai");
1356
+ var import_openai2 = require("@ai-sdk/openai");
1357
+ var import_openai_compatible2 = require("@ai-sdk/openai-compatible");
1358
+ var EMBEDDING_BATCH_SIZE = 10;
1359
+ async function readWikiGraphEmbeddingConfig() {
1360
+ const config = await readLocalConfigSection("embeddings");
1361
+ return {
1362
+ ...typeof config.apiKey === "string" ? { apiKey: config.apiKey } : {},
1363
+ ...typeof config.baseURL === "string" ? { baseURL: config.baseURL } : {},
1364
+ ...typeof config.dimensions === "number" ? { dimensions: config.dimensions } : {},
1365
+ ...typeof config.model === "string" ? { model: config.model } : {},
1366
+ ...typeof config.name === "string" ? { name: config.name } : {},
1367
+ ...typeof config.provider === "string" ? { provider: parseEmbeddingProvider(config.provider) } : {}
1368
+ };
1369
+ }
1370
+ function buildSearchIndexEmbeddingProvider(config) {
1371
+ const provider = requireEmbeddingProvider(config.provider);
1372
+ const model = requireEmbeddingModel(config.model);
1373
+ const embeddingModel = createEmbeddingModel(provider, model, config);
1374
+ const providerOptions = createEmbeddingProviderOptions(provider, config);
1375
+ return {
1376
+ ...config.dimensions === void 0 ? {} : { dimensions: config.dimensions },
1377
+ identity: createEmbeddingIdentity(provider, model, config),
1378
+ model,
1379
+ embedTexts: async (texts, options) => {
1380
+ const embeddings = [];
1381
+ let tokens = 0;
1382
+ for (const batch of chunkTexts(texts, EMBEDDING_BATCH_SIZE)) {
1383
+ options?.signal?.throwIfAborted();
1384
+ const result = await (0, import_ai.embedMany)({
1385
+ ...options?.signal === void 0 ? {} : { abortSignal: options.signal },
1386
+ model: embeddingModel,
1387
+ ...providerOptions === void 0 ? {} : { providerOptions },
1388
+ values: [...batch]
1389
+ });
1390
+ embeddings.push(...result.embeddings);
1391
+ tokens += result.usage.tokens ?? 0;
1392
+ }
1393
+ return {
1394
+ embeddings,
1395
+ ...tokens === 0 ? {} : { tokens }
1396
+ };
1397
+ }
1398
+ };
1399
+ }
1400
+ function createEmbeddingIdentity(provider, model, config) {
1401
+ return JSON.stringify({
1402
+ ...config.baseURL === void 0 ? {} : { baseURL: config.baseURL },
1403
+ ...config.dimensions === void 0 ? {} : { dimensions: config.dimensions },
1404
+ model,
1405
+ provider
1406
+ });
1407
+ }
1408
+ function* chunkTexts(texts, size) {
1409
+ for (let index = 0; index < texts.length; index += size) {
1410
+ yield texts.slice(index, index + size);
1411
+ }
1412
+ }
1413
+ function createEmbeddingModel(provider, model, config) {
1414
+ switch (provider) {
1415
+ case "openai": {
1416
+ if (config.baseURL !== void 0) {
1417
+ throw new Error(
1418
+ "openai does not accept embeddings.baseURL. Use openai-compatible for third-party OpenAI-style embeddings APIs."
1419
+ );
1420
+ }
1421
+ return (0, import_openai2.createOpenAI)({
1422
+ ...config.apiKey === void 0 ? {} : { apiKey: config.apiKey },
1423
+ ...config.name === void 0 ? {} : { name: config.name }
1424
+ }).embeddingModel(model);
1425
+ }
1426
+ case "openai-compatible": {
1427
+ if (config.baseURL === void 0) {
1428
+ throw new Error("openai-compatible requires embeddings.baseURL.");
1429
+ }
1430
+ return (0, import_openai_compatible2.createOpenAICompatible)({
1431
+ baseURL: config.baseURL,
1432
+ name: config.name ?? createOpenAICompatibleName2(config.baseURL),
1433
+ ...config.apiKey === void 0 ? {} : { apiKey: config.apiKey }
1434
+ }).embeddingModel(model);
1435
+ }
1436
+ }
1437
+ }
1438
+ function createEmbeddingProviderOptions(provider, config) {
1439
+ if (config.dimensions === void 0) {
1440
+ return void 0;
1441
+ }
1442
+ return {
1443
+ [getEmbeddingProviderOptionsName(provider, config)]: {
1444
+ dimensions: config.dimensions
1445
+ }
1446
+ };
1447
+ }
1448
+ function getEmbeddingProviderOptionsName(provider, _config) {
1449
+ if (provider === "openai") {
1450
+ return "openai";
1451
+ }
1452
+ return "openaiCompatible";
1453
+ }
1454
+ function requireEmbeddingProvider(provider) {
1455
+ if (provider !== void 0) {
1456
+ return provider;
1457
+ }
1458
+ throw new Error(
1459
+ "Missing embeddings configuration. Configure `wikg://local/config/embeddings` with provider and model."
1460
+ );
1461
+ }
1462
+ function requireEmbeddingModel(model) {
1463
+ if (model !== void 0) {
1464
+ return model;
1465
+ }
1466
+ throw new Error(
1467
+ "Missing embeddings.model. Configure `wikg://local/config/embeddings` before using Dense search."
1468
+ );
1469
+ }
1470
+ function parseEmbeddingProvider(value) {
1471
+ switch (value) {
1472
+ case "openai":
1473
+ case "openai-compatible":
1474
+ return value;
1475
+ default:
1476
+ throw new Error(
1477
+ `Invalid embeddings.provider: ${value}. Expected openai or openai-compatible.`
1478
+ );
1479
+ }
1480
+ }
1481
+ function createOpenAICompatibleName2(baseURL) {
1482
+ try {
1483
+ return new URL(baseURL).hostname;
1484
+ } catch {
1485
+ return "openai-compatible";
1486
+ }
1487
+ }
1488
+
1489
+ // src/query-runtime.ts
1490
+ async function createConfiguredEmbeddingProvider() {
1491
+ const config = await readWikiGraphEmbeddingConfig();
1492
+ if (config.provider === void 0 || config.model === void 0 || config.provider === "openai-compatible" && config.baseURL === void 0 || config.provider === "openai" && config.baseURL !== void 0) {
1493
+ return void 0;
1494
+ }
1495
+ return buildSearchIndexEmbeddingProvider(config);
1496
+ }
1497
+ async function withConfiguredWikimediaResolver(objectUri, operation) {
1498
+ if (!objectUri.endsWith("/wikipage")) return await operation({});
1499
+ const config = await loadWikiGraphRuntimeConfig();
1500
+ const resolver = await (0, import_wiki_graph_core3.openWikimediaResolver)(
1501
+ config.wikimedia === void 0 ? { kind: "local" } : {
1502
+ endpoint: config.wikimedia.endpoint,
1503
+ kind: "remote",
1504
+ token: config.wikimedia.token
1505
+ }
1506
+ );
1507
+ try {
1508
+ return await operation({ wikimediaResolver: resolver });
1509
+ } finally {
1510
+ await resolver.close();
1511
+ }
1512
+ }
1513
+
1514
+ // src/archive/inspect.ts
1515
+ var import_wiki_graph_core4 = require("wiki-graph-core");
1516
+
1517
+ // src/planning.ts
1518
+ var DEFAULT_GENERATION_JOB_CONCURRENCY = 3;
1519
+ var DEFAULT_GENERATION_REQUEST_CONCURRENCY = 6;
1520
+ function planGenerationTask(task, words, chapters, concurrent, model) {
1521
+ const profile = getGenerationPlanningProfile(task);
1522
+ const calls = Math.max(chapters, Math.ceil(words / profile.wordsPerCall));
1523
+ const effectiveConcurrency = Math.max(
1524
+ 1,
1525
+ task === "reading-graph" ? 1 : concurrent.request
1526
+ );
1527
+ const callBatches = Math.ceil(calls / effectiveConcurrency);
1528
+ const wordSeconds = words * profile.secondsPerWord;
1529
+ return {
1530
+ model,
1531
+ timeSeconds: {
1532
+ max: Math.ceil(callBatches * profile.maxSecondsPerCall + wordSeconds),
1533
+ min: Math.ceil(callBatches * profile.minSecondsPerCall + wordSeconds)
1534
+ },
1535
+ tokens: {
1536
+ cacheableInput: Math.ceil(words * profile.cacheableInputTokenPerWord),
1537
+ input: Math.ceil(words * profile.inputTokenPerWord),
1538
+ output: Math.ceil(words * profile.outputTokenPerWord)
1539
+ }
1540
+ };
1541
+ }
1542
+ function createGenerationPerformanceHints(input) {
1543
+ if (!input.hasJobWork) {
1544
+ return [];
1545
+ }
1546
+ const hints = [];
1547
+ const recommendedRequest = input.concurrent.request < 4 ? DEFAULT_GENERATION_REQUEST_CONCURRENCY : input.concurrent.request < 8 ? 8 : void 0;
1548
+ if (input.hasRequestWork && recommendedRequest !== void 0) {
1549
+ hints.push({
1550
+ configKey: "request",
1551
+ configValue: recommendedRequest,
1552
+ current: input.concurrent.request,
1553
+ kind: "request",
1554
+ message: "LLM request concurrency can often be higher. Use at least 4; 6-8 is usually faster when the provider allows it.",
1555
+ recommended: recommendedRequest
1556
+ });
1557
+ }
1558
+ if (input.concurrent.job < 4 && input.chapters > 1) {
1559
+ hints.push({
1560
+ configKey: "job",
1561
+ configValue: 4,
1562
+ current: input.concurrent.job,
1563
+ kind: "job",
1564
+ message: "Multiple chapters need work and job concurrency is below 4. Raising it lets chapter jobs run in parallel.",
1565
+ recommended: 4
1566
+ });
1567
+ }
1568
+ return hints;
1569
+ }
1570
+ function formatGenerationPlanningModel(llm) {
1571
+ if (llm?.model === void 0) {
1572
+ return "not configured";
1573
+ }
1574
+ return llm.provider === void 0 ? llm.model : `${llm.provider}/${llm.model}`;
1575
+ }
1576
+ function getGenerationPlanningProfile(task) {
1577
+ switch (task) {
1578
+ case "index-fts":
1579
+ return {
1580
+ cacheableInputTokenPerWord: 0,
1581
+ inputTokenPerWord: 0,
1582
+ maxSecondsPerCall: 1,
1583
+ minSecondsPerCall: 0,
1584
+ outputTokenPerWord: 0,
1585
+ secondsPerWord: 5e-4,
1586
+ wordsPerCall: 1e5
1587
+ };
1588
+ case "index-embedding-source":
1589
+ case "index-embedding-summary":
1590
+ return {
1591
+ cacheableInputTokenPerWord: 0,
1592
+ inputTokenPerWord: 0,
1593
+ maxSecondsPerCall: 10,
1594
+ minSecondsPerCall: 1,
1595
+ outputTokenPerWord: 0,
1596
+ secondsPerWord: 2e-3,
1597
+ wordsPerCall: 1e4
1598
+ };
1599
+ case "knowledge-graph":
1600
+ return {
1601
+ cacheableInputTokenPerWord: 60,
1602
+ inputTokenPerWord: 80,
1603
+ maxSecondsPerCall: 35,
1604
+ minSecondsPerCall: 15,
1605
+ outputTokenPerWord: 25,
1606
+ secondsPerWord: 0.02,
1607
+ wordsPerCall: 35
1608
+ };
1609
+ case "reading-graph":
1610
+ return {
1611
+ cacheableInputTokenPerWord: 20,
1612
+ inputTokenPerWord: 25,
1613
+ maxSecondsPerCall: 45,
1614
+ minSecondsPerCall: 15,
1615
+ outputTokenPerWord: 4,
1616
+ secondsPerWord: 0.01,
1617
+ wordsPerCall: 160
1618
+ };
1619
+ case "reading-summary":
1620
+ return {
1621
+ cacheableInputTokenPerWord: 45,
1622
+ inputTokenPerWord: 55,
1623
+ maxSecondsPerCall: 25,
1624
+ minSecondsPerCall: 10,
1625
+ outputTokenPerWord: 4,
1626
+ secondsPerWord: 0.01,
1627
+ wordsPerCall: 110
1628
+ };
1629
+ }
1630
+ }
1631
+
1632
+ // src/archive/inspect.ts
1633
+ async function createWikiGraphArchiveInspectReport(document, options) {
1634
+ const { archiveUri } = options;
1635
+ const [
1636
+ chapters,
1637
+ summaryWords,
1638
+ indexCacheCurrent,
1639
+ config,
1640
+ ftsArtifactCoverage,
1641
+ sourceEmbeddingArtifactCoverage,
1642
+ summaryEmbeddingArtifactCoverage
1643
+ ] = await Promise.all([
1644
+ readInspectChapters(document, options.chapterId),
1645
+ readSummaryWords(document, options.chapterId),
1646
+ (0, import_wiki_graph_core4.isArchiveSearchIndexCurrent)(document),
1647
+ loadWikiGraphRuntimeConfig(),
1648
+ readIndexArtifactCoverage(document, "fts", options.chapterId),
1649
+ readIndexArtifactCoverage(document, "embedding-source", options.chapterId),
1650
+ readIndexArtifactCoverage(document, "embedding-summary", options.chapterId)
1651
+ ]);
1652
+ const selectedChapter = options.chapterId === void 0 ? void 0 : chapters[0];
1653
+ const scopeUri = selectedChapter === void 0 ? archiveUri : formatInspectLocatedChapterUri(archiveUri, selectedChapter);
1654
+ const concurrent = {
1655
+ job: config.concurrent?.job ?? DEFAULT_GENERATION_JOB_CONCURRENCY,
1656
+ request: config.concurrent?.request ?? DEFAULT_GENERATION_REQUEST_CONCURRENCY
1657
+ };
1658
+ const planningModel = formatGenerationPlanningModel(config.llm);
1659
+ const contentChapters = chapters.filter(
1660
+ (chapter) => chapter.stage !== "planned"
1661
+ );
1662
+ const sourceWords = sumWords(contentChapters);
1663
+ const readingGraphCovered = contentChapters.filter(
1664
+ (chapter) => chapter.readingGraphReady
1665
+ );
1666
+ const knowledgeGraphCovered = contentChapters.filter(
1667
+ (chapter) => chapter.knowledgeGraphReady
1668
+ );
1669
+ const summaryCovered = contentChapters.filter(
1670
+ (chapter) => chapter.summaryReady
1671
+ );
1672
+ const ftsArtifactCovered = filterChaptersByCurrentIndexArtifact(
1673
+ contentChapters,
1674
+ ftsArtifactCoverage
1675
+ );
1676
+ const sourceEmbeddingArtifactCovered = filterChaptersByCurrentIndexArtifact(
1677
+ contentChapters,
1678
+ sourceEmbeddingArtifactCoverage
1679
+ );
1680
+ const summaryEmbeddingArtifactCovered = filterChaptersByCurrentIndexArtifact(
1681
+ contentChapters,
1682
+ summaryEmbeddingArtifactCoverage
1683
+ );
1684
+ const queryBlockedContentChapters = contentChapters.filter(
1685
+ (chapter) => !ftsArtifactCovered.includes(chapter) && !sourceEmbeddingArtifactCovered.includes(chapter)
1686
+ );
1687
+ const queryBlockedChapters = queryBlockedContentChapters.map(
1688
+ (chapter) => createInspectChapterReference(archiveUri, chapter)
1689
+ );
1690
+ const queryReady = queryBlockedChapters.length === 0;
1691
+ const improvements = createInspectImprovements({
1692
+ concurrent,
1693
+ contentChapters,
1694
+ indexCacheCurrent,
1695
+ planningModel,
1696
+ queryBlockedChapters: queryBlockedContentChapters,
1697
+ summaryCovered,
1698
+ knowledgeGraphCovered,
1699
+ readingGraphCovered
1700
+ });
1701
+ const performanceHints = createGenerationPerformanceHints({
1702
+ chapters: Math.max(
1703
+ 0,
1704
+ ...improvements.map((improvement) => improvement.missingChapters ?? 0)
1705
+ ),
1706
+ concurrent,
1707
+ hasJobWork: improvements.some(
1708
+ (improvement) => improvement.planning !== void 0
1709
+ ),
1710
+ hasRequestWork: improvements.some(
1711
+ (improvement) => improvement.planning !== void 0
1712
+ )
1713
+ });
1714
+ return {
1715
+ archiveUri,
1716
+ uri: scopeUri,
1717
+ scope: options.chapterId === void 0 ? { type: "archive" } : { chapterId: options.chapterId, type: "chapter" },
1718
+ content: {
1719
+ chapters: {
1720
+ content: contentChapters.length,
1721
+ planned: chapters.length - contentChapters.length,
1722
+ total: chapters.length
1723
+ },
1724
+ sourceWords,
1725
+ summaryWords
1726
+ },
1727
+ localIndexCache: {
1728
+ current: indexCacheCurrent,
1729
+ ...!queryReady ? {
1730
+ blockedBy: "missing-search-artifacts",
1731
+ impact: "The cache cannot be built until every content chapter has a current FTS or source embedding artifact."
1732
+ } : indexCacheCurrent ? {} : {
1733
+ impact: "The next archive query may need to sync index cache first.",
1734
+ resource: "local CPU/disk time only; no provider calls."
1735
+ },
1736
+ status: !queryReady ? "blocked" : indexCacheCurrent ? "current" : "missing-or-outdated",
1737
+ syncAvailable: queryReady && !indexCacheCurrent
1738
+ },
1739
+ coverage: {
1740
+ ftsIndexArtifact: createInspectCoverage(
1741
+ ftsArtifactCovered,
1742
+ contentChapters
1743
+ ),
1744
+ knowledgeGraph: createInspectCoverage(
1745
+ knowledgeGraphCovered,
1746
+ contentChapters
1747
+ ),
1748
+ readingGraph: createInspectCoverage(readingGraphCovered, contentChapters),
1749
+ summary: createInspectCoverage(summaryCovered, contentChapters),
1750
+ sourceEmbeddingIndexArtifact: createInspectCoverage(
1751
+ sourceEmbeddingArtifactCovered,
1752
+ contentChapters
1753
+ ),
1754
+ summaryEmbeddingIndexArtifact: createInspectCoverage(
1755
+ summaryEmbeddingArtifactCovered,
1756
+ contentChapters
1757
+ )
1758
+ },
1759
+ query: {
1760
+ ready: queryReady
1761
+ },
1762
+ searchArtifacts: {
1763
+ blockedChapters: queryBlockedChapters,
1764
+ ...!queryReady ? {
1765
+ resource: "local CPU/disk time only; no provider calls."
1766
+ } : {},
1767
+ ready: queryReady,
1768
+ status: queryReady ? "ready" : "incomplete"
1769
+ },
1770
+ retrievalGuidance: formatRetrievalGuidance({
1771
+ indexCacheCurrent,
1772
+ knowledgeGraphCovered,
1773
+ readingGraphCovered,
1774
+ contentChapters,
1775
+ queryReady,
1776
+ sourceWords
1777
+ }),
1778
+ improvements,
1779
+ performanceHints,
1780
+ summaryCoverageNote: "Summary coverage means a current summary artifact exists for the chapter; it does not guarantee that every source topic is represented in the summary text."
1781
+ };
1782
+ }
1783
+ function createInspectChapterReference(archiveUri, chapter) {
1784
+ return {
1785
+ chapterId: chapter.chapterId,
1786
+ locatedUri: formatInspectLocatedChapterUri(archiveUri, chapter),
1787
+ title: chapter.title,
1788
+ uri: chapter.uri
1789
+ };
1790
+ }
1791
+ function formatInspectLocatedChapterUri(archiveUri, chapter) {
1792
+ return `${archiveUri.replace(/\/+$/u, "")}/${chapter.uri.replace(/^wikg:\/\//u, "")}`;
1793
+ }
1794
+ async function readInspectChapters(document, chapterId) {
1795
+ const chapters = chapterId === void 0 ? await (0, import_wiki_graph_core4.listChapters)(document) : (await (0, import_wiki_graph_core4.listChapters)(document)).filter(
1796
+ (chapter) => chapter.chapterId === chapterId
1797
+ );
1798
+ if (chapterId !== void 0 && chapters.length === 0) {
1799
+ throw new Error(`Chapter ${chapterId} does not exist.`);
1800
+ }
1801
+ return await Promise.all(
1802
+ chapters.map(async (chapter) => {
1803
+ const serial = await document.serials.getById(chapter.chapterId);
1804
+ return {
1805
+ ...chapter,
1806
+ knowledgeGraphReady: serial?.knowledgeGraphReady === true,
1807
+ readingGraphReady: serial?.topologyReady === true,
1808
+ summaryReady: chapter.stage === "summarized"
1809
+ };
1810
+ })
1811
+ );
1812
+ }
1813
+ async function readSummaryWords(document, chapterId) {
1814
+ return await document.readDatabase(
1815
+ async (database) => await database.queryOne(
1816
+ `
1817
+ SELECT COALESCE(SUM(words_count), 0) AS words
1818
+ FROM text_sentence_records
1819
+ WHERE kind = 2
1820
+ ${chapterId === void 0 ? "" : "AND chapter_id = ?"}
1821
+ `,
1822
+ chapterId === void 0 ? void 0 : [chapterId],
1823
+ (row) => Number(row.words)
1824
+ ) ?? 0
1825
+ );
1826
+ }
1827
+ async function readIndexArtifactCoverage(document, kind, chapterId) {
1828
+ const coverage = await document.indexArtifacts.listCoverage(kind);
1829
+ return chapterId === void 0 ? coverage : coverage.filter((item) => item.serialId === chapterId);
1830
+ }
1831
+ function filterChaptersByCurrentIndexArtifact(chapters, coverage) {
1832
+ const currentChapterIds = new Set(
1833
+ coverage.filter((item) => item.current).map((item) => item.serialId)
1834
+ );
1835
+ return chapters.filter((chapter) => currentChapterIds.has(chapter.chapterId));
1836
+ }
1837
+ function createInspectCoverage(covered, total) {
1838
+ const coveredWords = sumWords(covered);
1839
+ const totalWords = sumWords(total);
1840
+ return {
1841
+ coveredChapters: covered.length,
1842
+ coveredWords,
1843
+ percent: formatPercent(coveredWords, totalWords),
1844
+ totalChapters: total.length,
1845
+ totalWords
1846
+ };
1847
+ }
1848
+ function formatRetrievalGuidance(input) {
1849
+ if (input.sourceWords === 0 || input.contentChapters.length === 0) {
1850
+ return [
1851
+ "Source content: empty.",
1852
+ "Add source text before using query coverage or graph-based retrieval."
1853
+ ];
1854
+ }
1855
+ const lines = [
1856
+ input.queryReady ? `Query: available; local index cache is ${input.indexCacheCurrent ? "current" : "missing or outdated and can be synced from artifacts"}.` : "Query: unavailable until every content chapter has a current FTS or source embedding artifact."
1857
+ ];
1858
+ lines.push(
1859
+ formatObjectSearchGuidance(
1860
+ "Reading Graph object retrieval",
1861
+ input.readingGraphCovered,
1862
+ input.contentChapters
1863
+ )
1864
+ );
1865
+ lines.push(
1866
+ formatObjectSearchGuidance(
1867
+ "Entity/triple retrieval",
1868
+ input.knowledgeGraphCovered,
1869
+ input.contentChapters
1870
+ )
1871
+ );
1872
+ return lines;
1873
+ }
1874
+ function formatObjectSearchGuidance(label, covered, total) {
1875
+ const coveredWords = sumWords(covered);
1876
+ const totalWords = sumWords(total);
1877
+ const ratio = totalWords === 0 ? 0 : coveredWords / totalWords;
1878
+ const coverage = formatPercent(coveredWords, totalWords);
1879
+ if (ratio >= 1) {
1880
+ return `${label}: covers all source content; it can represent the full scope for object retrieval.`;
1881
+ }
1882
+ if (ratio >= 0.9) {
1883
+ return `${label}: covers ${coverage}; use it as the main path, but source --query is needed for uncovered content.`;
1884
+ }
1885
+ if (ratio >= 0.5) {
1886
+ return `${label}: covers ${coverage}; use it as leads, not as a full-scope substitute for source --query.`;
1887
+ }
1888
+ return `${label}: covers ${coverage}; build missing graph coverage before relying on object retrieval.`;
1889
+ }
1890
+ function createInspectImprovements(input) {
1891
+ const improvements = [];
1892
+ if (input.contentChapters.length === 0) {
1893
+ improvements.push({
1894
+ kind: "add-source",
1895
+ recommendation: "No source content is available yet; add or import source text before graph or summary generation.",
1896
+ title: "Add source content"
1897
+ });
1898
+ return improvements;
1899
+ }
1900
+ if (input.queryBlockedChapters.length > 0) {
1901
+ improvements.push({
1902
+ kind: "build",
1903
+ missingChapters: input.queryBlockedChapters.length,
1904
+ missingWords: sumWords(input.queryBlockedChapters),
1905
+ recommendation: "Build local FTS artifacts to make this scope queryable. This does not call a provider.",
1906
+ task: "index-fts",
1907
+ title: "Enable query with local FTS"
1908
+ });
1909
+ } else if (!input.indexCacheCurrent) {
1910
+ improvements.push({
1911
+ kind: "sync-index",
1912
+ recommendation: "Sync the index cache from chapter index artifacts so query starts without a lazy cache rebuild.",
1913
+ title: "Sync index cache"
1914
+ });
1915
+ }
1916
+ improvements.push(
1917
+ ...createGraphImprovement({
1918
+ concurrent: input.concurrent,
1919
+ covered: input.readingGraphCovered,
1920
+ planningModel: input.planningModel,
1921
+ task: "reading-graph",
1922
+ title: "Complete Reading Graph coverage",
1923
+ total: input.contentChapters
1924
+ })
1925
+ );
1926
+ improvements.push(
1927
+ ...createGraphImprovement({
1928
+ concurrent: input.concurrent,
1929
+ covered: input.knowledgeGraphCovered,
1930
+ planningModel: input.planningModel,
1931
+ task: "knowledge-graph",
1932
+ title: "Complete Knowledge Graph coverage",
1933
+ total: input.contentChapters
1934
+ })
1935
+ );
1936
+ improvements.push(
1937
+ ...createGraphImprovement({
1938
+ concurrent: input.concurrent,
1939
+ covered: input.summaryCovered,
1940
+ planningModel: input.planningModel,
1941
+ task: "reading-summary",
1942
+ title: "Complete Summary coverage",
1943
+ total: input.contentChapters
1944
+ })
1945
+ );
1946
+ return improvements;
1947
+ }
1948
+ function createGraphImprovement(input) {
1949
+ const coveredIds = new Set(input.covered.map((chapter) => chapter.chapterId));
1950
+ const missing = input.total.filter(
1951
+ (chapter) => !coveredIds.has(chapter.chapterId)
1952
+ );
1953
+ if (missing.length === 0) {
1954
+ return [];
1955
+ }
1956
+ const missingWords = sumWords(missing);
1957
+ return [
1958
+ {
1959
+ kind: "build",
1960
+ missingChapters: missing.length,
1961
+ missingWords,
1962
+ planning: planGenerationTask(
1963
+ input.task,
1964
+ missingWords,
1965
+ missing.length,
1966
+ input.concurrent,
1967
+ input.planningModel
1968
+ ),
1969
+ recommendation: formatImprovementRecommendation(
1970
+ input.covered,
1971
+ input.total,
1972
+ missingWords
1973
+ ),
1974
+ task: input.task,
1975
+ title: input.title
1976
+ }
1977
+ ];
1978
+ }
1979
+ function formatImprovementRecommendation(covered, total, missingWords) {
1980
+ const totalWords = sumWords(total);
1981
+ const coveredWords = sumWords(covered);
1982
+ if (totalWords === 0) {
1983
+ return "No source content is available.";
1984
+ }
1985
+ if (total.length === 1 || coveredWords / totalWords >= 0.9 || missingWords <= 1e3) {
1986
+ return "Queue the full scope to finish the remaining gap.";
1987
+ }
1988
+ return "Queue selected chapters first if only part of the scope matters.";
1989
+ }
1990
+ function sumWords(chapters) {
1991
+ return chapters.reduce((total, chapter) => total + chapter.words, 0);
1992
+ }
1993
+ function formatPercent(numerator, denominator) {
1994
+ if (denominator === 0) {
1995
+ return "n/a";
1996
+ }
1997
+ const percent = numerator / denominator * 100;
1998
+ return `${Number.isInteger(percent) ? percent.toFixed(0) : percent.toFixed(1)}%`;
1999
+ }
2000
+
2001
+ // src/archive/archive.ts
2002
+ var WikiGraphArchiveExistsError = class extends Error {
2003
+ code = "WIKI_GRAPH_ARCHIVE_EXISTS";
2004
+ path;
2005
+ constructor(path) {
2006
+ super(`Archive already exists: ${path}`);
2007
+ this.name = "WikiGraphArchiveExistsError";
2008
+ this.path = path;
2009
+ }
2010
+ };
2011
+ var WikiGraphArchiveManager = class {
2012
+ #runtime;
2013
+ constructor(runtime) {
2014
+ this.#runtime = runtime;
2015
+ }
2016
+ async create(options) {
2017
+ return await this.#runtime.run(async () => {
2018
+ const path = resolveWikiGraphRuntimePath(options.path);
2019
+ if (options.replace !== true && await nodePathExists(path)) {
2020
+ throw new WikiGraphArchiveExistsError(path);
2021
+ }
2022
+ const outputPath = (0, import_path3.join)(
2023
+ (0, import_path3.dirname)(path),
2024
+ `.${(0, import_path3.basename)(path)}.${(0, import_crypto.randomUUID)()}.tmp.wikg`
2025
+ );
2026
+ try {
2027
+ if (options.importPath === void 0) {
2028
+ await createEmptyArchiveFile(outputPath);
2029
+ } else {
2030
+ await new WikiGraphConversionManager(this.#runtime).convert({
2031
+ input: { format: "epub", path: options.importPath },
2032
+ ...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
2033
+ output: { format: "wikg", path: outputPath },
2034
+ targetStage: "sourced"
2035
+ });
2036
+ }
2037
+ if (options.replace === true) {
2038
+ await (0, import_promises3.rename)(outputPath, path);
2039
+ } else {
2040
+ try {
2041
+ await (0, import_promises3.link)(outputPath, path);
2042
+ } catch (error) {
2043
+ if (isNodeEEXISTError(error)) {
2044
+ throw new WikiGraphArchiveExistsError(path);
2045
+ }
2046
+ throw error;
2047
+ }
2048
+ }
2049
+ return { locatedUri: (0, import_wiki_graph_core5.formatLocatedWikiGraphUri)(path), path };
2050
+ } finally {
2051
+ await (0, import_promises3.rm)(outputPath, { force: true, recursive: true });
2052
+ }
2053
+ }, options.signal);
2054
+ }
2055
+ async open(uriOrPath, options = {}) {
2056
+ const location = await this.#runtime.run(
2057
+ async () => await resolveWikiGraphArchiveLocation(uriOrPath),
2058
+ options.signal
2059
+ );
2060
+ return new WikiGraphArchiveHandle(this.#runtime, location);
2061
+ }
2062
+ };
2063
+ var WikiGraphArchiveHandle = class {
2064
+ #runtime;
2065
+ #location;
2066
+ constructor(runtime, location) {
2067
+ this.#runtime = runtime;
2068
+ this.#location = location;
2069
+ }
2070
+ get archiveKey() {
2071
+ return this.#location.archiveKey;
2072
+ }
2073
+ get indexScope() {
2074
+ return this.#location.indexScope;
2075
+ }
2076
+ get locatedUri() {
2077
+ return this.#location.locatedUri;
2078
+ }
2079
+ get objectUri() {
2080
+ return (0, import_wiki_graph_core5.parseLocatedWikiGraphUri)(this.#location.locatedUri).objectUri ?? "wikg://";
2081
+ }
2082
+ get path() {
2083
+ return this.#location.archivePath;
2084
+ }
2085
+ async inspect(options = {}) {
2086
+ return await this.#readDocument(
2087
+ async (document) => await createWikiGraphArchiveInspectReport(document, {
2088
+ archiveUri: this.#location.publicArchiveUri ?? this.locatedUri,
2089
+ ...options.chapterId === void 0 ? {} : { chapterId: options.chapterId }
2090
+ }),
2091
+ options
2092
+ );
2093
+ }
2094
+ async search(query, options = {}) {
2095
+ const embeddingProvider = await this.#runtime.run(
2096
+ createConfiguredEmbeddingProvider,
2097
+ options.signal
2098
+ );
2099
+ return await this.#writeDocument(
2100
+ async (document) => {
2101
+ const chapters = await this.#resolveQueryChapters(document, options);
2102
+ await ensureArchiveSearchIndex(document, {
2103
+ ...chapters === void 0 ? {} : { chapters },
2104
+ ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2105
+ });
2106
+ return await (0, import_wiki_graph_core5.findArchiveObjects)(document, query, {
2107
+ ...withoutOperationAndScope(options),
2108
+ archiveKey: this.archiveKey,
2109
+ ...chapters === void 0 ? {} : { chapters },
2110
+ ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2111
+ });
2112
+ },
2113
+ {
2114
+ searchIndexWritebackPolicy: "cache",
2115
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2116
+ }
2117
+ );
2118
+ }
2119
+ async list(options = {}) {
2120
+ return await this.#readDocument(async (document) => {
2121
+ if ((0, import_wiki_graph_core5.isSourceLocatorScopeUri)(this.objectUri)) {
2122
+ return await (0, import_wiki_graph_core5.listArchiveSourceLocators)(document, this.objectUri, {
2123
+ ...options.cursor === void 0 ? {} : { cursor: options.cursor },
2124
+ ...options.limit === void 0 ? {} : { limit: options.limit }
2125
+ });
2126
+ }
2127
+ const chapters = await this.#resolveScope(document, options);
2128
+ return await (0, import_wiki_graph_core5.listArchiveCollection)(document, {
2129
+ ...withoutOperationAndScope(options),
2130
+ ...chapters === void 0 ? {} : { chapters }
2131
+ });
2132
+ }, options);
2133
+ }
2134
+ async ensureSearchIndex(options = {}) {
2135
+ const embeddingProvider = await this.#runtime.run(
2136
+ createConfiguredEmbeddingProvider,
2137
+ options.signal
2138
+ );
2139
+ await this.#writeDocument(
2140
+ async (document) => {
2141
+ const chapters = await this.#resolveScope(document, options);
2142
+ await ensureArchiveSearchIndex(document, {
2143
+ ...chapters === void 0 ? {} : { chapters },
2144
+ ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2145
+ });
2146
+ },
2147
+ {
2148
+ searchIndexWritebackPolicy: "cache",
2149
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2150
+ }
2151
+ );
2152
+ }
2153
+ async getSearchIndexStatus(options = {}) {
2154
+ return await this.#readDocument(
2155
+ async (document) => ({
2156
+ capabilities: await (0, import_wiki_graph_core5.readSearchIndexCapabilityStatus)(document),
2157
+ current: await (0, import_wiki_graph_core5.isArchiveSearchIndexCurrent)(document)
2158
+ }),
2159
+ options
2160
+ );
2161
+ }
2162
+ async syncSearchIndex(options = {}) {
2163
+ const rebuilt = await this.#writeDocument(
2164
+ async (document) => {
2165
+ const scope = options.skipUnindexed === true ? { chapters: await (0, import_wiki_graph_core5.listArchiveQueryableChapterIds)(document) } : {};
2166
+ if (scope.chapters?.length === 0) {
2167
+ throw new Error(
2168
+ "Wiki Graph index cache is not ready. No chapters have a current FTS artifact or source embedding artifact."
2169
+ );
2170
+ }
2171
+ if (await (0, import_wiki_graph_core5.isArchiveSearchIndexCurrent)(document, scope)) return false;
2172
+ await (0, import_wiki_graph_core5.rebuildArchiveSearchIndex)(document, options.onProgress, scope);
2173
+ return true;
2174
+ },
2175
+ {
2176
+ searchIndexWritebackPolicy: "cache",
2177
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2178
+ }
2179
+ );
2180
+ await this.#runtime.run(
2181
+ async () => await (0, import_wiki_graph_core5.deleteArchiveSearchSessions)(this.path),
2182
+ options.signal
2183
+ );
2184
+ return { rebuilt };
2185
+ }
2186
+ async cleanSearchIndex(options = {}) {
2187
+ const status = await this.#writeDocument(
2188
+ async (document) => {
2189
+ await document.deleteSearchIndexDatabase();
2190
+ return {
2191
+ capabilities: {
2192
+ dense: { current: false },
2193
+ indexes: "missing"
2194
+ },
2195
+ current: false
2196
+ };
2197
+ },
2198
+ {
2199
+ searchIndexWritebackPolicy: "cache",
2200
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2201
+ }
2202
+ );
2203
+ await this.#runtime.run(
2204
+ async () => await (0, import_wiki_graph_core5.deleteArchiveSearchSessions)(this.path),
2205
+ options.signal
2206
+ );
2207
+ return status;
2208
+ }
2209
+ async listChapters(options = {}) {
2210
+ return await this.#readDocument(
2211
+ async (document) => await (0, import_wiki_graph_core5.listChapters)(document),
2212
+ options
2213
+ );
2214
+ }
2215
+ async getChapter(path, options = {}) {
2216
+ return await this.#readDocument(async (document) => {
2217
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2218
+ return requireChapter(await (0, import_wiki_graph_core5.listChapters)(document), chapterId);
2219
+ }, options);
2220
+ }
2221
+ async addChapter(options) {
2222
+ return await this.#writeDocument(async (document) => {
2223
+ const parentChapterId = await resolveOptionalChapterPath(
2224
+ document,
2225
+ options.parentPath
2226
+ );
2227
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobConflicts)({
2228
+ archive: this.#location.archiveFile,
2229
+ operation: "Adding chapter",
2230
+ scope: { kind: "archive" }
2231
+ });
2232
+ let details = await (0, import_wiki_graph_core5.addChapter)(document, {
2233
+ ...parentChapterId === void 0 ? {} : { parentChapterId },
2234
+ ...options.title === void 0 ? {} : { title: options.title }
2235
+ });
2236
+ if (options.source !== void 0) {
2237
+ details = await (0, import_wiki_graph_core5.setChapterSource)(
2238
+ document,
2239
+ details.chapterId,
2240
+ asTextStream(options.source)
2241
+ );
2242
+ }
2243
+ return details;
2244
+ });
2245
+ }
2246
+ async getChapterArtifact(path, kind) {
2247
+ return await this.#readDocument(async (document) => {
2248
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2249
+ const [artifact, revision] = await Promise.all([
2250
+ document.indexArtifacts.get(chapterId, kind),
2251
+ document.serials.getRevision(chapterId)
2252
+ ]);
2253
+ return {
2254
+ ...artifact === void 0 ? {} : {
2255
+ artifact: {
2256
+ createdAt: artifact.createdAt,
2257
+ metadata: artifact.metadata,
2258
+ sourceRevision: artifact.sourceRevision
2259
+ }
2260
+ },
2261
+ chapterId,
2262
+ current: artifact?.sourceRevision === revision,
2263
+ kind,
2264
+ missing: artifact === void 0,
2265
+ revision
2266
+ };
2267
+ });
2268
+ }
2269
+ async deleteChapterArtifact(path, kind) {
2270
+ return await this.#writeDocument(async (document) => {
2271
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2272
+ await document.indexArtifacts.delete(chapterId, kind);
2273
+ return { chapterId, deleted: true, kind };
2274
+ });
2275
+ }
2276
+ async moveChapter(path, options) {
2277
+ return await this.#writeDocument(async (document) => {
2278
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2279
+ const [afterChapterId, beforeChapterId, parentChapterId] = await Promise.all([
2280
+ resolveOptionalChapterPath(document, options.afterPath),
2281
+ resolveOptionalChapterPath(document, options.beforePath),
2282
+ resolveOptionalChapterPath(document, options.parentPath)
2283
+ ]);
2284
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobConflicts)({
2285
+ archive: this.#location.archiveFile,
2286
+ operation: "Moving chapter",
2287
+ scope: { kind: "archive" }
2288
+ });
2289
+ return await (0, import_wiki_graph_core5.moveChapter)(document, chapterId, {
2290
+ ...afterChapterId === void 0 ? {} : { afterChapterId },
2291
+ ...beforeChapterId === void 0 ? {} : { beforeChapterId },
2292
+ ...parentChapterId === void 0 ? {} : { parentChapterId },
2293
+ ...options.first === void 0 ? {} : { first: options.first },
2294
+ ...options.last === void 0 ? {} : { last: options.last },
2295
+ ...options.root === void 0 ? {} : { root: options.root }
2296
+ });
2297
+ });
2298
+ }
2299
+ async removeChapter(path, recursive = false) {
2300
+ await this.#writeDocument(async (document) => {
2301
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2302
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobConflicts)({
2303
+ archive: this.#location.archiveFile,
2304
+ operation: "Removing chapter",
2305
+ scope: { kind: "archive" }
2306
+ });
2307
+ await (0, import_wiki_graph_core5.removeChapter)(document, chapterId, { recursive });
2308
+ });
2309
+ }
2310
+ async resetChapter(path, stage) {
2311
+ return await this.#writeDocument(async (document) => {
2312
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2313
+ await assertChapterResetAllowed(
2314
+ this.#location.archiveFile,
2315
+ chapterId,
2316
+ stage
2317
+ );
2318
+ return await (0, import_wiki_graph_core5.resetChapter)(document, chapterId, stage);
2319
+ });
2320
+ }
2321
+ async setChapterSource(path, source, options = {}) {
2322
+ return await this.#writeDocument(async (document) => {
2323
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2324
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2325
+ archive: this.#location.archiveFile,
2326
+ chapterIds: [chapterId],
2327
+ operation: "Setting chapter source"
2328
+ });
2329
+ return await (0, import_wiki_graph_core5.setChapterSource)(
2330
+ document,
2331
+ chapterId,
2332
+ asTextStream(source),
2333
+ options
2334
+ );
2335
+ });
2336
+ }
2337
+ async setChapterSummary(path, summary) {
2338
+ return await this.#writeDocument(async (document) => {
2339
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2340
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2341
+ archive: this.#location.archiveFile,
2342
+ chapterIds: [chapterId],
2343
+ operation: "Setting chapter summary",
2344
+ requiresTarget: "reading-summary"
2345
+ });
2346
+ return await (0, import_wiki_graph_core5.setChapterSummary)(document, chapterId, summary);
2347
+ });
2348
+ }
2349
+ async setChapterTitle(path, title) {
2350
+ return await this.#writeDocument(async (document) => {
2351
+ const chapterId = await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2352
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2353
+ archive: this.#location.archiveFile,
2354
+ chapterIds: [chapterId],
2355
+ operation: "Setting chapter title"
2356
+ });
2357
+ return await (0, import_wiki_graph_core5.setChapterTitle)(document, chapterId, title);
2358
+ });
2359
+ }
2360
+ async getChapterTree() {
2361
+ return await this.#readDocument(
2362
+ async (document) => await (0, import_wiki_graph_core5.getChapterTree)(document)
2363
+ );
2364
+ }
2365
+ async applyChapterTree(tree, options = {}) {
2366
+ return await this.#writeDocument(
2367
+ async (document) => {
2368
+ if (options.dryRun !== true) {
2369
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobConflicts)({
2370
+ archive: this.#location.archiveFile,
2371
+ operation: "Changing chapter tree",
2372
+ scope: { kind: "archive" }
2373
+ });
2374
+ }
2375
+ return await (0, import_wiki_graph_core5.applyChapterTree)(document, tree, {
2376
+ dryRun: options.dryRun ?? false
2377
+ });
2378
+ },
2379
+ { refreshLibraryIndex: options.dryRun !== true }
2380
+ );
2381
+ }
2382
+ async page(objectUri = this.objectUri, options = {}) {
2383
+ return await this.#runtime.run(
2384
+ async () => await withConfiguredWikimediaResolver(
2385
+ objectUri,
2386
+ async (wikimediaOptions) => await this.#readDocument(
2387
+ async (document) => await (0, import_wiki_graph_core5.readArchivePage)(document, objectUri, {
2388
+ ...withoutOperation(options),
2389
+ ...wikimediaOptions
2390
+ }),
2391
+ options
2392
+ )
2393
+ ),
2394
+ options.signal
2395
+ );
2396
+ }
2397
+ async related(objectUri = this.objectUri, options = {}) {
2398
+ if (options.query === void 0) {
2399
+ return await this.#readDocument(async (document) => {
2400
+ const chapters = await this.#resolveScope(document, {});
2401
+ return await (0, import_wiki_graph_core5.listRelatedArchiveObjects)(document, objectUri, {
2402
+ ...withoutOperation(options),
2403
+ ...chapters === void 0 ? {} : { chapters }
2404
+ });
2405
+ }, options);
2406
+ }
2407
+ const embeddingProvider = await this.#runtime.run(
2408
+ createConfiguredEmbeddingProvider,
2409
+ options.signal
2410
+ );
2411
+ return await this.#writeDocument(
2412
+ async (document) => {
2413
+ const chapters = await this.#resolveQueryChapters(document, options);
2414
+ await ensureArchiveSearchIndex(document, {
2415
+ ...chapters === void 0 ? {} : { chapters },
2416
+ ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2417
+ });
2418
+ return await (0, import_wiki_graph_core5.listRelatedArchiveObjects)(document, objectUri, {
2419
+ ...withoutOperation(options),
2420
+ ...chapters === void 0 ? {} : { chapters }
2421
+ });
2422
+ },
2423
+ {
2424
+ searchIndexWritebackPolicy: "cache",
2425
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2426
+ }
2427
+ );
2428
+ }
2429
+ async evidence(objectUri = this.objectUri, options = {}) {
2430
+ if (options.query === void 0) {
2431
+ return await this.#readDocument(async (document) => {
2432
+ const chapters = await this.#resolveScope(document, {});
2433
+ return await (0, import_wiki_graph_core5.listArchiveEvidence)(document, objectUri, {
2434
+ ...withoutOperation(options),
2435
+ ...chapters === void 0 ? {} : { chapters }
2436
+ });
2437
+ }, options);
2438
+ }
2439
+ const embeddingProvider = await this.#runtime.run(
2440
+ createConfiguredEmbeddingProvider,
2441
+ options.signal
2442
+ );
2443
+ return await this.#writeDocument(
2444
+ async (document) => {
2445
+ const chapters = await this.#resolveQueryChapters(document, options);
2446
+ await ensureArchiveSearchIndex(document, {
2447
+ ...chapters === void 0 ? {} : { chapters },
2448
+ ...embeddingProvider === void 0 ? {} : { embeddingProvider }
2449
+ });
2450
+ return await (0, import_wiki_graph_core5.listArchiveEvidence)(document, objectUri, {
2451
+ ...withoutOperation(options),
2452
+ ...chapters === void 0 ? {} : { chapters }
2453
+ });
2454
+ },
2455
+ {
2456
+ searchIndexWritebackPolicy: "cache",
2457
+ ...options.signal === void 0 ? {} : { signal: options.signal }
2458
+ }
2459
+ );
2460
+ }
2461
+ async pack(objectUri = this.objectUri, budget = 5e3, options = {}) {
2462
+ return await this.#readDocument(
2463
+ async (document) => await (0, import_wiki_graph_core5.packArchiveContext)(document, objectUri, budget),
2464
+ options
2465
+ );
2466
+ }
2467
+ async getMetadata(objectPath, options = {}) {
2468
+ return await this.#readDocument(
2469
+ async (document) => await document.metadata.getMap(objectPath),
2470
+ options
2471
+ );
2472
+ }
2473
+ async readBookMeta(options = {}) {
2474
+ return await this.#readDocument(
2475
+ async (document) => await document.readBookMeta(),
2476
+ options
2477
+ );
2478
+ }
2479
+ async replaceBookMeta(meta, options = {}) {
2480
+ return await this.#writeDocument(async (document) => {
2481
+ await document.replaceBookMeta(meta);
2482
+ return meta;
2483
+ }, options);
2484
+ }
2485
+ async readCover(options = {}) {
2486
+ const app = new import_wiki_graph_core5.WikiGraph({});
2487
+ return await this.#runtime.run(
2488
+ async () => await app.openSession(
2489
+ this.#location.archiveFile,
2490
+ async (archive) => await archive.readCover()
2491
+ ),
2492
+ options.signal
2493
+ );
2494
+ }
2495
+ async replaceMetadata(objectPath, value, options = {}) {
2496
+ return await this.#writeDocument(async (document) => {
2497
+ await document.metadata.replaceMap(
2498
+ parseObjectMetadataTarget(objectPath),
2499
+ value
2500
+ );
2501
+ return await document.metadata.getMap(objectPath);
2502
+ }, options);
2503
+ }
2504
+ async putMetadata(objectPath, key, value, options = {}) {
2505
+ return await this.#writeDocument(async (document) => {
2506
+ await document.metadata.put(
2507
+ parseObjectMetadataTarget(objectPath),
2508
+ key,
2509
+ value
2510
+ );
2511
+ return await document.metadata.getMap(objectPath);
2512
+ }, options);
2513
+ }
2514
+ async deleteMetadata(objectPath, key, options = {}) {
2515
+ return await this.#writeDocument(async (document) => {
2516
+ await document.metadata.deleteKey(objectPath, key);
2517
+ return await document.metadata.getMap(objectPath);
2518
+ }, options);
2519
+ }
2520
+ async clearMetadata(objectPath, options = {}) {
2521
+ return await this.#writeDocument(async (document) => {
2522
+ await document.metadata.clear(objectPath);
2523
+ return await document.metadata.getMap(objectPath);
2524
+ }, options);
2525
+ }
2526
+ async resolveScope(options = {}) {
2527
+ return await this.#readDocument(
2528
+ async (document) => await resolveArchiveScope(document, this.objectUri, options.depth),
2529
+ options
2530
+ );
2531
+ }
2532
+ async #readDocument(operation, options = {}) {
2533
+ return await this.#runtime.run(
2534
+ async () => await new import_wiki_graph_core5.WikiGraphArchiveFile(this.#location.archiveFile).readDocument(
2535
+ operation
2536
+ ),
2537
+ options.signal
2538
+ );
2539
+ }
2540
+ async #writeDocument(operation, options = {}) {
2541
+ return await this.#runtime.run(async () => {
2542
+ const result = await new import_wiki_graph_core5.WikiGraphArchiveFile(
2543
+ this.#location.archiveFile
2544
+ ).write(operation, {
2545
+ ...options.searchIndexWritebackPolicy === void 0 ? {} : {
2546
+ searchIndexWritebackPolicy: options.searchIndexWritebackPolicy
2547
+ }
2548
+ });
2549
+ if (options.refreshLibraryIndex !== false) {
2550
+ await this.#refreshLibraryIndex(options.onIndexSyncError);
2551
+ }
2552
+ return result;
2553
+ }, options.signal);
2554
+ }
2555
+ async #resolveScope(document, options) {
2556
+ if (options.chapters !== void 0) return options.chapters;
2557
+ return (await resolveArchiveScope(document, this.objectUri, options.depth))?.chapterIds;
2558
+ }
2559
+ async #resolveQueryChapters(document, options) {
2560
+ const chapters = await this.#resolveScope(document, options);
2561
+ if (options.skipUnindexed !== true) return chapters;
2562
+ const queryable = await (0, import_wiki_graph_core5.listArchiveQueryableChapterIds)(document, {
2563
+ ...chapters === void 0 ? {} : { chapters }
2564
+ });
2565
+ if (queryable.length === 0) {
2566
+ throw new Error(
2567
+ "Wiki Graph query is not ready. No chapters in this scope have a current FTS artifact or source embedding artifact."
2568
+ );
2569
+ }
2570
+ return queryable;
2571
+ }
2572
+ async #refreshLibraryIndex(onError) {
2573
+ if (this.#location.libraryDirtyTarget === void 0) return;
2574
+ if (this.#location.libraryArchiveTarget !== void 0) {
2575
+ await (0, import_wiki_graph_core5.finalizeWikiGraphLibraryArchiveWrite)({
2576
+ target: this.#location.libraryArchiveTarget
2577
+ });
2578
+ }
2579
+ try {
2580
+ const state = await (0, import_wiki_graph_core5.readWikiGraphLibraryIndexState)(
2581
+ this.#location.libraryDirtyTarget
2582
+ );
2583
+ if (state.status !== "missing") {
2584
+ await (0, import_wiki_graph_core5.rebuildWikiGraphLibraryIndex)(this.#location.libraryDirtyTarget);
2585
+ }
2586
+ } catch (error) {
2587
+ onError?.(error);
2588
+ }
2589
+ }
2590
+ };
2591
+ async function resolveWikiGraphArchiveLocation(uriOrPath) {
2592
+ if (!uriOrPath.startsWith("wikg://")) {
2593
+ return createPathLocation(resolveWikiGraphRuntimePath(uriOrPath));
2594
+ }
2595
+ const parsed = (0, import_wiki_graph_core5.parseLocatedWikiGraphUri)(uriOrPath);
2596
+ const portableArchiveLocator = parsed.archivePath ?? uriOrPath;
2597
+ const archiveLocator = portableArchiveLocator.startsWith("wikg://lib/") ? portableArchiveLocator : resolveWikiGraphRuntimePath(portableArchiveLocator);
2598
+ const libraryArchiveTarget = archiveLocator.startsWith("wikg://lib/") ? (0, import_wiki_graph_core5.parseWikiGraphLibraryUri)(archiveLocator) : void 0;
2599
+ const archiveFile = libraryArchiveTarget?.kind === "archive" ? await (0, import_wiki_graph_core5.resolveWikiGraphLibraryArchiveFile)(archiveLocator) : new NodeFile(archiveLocator);
2600
+ const archivePath = getNodeResourcePath(archiveFile);
2601
+ return {
2602
+ archiveFile,
2603
+ archiveKey: archivePath,
2604
+ archivePath,
2605
+ indexScope: { archiveKey: archivePath, archivePath, kind: "archive-index" },
2606
+ ...libraryArchiveTarget?.kind === "archive" ? {
2607
+ libraryArchiveTarget,
2608
+ libraryDirtyTarget: {
2609
+ isDefault: libraryArchiveTarget.isDefault,
2610
+ kind: "scope",
2611
+ ...libraryArchiveTarget.publicId === void 0 ? {} : { publicId: libraryArchiveTarget.publicId }
2612
+ },
2613
+ publicArchiveUri: portableArchiveLocator
2614
+ } : {},
2615
+ locatedUri: (0, import_wiki_graph_core5.formatLocatedWikiGraphUri)(archivePath, parsed.objectUri)
2616
+ };
2617
+ }
2618
+ async function ensureArchiveSearchIndex(document, options = {}) {
2619
+ const scope = options.chapters === void 0 ? {} : { chapters: options.chapters };
2620
+ if (await (0, import_wiki_graph_core5.isArchiveSearchIndexCurrent)(document, scope)) return;
2621
+ await (0, import_wiki_graph_core5.rebuildArchiveSearchIndex)(document, void 0, {
2622
+ ...scope,
2623
+ ...options.embeddingProvider === void 0 ? {} : { embeddingProvider: options.embeddingProvider }
2624
+ });
2625
+ }
2626
+ async function resolveArchiveScope(document, objectUri, depth) {
2627
+ const parsed = parseChapterScopePath(objectUri);
2628
+ if (parsed === void 0 || parsed.kind === "collection" && depth === void 0) {
2629
+ return void 0;
2630
+ }
2631
+ const chapters = await (0, import_wiki_graph_core5.listChapters)(document);
2632
+ const entries = parsed.kind === "collection" ? chapters.filter((chapter) => chapter.depth <= depth) : selectChapterSubtree(
2633
+ chapters,
2634
+ await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, parsed.chapterPath),
2635
+ depth
2636
+ );
2637
+ return { chapterIds: entries.map((entry) => entry.chapterId), entries };
2638
+ }
2639
+ function parseChapterScopePath(objectUri) {
2640
+ if (objectUri === "wikg://chapter" || objectUri === "wikg://chapter/") {
2641
+ return { kind: "collection" };
2642
+ }
2643
+ const match = /^wikg:\/\/chapter\/([^/].*?)(?:\/)?$/u.exec(objectUri);
2644
+ return match?.[1] === void 0 ? void 0 : { chapterPath: decodeURIComponent(match[1]), kind: "chapter" };
2645
+ }
2646
+ function selectChapterSubtree(chapters, rootChapterId, depth) {
2647
+ const root = chapters.find((chapter) => chapter.chapterId === rootChapterId);
2648
+ if (root === void 0) {
2649
+ throw new Error(`Chapter ${rootChapterId} does not exist.`);
2650
+ }
2651
+ const prefix = `${root.path}/`;
2652
+ return chapters.filter(
2653
+ (chapter) => chapter.chapterId === root.chapterId || chapter.path.startsWith(prefix) && (depth === void 0 || chapter.depth - root.depth <= depth)
2654
+ );
2655
+ }
2656
+ function createPathLocation(path) {
2657
+ return {
2658
+ archiveFile: new NodeFile(path),
2659
+ archiveKey: path,
2660
+ archivePath: path,
2661
+ indexScope: { archiveKey: path, archivePath: path, kind: "archive-index" },
2662
+ locatedUri: (0, import_wiki_graph_core5.formatLocatedWikiGraphUri)(path)
2663
+ };
2664
+ }
2665
+ async function createEmptyArchiveFile(path) {
2666
+ const directoryPath = await (0, import_promises3.mkdtemp)(
2667
+ (0, import_path3.join)((0, import_os3.tmpdir)(), "wikigraph-archive-write-")
2668
+ );
2669
+ try {
2670
+ const directory = new NodeDirectory(directoryPath);
2671
+ const document = await import_wiki_graph_core5.DirectoryDocument.open(directory);
2672
+ try {
2673
+ await document.openSession(async (openedDocument) => {
2674
+ await openedDocument.writeToc({ items: [], version: import_wiki_graph_core5.TOC_FILE_VERSION });
2675
+ });
2676
+ } finally {
2677
+ await document.release();
2678
+ }
2679
+ await (0, import_wiki_graph_core5.writeWikgArchive)(directory, new NodeFile(path));
2680
+ } finally {
2681
+ await (0, import_promises3.rm)(directoryPath, { force: true, recursive: true });
2682
+ }
2683
+ }
2684
+ function isNodeEEXISTError(error) {
2685
+ return error instanceof Error && "code" in error && error.code === "EEXIST";
2686
+ }
2687
+ async function nodePathExists(path) {
2688
+ try {
2689
+ await (0, import_promises3.stat)(path);
2690
+ return true;
2691
+ } catch (error) {
2692
+ if (isNodeENOENTError(error)) return false;
2693
+ throw error;
2694
+ }
2695
+ }
2696
+ function isNodeENOENTError(error) {
2697
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
2698
+ }
2699
+ function parseObjectMetadataTarget(objectPath) {
2700
+ if (objectPath === "") {
2701
+ return { kind: import_wiki_graph_core5.ObjectMetadataKind.Archive, objectPath };
2702
+ }
2703
+ const chapter = /^chapter\/([1-9][0-9]*)(?:\/.*)?$/u.exec(objectPath);
2704
+ if (chapter?.[1] !== void 0) {
2705
+ return {
2706
+ chapterId: Number(chapter[1]),
2707
+ kind: import_wiki_graph_core5.ObjectMetadataKind.Chapter,
2708
+ objectPath
2709
+ };
2710
+ }
2711
+ const chunk = /^chunk\/([1-9][0-9]*)$/u.exec(objectPath);
2712
+ if (chunk?.[1] !== void 0) {
2713
+ return {
2714
+ chunkId: Number(chunk[1]),
2715
+ kind: import_wiki_graph_core5.ObjectMetadataKind.Chunk,
2716
+ objectPath
2717
+ };
2718
+ }
2719
+ const entity = /^entity\/(Q[1-9][0-9]*)$/u.exec(objectPath);
2720
+ if (entity?.[1] !== void 0) {
2721
+ return {
2722
+ entityQid: entity[1],
2723
+ kind: import_wiki_graph_core5.ObjectMetadataKind.Entity,
2724
+ objectPath
2725
+ };
2726
+ }
2727
+ const triple = /^triple\/(Q[1-9][0-9]*)\/([^/]+)\/(Q[1-9][0-9]*)$/u.exec(
2728
+ objectPath
2729
+ );
2730
+ if (triple?.[1] !== void 0) {
2731
+ return {
2732
+ kind: import_wiki_graph_core5.ObjectMetadataKind.Triple,
2733
+ objectPath,
2734
+ tripleObjectQid: triple[3],
2735
+ triplePredicate: decodeURIComponent(triple[2]),
2736
+ tripleSubjectQid: triple[1]
2737
+ };
2738
+ }
2739
+ return { kind: import_wiki_graph_core5.ObjectMetadataKind.Object, objectPath };
2740
+ }
2741
+ function withoutOperation(options) {
2742
+ const { signal: _signal, ...rest } = options;
2743
+ return rest;
2744
+ }
2745
+ function withoutOperationAndScope(options) {
2746
+ const {
2747
+ chapters: _chapters,
2748
+ depth: _depth,
2749
+ signal: _signal,
2750
+ ...rest
2751
+ } = options;
2752
+ return rest;
2753
+ }
2754
+ async function resolveOptionalChapterPath(document, path) {
2755
+ return path === void 0 ? void 0 : await (0, import_wiki_graph_core5.resolveChapterPathReadonly)(document, path);
2756
+ }
2757
+ function requireChapter(chapters, chapterId) {
2758
+ const chapter = chapters.find((entry) => entry.chapterId === chapterId);
2759
+ if (chapter === void 0) {
2760
+ throw new Error(`Chapter internal id ${chapterId} does not exist.`);
2761
+ }
2762
+ return chapter;
2763
+ }
2764
+ function asTextStream(text) {
2765
+ return import_stream.Readable.from([text]);
2766
+ }
2767
+ async function assertChapterResetAllowed(archive, chapterId, stage) {
2768
+ if (stage === "planned") {
2769
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2770
+ archive,
2771
+ chapterIds: [chapterId],
2772
+ operation: "Resetting chapter to planned"
2773
+ });
2774
+ return;
2775
+ }
2776
+ if (stage === "sourced") {
2777
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2778
+ archive,
2779
+ chapterIds: [chapterId],
2780
+ operation: "Resetting chapter graph",
2781
+ requiresTarget: "reading-graph"
2782
+ });
2783
+ }
2784
+ await (0, import_wiki_graph_core5.assertNoActiveBuildJobs)({
2785
+ archive,
2786
+ chapterIds: [chapterId],
2787
+ operation: "Resetting chapter summary",
2788
+ requiresTarget: "reading-summary"
2789
+ });
2790
+ }
2791
+ // Annotate the CommonJS export names for ESM import in node:
2792
+ 0 && (module.exports = {
2793
+ WikiGraphArchiveExistsError,
2794
+ WikiGraphArchiveHandle,
2795
+ WikiGraphArchiveManager,
2796
+ createWikiGraphArchiveInspectReport,
2797
+ resolveWikiGraphArchiveLocation
2798
+ });
2799
+ //# sourceMappingURL=archives.cjs.map