@gmickel/gno 1.46.0 → 2.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/README.md +17 -5
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.1.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.1.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +29 -4
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/detach.ts +3 -2
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/components/PublishExportDialog.tsx +266 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/globals.css +35 -0
- package/src/serve/public/lib/publish-export.ts +81 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/public/pages/Collections.tsx +12 -46
- package/src/serve/public/pages/DocView.tsx +14 -52
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
|
@@ -173,6 +173,7 @@ import {
|
|
|
173
173
|
listEgressAuditReceipts as listStoredEgressAuditReceipts,
|
|
174
174
|
purgeEgressAuditReceipts as purgeStoredEgressAuditReceipts,
|
|
175
175
|
} from "./egress-audit-store";
|
|
176
|
+
import { buildEligibleDocumentQuery } from "./eligibility";
|
|
176
177
|
import {
|
|
177
178
|
advanceFileRefactorReceipt as advanceStoredFileRefactorReceipt,
|
|
178
179
|
createFileRefactorPreparedReceipt as createStoredFileRefactorPreparedReceipt,
|
|
@@ -180,8 +181,17 @@ import {
|
|
|
180
181
|
getLatestFileRefactorReceiptByPlanDigest as getStoredLatestFileRefactorReceiptByPlanDigest,
|
|
181
182
|
} from "./file-refactor-journal-store";
|
|
182
183
|
import { loadFts5Snowball } from "./fts5-snowball";
|
|
184
|
+
import {
|
|
185
|
+
applyGraphEdges,
|
|
186
|
+
type DesiredGraphEdge,
|
|
187
|
+
} from "./graph-edge-application";
|
|
183
188
|
import { resolveGraphLinkTargets } from "./graph-link-resolver";
|
|
184
189
|
import { queryGraphNeighborsForSeeds } from "./graph-neighbors";
|
|
190
|
+
import { createGraphReferenceStore } from "./graph-reference-state";
|
|
191
|
+
import {
|
|
192
|
+
snapshotLegacyTitles,
|
|
193
|
+
reconcileLegacyTitles,
|
|
194
|
+
} from "./legacy-vector-ownership";
|
|
185
195
|
import {
|
|
186
196
|
appendExportManifest as appendStoredTraceExportManifest,
|
|
187
197
|
getBoundedTrace as getBoundedStoredTrace,
|
|
@@ -512,8 +522,17 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
512
522
|
private ftsTokenizer: FtsTokenizer = "unicode61";
|
|
513
523
|
private configPath = ""; // Set by CLI layer for status output
|
|
514
524
|
private txCounter = 0; // Savepoint counter for unique names
|
|
515
|
-
private readonly txContext = new AsyncLocalStorage<{
|
|
525
|
+
private readonly txContext = new AsyncLocalStorage<{
|
|
526
|
+
depth: number;
|
|
527
|
+
token: { revoked: boolean };
|
|
528
|
+
}>();
|
|
516
529
|
private txTail: Promise<void> = Promise.resolve();
|
|
530
|
+
private activeTransaction?: {
|
|
531
|
+
token: { revoked: boolean };
|
|
532
|
+
release: () => void;
|
|
533
|
+
};
|
|
534
|
+
private shutdownFenced = false;
|
|
535
|
+
private shutdownDeadline?: number;
|
|
517
536
|
private contextGeneration = 0;
|
|
518
537
|
|
|
519
538
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -527,6 +546,8 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
527
546
|
): Promise<StoreResult<MigrationResult>> {
|
|
528
547
|
try {
|
|
529
548
|
this.db = new Database(dbPath, { create: true });
|
|
549
|
+
this.shutdownFenced = false;
|
|
550
|
+
this.shutdownDeadline = undefined;
|
|
530
551
|
this.dbPath = dbPath;
|
|
531
552
|
this.ftsTokenizer = ftsTokenizer;
|
|
532
553
|
|
|
@@ -597,6 +618,8 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
597
618
|
): StoreResult<void> {
|
|
598
619
|
try {
|
|
599
620
|
this.db = new Database(dbPath, { readonly: true, strict: true });
|
|
621
|
+
this.shutdownFenced = false;
|
|
622
|
+
this.shutdownDeadline = undefined;
|
|
600
623
|
this.dbPath = dbPath;
|
|
601
624
|
this.db.exec("PRAGMA query_only = ON");
|
|
602
625
|
this.db.exec(
|
|
@@ -618,12 +641,44 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
618
641
|
}
|
|
619
642
|
|
|
620
643
|
async close(): Promise<void> {
|
|
644
|
+
this.fenceForShutdown();
|
|
621
645
|
if (this.db) {
|
|
622
646
|
this.db.close();
|
|
623
647
|
this.db = null;
|
|
624
648
|
}
|
|
625
649
|
}
|
|
626
650
|
|
|
651
|
+
/** Cap subsequent SQLite lock waits to the resident settlement deadline. */
|
|
652
|
+
beginShutdown(deadline: number): void {
|
|
653
|
+
this.shutdownDeadline = deadline;
|
|
654
|
+
if (this.db) this.capShutdownBusyWait(this.db);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private capShutdownBusyWait(db: Database): void {
|
|
658
|
+
if (this.shutdownDeadline === undefined) return;
|
|
659
|
+
const remaining = Math.max(
|
|
660
|
+
0,
|
|
661
|
+
Math.floor(this.shutdownDeadline - performance.now())
|
|
662
|
+
);
|
|
663
|
+
const current =
|
|
664
|
+
db.query<{ timeout: number }, []>("PRAGMA busy_timeout").get()?.timeout ??
|
|
665
|
+
0;
|
|
666
|
+
db.exec(`PRAGMA busy_timeout = ${Math.min(current, remaining)}`);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/** Revoke suspended transaction callbacks before closing their connection. */
|
|
670
|
+
fenceForShutdown(): void {
|
|
671
|
+
this.shutdownFenced = true;
|
|
672
|
+
const transaction = this.activeTransaction;
|
|
673
|
+
if (!transaction) return;
|
|
674
|
+
// JS cannot interleave this synchronous rollback with a running callback.
|
|
675
|
+
// A callback suspended at await must never commit or use the store again.
|
|
676
|
+
transaction.token.revoked = true;
|
|
677
|
+
this.db?.exec("ROLLBACK");
|
|
678
|
+
transaction.release();
|
|
679
|
+
this.activeTransaction = undefined;
|
|
680
|
+
}
|
|
681
|
+
|
|
627
682
|
isOpen(): boolean {
|
|
628
683
|
return this.db !== null;
|
|
629
684
|
}
|
|
@@ -636,27 +691,39 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
636
691
|
* explicit BEGIN/COMMIT to support async callbacks.
|
|
637
692
|
*/
|
|
638
693
|
async withTransaction<T>(fn: () => Promise<T>): Promise<StoreResult<T>> {
|
|
639
|
-
const db = this.ensureOpen();
|
|
640
694
|
const parent = this.txContext.getStore();
|
|
695
|
+
const connection = this.ensureOpen();
|
|
641
696
|
const isOuter = parent === undefined;
|
|
642
697
|
const savepoint = `sp_${++this.txCounter}`;
|
|
643
698
|
const releaseWriter = isOuter
|
|
644
699
|
? await this.acquireTransactionWriter()
|
|
645
700
|
: null;
|
|
701
|
+
const token = parent?.token ?? { revoked: false };
|
|
702
|
+
let db: Database | undefined;
|
|
646
703
|
|
|
647
704
|
try {
|
|
705
|
+
const current = this.ensureOpen();
|
|
706
|
+
if (current !== connection)
|
|
707
|
+
throw new Error("Transaction connection retired before admission");
|
|
708
|
+
db = current;
|
|
648
709
|
if (isOuter) {
|
|
649
710
|
// IMMEDIATE reduces lock churn for bulk writes
|
|
650
711
|
db.exec("BEGIN IMMEDIATE");
|
|
712
|
+
this.activeTransaction = { token, release: releaseWriter! };
|
|
651
713
|
} else {
|
|
652
714
|
db.exec(`SAVEPOINT ${savepoint}`);
|
|
653
715
|
}
|
|
654
716
|
|
|
655
717
|
const value = await this.txContext.run(
|
|
656
|
-
{ depth: (parent?.depth ?? 0) + 1 },
|
|
718
|
+
{ depth: (parent?.depth ?? 0) + 1, token },
|
|
657
719
|
fn
|
|
658
720
|
);
|
|
659
721
|
|
|
722
|
+
if (token.revoked || this.shutdownFenced)
|
|
723
|
+
throw new Error("Transaction revoked by resident shutdown");
|
|
724
|
+
|
|
725
|
+
this.capShutdownBusyWait(db);
|
|
726
|
+
|
|
660
727
|
if (isOuter) {
|
|
661
728
|
db.exec("COMMIT");
|
|
662
729
|
} else {
|
|
@@ -666,7 +733,9 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
666
733
|
return ok(value);
|
|
667
734
|
} catch (cause) {
|
|
668
735
|
try {
|
|
669
|
-
if (
|
|
736
|
+
if (!db || token.revoked) {
|
|
737
|
+
// Shutdown already rolled back. Never touch a replacement connection.
|
|
738
|
+
} else if (isOuter) {
|
|
670
739
|
db.exec("ROLLBACK");
|
|
671
740
|
} else {
|
|
672
741
|
db.exec(`ROLLBACK TO ${savepoint}`);
|
|
@@ -680,6 +749,8 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
680
749
|
cause instanceof Error ? cause.message : "Transaction failed";
|
|
681
750
|
return err("TRANSACTION_FAILED", message, cause);
|
|
682
751
|
} finally {
|
|
752
|
+
if (isOuter && this.activeTransaction?.token === token)
|
|
753
|
+
this.activeTransaction = undefined;
|
|
683
754
|
releaseWriter?.();
|
|
684
755
|
}
|
|
685
756
|
}
|
|
@@ -709,10 +780,18 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
709
780
|
return this.ensureOpen();
|
|
710
781
|
}
|
|
711
782
|
|
|
783
|
+
graphReferenceStore() {
|
|
784
|
+
return createGraphReferenceStore(this.ensureOpen());
|
|
785
|
+
}
|
|
786
|
+
|
|
712
787
|
private ensureOpen(): Database {
|
|
788
|
+
if (this.shutdownFenced || this.txContext.getStore()?.token.revoked) {
|
|
789
|
+
throw new Error("Database fenced by resident shutdown");
|
|
790
|
+
}
|
|
713
791
|
if (!this.db) {
|
|
714
792
|
throw new Error("Database not open");
|
|
715
793
|
}
|
|
794
|
+
this.capShutdownBusyWait(this.db);
|
|
716
795
|
return this.db;
|
|
717
796
|
}
|
|
718
797
|
|
|
@@ -1340,6 +1419,15 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
1340
1419
|
"SELECT * FROM documents WHERE collection = ? AND rel_path = ?"
|
|
1341
1420
|
)
|
|
1342
1421
|
.get(doc.collection, doc.relPath);
|
|
1422
|
+
const legacyTitles = snapshotLegacyTitles(
|
|
1423
|
+
db,
|
|
1424
|
+
!previousRow ||
|
|
1425
|
+
!previousRow.active ||
|
|
1426
|
+
previousRow.title !== (doc.title ?? null) ||
|
|
1427
|
+
previousRow.mirror_hash !== (doc.mirrorHash ?? null)
|
|
1428
|
+
? [previousRow?.mirror_hash, doc.mirrorHash]
|
|
1429
|
+
: []
|
|
1430
|
+
);
|
|
1343
1431
|
|
|
1344
1432
|
db.run(
|
|
1345
1433
|
`
|
|
@@ -1436,6 +1524,18 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
1436
1524
|
throw new Error("Failed to get document id after upsert");
|
|
1437
1525
|
}
|
|
1438
1526
|
|
|
1527
|
+
reconcileLegacyTitles(db, legacyTitles);
|
|
1528
|
+
|
|
1529
|
+
// Owner bindings describe the exact current source input. Keep inactive
|
|
1530
|
+
// bindings for identical restoration, but invalidate changed ownership.
|
|
1531
|
+
if (
|
|
1532
|
+
previousRow &&
|
|
1533
|
+
(previousRow.mirror_hash !== (doc.mirrorHash ?? null) ||
|
|
1534
|
+
previousRow.title !== (doc.title ?? null))
|
|
1535
|
+
) {
|
|
1536
|
+
db.run("DELETE FROM vector_owners WHERE document_id = ?", [idRow.id]);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1439
1539
|
// Conversion failures deliberately drop mirror ownership. Remove the
|
|
1440
1540
|
// old lexical projection in the same transaction so stale content can
|
|
1441
1541
|
// never join against the newly updated document metadata.
|
|
@@ -1599,7 +1699,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
1599
1699
|
const db = this.ensureOpen();
|
|
1600
1700
|
const row = db
|
|
1601
1701
|
.query<DbDocumentRow, [string]>(
|
|
1602
|
-
"SELECT * FROM documents WHERE docid = ?"
|
|
1702
|
+
"SELECT * FROM documents WHERE docid = ? ORDER BY active DESC, id ASC LIMIT 1"
|
|
1603
1703
|
)
|
|
1604
1704
|
.get(docid);
|
|
1605
1705
|
|
|
@@ -2229,13 +2329,22 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2229
2329
|
if (activeRows.length === 0) {
|
|
2230
2330
|
return 0;
|
|
2231
2331
|
}
|
|
2232
|
-
const
|
|
2332
|
+
const legacyTitles = snapshotLegacyTitles(
|
|
2333
|
+
db,
|
|
2334
|
+
activeRows.map((row) => row.mirror_hash)
|
|
2335
|
+
);
|
|
2336
|
+
db.run(
|
|
2233
2337
|
`UPDATE documents SET active = 0, updated_at = datetime('now')
|
|
2234
2338
|
WHERE collection = ?
|
|
2235
2339
|
AND active = 1
|
|
2236
2340
|
AND rel_path IN (${placeholders})`,
|
|
2237
2341
|
[collection, ...uniquePaths]
|
|
2238
2342
|
);
|
|
2343
|
+
// Bun run().changes includes trigger writes; report logical document rows.
|
|
2344
|
+
const changed =
|
|
2345
|
+
db.query<{ count: number }, []>("SELECT changes() AS count").get()
|
|
2346
|
+
?.count ?? 0;
|
|
2347
|
+
reconcileLegacyTitles(db, legacyTitles);
|
|
2239
2348
|
const observedAtMs = Date.now();
|
|
2240
2349
|
for (const activeRow of activeRows) {
|
|
2241
2350
|
const previous = snapshotDocumentChange(mapDocumentRow(activeRow));
|
|
@@ -2248,7 +2357,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2248
2357
|
observedAtMs,
|
|
2249
2358
|
});
|
|
2250
2359
|
}
|
|
2251
|
-
return
|
|
2360
|
+
return changed;
|
|
2252
2361
|
});
|
|
2253
2362
|
|
|
2254
2363
|
return ok(transaction());
|
|
@@ -2527,15 +2636,42 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2527
2636
|
const db = this.ensureOpen();
|
|
2528
2637
|
|
|
2529
2638
|
const transaction = db.transaction(() => {
|
|
2530
|
-
//
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2639
|
+
// Retain stable rows: DELETE cascades erase valid legacy vectors even
|
|
2640
|
+
// when duplicate ingestion produces exactly the same embedding input.
|
|
2641
|
+
const nextBySequence = new Map(
|
|
2642
|
+
chunks.map((chunk) => [chunk.seq, chunk])
|
|
2643
|
+
);
|
|
2644
|
+
const existing = db
|
|
2645
|
+
.query<{ seq: number; text: string }, [string]>(
|
|
2646
|
+
"SELECT seq, text FROM content_chunks WHERE mirror_hash = ?"
|
|
2647
|
+
)
|
|
2648
|
+
.all(mirrorHash);
|
|
2649
|
+
for (const old of existing) {
|
|
2650
|
+
const next = nextBySequence.get(old.seq);
|
|
2651
|
+
if (!next || next.text !== old.text) {
|
|
2652
|
+
db.run(
|
|
2653
|
+
"DELETE FROM vector_owners WHERE mirror_hash = ? AND seq = ?",
|
|
2654
|
+
[mirrorHash, old.seq]
|
|
2655
|
+
);
|
|
2656
|
+
db.run(
|
|
2657
|
+
"DELETE FROM content_chunks WHERE mirror_hash = ? AND seq = ?",
|
|
2658
|
+
[mirrorHash, old.seq]
|
|
2659
|
+
);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2534
2662
|
|
|
2535
|
-
// Insert new chunks
|
|
2536
2663
|
const stmt = db.prepare(`
|
|
2537
2664
|
INSERT INTO content_chunks (mirror_hash, seq, pos, text, start_line, end_line, language, token_count)
|
|
2538
2665
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
2666
|
+
ON CONFLICT(mirror_hash, seq) DO UPDATE SET
|
|
2667
|
+
pos = excluded.pos, start_line = excluded.start_line,
|
|
2668
|
+
end_line = excluded.end_line, language = excluded.language,
|
|
2669
|
+
token_count = excluded.token_count
|
|
2670
|
+
WHERE content_chunks.pos IS NOT excluded.pos
|
|
2671
|
+
OR content_chunks.start_line IS NOT excluded.start_line
|
|
2672
|
+
OR content_chunks.end_line IS NOT excluded.end_line
|
|
2673
|
+
OR content_chunks.language IS NOT excluded.language
|
|
2674
|
+
OR content_chunks.token_count IS NOT excluded.token_count
|
|
2539
2675
|
`);
|
|
2540
2676
|
|
|
2541
2677
|
for (const chunk of chunks) {
|
|
@@ -2638,6 +2774,52 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2638
2774
|
}
|
|
2639
2775
|
}
|
|
2640
2776
|
|
|
2777
|
+
async getChunksBySequenceBatch(
|
|
2778
|
+
keys: { mirrorHash: string; seq: number }[]
|
|
2779
|
+
): Promise<StoreResult<Map<string, ChunkRow[]>>> {
|
|
2780
|
+
try {
|
|
2781
|
+
const unique = new Map<string, { mirrorHash: string; seq: number }>();
|
|
2782
|
+
for (const key of keys) {
|
|
2783
|
+
if (key.mirrorHash)
|
|
2784
|
+
unique.set(JSON.stringify([key.mirrorHash, key.seq]), key);
|
|
2785
|
+
}
|
|
2786
|
+
const pairs = [...unique.values()];
|
|
2787
|
+
const result = new Map<string, ChunkRow[]>();
|
|
2788
|
+
if (pairs.length === 0) return ok(result);
|
|
2789
|
+
const db = this.ensureOpen();
|
|
2790
|
+
const batchSize = Math.floor(SQLITE_SAFE_PARAMETER_BATCH_SIZE / 2);
|
|
2791
|
+
for (let offset = 0; offset < pairs.length; offset += batchSize) {
|
|
2792
|
+
const batch = pairs.slice(offset, offset + batchSize);
|
|
2793
|
+
const values = batch.map(() => "(?, ?)").join(",");
|
|
2794
|
+
const rows = db
|
|
2795
|
+
.query<DbChunkRow, (string | number)[]>(`
|
|
2796
|
+
WITH requested(mirror_hash, seq) AS (VALUES ${values})
|
|
2797
|
+
SELECT c.* FROM requested r JOIN content_chunks c
|
|
2798
|
+
ON c.mirror_hash = r.mirror_hash AND c.seq = r.seq
|
|
2799
|
+
ORDER BY c.mirror_hash, c.seq
|
|
2800
|
+
`)
|
|
2801
|
+
.all(...batch.flatMap(({ mirrorHash, seq }) => [mirrorHash, seq]));
|
|
2802
|
+
for (const row of rows) {
|
|
2803
|
+
const mapped = mapChunkRow(row);
|
|
2804
|
+
const chunks = result.get(mapped.mirrorHash) ?? [];
|
|
2805
|
+
chunks.push(mapped);
|
|
2806
|
+
result.set(mapped.mirrorHash, chunks);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
for (const chunks of result.values())
|
|
2810
|
+
chunks.sort((a, b) => a.seq - b.seq);
|
|
2811
|
+
return ok(result);
|
|
2812
|
+
} catch (cause) {
|
|
2813
|
+
return err(
|
|
2814
|
+
"QUERY_FAILED",
|
|
2815
|
+
cause instanceof Error
|
|
2816
|
+
? cause.message
|
|
2817
|
+
: "Failed to get targeted chunks batch",
|
|
2818
|
+
cause
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2641
2823
|
// ─────────────────────────────────────────────────────────────────────────
|
|
2642
2824
|
// FTS Search
|
|
2643
2825
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -2654,67 +2836,7 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2654
2836
|
return err("INVALID_INPUT", builtQuery.error);
|
|
2655
2837
|
}
|
|
2656
2838
|
|
|
2657
|
-
|
|
2658
|
-
const tagConditions: string[] = [];
|
|
2659
|
-
const params: (string | number)[] = [];
|
|
2660
|
-
|
|
2661
|
-
// tagsAny: document has at least one of these tags
|
|
2662
|
-
if (options.tagsAny && options.tagsAny.length > 0) {
|
|
2663
|
-
const placeholders = options.tagsAny.map(() => "?").join(",");
|
|
2664
|
-
tagConditions.push(
|
|
2665
|
-
`EXISTS (SELECT 1 FROM doc_tags dt WHERE dt.document_id = d.id AND dt.tag IN (${placeholders}))`
|
|
2666
|
-
);
|
|
2667
|
-
params.push(...options.tagsAny);
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
// tagsAll: document has all of these tags
|
|
2671
|
-
if (options.tagsAll && options.tagsAll.length > 0) {
|
|
2672
|
-
for (const tag of options.tagsAll) {
|
|
2673
|
-
tagConditions.push(
|
|
2674
|
-
"EXISTS (SELECT 1 FROM doc_tags dt WHERE dt.document_id = d.id AND dt.tag = ?)"
|
|
2675
|
-
);
|
|
2676
|
-
params.push(tag);
|
|
2677
|
-
}
|
|
2678
|
-
}
|
|
2679
|
-
|
|
2680
|
-
if (options.since) {
|
|
2681
|
-
tagConditions.push("d.source_mtime >= ?");
|
|
2682
|
-
params.push(options.since);
|
|
2683
|
-
}
|
|
2684
|
-
if (options.until) {
|
|
2685
|
-
tagConditions.push("d.source_mtime <= ?");
|
|
2686
|
-
params.push(options.until);
|
|
2687
|
-
}
|
|
2688
|
-
if (options.categories && options.categories.length > 0) {
|
|
2689
|
-
const placeholders = options.categories.map(() => "?").join(",");
|
|
2690
|
-
tagConditions.push(
|
|
2691
|
-
`(d.content_type IN (${placeholders}) OR EXISTS (SELECT 1 FROM json_each(COALESCE(d.categories, '[]')) jc WHERE jc.value IN (${placeholders})))`
|
|
2692
|
-
);
|
|
2693
|
-
params.push(...options.categories, ...options.categories);
|
|
2694
|
-
}
|
|
2695
|
-
if (options.author) {
|
|
2696
|
-
tagConditions.push("LOWER(COALESCE(d.author, '')) LIKE ?");
|
|
2697
|
-
params.push(`%${options.author.toLowerCase()}%`);
|
|
2698
|
-
}
|
|
2699
|
-
|
|
2700
|
-
// Scope and supersession filters run inside the candidate subquery so
|
|
2701
|
-
// they narrow the corpus before the FTS LIMIT (never a post-filter).
|
|
2702
|
-
const innerConditions: string[] = [];
|
|
2703
|
-
const innerParams: string[] = [];
|
|
2704
|
-
if (options.memoryScopesAny && options.memoryScopesAny.length > 0) {
|
|
2705
|
-
const placeholders = options.memoryScopesAny.map(() => "?").join(",");
|
|
2706
|
-
innerConditions.push(
|
|
2707
|
-
`AND EXISTS (SELECT 1 FROM doc_memory_scopes ms WHERE ms.document_id = documents.id AND ms.scope IN (${placeholders}))`
|
|
2708
|
-
);
|
|
2709
|
-
innerParams.push(...options.memoryScopesAny);
|
|
2710
|
-
}
|
|
2711
|
-
if (options.excludeSuperseded) {
|
|
2712
|
-
innerConditions.push(SUPERSEDED_EXCLUSION_SQL("documents.id"));
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
const hasOuterFilters = tagConditions.length > 0;
|
|
2716
|
-
const ftsLimit = hasOuterFilters ? limit * 10 : limit;
|
|
2717
|
-
params.push(limit);
|
|
2839
|
+
const eligible = buildEligibleDocumentQuery(options, db);
|
|
2718
2840
|
|
|
2719
2841
|
// Document-level FTS search using an FTS-first CTE to keep collection and
|
|
2720
2842
|
// metadata filters from degrading the query plan into a broad scan.
|
|
@@ -2731,23 +2853,16 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2731
2853
|
) as score
|
|
2732
2854
|
FROM documents_fts
|
|
2733
2855
|
WHERE documents_fts MATCH ?
|
|
2734
|
-
AND
|
|
2735
|
-
SELECT
|
|
2736
|
-
WHERE
|
|
2737
|
-
${options.collection ? "AND collection = ?" : ""}
|
|
2738
|
-
${
|
|
2739
|
-
options.relPathPrefix !== undefined
|
|
2740
|
-
? "AND (COALESCE(NULLIF(record_source_path, ''), rel_path) = ? OR substr(COALESCE(NULLIF(record_source_path, ''), rel_path), 1, length(?) + 1) = ? || '/')"
|
|
2741
|
-
: ""
|
|
2742
|
-
}
|
|
2743
|
-
${innerConditions.join("\n ")}
|
|
2856
|
+
AND EXISTS (
|
|
2857
|
+
SELECT 1 FROM (${eligible.sql}) eligible_docs
|
|
2858
|
+
WHERE eligible_docs.id = documents_fts.rowid
|
|
2744
2859
|
)
|
|
2745
2860
|
ORDER BY score
|
|
2746
2861
|
LIMIT ?
|
|
2747
2862
|
)
|
|
2748
2863
|
SELECT
|
|
2749
2864
|
d.mirror_hash,
|
|
2750
|
-
0 as seq,
|
|
2865
|
+
${options.chunkLanguage ? "(SELECT min(lc.seq) FROM content_chunks lc WHERE lc.mirror_hash = d.mirror_hash AND lc.language = ?) as seq," : "0 as seq,"}
|
|
2751
2866
|
fm.score as score,
|
|
2752
2867
|
${options.snippet ? "fm.snippet as snippet," : ""}
|
|
2753
2868
|
d.docid,
|
|
@@ -2775,7 +2890,6 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2775
2890
|
FROM fts_matches fm
|
|
2776
2891
|
JOIN documents d ON d.id = fm.rowid AND d.active = 1
|
|
2777
2892
|
WHERE 1 = 1
|
|
2778
|
-
${tagConditions.length > 0 ? `AND ${tagConditions.join(" AND ")}` : ""}
|
|
2779
2893
|
ORDER BY fm.score
|
|
2780
2894
|
LIMIT ?
|
|
2781
2895
|
`;
|
|
@@ -2811,17 +2925,10 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
2811
2925
|
|
|
2812
2926
|
const queryParams = [
|
|
2813
2927
|
builtQuery.query,
|
|
2814
|
-
...
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
options.relPathPrefix,
|
|
2819
|
-
options.relPathPrefix,
|
|
2820
|
-
]
|
|
2821
|
-
: []),
|
|
2822
|
-
...innerParams,
|
|
2823
|
-
ftsLimit,
|
|
2824
|
-
...params,
|
|
2928
|
+
...eligible.params,
|
|
2929
|
+
limit,
|
|
2930
|
+
...(options.chunkLanguage ? [options.chunkLanguage] : []),
|
|
2931
|
+
limit,
|
|
2825
2932
|
];
|
|
2826
2933
|
const rows = db
|
|
2827
2934
|
.query<FtsRow, (string | number)[]>(sql)
|
|
@@ -3354,6 +3461,17 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
3354
3461
|
const db = this.ensureOpen();
|
|
3355
3462
|
|
|
3356
3463
|
const transaction = db.transaction(() => {
|
|
3464
|
+
// Link edits without a changed source identity cannot be located from
|
|
3465
|
+
// the prior document inventory, especially alongside unrelated changes.
|
|
3466
|
+
// Persist full-recovery authority in the same transaction as those edits.
|
|
3467
|
+
db.run(
|
|
3468
|
+
`UPDATE graph_projection_state SET dirty = 1, in_progress = 1
|
|
3469
|
+
WHERE id = 1 AND EXISTS (
|
|
3470
|
+
SELECT 1 FROM graph_reference_documents r JOIN documents d ON d.id = r.document_id
|
|
3471
|
+
WHERE d.id = ? AND r.source_hash IS d.source_hash AND r.mirror_hash IS d.mirror_hash
|
|
3472
|
+
)`,
|
|
3473
|
+
[documentId]
|
|
3474
|
+
);
|
|
3357
3475
|
// Delete existing links from this source
|
|
3358
3476
|
db.run("DELETE FROM doc_links WHERE source_doc_id = ? AND source = ?", [
|
|
3359
3477
|
documentId,
|
|
@@ -4253,36 +4371,18 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
4253
4371
|
try {
|
|
4254
4372
|
const db = this.ensureOpen();
|
|
4255
4373
|
|
|
4256
|
-
|
|
4257
|
-
db
|
|
4258
|
-
|
|
4374
|
+
applyGraphEdges(
|
|
4375
|
+
db,
|
|
4376
|
+
edges.map((edge) => ({
|
|
4377
|
+
sourceId: documentId,
|
|
4378
|
+
targetId: edge.targetDocId,
|
|
4379
|
+
edgeType: normalizeDocEdgeType(edge.edgeType),
|
|
4380
|
+
confidence: edge.confidence,
|
|
4259
4381
|
source,
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
}
|
|
4265
|
-
|
|
4266
|
-
const stmt = db.prepare(`
|
|
4267
|
-
INSERT INTO doc_edges (
|
|
4268
|
-
src_doc_id, dst_doc_id, edge_type, confidence, source
|
|
4269
|
-
) VALUES (?, ?, ?, ?, ?)
|
|
4270
|
-
ON CONFLICT(src_doc_id, dst_doc_id, edge_type, source) DO UPDATE SET
|
|
4271
|
-
confidence = excluded.confidence
|
|
4272
|
-
`);
|
|
4273
|
-
|
|
4274
|
-
for (const edge of edges) {
|
|
4275
|
-
stmt.run(
|
|
4276
|
-
documentId,
|
|
4277
|
-
edge.targetDocId,
|
|
4278
|
-
normalizeDocEdgeType(edge.edgeType),
|
|
4279
|
-
edge.confidence,
|
|
4280
|
-
source
|
|
4281
|
-
);
|
|
4282
|
-
}
|
|
4283
|
-
});
|
|
4284
|
-
|
|
4285
|
-
transaction();
|
|
4382
|
+
})),
|
|
4383
|
+
[source],
|
|
4384
|
+
[documentId]
|
|
4385
|
+
);
|
|
4286
4386
|
return ok(undefined);
|
|
4287
4387
|
} catch (cause) {
|
|
4288
4388
|
return err(
|
|
@@ -4947,74 +5047,42 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
4947
5047
|
if (sourceIds?.length === 0) {
|
|
4948
5048
|
return ok({ inserted: 0 });
|
|
4949
5049
|
}
|
|
4950
|
-
const sourcePlaceholders = sourceIds
|
|
4951
|
-
? sourceIds.map(() => "?").join(", ")
|
|
4952
|
-
: "";
|
|
4953
5050
|
const sourceFilter = sourceIds
|
|
4954
|
-
?
|
|
5051
|
+
? "AND src.id IN (SELECT value FROM json_each(?))"
|
|
4955
5052
|
: "";
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
["wikilink", "markdown-link", ...(sourceIds ?? [])]
|
|
4964
|
-
);
|
|
4965
|
-
|
|
4966
|
-
const insertWiki = db.run(
|
|
4967
|
-
`
|
|
4968
|
-
INSERT OR IGNORE INTO doc_edges (
|
|
4969
|
-
src_doc_id, dst_doc_id, edge_type, confidence, source
|
|
4970
|
-
)
|
|
4971
|
-
SELECT
|
|
4972
|
-
src.id,
|
|
4973
|
-
tgt.id,
|
|
4974
|
-
'mentions',
|
|
4975
|
-
'parsed',
|
|
4976
|
-
'wikilink'
|
|
4977
|
-
FROM documents src
|
|
4978
|
-
JOIN doc_links dl ON dl.source_doc_id = src.id
|
|
5053
|
+
const params = sourceIds ? [JSON.stringify(sourceIds)] : [];
|
|
5054
|
+
const inserted = db.transaction(() => {
|
|
5055
|
+
const wiki = db
|
|
5056
|
+
.query<DesiredGraphEdge, string[]>(`
|
|
5057
|
+
SELECT DISTINCT src.id AS sourceId, tgt.id AS targetId,
|
|
5058
|
+
'mentions' AS edgeType, 'parsed' AS confidence, 'wikilink' AS source
|
|
5059
|
+
FROM documents src JOIN doc_links dl ON dl.source_doc_id = src.id
|
|
4979
5060
|
JOIN documents tgt ON tgt.id = (${buildWikiBestMatchSubquery(
|
|
4980
5061
|
"COALESCE(dl.target_collection, src.collection)",
|
|
4981
5062
|
"dl.target_ref_norm"
|
|
4982
5063
|
)})
|
|
4983
|
-
WHERE src.active = 1
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
`
|
|
4993
|
-
INSERT OR IGNORE INTO doc_edges (
|
|
4994
|
-
src_doc_id, dst_doc_id, edge_type, confidence, source
|
|
4995
|
-
)
|
|
4996
|
-
SELECT
|
|
4997
|
-
src.id,
|
|
4998
|
-
tgt.id,
|
|
4999
|
-
'related_to',
|
|
5000
|
-
'parsed',
|
|
5001
|
-
'markdown-link'
|
|
5002
|
-
FROM documents src
|
|
5003
|
-
JOIN doc_links dl ON dl.source_doc_id = src.id
|
|
5064
|
+
WHERE src.active = 1 AND tgt.active = 1 AND dl.link_type = 'wiki'
|
|
5065
|
+
${sourceFilter}
|
|
5066
|
+
`)
|
|
5067
|
+
.all(...params);
|
|
5068
|
+
const markdown = db
|
|
5069
|
+
.query<DesiredGraphEdge, string[]>(`
|
|
5070
|
+
SELECT DISTINCT src.id AS sourceId, tgt.id AS targetId,
|
|
5071
|
+
'related_to' AS edgeType, 'parsed' AS confidence, 'markdown-link' AS source
|
|
5072
|
+
FROM documents src JOIN doc_links dl ON dl.source_doc_id = src.id
|
|
5004
5073
|
JOIN documents tgt ON tgt.active = 1
|
|
5005
5074
|
AND tgt.collection = COALESCE(dl.target_collection, src.collection)
|
|
5006
5075
|
AND tgt.rel_path = dl.target_ref_norm
|
|
5007
|
-
WHERE src.active = 1
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5076
|
+
WHERE src.active = 1 AND dl.link_type = 'markdown' ${sourceFilter}
|
|
5077
|
+
`)
|
|
5078
|
+
.all(...params);
|
|
5079
|
+
return applyGraphEdges(
|
|
5080
|
+
db,
|
|
5081
|
+
[...wiki, ...markdown],
|
|
5082
|
+
["wikilink", "markdown-link"],
|
|
5083
|
+
sourceIds
|
|
5012
5084
|
);
|
|
5013
|
-
|
|
5014
|
-
inserted = insertWiki.changes + insertMarkdown.changes;
|
|
5015
|
-
});
|
|
5016
|
-
|
|
5017
|
-
transaction();
|
|
5085
|
+
})();
|
|
5018
5086
|
return ok({ inserted });
|
|
5019
5087
|
} catch (cause) {
|
|
5020
5088
|
return err(
|