seer-mcp 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/.vscode/settings.json +3 -0
- package/LICENSE +176 -0
- package/README.md +272 -0
- package/README_dev.md +199 -0
- package/dist/bundle/ci.d.ts +47 -0
- package/dist/bundle/ci.d.ts.map +1 -0
- package/dist/bundle/ci.js +113 -0
- package/dist/bundle/ci.js.map +1 -0
- package/dist/bundle/contract.d.ts +111 -0
- package/dist/bundle/contract.d.ts.map +1 -0
- package/dist/bundle/contract.js +352 -0
- package/dist/bundle/contract.js.map +1 -0
- package/dist/bundle/export.d.ts +36 -0
- package/dist/bundle/export.d.ts.map +1 -0
- package/dist/bundle/export.js +152 -0
- package/dist/bundle/export.js.map +1 -0
- package/dist/bundle/external.d.ts +66 -0
- package/dist/bundle/external.d.ts.map +1 -0
- package/dist/bundle/external.js +238 -0
- package/dist/bundle/external.js.map +1 -0
- package/dist/bundle/format.d.ts +94 -0
- package/dist/bundle/format.d.ts.map +1 -0
- package/dist/bundle/format.js +42 -0
- package/dist/bundle/format.js.map +1 -0
- package/dist/bundle/import.d.ts +49 -0
- package/dist/bundle/import.d.ts.map +1 -0
- package/dist/bundle/import.js +116 -0
- package/dist/bundle/import.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1402 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +48 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +284 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/db/schema.d.ts +3 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +616 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/store.d.ts +1011 -0
- package/dist/db/store.d.ts.map +1 -0
- package/dist/db/store.js +3888 -0
- package/dist/db/store.js.map +1 -0
- package/dist/graph/pagerank.d.ts +9 -0
- package/dist/graph/pagerank.d.ts.map +1 -0
- package/dist/graph/pagerank.js +47 -0
- package/dist/graph/pagerank.js.map +1 -0
- package/dist/indexer/architecture.d.ts +72 -0
- package/dist/indexer/architecture.d.ts.map +1 -0
- package/dist/indexer/architecture.js +112 -0
- package/dist/indexer/architecture.js.map +1 -0
- package/dist/indexer/behavior.d.ts +75 -0
- package/dist/indexer/behavior.d.ts.map +1 -0
- package/dist/indexer/behavior.js +395 -0
- package/dist/indexer/behavior.js.map +1 -0
- package/dist/indexer/boundaries.d.ts +60 -0
- package/dist/indexer/boundaries.d.ts.map +1 -0
- package/dist/indexer/boundaries.js +366 -0
- package/dist/indexer/boundaries.js.map +1 -0
- package/dist/indexer/churn.d.ts +15 -0
- package/dist/indexer/churn.d.ts.map +1 -0
- package/dist/indexer/churn.js +49 -0
- package/dist/indexer/churn.js.map +1 -0
- package/dist/indexer/classify.d.ts +9 -0
- package/dist/indexer/classify.d.ts.map +1 -0
- package/dist/indexer/classify.js +90 -0
- package/dist/indexer/classify.js.map +1 -0
- package/dist/indexer/context.d.ts +176 -0
- package/dist/indexer/context.d.ts.map +1 -0
- package/dist/indexer/context.js +193 -0
- package/dist/indexer/context.js.map +1 -0
- package/dist/indexer/continuity.d.ts +67 -0
- package/dist/indexer/continuity.d.ts.map +1 -0
- package/dist/indexer/continuity.js +288 -0
- package/dist/indexer/continuity.js.map +1 -0
- package/dist/indexer/detectchanges.d.ts +32 -0
- package/dist/indexer/detectchanges.d.ts.map +1 -0
- package/dist/indexer/detectchanges.js +74 -0
- package/dist/indexer/detectchanges.js.map +1 -0
- package/dist/indexer/discovery.d.ts +37 -0
- package/dist/indexer/discovery.d.ts.map +1 -0
- package/dist/indexer/discovery.js +136 -0
- package/dist/indexer/discovery.js.map +1 -0
- package/dist/indexer/externaldeps.d.ts +18 -0
- package/dist/indexer/externaldeps.d.ts.map +1 -0
- package/dist/indexer/externaldeps.js +288 -0
- package/dist/indexer/externaldeps.js.map +1 -0
- package/dist/indexer/freshness.d.ts +48 -0
- package/dist/indexer/freshness.d.ts.map +1 -0
- package/dist/indexer/freshness.js +128 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/git.d.ts +144 -0
- package/dist/indexer/git.d.ts.map +1 -0
- package/dist/indexer/git.js +444 -0
- package/dist/indexer/git.js.map +1 -0
- package/dist/indexer/index.d.ts +145 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +930 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/modules.d.ts +62 -0
- package/dist/indexer/modules.d.ts.map +1 -0
- package/dist/indexer/modules.js +293 -0
- package/dist/indexer/modules.js.map +1 -0
- package/dist/indexer/preflight.d.ts +154 -0
- package/dist/indexer/preflight.d.ts.map +1 -0
- package/dist/indexer/preflight.js +399 -0
- package/dist/indexer/preflight.js.map +1 -0
- package/dist/indexer/protoScanner.d.ts +34 -0
- package/dist/indexer/protoScanner.d.ts.map +1 -0
- package/dist/indexer/protoScanner.js +133 -0
- package/dist/indexer/protoScanner.js.map +1 -0
- package/dist/indexer/risk.d.ts +115 -0
- package/dist/indexer/risk.d.ts.map +1 -0
- package/dist/indexer/risk.js +194 -0
- package/dist/indexer/risk.js.map +1 -0
- package/dist/indexer/serviceHostScanner.d.ts +25 -0
- package/dist/indexer/serviceHostScanner.d.ts.map +1 -0
- package/dist/indexer/serviceHostScanner.js +95 -0
- package/dist/indexer/serviceHostScanner.js.map +1 -0
- package/dist/indexer/serviceLinks.d.ts +105 -0
- package/dist/indexer/serviceLinks.d.ts.map +1 -0
- package/dist/indexer/serviceLinks.js +509 -0
- package/dist/indexer/serviceLinks.js.map +1 -0
- package/dist/indexer/shapehash.d.ts +98 -0
- package/dist/indexer/shapehash.d.ts.map +1 -0
- package/dist/indexer/shapehash.js +354 -0
- package/dist/indexer/shapehash.js.map +1 -0
- package/dist/indexer/skeleton.d.ts +15 -0
- package/dist/indexer/skeleton.d.ts.map +1 -0
- package/dist/indexer/skeleton.js +136 -0
- package/dist/indexer/skeleton.js.map +1 -0
- package/dist/indexer/symbolhistory.d.ts +41 -0
- package/dist/indexer/symbolhistory.d.ts.map +1 -0
- package/dist/indexer/symbolhistory.js +124 -0
- package/dist/indexer/symbolhistory.js.map +1 -0
- package/dist/indexer/watcher.d.ts +68 -0
- package/dist/indexer/watcher.d.ts.map +1 -0
- package/dist/indexer/watcher.js +179 -0
- package/dist/indexer/watcher.js.map +1 -0
- package/dist/mcp/server.d.ts +80 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +1610 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/parser/index.d.ts +8 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +33 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/languages/cpp.d.ts +3 -0
- package/dist/parser/languages/cpp.d.ts.map +1 -0
- package/dist/parser/languages/cpp.js +350 -0
- package/dist/parser/languages/cpp.js.map +1 -0
- package/dist/parser/languages/csharp.d.ts +3 -0
- package/dist/parser/languages/csharp.d.ts.map +1 -0
- package/dist/parser/languages/csharp.js +239 -0
- package/dist/parser/languages/csharp.js.map +1 -0
- package/dist/parser/languages/go.d.ts +3 -0
- package/dist/parser/languages/go.d.ts.map +1 -0
- package/dist/parser/languages/go.js +259 -0
- package/dist/parser/languages/go.js.map +1 -0
- package/dist/parser/languages/java.d.ts +3 -0
- package/dist/parser/languages/java.d.ts.map +1 -0
- package/dist/parser/languages/java.js +391 -0
- package/dist/parser/languages/java.js.map +1 -0
- package/dist/parser/languages/python.d.ts +3 -0
- package/dist/parser/languages/python.d.ts.map +1 -0
- package/dist/parser/languages/python.js +396 -0
- package/dist/parser/languages/python.js.map +1 -0
- package/dist/parser/languages/rust.d.ts +3 -0
- package/dist/parser/languages/rust.d.ts.map +1 -0
- package/dist/parser/languages/rust.js +159 -0
- package/dist/parser/languages/rust.js.map +1 -0
- package/dist/parser/languages/typescript.d.ts +3 -0
- package/dist/parser/languages/typescript.d.ts.map +1 -0
- package/dist/parser/languages/typescript.js +1442 -0
- package/dist/parser/languages/typescript.js.map +1 -0
- package/dist/parser/parserContext.d.ts +77 -0
- package/dist/parser/parserContext.d.ts.map +1 -0
- package/dist/parser/parserContext.js +354 -0
- package/dist/parser/parserContext.js.map +1 -0
- package/dist/parser/walker.d.ts +81 -0
- package/dist/parser/walker.d.ts.map +1 -0
- package/dist/parser/walker.js +217 -0
- package/dist/parser/walker.js.map +1 -0
- package/dist/parser/worker.d.ts +66 -0
- package/dist/parser/worker.d.ts.map +1 -0
- package/dist/parser/worker.js +129 -0
- package/dist/parser/worker.js.map +1 -0
- package/dist/parser/workerpool.d.ts +107 -0
- package/dist/parser/workerpool.d.ts.map +1 -0
- package/dist/parser/workerpool.js +383 -0
- package/dist/parser/workerpool.js.map +1 -0
- package/dist/scip/format.d.ts +87 -0
- package/dist/scip/format.d.ts.map +1 -0
- package/dist/scip/format.js +31 -0
- package/dist/scip/format.js.map +1 -0
- package/dist/scip/import.d.ts +37 -0
- package/dist/scip/import.d.ts.map +1 -0
- package/dist/scip/import.js +180 -0
- package/dist/scip/import.js.map +1 -0
- package/dist/types.d.ts +392 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/docs/architecture.md +105 -0
- package/docs/benchmarks/methodology.md +134 -0
- package/docs/benchmarks/raw-results.md +71 -0
- package/docs/benchmarks.md +74 -0
- package/docs/cli.md +148 -0
- package/docs/examples/behavior-tests.md +70 -0
- package/docs/examples/change-history.md +85 -0
- package/docs/examples/pre-edit-context.md +81 -0
- package/docs/examples/service-links.md +88 -0
- package/docs/examples.md +80 -0
- package/docs/faq.md +70 -0
- package/docs/internals.md +104 -0
- package/docs/languages.md +70 -0
- package/docs/limits.md +52 -0
- package/docs/mcp.md +199 -0
- package/docs/quickstart.md +119 -0
- package/docs/testing.md +123 -0
- package/docs/tools.md +115 -0
- package/package.json +52 -0
- package/research-codebase.md +578 -0
- package/seer-cli-docs.md +326 -0
- package/seer-master-guide.md +246 -0
- package/src/bundle/ci.ts +141 -0
- package/src/bundle/contract.ts +387 -0
- package/src/bundle/export.ts +175 -0
- package/src/bundle/external.ts +285 -0
- package/src/bundle/format.ts +92 -0
- package/src/bundle/import.ts +157 -0
- package/src/cli/index.ts +1249 -0
- package/src/cli/init.ts +389 -0
- package/src/db/schema.ts +614 -0
- package/src/db/store.ts +4306 -0
- package/src/graph/pagerank.ts +53 -0
- package/src/indexer/architecture.ts +148 -0
- package/src/indexer/behavior.ts +466 -0
- package/src/indexer/boundaries.ts +374 -0
- package/src/indexer/churn.ts +58 -0
- package/src/indexer/classify.ts +96 -0
- package/src/indexer/context.ts +340 -0
- package/src/indexer/continuity.ts +322 -0
- package/src/indexer/detectchanges.ts +94 -0
- package/src/indexer/discovery.ts +176 -0
- package/src/indexer/externaldeps.ts +243 -0
- package/src/indexer/freshness.ts +166 -0
- package/src/indexer/git.ts +453 -0
- package/src/indexer/index.ts +1092 -0
- package/src/indexer/modules.ts +358 -0
- package/src/indexer/preflight.ts +548 -0
- package/src/indexer/protoScanner.ts +147 -0
- package/src/indexer/risk.ts +304 -0
- package/src/indexer/serviceHostScanner.ts +92 -0
- package/src/indexer/serviceLinks.ts +543 -0
- package/src/indexer/shapehash.ts +370 -0
- package/src/indexer/skeleton.ts +169 -0
- package/src/indexer/symbolhistory.ts +172 -0
- package/src/indexer/watcher.ts +206 -0
- package/src/mcp/server.ts +1659 -0
- package/src/parser/index.ts +37 -0
- package/src/parser/languages/cpp.ts +361 -0
- package/src/parser/languages/csharp.ts +235 -0
- package/src/parser/languages/go.ts +259 -0
- package/src/parser/languages/java.ts +382 -0
- package/src/parser/languages/python.ts +370 -0
- package/src/parser/languages/rust.ts +164 -0
- package/src/parser/languages/typescript.ts +1435 -0
- package/src/parser/parserContext.ts +392 -0
- package/src/parser/walker.ts +306 -0
- package/src/parser/worker.ts +181 -0
- package/src/parser/workerpool.ts +448 -0
- package/src/scip/format.ts +83 -0
- package/src/scip/import.ts +216 -0
- package/src/types.ts +457 -0
- package/tests/benchmark-service-links.ts +244 -0
- package/tests/bug-regressions.ts +626 -0
- package/tests/filters.ts +264 -0
- package/tests/fixtures/Counter.tsx +38 -0
- package/tests/fixtures/caller.ts +7 -0
- package/tests/fixtures/collisions.ts +23 -0
- package/tests/fixtures/local_helper.ts +5 -0
- package/tests/fixtures/overloads.java +17 -0
- package/tests/fixtures/remote_helper.ts +4 -0
- package/tests/fixtures/sample.c +15 -0
- package/tests/fixtures/sample.cpp +47 -0
- package/tests/fixtures/sample.cs +62 -0
- package/tests/fixtures/sample.go +68 -0
- package/tests/fixtures/sample.h +30 -0
- package/tests/fixtures/sample.java +85 -0
- package/tests/fixtures/sample.py +46 -0
- package/tests/fixtures/sample.rs +78 -0
- package/tests/fixtures/sample.ts +76 -0
- package/tests/fixtures-service/HttpClients.cs +30 -0
- package/tests/fixtures-service/HttpClients.java +24 -0
- package/tests/fixtures-service/billing.ts +15 -0
- package/tests/fixtures-service/docker-compose.yml +15 -0
- package/tests/fixtures-service/gateway.ts +10 -0
- package/tests/fixtures-service/get_user.ts +11 -0
- package/tests/fixtures-service/graphql_client.ts +63 -0
- package/tests/fixtures-service/graphql_server.ts +30 -0
- package/tests/fixtures-service/grpc_client.go +30 -0
- package/tests/fixtures-service/http_clients.go +23 -0
- package/tests/fixtures-service/http_clients.py +38 -0
- package/tests/fixtures-service/http_clients.ts +49 -0
- package/tests/fixtures-service/k8s/payment-service.yaml +22 -0
- package/tests/fixtures-service/k8s_calls.ts +20 -0
- package/tests/fixtures-service/messaging.ts +87 -0
- package/tests/fixtures-service/trpc_client.ts +39 -0
- package/tests/fixtures-service/trpc_server.ts +39 -0
- package/tests/fixtures-service/user_service.proto +33 -0
- package/tests/fixtures-trackcd/Cargo.toml +11 -0
- package/tests/fixtures-trackcd/SpringController.java +36 -0
- package/tests/fixtures-trackcd/auth_service.ts +19 -0
- package/tests/fixtures-trackcd/complex_module.py +50 -0
- package/tests/fixtures-trackcd/express_app.js +30 -0
- package/tests/fixtures-trackcd/fastapi_app.py +49 -0
- package/tests/fixtures-trackcd/fastify_object_routes.js +32 -0
- package/tests/fixtures-trackcd/go.mod +8 -0
- package/tests/fixtures-trackcd/package.json +15 -0
- package/tests/fixtures-trackcd/requirements.txt +4 -0
- package/tests/fixtures-trackcd/tests/auth_service.test.ts +13 -0
- package/tests/fixtures-tracke/auth/AuthService.ts +23 -0
- package/tests/fixtures-tracke/auth/crypto.ts +7 -0
- package/tests/fixtures-tracke/billing/Billing.ts +20 -0
- package/tests/fixtures-tracke/billing/Invoice.ts +10 -0
- package/tests/fixtures-tracke/billing/server.ts +17 -0
- package/tests/fixtures-tracke/package.json +7 -0
- package/tests/fixtures-tracke/tests/auth.test.ts +23 -0
- package/tests/fixtures-tracke/tests/billing.test.ts +14 -0
- package/tests/fixtures-trackf/package.json +5 -0
- package/tests/fixtures-trackf/src/auth.ts +26 -0
- package/tests/fixtures-trackf/src/handlers.ts +35 -0
- package/tests/fixtures-tracki/billing/routes.ts +12 -0
- package/tests/fixtures-tracki/gateway/client.ts +13 -0
- package/tests/git-features.ts +267 -0
- package/tests/init.ts +141 -0
- package/tests/mcp-jit.ts +130 -0
- package/tests/mcp-smoke.ts +191 -0
- package/tests/mcp-trackcd.ts +169 -0
- package/tests/mcp-tracke.ts +229 -0
- package/tests/mcp-trackf.ts +330 -0
- package/tests/mcp-trackg.ts +219 -0
- package/tests/mcp-tracki.ts +174 -0
- package/tests/mcp-watcher.ts +126 -0
- package/tests/optspec.ts +194 -0
- package/tests/parallel-index.ts +333 -0
- package/tests/parallel-read.ts +125 -0
- package/tests/parallel-recovery.ts +241 -0
- package/tests/perf-callers.ts +145 -0
- package/tests/query-parity.ts +184 -0
- package/tests/query-perf.ts +55 -0
- package/tests/scale-parallel-parity.ts +225 -0
- package/tests/scale-test.ts +523 -0
- package/tests/smoke.ts +396 -0
- package/tests/trackcd.ts +325 -0
- package/tests/tracke-collisions.ts +255 -0
- package/tests/tracke.ts +314 -0
- package/tests/trackf-bugs.ts +406 -0
- package/tests/trackf.ts +390 -0
- package/tests/trackg.ts +1372 -0
- package/tests/tracki-boundaries.ts +202 -0
- package/tests/tracki-continuity.ts +253 -0
- package/tests/tracki-contract-diff.ts +249 -0
- package/tests/tracki-external-bundles.ts +341 -0
- package/tests/tracki-preflight.ts +251 -0
- package/tests/verify-roles.ts +51 -0
- package/tests/worker-parity.ts +286 -0
- package/tests/worker-pool.ts +262 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v10 External Bundle Layers — additive import of a peer repo's .seerbundle as
|
|
3
|
+
* read-only external evidence.
|
|
4
|
+
*
|
|
5
|
+
* The destructive bundle import (src/bundle/import.ts) replaces the local
|
|
6
|
+
* graph DB wholesale; this importer leaves local files/symbols untouched and
|
|
7
|
+
* only adds:
|
|
8
|
+
* - one row in external_bundles (source identity + manifest hash)
|
|
9
|
+
* - one phantom file per bundle (`__external_bundle__/<project>/<id>`)
|
|
10
|
+
* - rows in routes (and where present, service_endpoints) marked
|
|
11
|
+
* `external_bundle_id` so they participate in service-link resolution but
|
|
12
|
+
* can be wiped as a single layer on re-import.
|
|
13
|
+
*
|
|
14
|
+
* Design rules:
|
|
15
|
+
* - never touch local symbols / files / edges / pagerank
|
|
16
|
+
* - never write rows whose external_bundle_id is NULL
|
|
17
|
+
* - re-importing the same path with the same hash is a no-op
|
|
18
|
+
* - re-importing with a NEW hash replaces only that layer (FK cascade)
|
|
19
|
+
* - the bundle is read by spinning up a temp Store against the bundle's
|
|
20
|
+
* SQLite payload — same code path that bundle/info uses to enumerate the
|
|
21
|
+
* manifest, but with full row access scoped read-only
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import fs from 'fs';
|
|
25
|
+
import path from 'path';
|
|
26
|
+
import os from 'os';
|
|
27
|
+
import zlib from 'zlib';
|
|
28
|
+
import crypto from 'crypto';
|
|
29
|
+
import { BUNDLE_MAGIC, BUNDLE_FORMAT_VERSION, BundleManifest } from './format.js';
|
|
30
|
+
import { CURRENT_SCHEMA_VERSION } from '../db/schema.js';
|
|
31
|
+
import { Store } from '../db/store.js';
|
|
32
|
+
import { resolveServiceLinks } from '../indexer/serviceLinks.js';
|
|
33
|
+
|
|
34
|
+
export interface ExternalImportOptions {
|
|
35
|
+
/** Optional alias for the bundle (defaults to the manifest's gitBranch or basename). */
|
|
36
|
+
alias?: string;
|
|
37
|
+
/** When true, force re-import even if the same hash is already present. */
|
|
38
|
+
force?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Skip the dbSha256 / dbBytes integrity check. Off by default — the manifest
|
|
41
|
+
* integrity guarantee is what lets us trust a peer-repo bundle. Use only for
|
|
42
|
+
* forensic inspection of a corrupted bundle whose header still parses.
|
|
43
|
+
*/
|
|
44
|
+
skipIntegrityCheck?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Skip the schema-version compatibility check. Off by default — a bundle
|
|
47
|
+
* whose schemaVersion is newer than this build can't be safely read.
|
|
48
|
+
*/
|
|
49
|
+
skipSchemaCheck?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Rebuild service_links after import so cross-repo links are visible to the
|
|
52
|
+
* next query without waiting for a re-index. Default true.
|
|
53
|
+
*/
|
|
54
|
+
rebuildServiceLinks?: boolean;
|
|
55
|
+
/** Logger; defaults to no-op. */
|
|
56
|
+
log?: (msg: string) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ExternalImportResult {
|
|
60
|
+
bundlePath: string;
|
|
61
|
+
bundleId: number;
|
|
62
|
+
externalProject: string | null;
|
|
63
|
+
externalHash: string;
|
|
64
|
+
schemaVersion: number;
|
|
65
|
+
/** True when the same bundle path + hash was already imported and we skipped. */
|
|
66
|
+
alreadyImported: boolean;
|
|
67
|
+
routesImported: number;
|
|
68
|
+
serviceEndpointsImported: number;
|
|
69
|
+
elapsedMs: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Import a .seerbundle as an additive external layer into the given local
|
|
74
|
+
* Store. The local DB is NOT touched outside of the external_bundles row,
|
|
75
|
+
* the phantom file row, and the new external-marked routes/service_calls.
|
|
76
|
+
*/
|
|
77
|
+
export async function importExternalBundle(
|
|
78
|
+
bundlePath: string,
|
|
79
|
+
localStore: Store,
|
|
80
|
+
options: ExternalImportOptions = {},
|
|
81
|
+
): Promise<ExternalImportResult> {
|
|
82
|
+
const start = Date.now();
|
|
83
|
+
const log = options.log ?? (() => { /* */ });
|
|
84
|
+
|
|
85
|
+
if (localStore.isReadOnly()) {
|
|
86
|
+
throw new Error('Cannot import external bundle into a read-only Store');
|
|
87
|
+
}
|
|
88
|
+
if (!localStore.hasV10()) {
|
|
89
|
+
throw new Error('Local DB does not have v10 external_bundles tables — upgrade first');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!fs.existsSync(bundlePath)) {
|
|
93
|
+
throw new Error(`No bundle at ${bundlePath}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse the manifest + payload up front. We need both: the manifest gives us
|
|
97
|
+
// identity + schema_version, the payload gives us the read source.
|
|
98
|
+
const fileBuf = fs.readFileSync(bundlePath);
|
|
99
|
+
if (fileBuf.length < 12 || !fileBuf.subarray(0, 4).equals(BUNDLE_MAGIC)) {
|
|
100
|
+
throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
|
|
101
|
+
}
|
|
102
|
+
const formatVersion = fileBuf.readUInt32BE(4);
|
|
103
|
+
if (formatVersion > BUNDLE_FORMAT_VERSION) {
|
|
104
|
+
throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
|
|
105
|
+
}
|
|
106
|
+
const manifestLen = fileBuf.readUInt32BE(8);
|
|
107
|
+
const manifestEnd = 12 + manifestLen;
|
|
108
|
+
if (manifestLen <= 0 || manifestEnd > fileBuf.length) {
|
|
109
|
+
throw new Error(`Bundle truncated; manifest length ${manifestLen} exceeds file size`);
|
|
110
|
+
}
|
|
111
|
+
const manifest = JSON.parse(fileBuf.slice(12, manifestEnd).toString('utf-8')) as BundleManifest;
|
|
112
|
+
|
|
113
|
+
// Schema compatibility: a bundle built against a NEWER schema may carry
|
|
114
|
+
// tables/columns we don't understand. Reject up front rather than reading
|
|
115
|
+
// partial data. Older bundles are fine — listRoutes tolerates older shapes.
|
|
116
|
+
if (!options.skipSchemaCheck) {
|
|
117
|
+
if (typeof manifest.schemaVersion !== 'number' || manifest.schemaVersion <= 0) {
|
|
118
|
+
throw new Error(`Bundle has invalid schemaVersion=${manifest.schemaVersion}.`);
|
|
119
|
+
}
|
|
120
|
+
if (manifest.schemaVersion > CURRENT_SCHEMA_VERSION) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Bundle schemaVersion=${manifest.schemaVersion} is newer than this build's ` +
|
|
123
|
+
`CURRENT_SCHEMA_VERSION=${CURRENT_SCHEMA_VERSION}. Upgrade Seer or pass skipSchemaCheck=true.`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const externalHash = manifest.dbSha256;
|
|
129
|
+
const externalProject = options.alias
|
|
130
|
+
?? manifest.source.gitBranch
|
|
131
|
+
?? path.basename(bundlePath, path.extname(bundlePath));
|
|
132
|
+
const externalVersion = manifest.source.gitHead ?? null;
|
|
133
|
+
|
|
134
|
+
const absBundlePath = path.resolve(bundlePath);
|
|
135
|
+
|
|
136
|
+
// Idempotency check: same path + same hash → no-op.
|
|
137
|
+
const existing = localStore.findExternalBundleByPath(absBundlePath);
|
|
138
|
+
if (existing && existing.externalHash === externalHash && !options.force) {
|
|
139
|
+
log(`already imported with the same hash; skipping`);
|
|
140
|
+
return {
|
|
141
|
+
bundlePath: absBundlePath,
|
|
142
|
+
bundleId: existing.id,
|
|
143
|
+
externalProject: existing.externalProject,
|
|
144
|
+
externalHash,
|
|
145
|
+
schemaVersion: manifest.schemaVersion,
|
|
146
|
+
alreadyImported: true,
|
|
147
|
+
routesImported: 0,
|
|
148
|
+
serviceEndpointsImported: 0,
|
|
149
|
+
elapsedMs: Date.now() - start,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Decompress the embedded DB so we can read its tables.
|
|
154
|
+
const compressed = fileBuf.slice(manifestEnd);
|
|
155
|
+
const dbBuf = zlib.gunzipSync(compressed);
|
|
156
|
+
|
|
157
|
+
// Integrity check: the decompressed payload must match the manifest's
|
|
158
|
+
// sha256 + byte length. A tampered or truncated bundle is rejected here.
|
|
159
|
+
if (!options.skipIntegrityCheck) {
|
|
160
|
+
const dbSha = crypto.createHash('sha256').update(dbBuf).digest('hex');
|
|
161
|
+
if (dbSha !== manifest.dbSha256) {
|
|
162
|
+
throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbSha256}, got ${dbSha}.`);
|
|
163
|
+
}
|
|
164
|
+
if (typeof manifest.dbBytes === 'number' && dbBuf.length !== manifest.dbBytes) {
|
|
165
|
+
throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbBytes} bytes, got ${dbBuf.length}.`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seer-extbundle-'));
|
|
170
|
+
const externalDbPath = path.join(tmpDir, 'external.db');
|
|
171
|
+
fs.writeFileSync(externalDbPath, dbBuf);
|
|
172
|
+
|
|
173
|
+
let bundleId = 0;
|
|
174
|
+
let routesImported = 0;
|
|
175
|
+
let serviceEndpointsImported = 0;
|
|
176
|
+
try {
|
|
177
|
+
const externalStore = Store.openReadOnly(externalDbPath);
|
|
178
|
+
try {
|
|
179
|
+
// If we already had this path but with a different hash, wipe the
|
|
180
|
+
// previous layer first so the new import is a clean replace.
|
|
181
|
+
if (existing) {
|
|
182
|
+
const wiped = localStore.clearExternalBundle(existing.id);
|
|
183
|
+
log(`re-import: wiped previous layer (routes=${wiped.routes}, serviceCalls=${wiped.serviceCalls})`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Create the bundle row first so we have a stable id for the phantom
|
|
187
|
+
// file + the imported route rows.
|
|
188
|
+
bundleId = localStore.upsertExternalBundle({
|
|
189
|
+
bundlePath: absBundlePath,
|
|
190
|
+
externalProject,
|
|
191
|
+
externalVersion,
|
|
192
|
+
externalHash,
|
|
193
|
+
schemaVersion: manifest.schemaVersion,
|
|
194
|
+
routesImported: 0,
|
|
195
|
+
serviceCallsImported: 0,
|
|
196
|
+
serviceLinksImported: 0,
|
|
197
|
+
});
|
|
198
|
+
const externalFileId = localStore.ensureExternalFile(bundleId, externalProject);
|
|
199
|
+
|
|
200
|
+
// Import routes from the external bundle. We use the public listRoutes
|
|
201
|
+
// API which transparently handles pre-v9 column shapes (older bundles
|
|
202
|
+
// won't carry protocol/operation/etc.).
|
|
203
|
+
const localRoutes = externalStore.listRoutes({ limit: 1_000_000 });
|
|
204
|
+
// Filter: we never want external-bundle rows from a nested bundle. If
|
|
205
|
+
// the source bundle has external_bundle_id, skip those (they're
|
|
206
|
+
// someone else's external layer).
|
|
207
|
+
const importableRoutes = localRoutes.filter(r => {
|
|
208
|
+
// Cannot easily detect external_bundle_id via the public API; raw
|
|
209
|
+
// probe the column. listRoutes doesn't surface it today, so accept
|
|
210
|
+
// everything; the bundle generator should not have exported external
|
|
211
|
+
// rows itself (round-tripping external layers would be misleading).
|
|
212
|
+
return r.method && r.path;
|
|
213
|
+
});
|
|
214
|
+
log(`importing ${importableRoutes.length} routes from external bundle`);
|
|
215
|
+
localStore.begin();
|
|
216
|
+
try {
|
|
217
|
+
for (const r of importableRoutes) {
|
|
218
|
+
localStore.insertExternalRoute({
|
|
219
|
+
bundleId,
|
|
220
|
+
externalFileId,
|
|
221
|
+
method: r.method,
|
|
222
|
+
path: r.path,
|
|
223
|
+
framework: r.framework,
|
|
224
|
+
handlerName: r.handlerName,
|
|
225
|
+
line: r.line,
|
|
226
|
+
protocol: r.protocol ?? 'http',
|
|
227
|
+
operation: r.operation ?? null,
|
|
228
|
+
topic: r.topic ?? null,
|
|
229
|
+
queue: r.queue ?? null,
|
|
230
|
+
exchange: r.exchange ?? null,
|
|
231
|
+
service: r.service ?? null,
|
|
232
|
+
broker: r.broker ?? null,
|
|
233
|
+
metadataJson: r.metadataJson ?? null,
|
|
234
|
+
});
|
|
235
|
+
routesImported++;
|
|
236
|
+
}
|
|
237
|
+
localStore.commit();
|
|
238
|
+
} catch (err) {
|
|
239
|
+
localStore.rollback();
|
|
240
|
+
throw err;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Rebuild service links so the freshly-imported external routes are
|
|
244
|
+
// visible to the next query immediately, without waiting for a re-index.
|
|
245
|
+
let serviceLinksImported = 0;
|
|
246
|
+
if (options.rebuildServiceLinks !== false) {
|
|
247
|
+
try {
|
|
248
|
+
const sr = resolveServiceLinks(localStore);
|
|
249
|
+
serviceLinksImported = sr.linksInserted ?? 0;
|
|
250
|
+
log(`rebuilt service links (linked=${serviceLinksImported})`);
|
|
251
|
+
} catch (err) {
|
|
252
|
+
log(`service-link rebuild skipped: ${(err as Error).message}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Update final counts on the bundle row.
|
|
257
|
+
localStore.upsertExternalBundle({
|
|
258
|
+
bundlePath: absBundlePath,
|
|
259
|
+
externalProject,
|
|
260
|
+
externalVersion,
|
|
261
|
+
externalHash,
|
|
262
|
+
schemaVersion: manifest.schemaVersion,
|
|
263
|
+
routesImported,
|
|
264
|
+
serviceCallsImported: 0,
|
|
265
|
+
serviceLinksImported,
|
|
266
|
+
});
|
|
267
|
+
} finally {
|
|
268
|
+
externalStore.close();
|
|
269
|
+
}
|
|
270
|
+
} finally {
|
|
271
|
+
try { fs.unlinkSync(externalDbPath); fs.rmdirSync(tmpDir); } catch { /* */ }
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
bundlePath: absBundlePath,
|
|
276
|
+
bundleId,
|
|
277
|
+
externalProject,
|
|
278
|
+
externalHash,
|
|
279
|
+
schemaVersion: manifest.schemaVersion,
|
|
280
|
+
alreadyImported: false,
|
|
281
|
+
routesImported,
|
|
282
|
+
serviceEndpointsImported,
|
|
283
|
+
elapsedMs: Date.now() - start,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track-F portable .seer bundle format.
|
|
3
|
+
*
|
|
4
|
+
* A bundle is a single self-describing file that lets a team ship a fully
|
|
5
|
+
* built index without every developer having to re-run `seer index`. The
|
|
6
|
+
* format is deliberately tiny and dependency-free:
|
|
7
|
+
*
|
|
8
|
+
* [4 bytes: magic 'SEER']
|
|
9
|
+
* [4 bytes: bundle format version (uint32 BE)]
|
|
10
|
+
* [4 bytes: manifest length (uint32 BE)]
|
|
11
|
+
* [N bytes: manifest JSON (utf-8)]
|
|
12
|
+
* [M bytes: gzip-compressed SQLite DB]
|
|
13
|
+
*
|
|
14
|
+
* Why not tar/zip? Both add a dependency or a hand-rolled archive writer for
|
|
15
|
+
* a single payload file. Our payload IS the DB; everything else is a few
|
|
16
|
+
* structured fields. One header + one gzip stream gets us:
|
|
17
|
+
* - O(1) manifest read without decompressing the DB
|
|
18
|
+
* - cross-platform, no native deps
|
|
19
|
+
*
|
|
20
|
+
* On determinism: the gzip stream and the VACUUM INTO'd DB ARE deterministic
|
|
21
|
+
* for fixed input. The manifest, however, defaults `builtAt = Date.now()` so
|
|
22
|
+
* out-of-the-box exports of the same DB at different wall-clock times
|
|
23
|
+
* produce different bytes. If you need bit-identical bundles (build-cache
|
|
24
|
+
* keys, reproducible-builds CI), pass an explicit `builtAt` to `exportBundle`
|
|
25
|
+
* to pin it.
|
|
26
|
+
*
|
|
27
|
+
* The manifest is the source of truth for:
|
|
28
|
+
* - schema_version (so the consumer can refuse incompatible bundles)
|
|
29
|
+
* - source signature (repo root, git head, file count + hash) so the
|
|
30
|
+
* bundle's "shape" is auditable before the agent trusts it
|
|
31
|
+
* - tool versions, build time, SCIP layers contributing precision
|
|
32
|
+
*
|
|
33
|
+
* Bundles are produced by `seer bundle export` and consumed by
|
|
34
|
+
* `seer bundle import`. CI mode (`seer ci bundle`) is a thin wrapper that
|
|
35
|
+
* runs `index` then `bundle export` and surfaces the resulting path.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
export const BUNDLE_MAGIC = Buffer.from('SEER', 'utf-8');
|
|
39
|
+
export const BUNDLE_FORMAT_VERSION = 1;
|
|
40
|
+
|
|
41
|
+
export interface BundleManifest {
|
|
42
|
+
/**
|
|
43
|
+
* Bundle format version — bumped when the on-disk layout changes
|
|
44
|
+
* incompatibly. Distinct from the DB schema version, which can bump
|
|
45
|
+
* independently when only the SQL evolves.
|
|
46
|
+
*/
|
|
47
|
+
bundleFormatVersion: number;
|
|
48
|
+
/** Schema version of the embedded DB. Consumers refuse on mismatch. */
|
|
49
|
+
schemaVersion: number;
|
|
50
|
+
/** Wall-clock millis the bundle was built. */
|
|
51
|
+
builtAt: number;
|
|
52
|
+
/** Tool that produced the bundle. */
|
|
53
|
+
builtBy: string;
|
|
54
|
+
/** Source identity. */
|
|
55
|
+
source: {
|
|
56
|
+
repoRoot: string;
|
|
57
|
+
gitHead: string | null;
|
|
58
|
+
gitBranch: string | null;
|
|
59
|
+
/** Stable hash over the source file roster — `(relPath, fileHash)` pairs. */
|
|
60
|
+
rosterHash: string;
|
|
61
|
+
/** Number of source files indexed. */
|
|
62
|
+
fileCount: number;
|
|
63
|
+
};
|
|
64
|
+
/** Index summary so a consumer can size-check without unpacking. */
|
|
65
|
+
index: {
|
|
66
|
+
symbols: number;
|
|
67
|
+
edges: number;
|
|
68
|
+
resolvedEdges: number;
|
|
69
|
+
modules: number;
|
|
70
|
+
routes: number;
|
|
71
|
+
externalDependencies: number;
|
|
72
|
+
configKeys: number;
|
|
73
|
+
languages: Record<string, number>;
|
|
74
|
+
/** v7: provenance counts. */
|
|
75
|
+
provenance?: {
|
|
76
|
+
symbols: Record<string, number>;
|
|
77
|
+
edges: Record<string, number>;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
/** SCIP layers folded into the DB (if any). */
|
|
81
|
+
scipImports: Array<{
|
|
82
|
+
path: string; sha256: string; tool: string | null;
|
|
83
|
+
symbolCount: number; refCount: number;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* Sha-256 of the embedded DB (computed BEFORE compression). Lets the
|
|
87
|
+
* consumer verify a transferred bundle without trusting the gzip CRC alone.
|
|
88
|
+
*/
|
|
89
|
+
dbSha256: string;
|
|
90
|
+
/** Uncompressed DB size in bytes. */
|
|
91
|
+
dbBytes: number;
|
|
92
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import zlib from 'zlib';
|
|
5
|
+
import {
|
|
6
|
+
BUNDLE_MAGIC, BUNDLE_FORMAT_VERSION, BundleManifest,
|
|
7
|
+
} from './format.js';
|
|
8
|
+
import { CURRENT_SCHEMA_VERSION } from '../db/schema.js';
|
|
9
|
+
|
|
10
|
+
export interface ImportOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Where to land the extracted DB. Defaults to `<repoRoot>/.seer/graph.db`.
|
|
13
|
+
* If the path already exists and `overwrite` is false (the default), the
|
|
14
|
+
* import fails — bundles are meant to bootstrap a missing index, not to
|
|
15
|
+
* silently clobber a fresher local one.
|
|
16
|
+
*/
|
|
17
|
+
dbOut?: string;
|
|
18
|
+
overwrite?: boolean;
|
|
19
|
+
/** Logger; defaults to no-op. */
|
|
20
|
+
log?: (msg: string) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Skip the dbSha256 check. Off by default — the integrity guarantee is the
|
|
23
|
+
* whole point of the manifest. Use this only for forensic inspection of a
|
|
24
|
+
* corrupted bundle whose header still parses.
|
|
25
|
+
*/
|
|
26
|
+
skipIntegrityCheck?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Skip the schema-version compatibility check. Off by default — bundles
|
|
29
|
+
* whose schemaVersion is newer than this build's CURRENT_SCHEMA_VERSION
|
|
30
|
+
* are rejected because we can't safely open them. Override only when you
|
|
31
|
+
* KNOW the schema is compatible (e.g. forensics or a controlled upgrade).
|
|
32
|
+
*/
|
|
33
|
+
skipSchemaCheck?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Required source repo root for the imported index. Used to decide the
|
|
36
|
+
* default dbOut location only — the bundle's manifest.source.repoRoot is
|
|
37
|
+
* stored as metadata but never enforced (bundles are commonly built on
|
|
38
|
+
* a CI runner with a different absolute path).
|
|
39
|
+
*/
|
|
40
|
+
repoRoot: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ImportResult {
|
|
44
|
+
bundlePath: string;
|
|
45
|
+
dbPath: string;
|
|
46
|
+
manifest: BundleManifest;
|
|
47
|
+
bytes: number;
|
|
48
|
+
elapsedMs: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Read just the manifest from a bundle, without unpacking the DB. */
|
|
52
|
+
export function readBundleManifest(bundlePath: string): BundleManifest {
|
|
53
|
+
const fd = fs.openSync(bundlePath, 'r');
|
|
54
|
+
try {
|
|
55
|
+
const header = Buffer.alloc(12);
|
|
56
|
+
const headerRead = fs.readSync(fd, header, 0, 12, 0);
|
|
57
|
+
if (headerRead < 12 || !header.subarray(0, 4).equals(BUNDLE_MAGIC)) {
|
|
58
|
+
throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
|
|
59
|
+
}
|
|
60
|
+
const formatVersion = header.readUInt32BE(4);
|
|
61
|
+
if (formatVersion > BUNDLE_FORMAT_VERSION) {
|
|
62
|
+
throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
|
|
63
|
+
}
|
|
64
|
+
const manifestLen = header.readUInt32BE(8);
|
|
65
|
+
if (manifestLen <= 0 || manifestLen > 64 * 1024 * 1024) {
|
|
66
|
+
throw new Error(`Bundle manifest length out of range: ${manifestLen}`);
|
|
67
|
+
}
|
|
68
|
+
const manifestBuf = Buffer.alloc(manifestLen);
|
|
69
|
+
const read = fs.readSync(fd, manifestBuf, 0, manifestLen, 12);
|
|
70
|
+
if (read !== manifestLen) {
|
|
71
|
+
throw new Error(`Bundle truncated reading manifest`);
|
|
72
|
+
}
|
|
73
|
+
return JSON.parse(manifestBuf.toString('utf-8')) as BundleManifest;
|
|
74
|
+
} finally {
|
|
75
|
+
fs.closeSync(fd);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Restore a bundle into the workspace. The default destination is
|
|
81
|
+
* `<repoRoot>/.seer/graph.db`. Verifies the manifest, format version, and
|
|
82
|
+
* (unless skipped) the DB sha256.
|
|
83
|
+
*/
|
|
84
|
+
export async function importBundle(
|
|
85
|
+
bundlePath: string, options: ImportOptions,
|
|
86
|
+
): Promise<ImportResult> {
|
|
87
|
+
const start = Date.now();
|
|
88
|
+
const log = options.log ?? (() => { /* */ });
|
|
89
|
+
|
|
90
|
+
if (!fs.existsSync(bundlePath)) {
|
|
91
|
+
throw new Error(`No bundle at ${bundlePath}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fileBuf = fs.readFileSync(bundlePath);
|
|
95
|
+
if (fileBuf.length < 12 || !fileBuf.subarray(0, 4).equals(BUNDLE_MAGIC)) {
|
|
96
|
+
throw new Error(`Not a Seer bundle: ${bundlePath} (bad magic)`);
|
|
97
|
+
}
|
|
98
|
+
const formatVersion = fileBuf.readUInt32BE(4);
|
|
99
|
+
if (formatVersion > BUNDLE_FORMAT_VERSION) {
|
|
100
|
+
throw new Error(`Bundle format v${formatVersion} is newer than this build (v${BUNDLE_FORMAT_VERSION}). Upgrade Seer.`);
|
|
101
|
+
}
|
|
102
|
+
const manifestLen = fileBuf.readUInt32BE(8);
|
|
103
|
+
const manifestEnd = 12 + manifestLen;
|
|
104
|
+
if (manifestEnd > fileBuf.length) {
|
|
105
|
+
throw new Error(`Bundle truncated; manifest length ${manifestLen} exceeds file size`);
|
|
106
|
+
}
|
|
107
|
+
const manifest = JSON.parse(fileBuf.slice(12, manifestEnd).toString('utf-8')) as BundleManifest;
|
|
108
|
+
log(`Manifest: schemaVersion=${manifest.schemaVersion} symbols=${manifest.index.symbols} edges=${manifest.index.edges} builtAt=${new Date(manifest.builtAt).toISOString()}`);
|
|
109
|
+
|
|
110
|
+
// Schema compatibility check. The bundle's embedded DB was built against a
|
|
111
|
+
// specific schema version; if it's NEWER than this build of Seer, the DB
|
|
112
|
+
// contains tables/columns we don't understand and opening it would either
|
|
113
|
+
// crash on read or silently lose precision. Reject up front rather than
|
|
114
|
+
// crashing on first query. Older bundles are accepted because the Store's
|
|
115
|
+
// runMigrations() will catch the DB up at open time.
|
|
116
|
+
if (!options.skipSchemaCheck) {
|
|
117
|
+
if (manifest.schemaVersion > CURRENT_SCHEMA_VERSION) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Bundle schemaVersion=${manifest.schemaVersion} is newer than this build's ` +
|
|
120
|
+
`CURRENT_SCHEMA_VERSION=${CURRENT_SCHEMA_VERSION}. Upgrade Seer or pass skipSchemaCheck=true.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (manifest.schemaVersion <= 0) {
|
|
124
|
+
throw new Error(`Bundle has invalid schemaVersion=${manifest.schemaVersion}.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const compressed = fileBuf.slice(manifestEnd);
|
|
129
|
+
const dbBuf = zlib.gunzipSync(compressed);
|
|
130
|
+
|
|
131
|
+
if (!options.skipIntegrityCheck) {
|
|
132
|
+
const dbSha = crypto.createHash('sha256').update(dbBuf).digest('hex');
|
|
133
|
+
if (dbSha !== manifest.dbSha256) {
|
|
134
|
+
throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbSha256}, got ${dbSha}.`);
|
|
135
|
+
}
|
|
136
|
+
if (dbBuf.length !== manifest.dbBytes) {
|
|
137
|
+
throw new Error(`Bundle integrity check FAILED. Expected ${manifest.dbBytes} bytes, got ${dbBuf.length}.`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const dbOut = options.dbOut ?? path.join(options.repoRoot, '.seer', 'graph.db');
|
|
142
|
+
if (fs.existsSync(dbOut) && !options.overwrite) {
|
|
143
|
+
throw new Error(`Refusing to overwrite existing index at ${dbOut} — pass overwrite=true to replace it.`);
|
|
144
|
+
}
|
|
145
|
+
fs.mkdirSync(path.dirname(dbOut), { recursive: true });
|
|
146
|
+
fs.writeFileSync(dbOut, dbBuf);
|
|
147
|
+
// Drop the WAL/SHM siblings of the previous DB (if any) so the imported
|
|
148
|
+
// file isn't read against a stale journal.
|
|
149
|
+
try { fs.unlinkSync(dbOut + '-wal'); } catch { /* */ }
|
|
150
|
+
try { fs.unlinkSync(dbOut + '-shm'); } catch { /* */ }
|
|
151
|
+
log(`Wrote ${dbBuf.length} bytes to ${dbOut}`);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
bundlePath, dbPath: dbOut, manifest,
|
|
155
|
+
bytes: fileBuf.length, elapsedMs: Date.now() - start,
|
|
156
|
+
};
|
|
157
|
+
}
|