@omgbase/core 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/LICENSE +21 -0
- package/dist/src/cli/eval-matcher.d.ts +13 -0
- package/dist/src/cli/eval-matcher.js +36 -0
- package/dist/src/core/attach.d.ts +5 -0
- package/dist/src/core/attach.js +38 -0
- package/dist/src/core/cursor.d.ts +11 -0
- package/dist/src/core/cursor.js +35 -0
- package/dist/src/core/hash.d.ts +23 -0
- package/dist/src/core/hash.js +82 -0
- package/dist/src/core/ids.d.ts +6 -0
- package/dist/src/core/ids.js +26 -0
- package/dist/src/core/index.d.ts +1 -0
- package/dist/src/core/index.js +2 -0
- package/dist/src/core/ingest.d.ts +43 -0
- package/dist/src/core/ingest.js +287 -0
- package/dist/src/core/order-key.d.ts +8 -0
- package/dist/src/core/order-key.js +72 -0
- package/dist/src/core/parse/parse.d.ts +3 -0
- package/dist/src/core/parse/parse.js +104 -0
- package/dist/src/core/parse/render.d.ts +3 -0
- package/dist/src/core/parse/render.js +20 -0
- package/dist/src/core/parse/tree.d.ts +8 -0
- package/dist/src/core/parse/tree.js +43 -0
- package/dist/src/core/parse/types.d.ts +52 -0
- package/dist/src/core/parse/types.js +9 -0
- package/dist/src/core/read/document.d.ts +107 -0
- package/dist/src/core/read/document.js +191 -0
- package/dist/src/core/read/nodes.d.ts +47 -0
- package/dist/src/core/read/nodes.js +102 -0
- package/dist/src/core/read/outline.d.ts +12 -0
- package/dist/src/core/read/outline.js +76 -0
- package/dist/src/core/read/reader.d.ts +125 -0
- package/dist/src/core/read/reader.js +206 -0
- package/dist/src/core/read/refs.d.ts +15 -0
- package/dist/src/core/read/refs.js +51 -0
- package/dist/src/core/store/edges.d.ts +26 -0
- package/dist/src/core/store/edges.js +101 -0
- package/dist/src/core/store/fts.d.ts +10 -0
- package/dist/src/core/store/fts.js +22 -0
- package/dist/src/core/store/gc.d.ts +18 -0
- package/dist/src/core/store/gc.js +69 -0
- package/dist/src/core/store/nodes.d.ts +5 -0
- package/dist/src/core/store/nodes.js +57 -0
- package/dist/src/core/store/properties.d.ts +38 -0
- package/dist/src/core/store/properties.js +177 -0
- package/dist/src/core/store/rebuild.d.ts +3 -0
- package/dist/src/core/store/rebuild.js +29 -0
- package/dist/src/core/store/schema.d.ts +9 -0
- package/dist/src/core/store/schema.js +386 -0
- package/dist/src/core/store/sections.d.ts +2 -0
- package/dist/src/core/store/sections.js +29 -0
- package/dist/src/core/store/store.d.ts +24 -0
- package/dist/src/core/store/store.js +164 -0
- package/dist/src/core/store/writers.d.ts +52 -0
- package/dist/src/core/store/writers.js +89 -0
- package/dist/src/core/vec.d.ts +10 -0
- package/dist/src/core/vec.js +33 -0
- package/dist/src/format/adapter.d.ts +97 -0
- package/dist/src/format/adapter.js +14 -0
- package/dist/src/format/index.d.ts +6 -0
- package/dist/src/format/index.js +15 -0
- package/dist/src/format/json.d.ts +3 -0
- package/dist/src/format/json.js +324 -0
- package/dist/src/format/markdown.d.ts +5 -0
- package/dist/src/format/markdown.js +217 -0
- package/dist/src/format/registry.d.ts +6 -0
- package/dist/src/format/registry.js +24 -0
- package/dist/src/format/yaml.d.ts +5 -0
- package/dist/src/format/yaml.js +413 -0
- package/dist/src/graph/extract.d.ts +38 -0
- package/dist/src/graph/extract.js +242 -0
- package/dist/src/graph/history.d.ts +103 -0
- package/dist/src/graph/history.js +210 -0
- package/dist/src/graph/inbound-links.d.ts +33 -0
- package/dist/src/graph/inbound-links.js +130 -0
- package/dist/src/graph/link-destinations.d.ts +22 -0
- package/dist/src/graph/link-destinations.js +84 -0
- package/dist/src/graph/link-health.d.ts +79 -0
- package/dist/src/graph/link-health.js +146 -0
- package/dist/src/graph/links.d.ts +23 -0
- package/dist/src/graph/links.js +42 -0
- package/dist/src/index.d.ts +60 -0
- package/dist/src/index.js +70 -0
- package/dist/src/mcp/errors.d.ts +17 -0
- package/dist/src/mcp/errors.js +18 -0
- package/dist/src/mcp/graph.d.ts +63 -0
- package/dist/src/mcp/graph.js +212 -0
- package/dist/src/mcp/reference.d.ts +1 -0
- package/dist/src/mcp/reference.js +227 -0
- package/dist/src/mcp/server.d.ts +30 -0
- package/dist/src/mcp/server.js +1076 -0
- package/dist/src/mcp/stdio.d.ts +13 -0
- package/dist/src/mcp/stdio.js +24 -0
- package/dist/src/migrate/mrplex.d.ts +27 -0
- package/dist/src/migrate/mrplex.js +29 -0
- package/dist/src/mutate/apply.d.ts +83 -0
- package/dist/src/mutate/apply.js +302 -0
- package/dist/src/mutate/doc-store.d.ts +49 -0
- package/dist/src/mutate/doc-store.js +71 -0
- package/dist/src/mutate/docs.d.ts +60 -0
- package/dist/src/mutate/docs.js +232 -0
- package/dist/src/mutate/known-ids.d.ts +12 -0
- package/dist/src/mutate/known-ids.js +154 -0
- package/dist/src/mutate/load.d.ts +3 -0
- package/dist/src/mutate/load.js +66 -0
- package/dist/src/mutate/lower.d.ts +32 -0
- package/dist/src/mutate/lower.js +232 -0
- package/dist/src/mutate/macros.d.ts +91 -0
- package/dist/src/mutate/macros.js +230 -0
- package/dist/src/mutate/ops.d.ts +40 -0
- package/dist/src/mutate/ops.js +513 -0
- package/dist/src/mutate/opset.d.ts +92 -0
- package/dist/src/mutate/opset.js +79 -0
- package/dist/src/mutate/plan-update.d.ts +51 -0
- package/dist/src/mutate/plan-update.js +187 -0
- package/dist/src/mutate/tree.d.ts +46 -0
- package/dist/src/mutate/tree.js +167 -0
- package/dist/src/oqx/run.d.ts +1 -0
- package/dist/src/oqx/run.js +6 -0
- package/dist/src/oqx-js/context.d.ts +23 -0
- package/dist/src/oqx-js/context.js +564 -0
- package/dist/src/oqx-js/planner.d.ts +10 -0
- package/dist/src/oqx-js/planner.js +91 -0
- package/dist/src/oqx-js/run.d.ts +41 -0
- package/dist/src/oqx-js/run.js +332 -0
- package/dist/src/oqx-js/sql/translate.d.ts +27 -0
- package/dist/src/oqx-js/sql/translate.js +290 -0
- package/dist/src/reconcile/crossdoc.d.ts +34 -0
- package/dist/src/reconcile/crossdoc.js +76 -0
- package/dist/src/reconcile/eval/generator.d.ts +21 -0
- package/dist/src/reconcile/eval/generator.js +129 -0
- package/dist/src/reconcile/eval/metrics.d.ts +32 -0
- package/dist/src/reconcile/eval/metrics.js +74 -0
- package/dist/src/reconcile/flatten.d.ts +12 -0
- package/dist/src/reconcile/flatten.js +37 -0
- package/dist/src/reconcile/phase5.d.ts +8 -0
- package/dist/src/reconcile/phase5.js +167 -0
- package/dist/src/reconcile/phase6a.d.ts +2 -0
- package/dist/src/reconcile/phase6a.js +159 -0
- package/dist/src/reconcile/phases.d.ts +28 -0
- package/dist/src/reconcile/phases.js +0 -0
- package/dist/src/reconcile/reconcile.d.ts +21 -0
- package/dist/src/reconcile/reconcile.js +115 -0
- package/dist/src/reconcile/similarity.d.ts +7 -0
- package/dist/src/reconcile/similarity.js +41 -0
- package/dist/src/reconcile/types.d.ts +46 -0
- package/dist/src/reconcile/types.js +19 -0
- package/dist/src/search/cel/ast.d.ts +74 -0
- package/dist/src/search/cel/ast.js +3 -0
- package/dist/src/search/cel/compile.d.ts +60 -0
- package/dist/src/search/cel/compile.js +836 -0
- package/dist/src/search/cel/lexer.d.ts +9 -0
- package/dist/src/search/cel/lexer.js +139 -0
- package/dist/src/search/cel/parser.d.ts +7 -0
- package/dist/src/search/cel/parser.js +219 -0
- package/dist/src/search/drain.d.ts +39 -0
- package/dist/src/search/drain.js +97 -0
- package/dist/src/search/embeddings.d.ts +131 -0
- package/dist/src/search/embeddings.js +263 -0
- package/dist/src/search/external.d.ts +13 -0
- package/dist/src/search/external.js +177 -0
- package/dist/src/search/fts-query.d.ts +1 -0
- package/dist/src/search/fts-query.js +64 -0
- package/dist/src/search/provider.d.ts +17 -0
- package/dist/src/search/provider.js +13 -0
- package/dist/src/search/resolve.d.ts +18 -0
- package/dist/src/search/resolve.js +23 -0
- package/dist/src/search/rrf.d.ts +36 -0
- package/dist/src/search/rrf.js +84 -0
- package/dist/src/search/tasks.d.ts +12 -0
- package/dist/src/search/tasks.js +131 -0
- package/dist/src/search/text.d.ts +16 -0
- package/dist/src/search/text.js +21 -0
- package/dist/src/search/vector.d.ts +18 -0
- package/dist/src/search/vector.js +49 -0
- package/dist/src/sync/admin.d.ts +59 -0
- package/dist/src/sync/admin.js +53 -0
- package/dist/src/sync/attach.d.ts +13 -0
- package/dist/src/sync/attach.js +29 -0
- package/dist/src/sync/checkpoint.d.ts +21 -0
- package/dist/src/sync/checkpoint.js +52 -0
- package/dist/src/sync/driver.d.ts +25 -0
- package/dist/src/sync/driver.js +80 -0
- package/dist/src/sync/external-source.d.ts +14 -0
- package/dist/src/sync/external-source.js +137 -0
- package/dist/src/sync/freshness.d.ts +42 -0
- package/dist/src/sync/freshness.js +152 -0
- package/dist/src/sync/fs-util.d.ts +12 -0
- package/dist/src/sync/fs-util.js +55 -0
- package/dist/src/sync/git-heuristics.d.ts +6 -0
- package/dist/src/sync/git-heuristics.js +16 -0
- package/dist/src/sync/observe.d.ts +63 -0
- package/dist/src/sync/observe.js +84 -0
- package/dist/src/sync/plugin.d.ts +54 -0
- package/dist/src/sync/plugin.js +12 -0
- package/dist/src/sync/reconciling-ingest.d.ts +12 -0
- package/dist/src/sync/reconciling-ingest.js +209 -0
- package/dist/src/sync/recovery.d.ts +6 -0
- package/dist/src/sync/recovery.js +34 -0
- package/dist/src/sync/settings.d.ts +18 -0
- package/dist/src/sync/settings.js +58 -0
- package/dist/src/sync/sources.d.ts +37 -0
- package/dist/src/sync/sources.js +75 -0
- package/dist/src/sync/tombstone.d.ts +9 -0
- package/dist/src/sync/tombstone.js +55 -0
- package/dist/src/sync/watch-lease.d.ts +14 -0
- package/dist/src/sync/watch-lease.js +91 -0
- package/dist/src/sync/watcher.d.ts +19 -0
- package/dist/src/sync/watcher.js +34 -0
- package/dist/src/sync/workspace.d.ts +34 -0
- package/dist/src/sync/workspace.js +120 -0
- package/dist/src/sync/writer-lock.d.ts +16 -0
- package/dist/src/sync/writer-lock.js +121 -0
- package/package.json +80 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
// Full DDL — data-model §3 (durable) + §4 (derived). SQLite dialect.
|
|
2
|
+
// Kept as one string so migrations and rebuild-index can apply it verbatim.
|
|
3
|
+
// No dialect-specific SQL leaks above the store module (02 §8).
|
|
4
|
+
export const SCHEMA_VERSION = 13;
|
|
5
|
+
// file_stats (02 §4; derived, rebuildable by a full re-stat) backs the CLI
|
|
6
|
+
// freshness sweep (11 §3.3): (mtime_ns, size) cheap-change detection so a
|
|
7
|
+
// one-shot command re-ingests only files that changed on disk since last ingest.
|
|
8
|
+
export const FILE_STATS_DDL = /* sql */ `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS file_stats (
|
|
10
|
+
repo_id TEXT NOT NULL,
|
|
11
|
+
path TEXT NOT NULL,
|
|
12
|
+
mtime_ns INTEGER NOT NULL,
|
|
13
|
+
size INTEGER NOT NULL,
|
|
14
|
+
hash BLOB NOT NULL,
|
|
15
|
+
PRIMARY KEY (repo_id, path)
|
|
16
|
+
);
|
|
17
|
+
`;
|
|
18
|
+
// properties (properties-table). One indexed row per property value: the
|
|
19
|
+
// unified query surface for frontmatter, inline (key:: value), and computed
|
|
20
|
+
// ($title/$tags) document properties, superseding the docs.metadata JSON
|
|
21
|
+
// blob. `card` records the authored shape (scalar vs list) so scalar ==/!=/<
|
|
22
|
+
// match only scalar-authored rows while list() sees all — reproducing the
|
|
23
|
+
// json_extract scalar-vs-array distinction. Current-state, maintained
|
|
24
|
+
// transactionally at ingest alongside blocks (repopulated by re-ingest, not by
|
|
25
|
+
// rebuild-index — see docs/properties-table.md §6).
|
|
26
|
+
export const PROPERTIES_DDL = /* sql */ `
|
|
27
|
+
CREATE TABLE IF NOT EXISTS properties (
|
|
28
|
+
prop_id TEXT PRIMARY KEY,
|
|
29
|
+
repo_id TEXT NOT NULL,
|
|
30
|
+
doc_id TEXT NOT NULL,
|
|
31
|
+
block_id TEXT, -- NULL = document-scoped (frontmatter, computed)
|
|
32
|
+
source TEXT NOT NULL CHECK (source IN ('frontmatter','inline','computed')),
|
|
33
|
+
key TEXT NOT NULL, -- dotted, flattened: "layer","meta.owner"; computed carry $: "$title"
|
|
34
|
+
card TEXT NOT NULL CHECK (card IN ('scalar','list')),
|
|
35
|
+
ord INTEGER NOT NULL DEFAULT 0,
|
|
36
|
+
val_text TEXT,
|
|
37
|
+
val_num REAL,
|
|
38
|
+
val_bool INTEGER,
|
|
39
|
+
val_json TEXT,
|
|
40
|
+
type TEXT NOT NULL CHECK (type IN ('string','number','bool','null','json')),
|
|
41
|
+
created_commit TEXT NOT NULL,
|
|
42
|
+
deleted_commit TEXT
|
|
43
|
+
);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_props_doc ON properties(doc_id) WHERE deleted_commit IS NULL;
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_props_key_text ON properties(repo_id, key, val_text) WHERE deleted_commit IS NULL;
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_props_key_num ON properties(repo_id, key, val_num) WHERE deleted_commit IS NULL;
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_props_src_key ON properties(repo_id, source, key) WHERE deleted_commit IS NULL;
|
|
48
|
+
`;
|
|
49
|
+
// Sync adapters/sources/attachments (sync-plugins §2). A workspace-level
|
|
50
|
+
// registry: adapters map a name → external command; sources name an adapter +
|
|
51
|
+
// config (rendered to flags at spawn); attachments join repo ⇄ source (m:n).
|
|
52
|
+
// sync_state holds engine-owned per-attachment change-tracking (revision per
|
|
53
|
+
// path, cursor) so a stateless adapter can be spawned fresh each run (§6).
|
|
54
|
+
export const SYNC_DDL = /* sql */ `
|
|
55
|
+
CREATE TABLE IF NOT EXISTS adapters (
|
|
56
|
+
name TEXT PRIMARY KEY, -- workspace-unique adapter name (e.g. "fs")
|
|
57
|
+
command TEXT NOT NULL, -- argv[0] to spawn (e.g. "omgbase-fs-adapter")
|
|
58
|
+
args TEXT NOT NULL DEFAULT '[]' -- fixed leading args (JSON string[])
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS sources (
|
|
62
|
+
source_id TEXT PRIMARY KEY,
|
|
63
|
+
name TEXT NOT NULL UNIQUE, -- workspace-unique source name
|
|
64
|
+
adapter TEXT NOT NULL REFERENCES adapters(name),
|
|
65
|
+
config TEXT NOT NULL DEFAULT '{}', -- JSON object → rendered to flags (13 §3.1)
|
|
66
|
+
env TEXT NOT NULL DEFAULT '{}' -- JSON object → spawn env (secrets; 13 §3.2)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
CREATE TABLE IF NOT EXISTS attachments (
|
|
70
|
+
repo_id TEXT NOT NULL REFERENCES repos(repo_id),
|
|
71
|
+
source_id TEXT NOT NULL REFERENCES sources(source_id),
|
|
72
|
+
PRIMARY KEY (repo_id, source_id)
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
76
|
+
repo_id TEXT NOT NULL,
|
|
77
|
+
source_id TEXT NOT NULL,
|
|
78
|
+
path TEXT NOT NULL, -- '' reserved for the attachment-level cursor row
|
|
79
|
+
revision TEXT, -- last-observed source revision for this path
|
|
80
|
+
cursor TEXT, -- last poll/webhook cursor (attachment-level row)
|
|
81
|
+
PRIMARY KEY (repo_id, source_id, path)
|
|
82
|
+
);
|
|
83
|
+
`;
|
|
84
|
+
// Workspace-default settings (config-scope). A singleton row (id = 0) holding a
|
|
85
|
+
// JSON blob with the SAME shape as repos.settings. It is the default layer: a
|
|
86
|
+
// repo's own settings deep-merge on top (repo overrides workspace at the leaf).
|
|
87
|
+
// There is no separate "global config" namespace — top-level concepts are the
|
|
88
|
+
// tables (repos/adapters/sources); omg config speaks only this settings shape,
|
|
89
|
+
// at the workspace (default) layer or a specific repo layer.
|
|
90
|
+
export const WORKSPACE_SETTINGS_DDL = /* sql */ `
|
|
91
|
+
CREATE TABLE IF NOT EXISTS workspace_settings (
|
|
92
|
+
id INTEGER PRIMARY KEY CHECK (id = 0),
|
|
93
|
+
settings TEXT NOT NULL DEFAULT '{}'
|
|
94
|
+
);
|
|
95
|
+
INSERT OR IGNORE INTO workspace_settings (id, settings) VALUES (0, '{}');
|
|
96
|
+
`;
|
|
97
|
+
export const NODES_DDL = /* sql */ `
|
|
98
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
99
|
+
node_id TEXT PRIMARY KEY,
|
|
100
|
+
repo_id TEXT NOT NULL,
|
|
101
|
+
doc_id TEXT NOT NULL,
|
|
102
|
+
block_id TEXT,
|
|
103
|
+
kind TEXT NOT NULL,
|
|
104
|
+
name TEXT,
|
|
105
|
+
value TEXT,
|
|
106
|
+
span_start INTEGER,
|
|
107
|
+
span_end INTEGER,
|
|
108
|
+
attrs TEXT NOT NULL DEFAULT '{}'
|
|
109
|
+
);
|
|
110
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_doc ON nodes(doc_id);
|
|
111
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
|
|
112
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name) WHERE name IS NOT NULL;
|
|
113
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
114
|
+
name, value, content='nodes', content_rowid='rowid', tokenize='porter unicode61'
|
|
115
|
+
);
|
|
116
|
+
`;
|
|
117
|
+
// doc_embeddings (doc-grain semantic retrieval). One vector per (doc_id, model)
|
|
118
|
+
// so `from=docs semantic` ranks whole documents by topical relevance rather than
|
|
119
|
+
// re-labelling block hits. `input_hash` is the freshness key: the sha256 of the
|
|
120
|
+
// exact bytes that produced the vector (whole-doc embed input, or the pooled
|
|
121
|
+
// fallback's composite of block hashes) — a stored row whose input_hash no
|
|
122
|
+
// longer matches the current document's is stale and re-embedded. `method`
|
|
123
|
+
// records which strategy produced it ('whole' = whole-document embedding;
|
|
124
|
+
// 'pooled' = token-weighted mean of block vectors, used only when the doc's
|
|
125
|
+
// embed input exceeds the provider's max input length). Derived/rebuildable
|
|
126
|
+
// like the block `embeddings` cache; keyed by doc_id (not content_hash) because
|
|
127
|
+
// a document has no single stable content hash across block edits.
|
|
128
|
+
export const DOC_EMBEDDINGS_DDL = /* sql */ `
|
|
129
|
+
CREATE TABLE IF NOT EXISTS doc_embeddings (
|
|
130
|
+
doc_id TEXT NOT NULL,
|
|
131
|
+
model TEXT NOT NULL,
|
|
132
|
+
input_hash BLOB NOT NULL,
|
|
133
|
+
method TEXT NOT NULL CHECK (method IN ('whole','pooled')),
|
|
134
|
+
dim INTEGER NOT NULL,
|
|
135
|
+
vec BLOB NOT NULL,
|
|
136
|
+
PRIMARY KEY (doc_id, model)
|
|
137
|
+
);
|
|
138
|
+
`;
|
|
139
|
+
// Additive migrations keyed by the version they upgrade TO. Each runs inside a
|
|
140
|
+
// transaction. Only forward, idempotent DDL (CREATE ... IF NOT EXISTS) — no
|
|
141
|
+
// destructive changes. store.ts applies these in order for an older db.
|
|
142
|
+
// Migrations 3, 4, 6, 7, 9, 11 are programmatic — see store.ts.
|
|
143
|
+
export const MIGRATIONS = {
|
|
144
|
+
2: FILE_STATS_DDL,
|
|
145
|
+
3: "", // handled programmatically in store.ts
|
|
146
|
+
4: "", // handled programmatically in store.ts
|
|
147
|
+
5: NODES_DDL,
|
|
148
|
+
6: "", // handled programmatically in store.ts
|
|
149
|
+
7: "", // handled programmatically in store.ts
|
|
150
|
+
8: PROPERTIES_DDL,
|
|
151
|
+
9: "", // SYNC_DDL + repos.root_path→nullable, handled programmatically in store.ts
|
|
152
|
+
10: WORKSPACE_SETTINGS_DDL,
|
|
153
|
+
11: "", // ALTER TABLE documents RENAME TO docs, handled programmatically in store.ts
|
|
154
|
+
12: DOC_EMBEDDINGS_DDL, // doc-grain semantic vectors
|
|
155
|
+
13: "", // drop repos.root_path (migrate → fs sources), handled programmatically in store.ts
|
|
156
|
+
};
|
|
157
|
+
export const DDL = /* sql */ `
|
|
158
|
+
-- ---- durable tables (02 §3) --------------------------------------------------
|
|
159
|
+
CREATE TABLE IF NOT EXISTS repos (
|
|
160
|
+
repo_id TEXT PRIMARY KEY,
|
|
161
|
+
slug TEXT NOT NULL UNIQUE,
|
|
162
|
+
settings TEXT NOT NULL DEFAULT '{}'
|
|
163
|
+
);
|
|
164
|
+
-- A repo owns identity + history, NOT a filesystem (ADR-014). Where its bytes
|
|
165
|
+
-- come from is a sources row (adapter=fs, config.root) joined via attachments;
|
|
166
|
+
-- the former repos.root_path column was removed in schema v13.
|
|
167
|
+
|
|
168
|
+
CREATE TABLE IF NOT EXISTS docs (
|
|
169
|
+
doc_id TEXT PRIMARY KEY,
|
|
170
|
+
repo_id TEXT NOT NULL REFERENCES repos(repo_id),
|
|
171
|
+
path TEXT NOT NULL,
|
|
172
|
+
format TEXT NOT NULL DEFAULT 'markdown',
|
|
173
|
+
current_rev TEXT,
|
|
174
|
+
file_hash BLOB,
|
|
175
|
+
conflicted INTEGER NOT NULL DEFAULT 0,
|
|
176
|
+
leading_trivia TEXT NOT NULL DEFAULT '',
|
|
177
|
+
frontmatter_trivia TEXT, -- separator between frontmatter and first body block (NULL = no frontmatter)
|
|
178
|
+
deleted_commit TEXT,
|
|
179
|
+
UNIQUE (repo_id, path)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
CREATE TABLE IF NOT EXISTS blocks (
|
|
183
|
+
block_id TEXT PRIMARY KEY,
|
|
184
|
+
repo_id TEXT NOT NULL REFERENCES repos(repo_id),
|
|
185
|
+
doc_id TEXT NOT NULL REFERENCES docs(doc_id),
|
|
186
|
+
parent_block TEXT,
|
|
187
|
+
order_key TEXT NOT NULL,
|
|
188
|
+
ordinal INTEGER NOT NULL,
|
|
189
|
+
depth INTEGER NOT NULL,
|
|
190
|
+
ancestor_path TEXT NOT NULL,
|
|
191
|
+
type TEXT NOT NULL,
|
|
192
|
+
attrs TEXT NOT NULL DEFAULT '{}',
|
|
193
|
+
text TEXT NOT NULL,
|
|
194
|
+
raw_hash BLOB NOT NULL,
|
|
195
|
+
norm_hash BLOB NOT NULL,
|
|
196
|
+
trivia_hash BLOB, -- trailing trivia blob hash (NULL = no trivia)
|
|
197
|
+
created_commit TEXT NOT NULL,
|
|
198
|
+
deleted_commit TEXT
|
|
199
|
+
);
|
|
200
|
+
CREATE INDEX IF NOT EXISTS idx_blocks_doc ON blocks(doc_id, parent_block, order_key) WHERE deleted_commit IS NULL;
|
|
201
|
+
CREATE INDEX IF NOT EXISTS idx_blocks_type ON blocks(repo_id, type) WHERE deleted_commit IS NULL;
|
|
202
|
+
CREATE INDEX IF NOT EXISTS idx_blocks_rawhash ON blocks(raw_hash);
|
|
203
|
+
CREATE INDEX IF NOT EXISTS idx_blocks_ancestor ON blocks(doc_id, ancestor_path);
|
|
204
|
+
|
|
205
|
+
CREATE TABLE IF NOT EXISTS blobs (
|
|
206
|
+
hash BLOB PRIMARY KEY,
|
|
207
|
+
bytes BLOB NOT NULL,
|
|
208
|
+
size INTEGER NOT NULL
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
CREATE TABLE IF NOT EXISTS tree_nodes (
|
|
212
|
+
hash BLOB PRIMARY KEY,
|
|
213
|
+
entries TEXT NOT NULL
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
CREATE TABLE IF NOT EXISTS revisions (
|
|
217
|
+
rev_id TEXT PRIMARY KEY,
|
|
218
|
+
doc_id TEXT NOT NULL REFERENCES docs(doc_id),
|
|
219
|
+
seq INTEGER NOT NULL,
|
|
220
|
+
root_tree BLOB NOT NULL REFERENCES tree_nodes(hash),
|
|
221
|
+
frontmatter_blob BLOB REFERENCES blobs(hash),
|
|
222
|
+
rendered_hash BLOB NOT NULL,
|
|
223
|
+
path TEXT NOT NULL,
|
|
224
|
+
commit_id TEXT NOT NULL REFERENCES commits(commit_id),
|
|
225
|
+
UNIQUE (doc_id, seq)
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
CREATE TABLE IF NOT EXISTS commits (
|
|
229
|
+
commit_id TEXT PRIMARY KEY,
|
|
230
|
+
repo_id TEXT NOT NULL REFERENCES repos(repo_id),
|
|
231
|
+
seq INTEGER NOT NULL,
|
|
232
|
+
ts TEXT NOT NULL,
|
|
233
|
+
origin TEXT NOT NULL CHECK (origin IN ('api','observed','import','projection')),
|
|
234
|
+
actor TEXT,
|
|
235
|
+
reason TEXT,
|
|
236
|
+
checkpoint_id TEXT,
|
|
237
|
+
ops TEXT,
|
|
238
|
+
UNIQUE (repo_id, seq)
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
CREATE TABLE IF NOT EXISTS dispositions (
|
|
242
|
+
commit_id TEXT NOT NULL REFERENCES commits(commit_id),
|
|
243
|
+
block_id TEXT NOT NULL,
|
|
244
|
+
kind TEXT NOT NULL CHECK (kind IN
|
|
245
|
+
('same','edited','moved','edited_moved','inserted','deleted',
|
|
246
|
+
'split_from','merged_into','copied_from','resurrected','bulk_rewrite')),
|
|
247
|
+
confidence REAL,
|
|
248
|
+
reason TEXT,
|
|
249
|
+
matcher_v TEXT,
|
|
250
|
+
detail TEXT NOT NULL DEFAULT '{}',
|
|
251
|
+
PRIMARY KEY (commit_id, block_id, kind)
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
255
|
+
edge_id TEXT PRIMARY KEY,
|
|
256
|
+
repo_id TEXT NOT NULL,
|
|
257
|
+
src_doc TEXT NOT NULL,
|
|
258
|
+
src_block TEXT,
|
|
259
|
+
src_field TEXT,
|
|
260
|
+
predicate TEXT NOT NULL,
|
|
261
|
+
dst_kind TEXT NOT NULL CHECK (dst_kind IN ('document','block','external','collection')),
|
|
262
|
+
dst_node TEXT NOT NULL,
|
|
263
|
+
anchor TEXT,
|
|
264
|
+
provenance TEXT NOT NULL CHECK (provenance IN ('link','frontmatter','inline_field','projected',
|
|
265
|
+
'yaml_ref','yaml_schema','yaml_extends','json_ref','json_schema')),
|
|
266
|
+
via_node TEXT,
|
|
267
|
+
from_commit TEXT NOT NULL,
|
|
268
|
+
to_commit TEXT
|
|
269
|
+
);
|
|
270
|
+
CREATE INDEX IF NOT EXISTS idx_edges_src ON edges(src_doc, predicate) WHERE to_commit IS NULL;
|
|
271
|
+
CREATE INDEX IF NOT EXISTS idx_edges_dst ON edges(dst_node, predicate) WHERE to_commit IS NULL;
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_edges_temporal ON edges(from_commit, to_commit);
|
|
273
|
+
|
|
274
|
+
CREATE TABLE IF NOT EXISTS external_nodes (
|
|
275
|
+
node_id TEXT PRIMARY KEY,
|
|
276
|
+
repo_id TEXT NOT NULL,
|
|
277
|
+
uri TEXT NOT NULL,
|
|
278
|
+
title TEXT,
|
|
279
|
+
UNIQUE (repo_id, uri)
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
CREATE TABLE IF NOT EXISTS collections (
|
|
283
|
+
node_id TEXT PRIMARY KEY,
|
|
284
|
+
repo_id TEXT NOT NULL,
|
|
285
|
+
name TEXT NOT NULL,
|
|
286
|
+
spec TEXT NOT NULL DEFAULT '{}'
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
CREATE TABLE IF NOT EXISTS checkpoints (
|
|
290
|
+
id TEXT PRIMARY KEY,
|
|
291
|
+
repo_id TEXT NOT NULL,
|
|
292
|
+
ts TEXT NOT NULL,
|
|
293
|
+
files TEXT NOT NULL,
|
|
294
|
+
git_head TEXT
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
CREATE TABLE IF NOT EXISTS resurrection_pool (
|
|
298
|
+
block_id TEXT PRIMARY KEY,
|
|
299
|
+
repo_id TEXT NOT NULL,
|
|
300
|
+
doc_id TEXT NOT NULL,
|
|
301
|
+
raw_hash BLOB NOT NULL,
|
|
302
|
+
norm_hash BLOB NOT NULL,
|
|
303
|
+
type TEXT NOT NULL,
|
|
304
|
+
deleted_commit TEXT NOT NULL,
|
|
305
|
+
expires_ts TEXT NOT NULL
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
-- ---- derived tables (02 §4; rebuildable) ------------------------------------
|
|
309
|
+
CREATE TABLE IF NOT EXISTS sections (
|
|
310
|
+
heading_block TEXT NOT NULL,
|
|
311
|
+
doc_id TEXT NOT NULL,
|
|
312
|
+
level INTEGER NOT NULL,
|
|
313
|
+
first_ordinal INTEGER NOT NULL,
|
|
314
|
+
last_ordinal INTEGER NOT NULL,
|
|
315
|
+
PRIMARY KEY (doc_id, heading_block)
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
CREATE TABLE IF NOT EXISTS doc_edges (
|
|
319
|
+
src_doc TEXT NOT NULL,
|
|
320
|
+
predicate TEXT NOT NULL,
|
|
321
|
+
dst_node TEXT NOT NULL,
|
|
322
|
+
dst_kind TEXT NOT NULL,
|
|
323
|
+
count INTEGER NOT NULL,
|
|
324
|
+
samples TEXT NOT NULL,
|
|
325
|
+
PRIMARY KEY (src_doc, predicate, dst_node)
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
CREATE TABLE IF NOT EXISTS block_changes (
|
|
329
|
+
block_id TEXT NOT NULL,
|
|
330
|
+
commit_id TEXT NOT NULL,
|
|
331
|
+
kind TEXT NOT NULL,
|
|
332
|
+
PRIMARY KEY (block_id, commit_id, kind)
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
CREATE TABLE IF NOT EXISTS inferred_edges (
|
|
336
|
+
src_node TEXT NOT NULL,
|
|
337
|
+
dst_node TEXT NOT NULL,
|
|
338
|
+
predicate TEXT NOT NULL,
|
|
339
|
+
method TEXT NOT NULL,
|
|
340
|
+
score REAL NOT NULL,
|
|
341
|
+
model_v TEXT NOT NULL,
|
|
342
|
+
computed_at TEXT NOT NULL,
|
|
343
|
+
PRIMARY KEY (src_node, dst_node, predicate, method)
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
347
|
+
content_hash BLOB NOT NULL,
|
|
348
|
+
ctx_hash BLOB NOT NULL,
|
|
349
|
+
model TEXT NOT NULL,
|
|
350
|
+
dim INTEGER NOT NULL,
|
|
351
|
+
vec BLOB NOT NULL,
|
|
352
|
+
PRIMARY KEY (content_hash, ctx_hash, model)
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
${DOC_EMBEDDINGS_DDL}
|
|
356
|
+
|
|
357
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS blocks_fts USING fts5(
|
|
358
|
+
text, content='blocks', content_rowid='rowid', tokenize='porter unicode61'
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
CREATE TABLE IF NOT EXISTS nodes (
|
|
362
|
+
node_id TEXT PRIMARY KEY,
|
|
363
|
+
repo_id TEXT NOT NULL,
|
|
364
|
+
doc_id TEXT NOT NULL,
|
|
365
|
+
block_id TEXT,
|
|
366
|
+
kind TEXT NOT NULL,
|
|
367
|
+
name TEXT,
|
|
368
|
+
value TEXT,
|
|
369
|
+
span_start INTEGER,
|
|
370
|
+
span_end INTEGER,
|
|
371
|
+
attrs TEXT NOT NULL DEFAULT '{}'
|
|
372
|
+
);
|
|
373
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_doc ON nodes(doc_id);
|
|
374
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_kind ON nodes(kind);
|
|
375
|
+
CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name) WHERE name IS NOT NULL;
|
|
376
|
+
|
|
377
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
|
|
378
|
+
name, value, content='nodes', content_rowid='rowid', tokenize='porter unicode61'
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
${FILE_STATS_DDL}
|
|
382
|
+
${PROPERTIES_DDL}
|
|
383
|
+
${SYNC_DDL}
|
|
384
|
+
${WORKSPACE_SETTINGS_DDL}
|
|
385
|
+
`;
|
|
386
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function rebuildSections(db, docId) {
|
|
2
|
+
db.prepare("DELETE FROM sections WHERE doc_id = ?").run(docId);
|
|
3
|
+
const tops = db
|
|
4
|
+
.prepare(`SELECT block_id, ordinal, type,
|
|
5
|
+
json_extract(attrs, '$.level') AS level
|
|
6
|
+
FROM blocks
|
|
7
|
+
WHERE doc_id = ? AND parent_block IS NULL AND deleted_commit IS NULL
|
|
8
|
+
ORDER BY ordinal`)
|
|
9
|
+
.all(docId);
|
|
10
|
+
const maxOrdinal = tops.length > 0 ? tops[tops.length - 1].ordinal : -1;
|
|
11
|
+
const headings = tops.filter((t) => t.type === "heading");
|
|
12
|
+
const insert = db.prepare(`INSERT INTO sections (heading_block, doc_id, level, first_ordinal, last_ordinal)
|
|
13
|
+
VALUES (?, ?, ?, ?, ?)`);
|
|
14
|
+
for (let i = 0; i < headings.length; i++) {
|
|
15
|
+
const h = headings[i];
|
|
16
|
+
const level = h.level ?? 1;
|
|
17
|
+
// last_ordinal = one before the next heading with level <= this one.
|
|
18
|
+
let last = maxOrdinal;
|
|
19
|
+
for (let j = i + 1; j < headings.length; j++) {
|
|
20
|
+
const next = headings[j];
|
|
21
|
+
if ((next.level ?? 1) <= level) {
|
|
22
|
+
last = next.ordinal - 1;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
insert.run(h.block_id, docId, level, h.ordinal, last);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=sections.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
export interface StoreOptions {
|
|
3
|
+
/** ':memory:' for tests, or a filesystem path (parent dirs created). */
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class Store {
|
|
7
|
+
readonly db: Database.Database;
|
|
8
|
+
constructor(opts: StoreOptions);
|
|
9
|
+
private migrate;
|
|
10
|
+
private migrateV3;
|
|
11
|
+
private migrateV4;
|
|
12
|
+
private migrateV6;
|
|
13
|
+
private migrateV7;
|
|
14
|
+
private migrateV9;
|
|
15
|
+
private migrateV11;
|
|
16
|
+
private migrateV13;
|
|
17
|
+
/**
|
|
18
|
+
* Run fn inside a single write transaction (02 §2: one transaction per
|
|
19
|
+
* commit). better-sqlite3 transactions are synchronous and serialized.
|
|
20
|
+
*/
|
|
21
|
+
write<T>(fn: (db: Database.Database) => T): T;
|
|
22
|
+
pragma(name: string): unknown;
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { DDL, SCHEMA_VERSION, MIGRATIONS, SYNC_DDL } from "./schema.js";
|
|
5
|
+
import { cosineBytes } from "../vec.js";
|
|
6
|
+
import { mintId } from "../ids.js";
|
|
7
|
+
export class Store {
|
|
8
|
+
db;
|
|
9
|
+
constructor(opts) {
|
|
10
|
+
if (opts.path !== ":memory:")
|
|
11
|
+
mkdirSync(dirname(opts.path), { recursive: true });
|
|
12
|
+
this.db = new Database(opts.path);
|
|
13
|
+
this.db.pragma("journal_mode = WAL");
|
|
14
|
+
this.db.pragma("synchronous = NORMAL");
|
|
15
|
+
this.db.pragma("foreign_keys = ON");
|
|
16
|
+
// cosine(vecBlob, queryBlob) → REAL: the similarity used by OQX's semantic()
|
|
17
|
+
// scalar. Deterministic (same inputs → same score) so SQLite may cache it.
|
|
18
|
+
this.db.function("cosine", { deterministic: true }, (a, b) => cosineBytes(a, b));
|
|
19
|
+
this.migrate();
|
|
20
|
+
}
|
|
21
|
+
migrate() {
|
|
22
|
+
const current = this.db.pragma("user_version", { simple: true });
|
|
23
|
+
if (current === 0) {
|
|
24
|
+
this.db.exec(DDL);
|
|
25
|
+
this.db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (current === SCHEMA_VERSION)
|
|
29
|
+
return;
|
|
30
|
+
if (current > SCHEMA_VERSION) {
|
|
31
|
+
throw new Error(`database schema (v${current}) is newer than this build (v${SCHEMA_VERSION}); upgrade omgbase`);
|
|
32
|
+
}
|
|
33
|
+
// Apply forward additive migrations in order (each idempotent DDL).
|
|
34
|
+
for (let v = current + 1; v <= SCHEMA_VERSION; v++) {
|
|
35
|
+
if (v === 3) {
|
|
36
|
+
this.migrateV3();
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (v === 4) {
|
|
40
|
+
this.migrateV4();
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (v === 6) {
|
|
44
|
+
this.migrateV6();
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (v === 7) {
|
|
48
|
+
this.migrateV7();
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (v === 9) {
|
|
52
|
+
this.migrateV9();
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (v === 11) {
|
|
56
|
+
this.migrateV11();
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (v === 13) {
|
|
60
|
+
this.migrateV13();
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const ddl = MIGRATIONS[v];
|
|
64
|
+
if (!ddl)
|
|
65
|
+
throw new Error(`no migration to schema v${v}`);
|
|
66
|
+
this.db.exec(ddl);
|
|
67
|
+
}
|
|
68
|
+
this.db.pragma(`user_version = ${SCHEMA_VERSION}`);
|
|
69
|
+
}
|
|
70
|
+
migrateV3() {
|
|
71
|
+
const cols = this.db.pragma("table_info(documents)");
|
|
72
|
+
if (cols.length > 0 && !cols.some((c) => c.name === "format")) {
|
|
73
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN format TEXT NOT NULL DEFAULT 'markdown'");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
migrateV4() {
|
|
77
|
+
const cols = this.db.pragma("table_info(documents)");
|
|
78
|
+
if (cols.length > 0 && cols.some((c) => c.name === "frontmatter") && !cols.some((c) => c.name === "metadata")) {
|
|
79
|
+
this.db.exec("ALTER TABLE documents RENAME COLUMN frontmatter TO metadata");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
migrateV6() {
|
|
83
|
+
const docCols = this.db.pragma("table_info(documents)");
|
|
84
|
+
if (docCols.length > 0 && !docCols.some((c) => c.name === "leading_trivia")) {
|
|
85
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN leading_trivia TEXT NOT NULL DEFAULT ''");
|
|
86
|
+
}
|
|
87
|
+
const blockCols = this.db.pragma("table_info(blocks)");
|
|
88
|
+
if (blockCols.length > 0 && !blockCols.some((c) => c.name === "trivia_hash")) {
|
|
89
|
+
this.db.exec("ALTER TABLE blocks ADD COLUMN trivia_hash BLOB");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
migrateV7() {
|
|
93
|
+
const docCols = this.db.pragma("table_info(documents)");
|
|
94
|
+
if (docCols.length > 0 && !docCols.some((c) => c.name === "frontmatter_trivia")) {
|
|
95
|
+
this.db.exec("ALTER TABLE documents ADD COLUMN frontmatter_trivia TEXT");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// v9 (sync-plugins): add the adapters/sources/attachments/sync_state tables.
|
|
99
|
+
// repos.root_path is relaxed to nullable in the base DDL for fresh dbs; an
|
|
100
|
+
// existing NOT NULL column still accepts all prior (non-null) rows, so no table
|
|
101
|
+
// rebuild is needed to migrate — new nullable inserts only happen on fresh dbs.
|
|
102
|
+
migrateV9() {
|
|
103
|
+
this.db.exec(SYNC_DDL);
|
|
104
|
+
}
|
|
105
|
+
// v11: rename the `documents` table to `docs`. SQLite (>= 3.25, legacy_alter_table
|
|
106
|
+
// off by default) rewrites the FK references in blocks/revisions automatically,
|
|
107
|
+
// so no child-table rebuild is needed. Idempotent: skip if `docs` already exists
|
|
108
|
+
// (fresh dbs get `docs` straight from the DDL and never run this).
|
|
109
|
+
migrateV11() {
|
|
110
|
+
const tableExists = (name) => !!this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
111
|
+
// Fresh dbs already have `docs` from the DDL; a `documents` table only exists
|
|
112
|
+
// on real pre-v11 dbs. Rename only when the old table is present and the new
|
|
113
|
+
// one is not — otherwise there's nothing to do.
|
|
114
|
+
if (tableExists("docs") || !tableExists("documents"))
|
|
115
|
+
return;
|
|
116
|
+
this.db.exec("ALTER TABLE documents RENAME TO docs");
|
|
117
|
+
}
|
|
118
|
+
// v13 (ADR-014): a repo owns identity, not a filesystem. Drop repos.root_path,
|
|
119
|
+
// migrating each repo's former root_path into an `fs` source + attachment (the
|
|
120
|
+
// durable "where its bytes come from"). Idempotent: skip if the column is gone
|
|
121
|
+
// (fresh dbs get the columnless repos table straight from the DDL). Runs in one
|
|
122
|
+
// transaction; DROP COLUMN needs modern SQLite (>= 3.35, bundled by
|
|
123
|
+
// better-sqlite3), and root_path is a plain unindexed column so it drops cleanly.
|
|
124
|
+
migrateV13() {
|
|
125
|
+
const cols = this.db.pragma("table_info(repos)");
|
|
126
|
+
if (cols.length === 0 || !cols.some((c) => c.name === "root_path"))
|
|
127
|
+
return;
|
|
128
|
+
this.db.transaction(() => {
|
|
129
|
+
// The fs adapter row backs the migrated sources (sources.adapter FK).
|
|
130
|
+
this.db.exec("INSERT OR IGNORE INTO adapters (name, command, args) VALUES ('fs', 'omgbase-fs-adapter', '[]')");
|
|
131
|
+
const repos = this.db
|
|
132
|
+
.prepare("SELECT repo_id, slug, root_path FROM repos WHERE root_path IS NOT NULL AND root_path != ''")
|
|
133
|
+
.all();
|
|
134
|
+
const findByName = this.db.prepare("SELECT source_id FROM sources WHERE name = ?");
|
|
135
|
+
const insSource = this.db.prepare("INSERT INTO sources (source_id, name, adapter, config, env) VALUES (?, ?, 'fs', ?, '{}')");
|
|
136
|
+
const insAttach = this.db.prepare("INSERT OR IGNORE INTO attachments (repo_id, source_id) VALUES (?, ?)");
|
|
137
|
+
for (const r of repos) {
|
|
138
|
+
const name = `${r.slug}-fs`;
|
|
139
|
+
const existing = findByName.get(name);
|
|
140
|
+
let sourceId = existing?.source_id;
|
|
141
|
+
if (!sourceId) {
|
|
142
|
+
sourceId = mintId("src");
|
|
143
|
+
insSource.run(sourceId, name, JSON.stringify({ root: r.root_path }));
|
|
144
|
+
}
|
|
145
|
+
insAttach.run(r.repo_id, sourceId);
|
|
146
|
+
}
|
|
147
|
+
this.db.exec("ALTER TABLE repos DROP COLUMN root_path");
|
|
148
|
+
})();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Run fn inside a single write transaction (02 §2: one transaction per
|
|
152
|
+
* commit). better-sqlite3 transactions are synchronous and serialized.
|
|
153
|
+
*/
|
|
154
|
+
write(fn) {
|
|
155
|
+
return this.db.transaction(fn)(this.db);
|
|
156
|
+
}
|
|
157
|
+
pragma(name) {
|
|
158
|
+
return this.db.pragma(name, { simple: true });
|
|
159
|
+
}
|
|
160
|
+
close() {
|
|
161
|
+
this.db.close();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Database } from "better-sqlite3";
|
|
2
|
+
import { type TreeEntry } from "../hash.js";
|
|
3
|
+
import type { RawBlock } from "../parse/types.js";
|
|
4
|
+
/** A block with a minted/assigned id, ready to encode into the tree. */
|
|
5
|
+
export interface TreeInputBlock {
|
|
6
|
+
blockId: string;
|
|
7
|
+
type: string;
|
|
8
|
+
raw: string;
|
|
9
|
+
trivia: string;
|
|
10
|
+
attrs: Record<string, unknown>;
|
|
11
|
+
children: TreeInputBlock[];
|
|
12
|
+
}
|
|
13
|
+
/** Assign fresh minted ids to a parsed block tree (Stage-1 re-mint path). */
|
|
14
|
+
export declare function assignIds(blocks: RawBlock[]): TreeInputBlock[];
|
|
15
|
+
/** Content-address raw text into blobs; returns hex hash. Dedups. */
|
|
16
|
+
export declare function putBlob(db: Database, text: string): string;
|
|
17
|
+
/** Content-address a tree node's entries; returns hex hash. Dedups. */
|
|
18
|
+
export declare function putTreeNode(db: Database, entries: TreeEntry[]): string;
|
|
19
|
+
/**
|
|
20
|
+
* Encode a list of sibling blocks into a Merkle tree node bottom-up, writing
|
|
21
|
+
* blobs (raw + trivia) and child tree nodes. Returns the node's hex hash.
|
|
22
|
+
* Structural sharing: unchanged subtrees produce identical hashes and are not
|
|
23
|
+
* re-inserted.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeBlockTree(db: Database, blocks: TreeInputBlock[]): string;
|
|
26
|
+
export interface NewCommitInput {
|
|
27
|
+
repoId: string;
|
|
28
|
+
ts: string;
|
|
29
|
+
origin: "api" | "observed" | "import" | "projection";
|
|
30
|
+
actor?: string | null;
|
|
31
|
+
reason?: string | null;
|
|
32
|
+
checkpointId?: string | null;
|
|
33
|
+
ops?: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** Append a commit; assigns the per-repo total-order seq. */
|
|
36
|
+
export declare function newCommit(db: Database, input: NewCommitInput): {
|
|
37
|
+
commitId: string;
|
|
38
|
+
seq: number;
|
|
39
|
+
};
|
|
40
|
+
export interface NewRevisionInput {
|
|
41
|
+
docId: string;
|
|
42
|
+
rootTreeHex: string;
|
|
43
|
+
frontmatterBlobHex: string | null;
|
|
44
|
+
renderedHash: Buffer;
|
|
45
|
+
path: string;
|
|
46
|
+
commitId: string;
|
|
47
|
+
}
|
|
48
|
+
/** Append a revision; assigns the per-document monotonic seq. */
|
|
49
|
+
export declare function writeRevision(db: Database, input: NewRevisionInput): {
|
|
50
|
+
revId: string;
|
|
51
|
+
seq: number;
|
|
52
|
+
};
|