@anokye-labs/kbexplorer-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/store.cjs ADDED
@@ -0,0 +1,1112 @@
1
+ 'use strict';
2
+
3
+ var module$1 = require('module');
4
+ var kbexplorerCore = require('@anokye-labs/kbexplorer-core');
5
+ var initSqlJs = require('sql.js');
6
+ var marked = require('marked');
7
+ var sanitizeHtml = require('sanitize-html');
8
+ require('yaml');
9
+
10
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var initSqlJs__default = /*#__PURE__*/_interopDefault(initSqlJs);
14
+ var sanitizeHtml__default = /*#__PURE__*/_interopDefault(sanitizeHtml);
15
+
16
+ var __defProp = Object.defineProperty;
17
+ var __getOwnPropNames = Object.getOwnPropertyNames;
18
+ var __esm = (fn, res, err) => function __init() {
19
+ if (err) throw err[0];
20
+ try {
21
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
22
+ } catch (e) {
23
+ throw err = [e], e;
24
+ }
25
+ };
26
+ var __export = (target, all) => {
27
+ for (var name in all)
28
+ __defProp(target, name, { get: all[name], enumerable: true });
29
+ };
30
+
31
+ // src/store/node-wasm.ts
32
+ var node_wasm_exports = {};
33
+ __export(node_wasm_exports, {
34
+ nodeLocateFile: () => nodeLocateFile
35
+ });
36
+ function nodeLocateFile() {
37
+ const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('store.cjs', document.baseURI).href)));
38
+ return (file) => require2.resolve(`sql.js/dist/${file}`);
39
+ }
40
+ var init_node_wasm = __esm({
41
+ "src/store/node-wasm.ts"() {
42
+ }
43
+ });
44
+
45
+ // src/store/config.ts
46
+ function resolveGraphStoreOptions(env) {
47
+ const engineEnv = env ?? {};
48
+ const raw = engineEnv.VITE_KB_GRAPH_STORE;
49
+ const value = typeof raw === "string" ? raw.trim().toLowerCase() : "";
50
+ if (!value || value === "off" || value === "false" || value === "0") {
51
+ return { mode: "off" };
52
+ }
53
+ if (value === "sqlite") return { mode: "sqlite" };
54
+ throw new Error(`Unsupported VITE_KB_GRAPH_STORE value: ${String(raw)}`);
55
+ }
56
+ function isGraphStoreEnabled(env) {
57
+ return resolveGraphStoreOptions(env).mode === "sqlite";
58
+ }
59
+ var GRAPH_STORE_DERIVATION_VERSION = "template-graph-derivation-v3";
60
+ var GRAPH_STORE_PROVIDER_ID = "provider-pipeline";
61
+ function buildProviderResultCacheKey(source, config, data, providerId = GRAPH_STORE_PROVIDER_ID, previousContentHash) {
62
+ return contentHashFor({
63
+ apiVersion: kbexplorerCore.GRAPH_STORE_API_VERSION,
64
+ cacheKeyVersion: kbexplorerCore.GRAPH_STORE_CACHE_KEY_VERSION,
65
+ derivationVersion: GRAPH_STORE_DERIVATION_VERSION,
66
+ sourceId: source.id,
67
+ providerId,
68
+ previousContentHash,
69
+ config,
70
+ data: stableProviderData(data, providerId)
71
+ }).then((contentHash) => ({
72
+ scope: "provider-result",
73
+ providerId,
74
+ sourceId: sourceIdFor(source, config),
75
+ contentHash,
76
+ variant: [
77
+ kbexplorerCore.GRAPH_STORE_API_VERSION,
78
+ kbexplorerCore.GRAPH_STORE_CACHE_KEY_VERSION,
79
+ GRAPH_STORE_DERIVATION_VERSION
80
+ ].join(":")
81
+ }));
82
+ }
83
+ function hashProviderResultPrefix(providerId, nodes) {
84
+ return contentHashFor({
85
+ apiVersion: kbexplorerCore.GRAPH_STORE_API_VERSION,
86
+ cacheKeyVersion: kbexplorerCore.GRAPH_STORE_CACHE_KEY_VERSION,
87
+ derivationVersion: GRAPH_STORE_DERIVATION_VERSION,
88
+ providerId,
89
+ nodes
90
+ });
91
+ }
92
+ function sourceIdFor(source, config) {
93
+ const sourceConfig = config.source;
94
+ return [
95
+ source.id,
96
+ sourceConfig.owner,
97
+ sourceConfig.repo,
98
+ sourceConfig.branch ?? "main",
99
+ sourceConfig.path ?? ""
100
+ ].join(":");
101
+ }
102
+ async function contentHashFor(value) {
103
+ const crypto = globalThis.crypto?.subtle;
104
+ if (!crypto) {
105
+ throw new Error("Graph store hashing requires Web Crypto SubtleCrypto support.");
106
+ }
107
+ const bytes = new TextEncoder().encode(stableStringify(value));
108
+ const digest = await crypto.digest("SHA-256", bytes);
109
+ return {
110
+ algorithm: "sha256",
111
+ digest: bytesToHex(new Uint8Array(digest)),
112
+ encoding: "hex"
113
+ };
114
+ }
115
+ function bytesToHex(bytes) {
116
+ return [...bytes].map((byte) => byte.toString(16).padStart(2, "0")).join("");
117
+ }
118
+ function stableRepoData(data) {
119
+ return {
120
+ repo: data.repo,
121
+ tree: data.tree.map((item) => ({
122
+ path: item.path,
123
+ mode: item.mode,
124
+ type: item.type,
125
+ sha: item.sha,
126
+ size: item.size
127
+ })),
128
+ authoredContent: data.authoredContent,
129
+ nodemapRaw: data.nodemapRaw,
130
+ nodemapFiles: data.nodemapFiles,
131
+ nodemapDirs: data.nodemapDirs,
132
+ issues: data.issues.map((issue) => ({
133
+ number: issue.number,
134
+ title: issue.title,
135
+ body: issue.body,
136
+ state: issue.state,
137
+ labels: issue.labels,
138
+ assignees: issue.assignees,
139
+ user: issue.user,
140
+ html_url: issue.html_url,
141
+ created_at: issue.created_at,
142
+ updated_at: issue.updated_at
143
+ })),
144
+ pullRequests: data.pullRequests,
145
+ commits: data.commits,
146
+ branches: data.branches,
147
+ repoMetadata: data.repoMetadata,
148
+ releases: data.releases,
149
+ structuralFiles: data.structuralFiles,
150
+ structuredNodeMapRaw: data.structuredNodeMapRaw,
151
+ contentModel: data.contentModel,
152
+ readme: data.readme
153
+ };
154
+ }
155
+ function stableProviderData(data, providerId) {
156
+ switch (providerId) {
157
+ case "files":
158
+ return {
159
+ repo: data.repo,
160
+ tree: data.tree.map((item) => ({
161
+ path: item.path,
162
+ mode: item.mode,
163
+ type: item.type,
164
+ sha: item.sha,
165
+ size: item.size
166
+ }))
167
+ };
168
+ case "authored":
169
+ return {
170
+ authoredContent: data.authoredContent,
171
+ nodemapRaw: data.nodemapRaw,
172
+ nodemapFiles: data.nodemapFiles,
173
+ nodemapDirs: data.nodemapDirs
174
+ };
175
+ case "work":
176
+ return {
177
+ issues: data.issues.map((issue) => ({
178
+ number: issue.number,
179
+ title: issue.title,
180
+ body: issue.body,
181
+ state: issue.state,
182
+ labels: issue.labels,
183
+ assignees: issue.assignees,
184
+ user: issue.user,
185
+ html_url: issue.html_url,
186
+ created_at: issue.created_at,
187
+ updated_at: issue.updated_at
188
+ })),
189
+ pullRequests: data.pullRequests,
190
+ commits: data.commits,
191
+ branches: data.branches,
192
+ repoMetadata: data.repoMetadata,
193
+ releases: data.releases
194
+ };
195
+ case "content-model":
196
+ return {
197
+ contentModel: data.contentModel
198
+ };
199
+ case "person":
200
+ return {
201
+ issues: data.issues.map((issue) => ({
202
+ number: issue.number,
203
+ title: issue.title,
204
+ state: issue.state,
205
+ assignees: issue.assignees,
206
+ user: issue.user
207
+ })),
208
+ pullRequests: data.pullRequests.map((pr) => ({
209
+ number: pr.number,
210
+ title: pr.title,
211
+ state: pr.state,
212
+ html_url: pr.html_url,
213
+ user: pr.user,
214
+ assignees: pr.assignees
215
+ }))
216
+ };
217
+ case "structural":
218
+ return {
219
+ structuralFiles: data.structuralFiles,
220
+ structuredNodeMapRaw: data.structuredNodeMapRaw
221
+ };
222
+ default:
223
+ return stableRepoData(data);
224
+ }
225
+ }
226
+ function stableStringify(value) {
227
+ return JSON.stringify(normalizeForJson(value));
228
+ }
229
+ function normalizeForJson(value) {
230
+ if (Array.isArray(value)) return value.map(normalizeForJson);
231
+ if (!value || typeof value !== "object") return value;
232
+ const out = {};
233
+ for (const key of Object.keys(value).sort()) {
234
+ const item = value[key];
235
+ if (typeof item !== "function" && item !== void 0) {
236
+ out[key] = normalizeForJson(item);
237
+ }
238
+ }
239
+ return out;
240
+ }
241
+ var DB_NAME = "kbexplorer-graph-store";
242
+ var STORE_NAME = "sqlite";
243
+ var DB_KEY = "graph-store.sqlite";
244
+ var sqlModuleCache = /* @__PURE__ */ new Map();
245
+ async function loadSqlJs(locateFile) {
246
+ const cacheKey = locateFile ?? "default";
247
+ const cached = sqlModuleCache.get(cacheKey);
248
+ if (cached) return cached;
249
+ const promise = (async () => {
250
+ if (locateFile) {
251
+ return initSqlJs__default.default({ locateFile });
252
+ }
253
+ if (typeof process !== "undefined" && process.versions?.node) {
254
+ const { nodeLocateFile: nodeLocateFile2 } = await Promise.resolve().then(() => (init_node_wasm(), node_wasm_exports));
255
+ return initSqlJs__default.default({ locateFile: nodeLocateFile2() });
256
+ }
257
+ return initSqlJs__default.default();
258
+ })();
259
+ sqlModuleCache.set(cacheKey, promise);
260
+ return promise;
261
+ }
262
+ async function openPersistedDatabase(byteStore = new IndexedDbSqliteByteStore(), locateFile) {
263
+ const SQL = await loadSqlJs(locateFile);
264
+ const bytes = await byteStore.load();
265
+ const db = bytes ? new SQL.Database(bytes) : new SQL.Database();
266
+ return {
267
+ db,
268
+ persist: async () => {
269
+ await byteStore.save(db.export());
270
+ }
271
+ };
272
+ }
273
+ var MemorySqliteByteStore = class {
274
+ bytes;
275
+ async load() {
276
+ return this.bytes ? new Uint8Array(this.bytes) : void 0;
277
+ }
278
+ async save(bytes) {
279
+ this.bytes = new Uint8Array(bytes);
280
+ }
281
+ };
282
+ var IndexedDbSqliteByteStore = class {
283
+ async load() {
284
+ const db = await openIndexedDb();
285
+ return requestToPromise(
286
+ db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(DB_KEY)
287
+ ).finally(() => db.close());
288
+ }
289
+ async save(bytes) {
290
+ const db = await openIndexedDb();
291
+ await requestToPromise(
292
+ db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(bytes, DB_KEY)
293
+ ).finally(() => db.close());
294
+ }
295
+ };
296
+ function openIndexedDb() {
297
+ if (!globalThis.indexedDB) {
298
+ throw new Error("Graph store SQLite persistence requires IndexedDB support.");
299
+ }
300
+ return new Promise((resolve, reject) => {
301
+ const request = globalThis.indexedDB.open(DB_NAME, 1);
302
+ request.onupgradeneeded = () => {
303
+ request.result.createObjectStore(STORE_NAME);
304
+ };
305
+ request.onsuccess = () => resolve(request.result);
306
+ request.onerror = () => reject(request.error ?? new Error("Failed to open graph store IndexedDB database."));
307
+ });
308
+ }
309
+ function requestToPromise(request) {
310
+ return new Promise((resolve, reject) => {
311
+ request.onsuccess = () => resolve(request.result);
312
+ request.onerror = () => reject(request.error ?? new Error("IndexedDB graph store request failed."));
313
+ });
314
+ }
315
+
316
+ // src/store/sqlite-graph-store.ts
317
+ var SQLITE_SCHEMA_VERSION = "sqlite-graph-store-v1";
318
+ var SQLiteGraphStore = class _SQLiteGraphStore {
319
+ db;
320
+ persist;
321
+ constructor(db, persist) {
322
+ this.db = db;
323
+ this.persist = persist;
324
+ this.migrate();
325
+ }
326
+ static async create(byteStore, locateFile) {
327
+ const { db, persist } = await openPersistedDatabase(byteStore, locateFile);
328
+ return new _SQLiteGraphStore(db, persist);
329
+ }
330
+ async get(key) {
331
+ const row = this.selectEntry(kbexplorerCore.formatGraphStoreCacheKey(key));
332
+ if (!row) return void 0;
333
+ const entry = rowToEntry(row);
334
+ if (kbexplorerCore.formatGraphStoreCacheKey(entry.key) !== kbexplorerCore.formatGraphStoreCacheKey(key)) {
335
+ return void 0;
336
+ }
337
+ return entry;
338
+ }
339
+ async put(entry) {
340
+ const now = (/* @__PURE__ */ new Date()).toISOString();
341
+ const createdAt = entry.createdAt ?? now;
342
+ const updatedAt = entry.updatedAt ?? now;
343
+ this.db.run(
344
+ `insert into entries (
345
+ cache_key, key_json, scope, provider_id, source_id, variant, content_hash,
346
+ value_json, dependencies_json, metadata_json, created_at, updated_at
347
+ ) values (
348
+ $cache_key, $key_json, $scope, $provider_id, $source_id, $variant, $content_hash,
349
+ $value_json, $dependencies_json, $metadata_json, $created_at, $updated_at
350
+ )
351
+ on conflict(cache_key) do update set
352
+ key_json = excluded.key_json,
353
+ scope = excluded.scope,
354
+ provider_id = excluded.provider_id,
355
+ source_id = excluded.source_id,
356
+ variant = excluded.variant,
357
+ content_hash = excluded.content_hash,
358
+ value_json = excluded.value_json,
359
+ dependencies_json = excluded.dependencies_json,
360
+ metadata_json = excluded.metadata_json,
361
+ updated_at = excluded.updated_at`,
362
+ {
363
+ $cache_key: kbexplorerCore.formatGraphStoreCacheKey(entry.key),
364
+ $key_json: JSON.stringify(entry.key),
365
+ $scope: entry.key.scope,
366
+ $provider_id: entry.key.providerId,
367
+ $source_id: entry.key.sourceId ?? null,
368
+ $variant: entry.key.variant ?? null,
369
+ $content_hash: kbexplorerCore.formatContentHash(entry.key.contentHash),
370
+ $value_json: JSON.stringify(entry.value),
371
+ $dependencies_json: JSON.stringify(entry.dependencies ?? []),
372
+ $metadata_json: JSON.stringify(entry.metadata ?? {}),
373
+ $created_at: createdAt,
374
+ $updated_at: updatedAt
375
+ }
376
+ );
377
+ await this.persist();
378
+ }
379
+ async delete(key) {
380
+ const cacheKey = kbexplorerCore.formatGraphStoreCacheKey(key);
381
+ const before = this.countEntries();
382
+ this.db.run("delete from entries where cache_key = $cache_key", { $cache_key: cacheKey });
383
+ const deleted = this.countEntries() < before;
384
+ if (deleted) await this.persist();
385
+ return deleted;
386
+ }
387
+ async invalidate(match) {
388
+ const rows = this.selectEntries();
389
+ let deleted = 0;
390
+ for (const row of rows) {
391
+ const entry = rowToEntry(row);
392
+ if (matchesInvalidation(entry, match)) {
393
+ this.db.run("delete from entries where cache_key = $cache_key", { $cache_key: row.cache_key });
394
+ deleted++;
395
+ }
396
+ }
397
+ if (deleted > 0) await this.persist();
398
+ return deleted;
399
+ }
400
+ migrate() {
401
+ this.db.run(`
402
+ create table if not exists metadata (
403
+ key text primary key,
404
+ value text not null
405
+ );
406
+ create table if not exists entries (
407
+ cache_key text primary key,
408
+ key_json text not null,
409
+ scope text not null,
410
+ provider_id text not null,
411
+ source_id text,
412
+ variant text,
413
+ content_hash text not null,
414
+ value_json text not null,
415
+ dependencies_json text not null,
416
+ metadata_json text not null,
417
+ created_at text not null,
418
+ updated_at text not null
419
+ );
420
+ create index if not exists idx_entries_scope on entries(scope);
421
+ create index if not exists idx_entries_provider on entries(provider_id);
422
+ create index if not exists idx_entries_source on entries(source_id);
423
+ create index if not exists idx_entries_variant on entries(variant);
424
+ create index if not exists idx_entries_content_hash on entries(content_hash);
425
+ `);
426
+ this.setMetadata("sqlite_schema_version", SQLITE_SCHEMA_VERSION);
427
+ this.setMetadata("graph_store_api_version", kbexplorerCore.GRAPH_STORE_API_VERSION);
428
+ this.setMetadata("graph_store_cache_key_version", kbexplorerCore.GRAPH_STORE_CACHE_KEY_VERSION);
429
+ this.setMetadata("graph_store_derivation_version", GRAPH_STORE_DERIVATION_VERSION);
430
+ }
431
+ setMetadata(key, value) {
432
+ this.db.run(
433
+ `insert into metadata (key, value) values ($key, $value)
434
+ on conflict(key) do update set value = excluded.value`,
435
+ { $key: key, $value: value }
436
+ );
437
+ }
438
+ selectEntry(cacheKey) {
439
+ const statement = this.db.prepare(
440
+ `select cache_key, key_json, value_json, dependencies_json, metadata_json, created_at, updated_at
441
+ from entries where cache_key = $cache_key limit 1`
442
+ );
443
+ try {
444
+ statement.bind({ $cache_key: cacheKey });
445
+ if (!statement.step()) return void 0;
446
+ return statement.getAsObject();
447
+ } finally {
448
+ statement.free();
449
+ }
450
+ }
451
+ selectEntries() {
452
+ const statement = this.db.prepare(
453
+ "select cache_key, key_json, value_json, dependencies_json, metadata_json, created_at, updated_at from entries"
454
+ );
455
+ const rows = [];
456
+ try {
457
+ while (statement.step()) {
458
+ rows.push(statement.getAsObject());
459
+ }
460
+ return rows;
461
+ } finally {
462
+ statement.free();
463
+ }
464
+ }
465
+ countEntries() {
466
+ const statement = this.db.prepare("select count(*) as count from entries");
467
+ try {
468
+ if (!statement.step()) return 0;
469
+ const row = statement.getAsObject();
470
+ return row.count;
471
+ } finally {
472
+ statement.free();
473
+ }
474
+ }
475
+ };
476
+ function rowToEntry(row) {
477
+ try {
478
+ return {
479
+ key: JSON.parse(row.key_json),
480
+ value: JSON.parse(row.value_json),
481
+ dependencies: JSON.parse(row.dependencies_json) ?? [],
482
+ metadata: JSON.parse(row.metadata_json),
483
+ createdAt: row.created_at,
484
+ updatedAt: row.updated_at
485
+ };
486
+ } catch (err) {
487
+ throw new Error(`Failed to deserialize graph store entry ${row.cache_key}: ${err instanceof Error ? err.message : String(err)}`, {
488
+ cause: err
489
+ });
490
+ }
491
+ }
492
+ function matchesInvalidation(entry, match) {
493
+ if (match.scope && entry.key.scope !== match.scope) return false;
494
+ if (match.providerId && entry.key.providerId !== match.providerId) return false;
495
+ if (match.sourceId && entry.key.sourceId !== match.sourceId) {
496
+ const dependencyMatch = entry.dependencies?.some((dep) => dep.sourceId === match.sourceId) ?? false;
497
+ if (!dependencyMatch) return false;
498
+ }
499
+ if (match.variant && entry.key.variant !== match.variant) return false;
500
+ if (match.contentHash) {
501
+ const hash = kbexplorerCore.formatContentHash(match.contentHash);
502
+ const keyMatch = kbexplorerCore.formatContentHash(entry.key.contentHash) === hash;
503
+ const dependencyMatch = entry.dependencies?.some((dep) => kbexplorerCore.formatContentHash(dep.contentHash) === hash) ?? false;
504
+ if (!keyMatch && !dependencyMatch) return false;
505
+ }
506
+ return true;
507
+ }
508
+ var SANITIZE_OPTIONS = {
509
+ allowedTags: [
510
+ // Blocks / structure
511
+ "p",
512
+ "br",
513
+ "hr",
514
+ "h1",
515
+ "h2",
516
+ "h3",
517
+ "h4",
518
+ "h5",
519
+ "h6",
520
+ "ul",
521
+ "ol",
522
+ "li",
523
+ "blockquote",
524
+ "pre",
525
+ "code",
526
+ "div",
527
+ "span",
528
+ // Inline formatting
529
+ "em",
530
+ "strong",
531
+ "del",
532
+ "a",
533
+ "img",
534
+ // Tables (GFM)
535
+ "table",
536
+ "thead",
537
+ "tbody",
538
+ "tr",
539
+ "td",
540
+ "th",
541
+ // Collapsible sections + theme-aware images (used in real issue/PR/README HTML)
542
+ "details",
543
+ "summary",
544
+ "picture",
545
+ "source",
546
+ // GFM task-list checkboxes are markdown-generated (`- [ ]` / `- [x]`)
547
+ "input"
548
+ ],
549
+ allowedAttributes: {
550
+ a: ["href", "title"],
551
+ img: ["src", "alt", "title", "width", "height"],
552
+ source: ["srcset", "media", "type", "sizes"],
553
+ // `start` is markdown-generated for ordered lists that don't begin at 1
554
+ // (`4.` → `<ol start="4">`); `reversed`/`type` are safe presentational
555
+ // siblings. Dropping `start` would silently reset list numbering.
556
+ ol: ["start", "reversed", "type"],
557
+ // GFM column alignment renders as `align` on the header/data cells
558
+ // (`|:-:|` → `<th align="center">`); the rest are safe structural/a11y
559
+ // attributes real HTML tables use. All are presentational — no script sink.
560
+ th: ["align", "colspan", "rowspan", "scope"],
561
+ td: ["align", "colspan", "rowspan"],
562
+ // Only the attributes marked emits for task-list checkboxes — no `on*`,
563
+ // no `src`/`formaction`, so an allowed `<input>` is inert.
564
+ input: ["type", "checked", "disabled"],
565
+ // `class` carries `language-*` on fenced code, which the diagram/mermaid
566
+ // detection and syntax styling read. Kept minimal — no `style`, no `id`.
567
+ code: ["class"],
568
+ pre: ["class"],
569
+ span: ["class"],
570
+ div: ["class"]
571
+ },
572
+ // URL schemes permitted on href/src/srcset after entity + whitespace
573
+ // normalization. Relative targets (no scheme) are always allowed; anything
574
+ // with a `javascript:`/`data:`/`vbscript:`/etc. scheme is dropped.
575
+ allowedSchemes: ["http", "https", "mailto"],
576
+ allowedSchemesAppliedToAttributes: ["href", "src", "srcset"],
577
+ // Reject protocol-relative (`//host/…`) targets — they inherit the page
578
+ // scheme and can point at an arbitrary host.
579
+ allowProtocolRelative: false,
580
+ // Non-allowlisted tags become visible escaped text rather than being dropped,
581
+ // preserving the previous renderer's "hostile markup shows as inert text"
582
+ // property for tags like <script>/<style>/<iframe>/<svg>.
583
+ disallowedTagsMode: "escape"
584
+ };
585
+ var markdown = new marked.Marked();
586
+ function renderSafeMarkdown(body) {
587
+ const html = markdown.parse(body, { async: false });
588
+ return sanitizeHtml__default.default(html, SANITIZE_OPTIONS);
589
+ }
590
+ var TEMPLATE_SENSITIVE_CLASSIFICATIONS = /* @__PURE__ */ new Set(["restricted", "confidential", "unknown"]);
591
+ var TEMPLATE_SENSITIVE_VISIBILITIES = /* @__PURE__ */ new Set(["private"]);
592
+ var TEMPLATE_KNOWN_CLASSIFICATIONS = /* @__PURE__ */ new Set(["public", "internal", "confidential", "restricted", "unknown"]);
593
+ var TEMPLATE_CORE_EXCLUSION = kbexplorerCore.resolveAccessExclusion(kbexplorerCore.DEFAULT_ACCESS_EXCLUSION);
594
+ function normalizeAccessValue(value) {
595
+ return kbexplorerCore.normalizeAccessLabel(value) ?? kbexplorerCore.coerceAccessLabel(value);
596
+ }
597
+ function isTemplateCoreExcluded(label) {
598
+ if (!label) return false;
599
+ const classification = label.classification?.trim().toLowerCase();
600
+ if (classification && !TEMPLATE_KNOWN_CLASSIFICATIONS.has(classification)) {
601
+ return false;
602
+ }
603
+ return kbexplorerCore.isExcludedByDefault(label, TEMPLATE_CORE_EXCLUSION);
604
+ }
605
+ function isAccessWithheld(node) {
606
+ const access = node.access;
607
+ if (!access) return false;
608
+ const label = normalizeAccessValue(access);
609
+ if (!label) return false;
610
+ const classification = label.classification?.trim().toLowerCase();
611
+ if (classification && TEMPLATE_SENSITIVE_CLASSIFICATIONS.has(classification)) {
612
+ return true;
613
+ }
614
+ const visibility = label.visibility?.trim().toLowerCase();
615
+ if (visibility && TEMPLATE_SENSITIVE_VISIBILITIES.has(visibility)) {
616
+ return true;
617
+ }
618
+ return isTemplateCoreExcluded(label);
619
+ }
620
+ function filterAccessWithheld(nodes) {
621
+ const kept = nodes.filter((n) => !isAccessWithheld(n));
622
+ return kept.length === nodes.length ? nodes : kept;
623
+ }
624
+
625
+ // src/parser.ts
626
+ function extractIssueRefs(body) {
627
+ if (!body) return [];
628
+ const matches = body.matchAll(/#(\d+)/g);
629
+ return [...matches].map((m) => Number(m[1]));
630
+ }
631
+ function splitIntoSections(parentId, parentTitle, rawContent, cluster, emoji, source, allNodes) {
632
+ const lines = rawContent.split("\n");
633
+ const sections = [];
634
+ let currentSection = null;
635
+ const introLines = [];
636
+ for (const line of lines) {
637
+ const headingMatch = line.match(/^##\s+(.+)/);
638
+ if (headingMatch) {
639
+ if (currentSection) sections.push(currentSection);
640
+ currentSection = { title: headingMatch[1].trim(), lines: [] };
641
+ } else if (currentSection) {
642
+ currentSection.lines.push(line);
643
+ } else {
644
+ introLines.push(line);
645
+ }
646
+ }
647
+ if (currentSection) sections.push(currentSection);
648
+ if (sections.length < 2) return [];
649
+ const result = [];
650
+ const introContent = introLines.join("\n").trim();
651
+ const introHtml = introContent ? renderSafeMarkdown(introContent) : "";
652
+ const sectionIds = sections.map((s, i) => `${parentId}/${slugify(s.title, i)}`);
653
+ const parentNode = {
654
+ id: parentId,
655
+ title: parentTitle,
656
+ cluster,
657
+ content: introHtml,
658
+ rawContent: introContent,
659
+ emoji,
660
+ nodeType: "parent",
661
+ connections: sectionIds.map((sid) => ({ to: sid, type: "contains", description: "Contains", source: "inferred" })),
662
+ source
663
+ };
664
+ const lower = rawContent.toLowerCase();
665
+ for (const n of allNodes) {
666
+ if (n.id === parentId) continue;
667
+ const titleWords = n.title.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
668
+ if (titleWords.length === 0) continue;
669
+ const matchCount = titleWords.filter((w) => lower.includes(w)).length;
670
+ if (matchCount >= Math.ceil(titleWords.length * 0.6)) {
671
+ parentNode.connections.push({ to: n.id, type: "mentions", description: "Mentions", source: "inferred" });
672
+ }
673
+ }
674
+ result.push(parentNode);
675
+ for (let i = 0; i < sections.length; i++) {
676
+ const s = sections[i];
677
+ const sectionId = sectionIds[i];
678
+ const sectionBody = s.lines.join("\n").trim();
679
+ const sectionHtml = sectionBody ? renderSafeMarkdown(sectionBody) : "";
680
+ const sectionNode = {
681
+ id: sectionId,
682
+ title: s.title,
683
+ cluster,
684
+ content: sectionHtml,
685
+ rawContent: sectionBody,
686
+ emoji,
687
+ parent: parentId,
688
+ nodeType: "section",
689
+ connections: [],
690
+ source
691
+ };
692
+ const sLower = sectionBody.toLowerCase();
693
+ const refs = extractIssueRefs(sectionBody);
694
+ for (const num of refs) {
695
+ const refId = `issue-${num}`;
696
+ if (allNodes.some((n) => n.id === refId)) {
697
+ sectionNode.connections.push({ to: refId, type: "cross_references", description: `References #${num}`, source: "inline" });
698
+ }
699
+ }
700
+ for (const n of allNodes) {
701
+ if (n.source.type === "file") {
702
+ const dirName = n.title.replace(/\/$/, "").toLowerCase();
703
+ if (sLower.includes(`${dirName}/`) || sLower.includes(`\`${dirName}\``)) {
704
+ sectionNode.connections.push({ to: n.id, type: "references", description: `References ${n.title}`, source: "inferred" });
705
+ }
706
+ }
707
+ }
708
+ result.push(sectionNode);
709
+ }
710
+ return result;
711
+ }
712
+ function slugify(title, idx) {
713
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
714
+ return slug || `section-${idx}`;
715
+ }
716
+ function extractClusters(nodes, config) {
717
+ const configClusters = new Map(
718
+ Object.entries(config.clusters).map(([id, c]) => [id, { id, ...c }])
719
+ );
720
+ const palette = [
721
+ "#E8A838",
722
+ "#4A9CC8",
723
+ "#8CB050",
724
+ "#C07840",
725
+ "#D4A050",
726
+ "#5A98A8",
727
+ "#9A8A78",
728
+ "#C04040",
729
+ "#A86FDF",
730
+ "#39FF14",
731
+ "#FF6B6B",
732
+ "#4ECDC4"
733
+ ];
734
+ let colorIdx = 0;
735
+ const seenIds = /* @__PURE__ */ new Set();
736
+ for (const node of nodes) {
737
+ if (!seenIds.has(node.cluster)) {
738
+ seenIds.add(node.cluster);
739
+ if (!configClusters.has(node.cluster)) {
740
+ configClusters.set(node.cluster, {
741
+ id: node.cluster,
742
+ name: node.cluster.split(/[-_]/).map((w) => w.length <= 3 ? w.toUpperCase() : w.charAt(0).toUpperCase() + w.slice(1)).join(" "),
743
+ color: palette[colorIdx % palette.length]
744
+ });
745
+ colorIdx++;
746
+ }
747
+ }
748
+ }
749
+ return [...configClusters.values()];
750
+ }
751
+
752
+ // src/edge-weights.ts
753
+ var EDGE_TYPE_WEIGHTS = {
754
+ contains: 5,
755
+ derived_from: 3,
756
+ imports: 2,
757
+ references: 2,
758
+ frontmatter: 1.5,
759
+ cross_references: 1.5,
760
+ modifies: 1,
761
+ closes: 2,
762
+ mentions: 0.5,
763
+ related: 0.3
764
+ };
765
+ function getEdgeWeight(type) {
766
+ return EDGE_TYPE_WEIGHTS[type ?? "related"] ?? 1;
767
+ }
768
+
769
+ // src/node-types/registry.ts
770
+ var registry = /* @__PURE__ */ new Map();
771
+ var BUILT_IN_NODE_TYPES = [
772
+ { id: "authored", layer: "content", label: "Authored" },
773
+ { id: "readme", layer: "content", label: "README" },
774
+ { id: "derived", layer: "content", label: "Derived" },
775
+ { id: "section", layer: "content", label: "Section" },
776
+ { id: "structured", layer: "content", label: "Structured" },
777
+ { id: "issue", layer: "work", label: "Issue" },
778
+ { id: "pull_request", layer: "work", label: "Pull Request" },
779
+ { id: "commit", layer: "work", label: "Commit" },
780
+ { id: "branch", layer: "work", label: "Branch" },
781
+ { id: "workflow", layer: "work", label: "Workflow" },
782
+ { id: "repository", layer: "work", label: "Repository" },
783
+ { id: "release", layer: "work", label: "Release", cluster: "releases", description: "A GitHub release (tag, name, release notes)." },
784
+ // Person nodes derived from GitHub activity (#235)
785
+ { id: "person", layer: "work", label: "Person", cluster: "person", description: "A person derived from GitHub activity or a content-model descriptor." },
786
+ { id: "file", layer: "file", label: "File" },
787
+ { id: "external", layer: "file", label: "External" }
788
+ ];
789
+ function registerBuiltInNodeTypes() {
790
+ for (const def of BUILT_IN_NODE_TYPES) {
791
+ if (!registry.has(def.id)) registry.set(def.id, def);
792
+ }
793
+ }
794
+ function resolveNodeLayer(node) {
795
+ const byEntity = node.entityType ? registry.get(node.entityType) : void 0;
796
+ if (byEntity?.layer) return byEntity.layer;
797
+ const bySource = registry.get(node.source.type);
798
+ if (bySource?.layer) return bySource.layer;
799
+ return "file";
800
+ }
801
+ registerBuiltInNodeTypes();
802
+
803
+ // src/graph.ts
804
+ function buildGraph(nodes, clusters) {
805
+ nodes = filterAccessWithheld(nodes);
806
+ for (const node of nodes) {
807
+ node.layer = resolveNodeLayer(node);
808
+ }
809
+ const nodeMap = /* @__PURE__ */ new Map();
810
+ for (const n of nodes) {
811
+ const prev = nodeMap.get(n.id);
812
+ if (prev && (prev.provider ?? "(none)") !== (n.provider ?? "(none)")) {
813
+ console.warn(
814
+ `[kbexplorer] cross-provider id collision: "${n.id}" is produced by provider "${prev.provider ?? "(none)"}" and provider "${n.provider ?? "(none)"}" \u2014 edge/related resolution will last-win on the latter. Give one a distinct id.`
815
+ );
816
+ }
817
+ nodeMap.set(n.id, n);
818
+ }
819
+ const edges = buildEdges(nodes, nodeMap);
820
+ const connected = /* @__PURE__ */ new Set();
821
+ for (const e of edges) {
822
+ connected.add(e.from);
823
+ connected.add(e.to);
824
+ }
825
+ const orphans = nodes.filter((n) => !connected.has(n.id));
826
+ if (orphans.length > 0) {
827
+ const degrees = /* @__PURE__ */ new Map();
828
+ for (const n of nodes) degrees.set(n.id, 0);
829
+ for (const e of edges) {
830
+ degrees.set(e.from, (degrees.get(e.from) ?? 0) + 1);
831
+ degrees.set(e.to, (degrees.get(e.to) ?? 0) + 1);
832
+ }
833
+ let hubId = nodes[0]?.id;
834
+ let hubDeg = 0;
835
+ for (const [id, deg] of degrees) {
836
+ if (deg > hubDeg) {
837
+ hubDeg = deg;
838
+ hubId = id;
839
+ }
840
+ }
841
+ for (const orphan of orphans) {
842
+ const sibling = nodes.find((n) => n.id !== orphan.id && n.cluster === orphan.cluster && connected.has(n.id));
843
+ const targetId = sibling?.id ?? hubId;
844
+ if (targetId && targetId !== orphan.id) {
845
+ edges.push({ from: targetId, to: orphan.id, type: "related", description: "Related", source: "inferred", weight: EDGE_TYPE_WEIGHTS.related });
846
+ connected.add(orphan.id);
847
+ }
848
+ }
849
+ }
850
+ const related = computeRelated(nodes, edges);
851
+ return { nodes, edges, clusters, related };
852
+ }
853
+ function buildEdges(nodes, nodeMap) {
854
+ const edgeSet = /* @__PURE__ */ new Map();
855
+ const addEdge = (edge) => {
856
+ const key = edgeKey(edge);
857
+ if (!edgeSet.has(key)) {
858
+ edgeSet.set(key, edge);
859
+ }
860
+ };
861
+ for (const node of nodes) {
862
+ for (const conn of node.connections) {
863
+ if (nodeMap.has(conn.to)) {
864
+ const edgeType = conn.type ?? "references";
865
+ addEdge({
866
+ from: node.id,
867
+ to: conn.to,
868
+ type: edgeType,
869
+ description: conn.description,
870
+ source: conn.source ?? "frontmatter",
871
+ weight: conn.weight ?? getEdgeWeight(edgeType),
872
+ ...conn.relation ? { relation: conn.relation } : {}
873
+ });
874
+ }
875
+ }
876
+ if (node.parent && nodeMap.has(node.parent)) {
877
+ addEdge({
878
+ from: node.parent,
879
+ to: node.id,
880
+ type: "contains",
881
+ description: "Contains",
882
+ source: "inferred",
883
+ weight: EDGE_TYPE_WEIGHTS.contains
884
+ });
885
+ }
886
+ }
887
+ return [...edgeSet.values()];
888
+ }
889
+ function edgeKey(edge) {
890
+ return `${edge.from}\0${edge.to}\0${edge.type}\0${edge.relation ?? ""}`;
891
+ }
892
+ function computeRelated(nodes, edges) {
893
+ const adj = /* @__PURE__ */ new Map();
894
+ for (const node of nodes) {
895
+ adj.set(node.id, /* @__PURE__ */ new Map());
896
+ }
897
+ for (const edge of edges) {
898
+ const fwd = adj.get(edge.from);
899
+ const rev = adj.get(edge.to);
900
+ if (fwd && (!fwd.has(edge.to) || edge.weight > (fwd.get(edge.to) ?? 0))) {
901
+ fwd.set(edge.to, edge.weight);
902
+ }
903
+ if (rev && (!rev.has(edge.from) || edge.weight > (rev.get(edge.from) ?? 0))) {
904
+ rev.set(edge.from, edge.weight);
905
+ }
906
+ }
907
+ const degree = /* @__PURE__ */ new Map();
908
+ for (const [id, neighbors] of adj) {
909
+ degree.set(id, neighbors.size);
910
+ }
911
+ const related = {};
912
+ for (const [id, neighbors] of adj) {
913
+ related[id] = [...neighbors.entries()].sort((a, b) => {
914
+ const weightDiff = b[1] - a[1];
915
+ if (Math.abs(weightDiff) > 0.01) return weightDiff;
916
+ return (degree.get(b[0]) ?? 0) - (degree.get(a[0]) ?? 0);
917
+ }).map(([neighborId]) => neighborId).slice(0, 12);
918
+ }
919
+ return related;
920
+ }
921
+
922
+ // src/transforms.ts
923
+ function selectIssueNodes(nodes) {
924
+ return nodes.filter((n) => n.source.type === "issue");
925
+ }
926
+ function selectDirNodes(nodes) {
927
+ return nodes.filter((n) => n.provider === "files");
928
+ }
929
+ var readmeTransform = {
930
+ name: "readme",
931
+ apply(nodes, ctx) {
932
+ if (!ctx.readme) return nodes;
933
+ const readme = ctx.readme;
934
+ const issueNodes = selectIssueNodes(nodes);
935
+ const dirNodes = selectDirNodes(nodes);
936
+ const readmeConns = [];
937
+ const lower = readme.toLowerCase();
938
+ const issueRefs = extractIssueRefs(readme);
939
+ for (const num of issueRefs) {
940
+ const id = `issue-${num}`;
941
+ if (issueNodes.some((n) => n.id === id)) {
942
+ readmeConns.push({ to: id, description: `References #${num}` });
943
+ }
944
+ }
945
+ for (const node of issueNodes) {
946
+ if (readmeConns.some((c) => c.to === node.id)) continue;
947
+ const titleWords = node.title.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
948
+ if (titleWords.length === 0) continue;
949
+ const matchCount = titleWords.filter((w) => lower.includes(w)).length;
950
+ if (matchCount >= Math.ceil(titleWords.length * 0.6)) {
951
+ readmeConns.push({ to: node.id, description: "Mentions" });
952
+ }
953
+ }
954
+ for (const dir of dirNodes) {
955
+ const dirName = dir.title.replace(/\/$/, "");
956
+ if (lower.includes(`${dirName}/`) || lower.includes(`\`${dirName}\``)) {
957
+ readmeConns.push({ to: dir.id, description: `References ${dirName}/` });
958
+ }
959
+ }
960
+ readmeConns.push({ to: "repo-root", description: "Documents" });
961
+ const readmeConnectedTo = new Set(readmeConns.map((c) => c.to));
962
+ for (const m of readme.matchAll(/\[([^\]]+)\]\(([^)]+)\)/g)) {
963
+ const target = m[2].trim();
964
+ if (target.startsWith("http") || target.startsWith("#") || target.startsWith("/")) continue;
965
+ if (target.match(/\.(png|jpg|jpeg|gif|svg|webp|md)$/i)) continue;
966
+ if (readmeConnectedTo.has(target)) continue;
967
+ readmeConns.push({ to: target, description: m[1] });
968
+ readmeConnectedTo.add(target);
969
+ }
970
+ const html = renderSafeMarkdown(readme);
971
+ nodes.push({
972
+ id: "readme",
973
+ title: "README",
974
+ cluster: "docs",
975
+ content: html,
976
+ rawContent: readme,
977
+ emoji: "Document",
978
+ parent: "repo-root",
979
+ identity: "urn:content:readme",
980
+ connections: readmeConns,
981
+ source: { type: "readme" }
982
+ });
983
+ return nodes;
984
+ }
985
+ };
986
+ var issueDirectoryLinkTransform = {
987
+ name: "issue-directory-link",
988
+ apply(nodes) {
989
+ const issueNodes = selectIssueNodes(nodes);
990
+ const dirNodes = selectDirNodes(nodes);
991
+ const dirNames = dirNodes.map((d) => d.title.replace(/\/$/, ""));
992
+ for (const node of issueNodes) {
993
+ for (let i = 0; i < dirNames.length; i++) {
994
+ const dir = dirNames[i];
995
+ if (node.rawContent && (node.rawContent.includes(`${dir}/`) || node.rawContent.includes(`\`${dir}\``) || node.rawContent.toLowerCase().includes(dir.toLowerCase()))) {
996
+ node.connections.push({ to: dirNodes[i].id, description: `References ${dir}/` });
997
+ }
998
+ }
999
+ }
1000
+ return nodes;
1001
+ }
1002
+ };
1003
+ var issueSplitTransform = {
1004
+ name: "issue-split",
1005
+ apply(nodes) {
1006
+ const issueNodes = selectIssueNodes(nodes);
1007
+ const dirNodes = selectDirNodes(nodes);
1008
+ const expandedIssues = [];
1009
+ for (const node of issueNodes) {
1010
+ const sectionNodes = splitIntoSections(
1011
+ node.id,
1012
+ node.title,
1013
+ node.rawContent,
1014
+ node.cluster,
1015
+ node.emoji ?? "Pin",
1016
+ node.source,
1017
+ [...issueNodes, ...dirNodes]
1018
+ );
1019
+ if (sectionNodes.length > 0) {
1020
+ const idx = nodes.indexOf(node);
1021
+ if (idx >= 0) nodes.splice(idx, 1);
1022
+ expandedIssues.push(...sectionNodes);
1023
+ }
1024
+ }
1025
+ nodes.push(...expandedIssues);
1026
+ return nodes;
1027
+ }
1028
+ };
1029
+ var DEFAULT_TRANSFORMS = [
1030
+ readmeTransform,
1031
+ issueDirectoryLinkTransform,
1032
+ issueSplitTransform
1033
+ ];
1034
+ function applyTransforms(nodes, ctx, transforms = DEFAULT_TRANSFORMS) {
1035
+ let current = nodes;
1036
+ for (const transform of transforms) {
1037
+ current = transform.apply(current, ctx);
1038
+ }
1039
+ return current;
1040
+ }
1041
+ async function orchestrateWithProviderResultStore(registry2, config, ctx, store, buildCacheKey, transforms = DEFAULT_TRANSFORMS) {
1042
+ const providers = registry2.getExecutionOrder();
1043
+ let allNodes = [];
1044
+ let previousContentHash;
1045
+ for (const provider of providers) {
1046
+ const key = await buildCacheKey(provider.id, previousContentHash);
1047
+ const cached = await store.get(key);
1048
+ if (cached) {
1049
+ allNodes = cloneProviderResult(
1050
+ validateProviderResult(cached.value, `cached graph store entry for ${provider.id}`)
1051
+ ).nodes;
1052
+ previousContentHash = await hashProviderResultPrefix(provider.id, allNodes);
1053
+ continue;
1054
+ }
1055
+ const result = await provider.resolve(config, allNodes);
1056
+ allNodes.push(...result.nodes);
1057
+ const value = cloneProviderResult({ nodes: allNodes, edges: [] });
1058
+ await store.put({
1059
+ key,
1060
+ value,
1061
+ dependencies: [dependencyFor(key, previousContentHash)],
1062
+ metadata: {
1063
+ graphStoreApiVersion: kbexplorerCore.GRAPH_STORE_API_VERSION,
1064
+ graphStoreCacheKeyVersion: kbexplorerCore.GRAPH_STORE_CACHE_KEY_VERSION,
1065
+ graphStoreDerivationVersion: GRAPH_STORE_DERIVATION_VERSION,
1066
+ providerId: provider.id
1067
+ }
1068
+ });
1069
+ previousContentHash = await hashProviderResultPrefix(provider.id, allNodes);
1070
+ }
1071
+ const transformed = applyTransforms(allNodes, ctx, transforms);
1072
+ return buildGraph(transformed, extractClusters(transformed, config));
1073
+ }
1074
+ function dependencyFor(key, previousContentHash) {
1075
+ return {
1076
+ href: previousContentHash ? `${key.sourceId ?? key.providerId}#previous` : key.sourceId ?? key.providerId,
1077
+ contentHash: previousContentHash ?? key.contentHash,
1078
+ ...key.sourceId !== void 0 ? { sourceId: key.sourceId } : {}
1079
+ };
1080
+ }
1081
+ function cloneProviderResult(value) {
1082
+ if (typeof structuredClone === "function") {
1083
+ return structuredClone(value);
1084
+ }
1085
+ return JSON.parse(JSON.stringify(value));
1086
+ }
1087
+ function validateProviderResult(value, label) {
1088
+ if (!value || !Array.isArray(value.nodes) || !Array.isArray(value.edges)) {
1089
+ throw new Error(`Invalid ${label}: expected ProviderResult with nodes and edges arrays.`);
1090
+ }
1091
+ for (const node of value.nodes) {
1092
+ validateNode(node, label);
1093
+ }
1094
+ return value;
1095
+ }
1096
+ function validateNode(node, label) {
1097
+ if (!node || typeof node.id !== "string" || typeof node.title !== "string" || typeof node.cluster !== "string" || !Array.isArray(node.connections)) {
1098
+ throw new Error(`Invalid ${label}: malformed KBNode.`);
1099
+ }
1100
+ }
1101
+
1102
+ exports.GRAPH_STORE_DERIVATION_VERSION = GRAPH_STORE_DERIVATION_VERSION;
1103
+ exports.GRAPH_STORE_PROVIDER_ID = GRAPH_STORE_PROVIDER_ID;
1104
+ exports.IndexedDbSqliteByteStore = IndexedDbSqliteByteStore;
1105
+ exports.MemorySqliteByteStore = MemorySqliteByteStore;
1106
+ exports.SQLiteGraphStore = SQLiteGraphStore;
1107
+ exports.buildProviderResultCacheKey = buildProviderResultCacheKey;
1108
+ exports.isGraphStoreEnabled = isGraphStoreEnabled;
1109
+ exports.loadSqlJs = loadSqlJs;
1110
+ exports.openPersistedDatabase = openPersistedDatabase;
1111
+ exports.orchestrateWithProviderResultStore = orchestrateWithProviderResultStore;
1112
+ exports.resolveGraphStoreOptions = resolveGraphStoreOptions;