wiki-graph-sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  import { AddBuildJobOptions, BuildJob, BuildJobListOptions, BuildJobEventChunk, BuildJobEvent, BuildJobTarget, ChapterEntry } from 'wiki-graph-core';
2
+ import { W as WikiGraphArchiveTarget } from './target-CZ7elwmj.js';
2
3
 
3
4
  interface WikiGraphJobBackend {
4
5
  add(options: AddBuildJobOptions): Promise<BuildJob>;
@@ -13,18 +14,25 @@ interface WikiGraphJobBackend {
13
14
  setTarget(jobId: string, target: BuildJobTarget): Promise<BuildJob>;
14
15
  }
15
16
  interface WikiGraphJobCreateOptions extends Omit<AddBuildJobOptions, "archive"> {
16
- readonly archive: string | AddBuildJobOptions["archive"];
17
+ readonly archive: WikiGraphArchiveTarget;
17
18
  }
18
19
  interface WikiGraphJobListOptions extends Omit<BuildJobListOptions, "archive"> {
19
- readonly archive?: string | BuildJobListOptions["archive"];
20
+ readonly archive?: WikiGraphArchiveTarget;
20
21
  }
21
22
  interface WikiGraphJobChapterReference {
22
23
  readonly locatedUri: string;
23
24
  readonly title: string | null;
24
25
  readonly uri: string;
25
26
  }
27
+ interface WikiGraphJobSnapshot extends Omit<BuildJob, "archive" | "cache" | "events" | "log" | "workspace"> {
28
+ readonly archive: WikiGraphArchiveTarget;
29
+ readonly cachePath?: string;
30
+ readonly eventsPath?: string;
31
+ readonly logPath?: string;
32
+ readonly workspacePath?: string;
33
+ }
26
34
  interface WikiGraphJobEnqueueOptions {
27
- readonly archive: string;
35
+ readonly archive: WikiGraphArchiveTarget;
28
36
  readonly boost?: boolean;
29
37
  readonly chapterId?: number;
30
38
  readonly chapterIds?: readonly number[];
@@ -73,8 +81,8 @@ declare class WikiGraphJobManager {
73
81
  get(jobId: string): Promise<WikiGraphJob>;
74
82
  resolveId(reference: string): Promise<string>;
75
83
  list(options?: WikiGraphJobListOptions): Promise<readonly WikiGraphJob[]>;
76
- resolveChapters(jobs: readonly BuildJob[]): Promise<ReadonlyMap<string, WikiGraphJobChapterReference>>;
77
- resolveChapter(job: BuildJob): Promise<WikiGraphJobChapterReference | undefined>;
84
+ resolveChapters(jobs: readonly WikiGraphJobSnapshot[]): Promise<ReadonlyMap<string, WikiGraphJobChapterReference>>;
85
+ resolveChapter(job: WikiGraphJobSnapshot): Promise<WikiGraphJobChapterReference | undefined>;
78
86
  enqueue(options: WikiGraphJobEnqueueOptions): Promise<WikiGraphJobEnqueueResult>;
79
87
  planEnqueue(options: WikiGraphJobEnqueueOptions): Promise<WikiGraphJobEnqueuePlan>;
80
88
  close(): void;
@@ -82,17 +90,17 @@ declare class WikiGraphJobManager {
82
90
  }
83
91
  declare class WikiGraphJob {
84
92
  #private;
85
- constructor(manager: WikiGraphJobManager, runtime: WikiGraphJobRuntime, backend: WikiGraphJobBackend, snapshot: BuildJob);
93
+ constructor(manager: WikiGraphJobManager, runtime: WikiGraphJobRuntime, backend: WikiGraphJobBackend, snapshot: BuildJob, archiveTarget: WikiGraphArchiveTarget);
86
94
  get id(): string;
87
- get snapshot(): BuildJob;
88
- status(): Promise<BuildJob>;
89
- pause(): Promise<BuildJob>;
90
- resume(): Promise<BuildJob>;
91
- cancel(): Promise<BuildJob>;
92
- boost(): Promise<BuildJob>;
93
- setTarget(target: BuildJobTarget): Promise<BuildJob>;
95
+ get snapshot(): WikiGraphJobSnapshot;
96
+ status(): Promise<WikiGraphJobSnapshot>;
97
+ pause(): Promise<WikiGraphJobSnapshot>;
98
+ resume(): Promise<WikiGraphJobSnapshot>;
99
+ cancel(): Promise<WikiGraphJobSnapshot>;
100
+ boost(): Promise<WikiGraphJobSnapshot>;
101
+ setTarget(target: BuildJobTarget): Promise<WikiGraphJobSnapshot>;
94
102
  events(options?: WikiGraphJobEventsOptions): AsyncIterable<BuildJobEvent>;
95
103
  subscribe(listener: WikiGraphJobEventListener, options?: WikiGraphJobSubscriptionOptions): () => void;
96
104
  }
97
105
 
98
- export { type WikiGraphJobRuntime as W, WikiGraphJobManager as a, WikiGraphJob as b, type WikiGraphJobBackend as c, type WikiGraphJobChapterReference as d, type WikiGraphJobCreateOptions as e, type WikiGraphJobEnqueueOptions as f, type WikiGraphJobEnqueuePlan as g, type WikiGraphJobEnqueueResult as h, type WikiGraphJobEventListener as i, type WikiGraphJobEventsOptions as j, type WikiGraphJobListOptions as k, type WikiGraphJobSubscriptionOptions as l };
106
+ export { type WikiGraphJobRuntime as W, WikiGraphJobManager as a, WikiGraphJob as b, type WikiGraphJobBackend as c, type WikiGraphJobChapterReference as d, type WikiGraphJobCreateOptions as e, type WikiGraphJobEnqueueOptions as f, type WikiGraphJobEnqueuePlan as g, type WikiGraphJobEnqueueResult as h, type WikiGraphJobEventListener as i, type WikiGraphJobEventsOptions as j, type WikiGraphJobListOptions as k, type WikiGraphJobSnapshot as l, type WikiGraphJobSubscriptionOptions as m };
@@ -34,8 +34,8 @@ __export(maintenance_exports, {
34
34
  WikiGraphMaintenanceManager: () => WikiGraphMaintenanceManager
35
35
  });
36
36
  module.exports = __toCommonJS(maintenance_exports);
37
- var import_wiki_graph_core = require("wiki-graph-core");
38
- var import_path2 = require("path");
37
+ var import_wiki_graph_core2 = require("wiki-graph-core");
38
+ var import_path3 = require("path");
39
39
 
40
40
  // src/node-platform.ts
41
41
  var asyncHooks = __toESM(require("async_hooks"), 1);
@@ -264,6 +264,14 @@ function decodeNodeResourceIdentity(identity, kind) {
264
264
  return void 0;
265
265
  }
266
266
  }
267
+ function getNodeResourcePath(resource) {
268
+ const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
269
+ if (brandedPath !== void 0) return brandedPath;
270
+ if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
271
+ return resource.path;
272
+ }
273
+ throw new TypeError("Expected a Node File or Directory resource");
274
+ }
267
275
  function assertChildName(name) {
268
276
  if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
269
277
  throw new TypeError(`Directory child must be a relative name: ${name}`);
@@ -275,42 +283,42 @@ var NodeDatabaseConnection = class {
275
283
  }
276
284
  database;
277
285
  async close() {
278
- await new Promise((resolve3, reject) => {
286
+ await new Promise((resolve4, reject) => {
279
287
  this.database.close((error) => {
280
288
  if (error) reject(error);
281
- else resolve3();
289
+ else resolve4();
282
290
  });
283
291
  });
284
292
  }
285
293
  async execute(sql) {
286
- await new Promise((resolve3, reject) => {
294
+ await new Promise((resolve4, reject) => {
287
295
  this.database.exec(sql, (error) => {
288
296
  if (error) reject(error);
289
- else resolve3();
297
+ else resolve4();
290
298
  });
291
299
  });
292
300
  }
293
301
  async queryAll(sql, params = []) {
294
- return await new Promise((resolve3, reject) => {
302
+ return await new Promise((resolve4, reject) => {
295
303
  this.database.all(sql, [...params], (error, rows) => {
296
304
  if (error) reject(error);
297
- else resolve3(rows);
305
+ else resolve4(rows);
298
306
  });
299
307
  });
300
308
  }
301
309
  async queryOne(sql, params = []) {
302
- return await new Promise((resolve3, reject) => {
310
+ return await new Promise((resolve4, reject) => {
303
311
  this.database.get(sql, [...params], (error, row) => {
304
312
  if (error) reject(error);
305
- else resolve3(row);
313
+ else resolve4(row);
306
314
  });
307
315
  });
308
316
  }
309
317
  async run(sql, params = []) {
310
- await new Promise((resolve3, reject) => {
318
+ await new Promise((resolve4, reject) => {
311
319
  this.database.run(sql, [...params], (error) => {
312
320
  if (error) reject(error);
313
- else resolve3();
321
+ else resolve4();
314
322
  });
315
323
  });
316
324
  }
@@ -324,10 +332,10 @@ async function openNativeNodeDatabase(file, flags) {
324
332
  if (path === void 0) {
325
333
  throw new TypeError("The Node database adapter requires a NodeFile");
326
334
  }
327
- return await new Promise((resolve3, reject) => {
335
+ return await new Promise((resolve4, reject) => {
328
336
  const database = new nodeSqlite3.Database(path, flags, (error) => {
329
337
  if (error) reject(error);
330
- else resolve3(database);
338
+ else resolve4(database);
331
339
  });
332
340
  });
333
341
  }
@@ -336,19 +344,19 @@ async function openNodeZip(file) {
336
344
  if (path === void 0) {
337
345
  throw new TypeError("The Node ZIP adapter requires a NodeFile");
338
346
  }
339
- const zipFile = await new Promise((resolve3, reject) => {
347
+ const zipFile = await new Promise((resolve4, reject) => {
340
348
  yauzl.open(
341
349
  path,
342
350
  { autoClose: false, lazyEntries: true },
343
351
  (error, opened) => {
344
352
  if (error || opened === void 0)
345
353
  reject(error ?? new Error("Cannot open ZIP"));
346
- else resolve3(opened);
354
+ else resolve4(opened);
347
355
  }
348
356
  );
349
357
  });
350
358
  const entries = await new Promise(
351
- (resolve3, reject) => {
359
+ (resolve4, reject) => {
352
360
  const discovered = /* @__PURE__ */ new Map();
353
361
  const rejectAndClose = (error) => {
354
362
  zipFile.close();
@@ -358,7 +366,7 @@ async function openNodeZip(file) {
358
366
  };
359
367
  const resolveEntries = () => {
360
368
  zipFile.off("error", rejectAndClose);
361
- resolve3(discovered);
369
+ resolve4(discovered);
362
370
  };
363
371
  zipFile.on("entry", (entry) => {
364
372
  if (!entry.fileName.endsWith("/")) {
@@ -468,12 +476,12 @@ async function collectDecodedNodeStreamRange(input, offset, length) {
468
476
  return Buffer.concat(chunks, collected);
469
477
  }
470
478
  async function openNodeZipEntryStream(zipFile, entry, options) {
471
- return await new Promise((resolve3, reject) => {
479
+ return await new Promise((resolve4, reject) => {
472
480
  const callback = (error, opened) => {
473
481
  if (error || opened === void 0) {
474
482
  reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
475
483
  } else {
476
- resolve3(opened);
484
+ resolve4(opened);
477
485
  }
478
486
  };
479
487
  if (options === void 0) zipFile.openReadStream(entry, callback);
@@ -604,11 +612,11 @@ async function writeNodeStream(input, writer) {
604
612
  );
605
613
  }
606
614
  async function collectNodeStream(input) {
607
- return await new Promise((resolve3, reject) => {
615
+ return await new Promise((resolve4, reject) => {
608
616
  const chunks = [];
609
617
  input.on("data", (chunk) => chunks.push(chunk));
610
618
  input.once("error", reject);
611
- input.once("end", () => resolve3(Buffer.concat(chunks)));
619
+ input.once("end", () => resolve4(Buffer.concat(chunks)));
612
620
  });
613
621
  }
614
622
  var nodeWikiGraphPlatform = {
@@ -741,6 +749,107 @@ function resolveWikiGraphRuntimePath(path) {
741
749
  return (0, import_path.isAbsolute)(path) ? path : (0, import_path.resolve)(context.cwd, path);
742
750
  }
743
751
 
752
+ // src/archive/target.ts
753
+ var import_promises2 = require("fs/promises");
754
+ var import_path2 = require("path");
755
+ var import_wiki_graph_core = require("wiki-graph-core");
756
+ var WikiGraphArchiveOwnershipError = class extends Error {
757
+ code = "WIKI_GRAPH_ARCHIVE_OWNERSHIP_MISMATCH";
758
+ constructor(message) {
759
+ super(message);
760
+ this.name = "WikiGraphArchiveOwnershipError";
761
+ }
762
+ };
763
+ async function assertStandaloneWikiGraphArchivePath(inputPath) {
764
+ const path = resolveWikiGraphRuntimePath(inputPath);
765
+ const candidate = await canonicalPath(path);
766
+ const candidateIdentity = await readFileIdentity(path);
767
+ const libraries = await (0, import_wiki_graph_core.listWikiGraphLibraries)();
768
+ const matches = await findMatchingMembers(
769
+ libraries,
770
+ candidate,
771
+ candidateIdentity
772
+ );
773
+ if (matches.length > 0) {
774
+ const match = requireUnambiguousManagedMatch(path, matches);
775
+ throw new WikiGraphArchiveOwnershipError(
776
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
777
+ match.library.isDefault ? void 0 : match.library.publicId
778
+ )}: ${path}. Use the library UUID URI ${match.archive.uri} instead.`
779
+ );
780
+ }
781
+ for (const library of libraries) {
782
+ const rootPath = getNodeResourcePath(library.folder);
783
+ const root = await canonicalPath(rootPath);
784
+ if (!isWithin(root, candidate)) continue;
785
+ throw new WikiGraphArchiveOwnershipError(
786
+ `Standalone archive path belongs to registered library ${(0, import_wiki_graph_core.formatWikiGraphLibraryUri)(
787
+ library.isDefault ? void 0 : library.publicId
788
+ )}: ${path}. Scan the library, then address the archive by its library UUID URI.`
789
+ );
790
+ }
791
+ return path;
792
+ }
793
+ async function canonicalPath(path) {
794
+ try {
795
+ return await (0, import_promises2.realpath)(path);
796
+ } catch {
797
+ const absolute = (0, import_path2.resolve)(path);
798
+ const parent = (0, import_path2.dirname)(absolute);
799
+ return parent === absolute ? absolute : (0, import_path2.join)(await canonicalPath(parent), (0, import_path2.basename)(absolute));
800
+ }
801
+ }
802
+ function isWithin(root, candidate) {
803
+ const child = (0, import_path2.relative)(root, candidate);
804
+ return child === "" || !child.startsWith("..") && !(0, import_path2.isAbsolute)(child);
805
+ }
806
+ async function findMatchingMembers(libraries, candidate, candidateIdentity) {
807
+ const members = [];
808
+ for (const library of libraries) {
809
+ const archives = await (0, import_wiki_graph_core.listWikiGraphLibraryArchives)({
810
+ isDefault: library.isDefault,
811
+ kind: "scope",
812
+ ...library.isDefault ? {} : { publicId: library.publicId }
813
+ });
814
+ for (const archive of archives) {
815
+ if (archive.file === void 0) continue;
816
+ members.push({
817
+ archive,
818
+ library,
819
+ memberPath: getNodeResourcePath(archive.file)
820
+ });
821
+ }
822
+ }
823
+ const exact = [];
824
+ for (const member of members) {
825
+ if (await canonicalPath(member.memberPath) === candidate) {
826
+ exact.push(member);
827
+ }
828
+ }
829
+ if (exact.length > 0 || candidateIdentity === void 0) return exact;
830
+ const identityMatches = [];
831
+ for (const member of members) {
832
+ if (await readFileIdentity(member.memberPath) === candidateIdentity) {
833
+ identityMatches.push(member);
834
+ }
835
+ }
836
+ return identityMatches;
837
+ }
838
+ function requireUnambiguousManagedMatch(path, matches) {
839
+ if (matches.length === 1) return matches[0];
840
+ throw new WikiGraphArchiveOwnershipError(
841
+ `Archive file identity is ambiguous across managed archives: ${path}. Use an explicit library UUID URI (${matches.map((match) => match.archive.uri).join(", ")}).`
842
+ );
843
+ }
844
+ async function readFileIdentity(path) {
845
+ try {
846
+ const value = await (0, import_promises2.stat)(path);
847
+ return `${value.dev}:${value.ino}`;
848
+ } catch {
849
+ return void 0;
850
+ }
851
+ }
852
+
744
853
  // src/maintenance.ts
745
854
  var WikiGraphMaintenanceManager = class {
746
855
  #runtime;
@@ -751,22 +860,22 @@ var WikiGraphMaintenanceManager = class {
751
860
  return await this.#runtime.run(
752
861
  async () => {
753
862
  if (target.kind === "home") {
754
- return await (0, import_wiki_graph_core.upgradeWikiGraphMaintenanceTarget)({ kind: "home" });
863
+ return await (0, import_wiki_graph_core2.upgradeWikiGraphMaintenanceTarget)({ kind: "home" });
755
864
  }
756
865
  if (target.kind === "library") {
757
- const parsed = (0, import_wiki_graph_core.parseWikiGraphLibraryUri)(target.uri);
866
+ const parsed = (0, import_wiki_graph_core2.parseWikiGraphLibraryUri)(target.uri);
758
867
  if (parsed === void 0 || parsed.kind === "archive") {
759
868
  throw new Error(
760
869
  `Invalid Wiki Graph library upgrade target: ${target.uri}`
761
870
  );
762
871
  }
763
- return await (0, import_wiki_graph_core.upgradeWikiGraphMaintenanceTarget)({
872
+ return await (0, import_wiki_graph_core2.upgradeWikiGraphMaintenanceTarget)({
764
873
  kind: "library",
765
874
  target: parsed
766
875
  });
767
876
  }
768
- const path = resolveWikiGraphRuntimePath(target.path);
769
- return await (0, import_wiki_graph_core.upgradeWikiGraphMaintenanceTarget)({
877
+ const path = await assertStandaloneWikiGraphArchivePath(target.path);
878
+ return await (0, import_wiki_graph_core2.upgradeWikiGraphMaintenanceTarget)({
770
879
  file: new NodeFile(path),
771
880
  kind: "archive"
772
881
  });
@@ -779,9 +888,9 @@ var WikiGraphMaintenanceManager = class {
779
888
  return await this.#runtime.run(async () => {
780
889
  const resolvedInputPath = resolveWikiGraphRuntimePath(inputPath);
781
890
  const resolvedOutputPath = resolveWikiGraphRuntimePath(outputPath);
782
- await (0, import_wiki_graph_core.migrateLegacySdpubToWikg)(
891
+ await (0, import_wiki_graph_core2.migrateLegacySdpubToWikg)(
783
892
  new NodeFile(resolvedInputPath),
784
- new NodeFile(resolvedOutputPath, (0, import_path2.basename)(resolvedOutputPath))
893
+ new NodeFile(resolvedOutputPath, (0, import_path3.basename)(resolvedOutputPath))
785
894
  );
786
895
  return {
787
896
  inputPath: resolvedInputPath,