@vibesdotdev/localdb 0.0.1
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 +121 -0
- package/SPEC.md +119 -0
- package/dist/cli/commands/_shared.consumer.d.ts +5 -0
- package/dist/cli/commands/_shared.consumer.d.ts.map +1 -0
- package/dist/cli/commands/_shared.consumer.js +17 -0
- package/dist/cli/commands/_shared.consumer.js.map +1 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.d.ts +19 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.js +19 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.js.map +1 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.d.ts +5 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.d.ts.map +1 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.js +80 -0
- package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.js.map +1 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.d.ts +19 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.js +16 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.js.map +1 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.d.ts +5 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.js +33 -0
- package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.js.map +1 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.d.ts +15 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.js +15 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.js.map +1 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.d.ts +5 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.d.ts.map +1 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.js +28 -0
- package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.js.map +1 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.d.ts +24 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.js +20 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.js.map +1 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.d.ts +5 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.d.ts.map +1 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.js +63 -0
- package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.js.map +1 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.d.ts +19 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.d.ts.map +1 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.js +19 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.js.map +1 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.d.ts +5 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.d.ts.map +1 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.js +73 -0
- package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.js.map +1 -0
- package/dist/core/connection-pool.d.ts +84 -0
- package/dist/core/connection-pool.d.ts.map +1 -0
- package/dist/core/connection-pool.js +191 -0
- package/dist/core/connection-pool.js.map +1 -0
- package/dist/core/database.d.ts +137 -0
- package/dist/core/database.d.ts.map +1 -0
- package/dist/core/database.js +347 -0
- package/dist/core/database.js.map +1 -0
- package/dist/core/error-context.d.ts +2 -0
- package/dist/core/error-context.d.ts.map +1 -0
- package/dist/core/error-context.js +17 -0
- package/dist/core/error-context.js.map +1 -0
- package/dist/core/migration-registry.d.ts +89 -0
- package/dist/core/migration-registry.d.ts.map +1 -0
- package/dist/core/migration-registry.js +226 -0
- package/dist/core/migration-registry.js.map +1 -0
- package/dist/core/runtime.d.ts +3 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +17 -0
- package/dist/core/runtime.js.map +1 -0
- package/dist/dev.localdb.cli-group.descriptor.d.ts +9 -0
- package/dist/dev.localdb.cli-group.descriptor.d.ts.map +1 -0
- package/dist/dev.localdb.cli-group.descriptor.js +17 -0
- package/dist/dev.localdb.cli-group.descriptor.js.map +1 -0
- package/dist/dev.localdb.context.descriptor.d.ts +21 -0
- package/dist/dev.localdb.context.descriptor.d.ts.map +1 -0
- package/dist/dev.localdb.context.descriptor.js +12 -0
- package/dist/dev.localdb.context.descriptor.js.map +1 -0
- package/dist/dev.localdb.context.impl.consumer.d.ts +9 -0
- package/dist/dev.localdb.context.impl.consumer.d.ts.map +1 -0
- package/dist/dev.localdb.context.impl.consumer.js +10 -0
- package/dist/dev.localdb.context.impl.consumer.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/localdb.cloud.plugin.d.ts +17 -0
- package/dist/localdb.cloud.plugin.d.ts.map +1 -0
- package/dist/localdb.cloud.plugin.js +23 -0
- package/dist/localdb.cloud.plugin.js.map +1 -0
- package/dist/localdb.plugin.d.ts +8 -0
- package/dist/localdb.plugin.d.ts.map +1 -0
- package/dist/localdb.plugin.js +38 -0
- package/dist/localdb.plugin.js.map +1 -0
- package/dist/migrations/load-sql.d.ts +45 -0
- package/dist/migrations/load-sql.d.ts.map +1 -0
- package/dist/migrations/load-sql.js +117 -0
- package/dist/migrations/load-sql.js.map +1 -0
- package/dist/schemas/api.d.ts +82 -0
- package/dist/schemas/api.d.ts.map +1 -0
- package/dist/schemas/api.js +2 -0
- package/dist/schemas/api.js.map +1 -0
- package/package.json +146 -0
- package/src/cli/commands/_shared.consumer.ts +20 -0
- package/src/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.ts +20 -0
- package/src/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.ts +97 -0
- package/src/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.ts +17 -0
- package/src/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.ts +46 -0
- package/src/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.ts +16 -0
- package/src/cli/commands/pools/dev.localdb.pools.cli-command.impl.ts +37 -0
- package/src/cli/commands/query/dev.localdb.query.cli-command.descriptor.ts +21 -0
- package/src/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.ts +69 -0
- package/src/cli/commands/status/dev.localdb.status.cli-command.descriptor.ts +20 -0
- package/src/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.ts +93 -0
- package/src/core/connection-pool.ts +240 -0
- package/src/core/database.ts +419 -0
- package/src/core/error-context.ts +19 -0
- package/src/core/migration-registry.ts +321 -0
- package/src/core/runtime.ts +17 -0
- package/src/dev.localdb.cli-group.descriptor.ts +20 -0
- package/src/dev.localdb.context.descriptor.ts +13 -0
- package/src/dev.localdb.context.impl.consumer.ts +12 -0
- package/src/index.ts +28 -0
- package/src/localdb.cloud.plugin.ts +24 -0
- package/src/localdb.plugin.ts +43 -0
- package/src/migrations/atlas/001-initial-schema.sql +173 -0
- package/src/migrations/atlas/002-lang-server-fields.sql +12 -0
- package/src/migrations/atlas/003-config-id-dedup.sql +31 -0
- package/src/migrations/atlas/004-fix-on-conflict-constraints.sql +25 -0
- package/src/migrations/atlas/005-diagnostics.sql +66 -0
- package/src/migrations/atlas/006-diagnostic-summaries.sql +65 -0
- package/src/migrations/atlas/007-diagnostic-slice.sql +83 -0
- package/src/migrations/load-sql.ts +133 -0
- package/src/schemas/api.ts +92 -0
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Registry
|
|
3
|
+
*
|
|
4
|
+
* Centralized registration and execution of database migrations.
|
|
5
|
+
* Consumers register their migrations with the registry, which ensures
|
|
6
|
+
* they run in order with proper error handling and version tracking.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Database } from 'bun:sqlite';
|
|
10
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
11
|
+
import { toErrorContext } from './error-context';
|
|
12
|
+
|
|
13
|
+
const logger = getLogger('localdb:migrations');
|
|
14
|
+
|
|
15
|
+
export interface MigrationFunction {
|
|
16
|
+
(db: Database): void | Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Migration {
|
|
20
|
+
version: number;
|
|
21
|
+
name: string;
|
|
22
|
+
up: MigrationFunction;
|
|
23
|
+
down?: MigrationFunction;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MigrationNamespace {
|
|
27
|
+
namespace: string;
|
|
28
|
+
migrations: Migration[];
|
|
29
|
+
currentVersion: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class MigrationRegistry {
|
|
33
|
+
private namespaces = new Map<string, MigrationNamespace>();
|
|
34
|
+
|
|
35
|
+
private hasVersionTable(db: Database): boolean {
|
|
36
|
+
const row = db
|
|
37
|
+
.query<
|
|
38
|
+
{ name: string },
|
|
39
|
+
[]
|
|
40
|
+
>("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'vibes_schema_versions'")
|
|
41
|
+
.get();
|
|
42
|
+
return row?.name === 'vibes_schema_versions';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private ensureVersionTable(db: Database): void {
|
|
46
|
+
if (this.hasVersionTable(db)) return;
|
|
47
|
+
|
|
48
|
+
db.exec(`
|
|
49
|
+
CREATE TABLE IF NOT EXISTS vibes_schema_versions (
|
|
50
|
+
namespace TEXT PRIMARY KEY,
|
|
51
|
+
version INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
53
|
+
)
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private getLegacyUserVersion(db: Database): number {
|
|
58
|
+
let lastError: Error | undefined;
|
|
59
|
+
const maxRetries = 3;
|
|
60
|
+
const baseDelay = 100;
|
|
61
|
+
|
|
62
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
63
|
+
try {
|
|
64
|
+
const result = db.query('PRAGMA user_version').get() as { user_version: number };
|
|
65
|
+
return result.user_version;
|
|
66
|
+
} catch (error) {
|
|
67
|
+
lastError = error as Error;
|
|
68
|
+
if ((error as { code?: string })?.code === 'SQLITE_BUSY' && attempt < maxRetries - 1) {
|
|
69
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
70
|
+
Bun.sleepSync(delay);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw lastError ?? new Error('All retries exhausted');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private getTrackedSchemaVersion(db: Database, namespace: string): number | null {
|
|
81
|
+
this.ensureVersionTable(db);
|
|
82
|
+
|
|
83
|
+
const row = db
|
|
84
|
+
.query<
|
|
85
|
+
{ version: number },
|
|
86
|
+
[string]
|
|
87
|
+
>('SELECT version FROM vibes_schema_versions WHERE namespace = ?')
|
|
88
|
+
.get(namespace);
|
|
89
|
+
|
|
90
|
+
return row ? row.version : null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private bootstrapSchemaVersion(db: Database, namespace: string): number {
|
|
94
|
+
const tracked = this.getTrackedSchemaVersion(db, namespace);
|
|
95
|
+
if (tracked !== null) return tracked;
|
|
96
|
+
|
|
97
|
+
const ns = this.namespaces.get(namespace);
|
|
98
|
+
if (!ns) {
|
|
99
|
+
throw new Error(`Unknown migration namespace: ${namespace}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const legacyVersion = this.getLegacyUserVersion(db);
|
|
103
|
+
const trackedNamespaceCount =
|
|
104
|
+
db.query<{ count: number }, []>('SELECT COUNT(*) as count FROM vibes_schema_versions').get()
|
|
105
|
+
?.count ?? 0;
|
|
106
|
+
|
|
107
|
+
const bootstrapVersion =
|
|
108
|
+
trackedNamespaceCount === 0 && legacyVersion > 0 && legacyVersion <= ns.currentVersion
|
|
109
|
+
? legacyVersion
|
|
110
|
+
: 0;
|
|
111
|
+
|
|
112
|
+
this.setSchemaVersion(db, namespace, bootstrapVersion);
|
|
113
|
+
return bootstrapVersion;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Register migrations for a namespace (e.g., 'atlas', 'session')
|
|
118
|
+
*/
|
|
119
|
+
register(namespace: string, migrations: Migration[]): void {
|
|
120
|
+
if (this.namespaces.has(namespace)) {
|
|
121
|
+
throw new Error(`Migration namespace '${namespace}' is already registered`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Sort migrations by version
|
|
125
|
+
const sorted = [...migrations].sort((a, b) => a.version - b.version);
|
|
126
|
+
|
|
127
|
+
// Validate version sequence
|
|
128
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
129
|
+
const expected = i + 1;
|
|
130
|
+
const actual = sorted[i].version;
|
|
131
|
+
if (actual !== expected) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
`Invalid migration sequence in '${namespace}': expected version ${expected}, got ${actual}`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const currentVersion = sorted.length > 0 ? sorted[sorted.length - 1].version : 0;
|
|
139
|
+
|
|
140
|
+
this.namespaces.set(namespace, {
|
|
141
|
+
namespace,
|
|
142
|
+
migrations: sorted,
|
|
143
|
+
currentVersion
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
logger.debug(`Registered ${migrations.length} migration(s) for namespace '${namespace}'`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get current schema version from database
|
|
151
|
+
*/
|
|
152
|
+
getSchemaVersion(db: Database, namespace: string): number {
|
|
153
|
+
return this.bootstrapSchemaVersion(db, namespace);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Set schema version in database
|
|
158
|
+
*/
|
|
159
|
+
private setSchemaVersion(db: Database, namespace: string, version: number): void {
|
|
160
|
+
this.ensureVersionTable(db);
|
|
161
|
+
|
|
162
|
+
db.query(
|
|
163
|
+
`INSERT INTO vibes_schema_versions (namespace, version, updated_at)
|
|
164
|
+
VALUES (?, ?, datetime('now'))
|
|
165
|
+
ON CONFLICT(namespace) DO UPDATE SET
|
|
166
|
+
version = excluded.version,
|
|
167
|
+
updated_at = datetime('now')`
|
|
168
|
+
).run(namespace, version);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check if database needs migration
|
|
173
|
+
*/
|
|
174
|
+
needsMigration(db: Database, namespace: string): boolean {
|
|
175
|
+
const ns = this.namespaces.get(namespace);
|
|
176
|
+
if (!ns) {
|
|
177
|
+
throw new Error(`Unknown migration namespace: ${namespace}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const currentVersion = this.getSchemaVersion(db, namespace);
|
|
181
|
+
return currentVersion < ns.currentVersion;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if database is from an incompatible/old migration system
|
|
186
|
+
* This happens when the version is higher than our max (legacy system)
|
|
187
|
+
*/
|
|
188
|
+
isIncompatibleDatabase(db: Database, namespace: string): boolean {
|
|
189
|
+
const ns = this.namespaces.get(namespace);
|
|
190
|
+
if (!ns) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const trackedVersion = this.getTrackedSchemaVersion(db, namespace);
|
|
195
|
+
if (trackedVersion === null) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return trackedVersion > ns.currentVersion;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Run all pending migrations for a namespace
|
|
204
|
+
*/
|
|
205
|
+
async runMigrations(db: Database, namespace: string): Promise<void> {
|
|
206
|
+
const ns = this.namespaces.get(namespace);
|
|
207
|
+
if (!ns) {
|
|
208
|
+
throw new Error(`Unknown migration namespace: ${namespace}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let currentVersion = this.getSchemaVersion(db, namespace);
|
|
212
|
+
|
|
213
|
+
logger.debug(
|
|
214
|
+
`Running migrations for '${namespace}' (v${currentVersion} → v${ns.currentVersion})`
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
for (const migration of ns.migrations) {
|
|
218
|
+
if (migration.version > currentVersion) {
|
|
219
|
+
logger.debug(`Applying migration ${migration.version}: ${migration.name}`);
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
await migration.up(db);
|
|
223
|
+
currentVersion = migration.version;
|
|
224
|
+
this.setSchemaVersion(db, namespace, currentVersion);
|
|
225
|
+
logger.debug(`[LocalDB] Migration ${migration.version} completed`);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
logger.error(
|
|
228
|
+
`[LocalDB] Migration ${migration.version} failed:`,
|
|
229
|
+
toErrorContext(error)
|
|
230
|
+
);
|
|
231
|
+
throw new Error(`Migration ${migration.version} (${migration.name}) failed: ${error}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
logger.debug(`[LocalDB] All migrations completed for '${namespace}'`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get migration status for all namespaces
|
|
241
|
+
*/
|
|
242
|
+
getStatus(): Array<{
|
|
243
|
+
namespace: string;
|
|
244
|
+
currentVersion: number;
|
|
245
|
+
targetVersion: number;
|
|
246
|
+
pendingMigrations: number;
|
|
247
|
+
}> {
|
|
248
|
+
const status: Array<{
|
|
249
|
+
namespace: string;
|
|
250
|
+
currentVersion: number;
|
|
251
|
+
targetVersion: number;
|
|
252
|
+
pendingMigrations: number;
|
|
253
|
+
}> = [];
|
|
254
|
+
|
|
255
|
+
for (const ns of this.namespaces.values()) {
|
|
256
|
+
status.push({
|
|
257
|
+
namespace: ns.namespace,
|
|
258
|
+
currentVersion: 0, // Will be determined per-database
|
|
259
|
+
targetVersion: ns.currentVersion,
|
|
260
|
+
pendingMigrations: 0 // Will be determined per-database
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return status;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get migration status for a specific database and namespace
|
|
269
|
+
*/
|
|
270
|
+
getDatabaseStatus(
|
|
271
|
+
db: Database,
|
|
272
|
+
namespace: string
|
|
273
|
+
): {
|
|
274
|
+
namespace: string;
|
|
275
|
+
currentVersion: number;
|
|
276
|
+
targetVersion: number;
|
|
277
|
+
pendingMigrations: number;
|
|
278
|
+
migrations: Array<{ version: number; name: string; applied: boolean }>;
|
|
279
|
+
} {
|
|
280
|
+
const ns = this.namespaces.get(namespace);
|
|
281
|
+
if (!ns) {
|
|
282
|
+
throw new Error(`Unknown migration namespace: ${namespace}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const currentVersion = this.getSchemaVersion(db, namespace);
|
|
286
|
+
const pending = Math.max(0, ns.currentVersion - currentVersion);
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
namespace,
|
|
290
|
+
currentVersion,
|
|
291
|
+
targetVersion: ns.currentVersion,
|
|
292
|
+
pendingMigrations: pending,
|
|
293
|
+
migrations: ns.migrations.map((m) => ({
|
|
294
|
+
version: m.version,
|
|
295
|
+
name: m.name,
|
|
296
|
+
applied: m.version <= currentVersion
|
|
297
|
+
}))
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* List all registered namespaces
|
|
303
|
+
*/
|
|
304
|
+
listNamespaces(): string[] {
|
|
305
|
+
return Array.from(this.namespaces.keys());
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get migrations for a namespace
|
|
310
|
+
*/
|
|
311
|
+
getMigrations(namespace: string): Migration[] {
|
|
312
|
+
const ns = this.namespaces.get(namespace);
|
|
313
|
+
if (!ns) {
|
|
314
|
+
throw new Error(`Unknown migration namespace: ${namespace}`);
|
|
315
|
+
}
|
|
316
|
+
return [...ns.migrations];
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Singleton instance
|
|
321
|
+
export const migrationRegistry = new MigrationRegistry();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function hasRuntimeProcessVersion(name: 'bun' | 'node'): boolean {
|
|
2
|
+
if (typeof process === 'undefined') return false;
|
|
3
|
+
const value = process.versions?.[name];
|
|
4
|
+
return typeof value === 'string' && value.length > 0;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isLocaldbBrowserRuntime(scope: typeof globalThis = globalThis): boolean {
|
|
8
|
+
const globals = scope as typeof globalThis & Record<string, unknown>;
|
|
9
|
+
const hasWindow = typeof globals.window !== 'undefined' && globals.window !== null;
|
|
10
|
+
const hasDocument = typeof globals.document !== 'undefined' && globals.document !== null;
|
|
11
|
+
const hasServerRuntime = hasRuntimeProcessVersion('bun') || hasRuntimeProcessVersion('node');
|
|
12
|
+
return hasDocument || (hasWindow && !hasServerRuntime);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isLocaldbServerRuntime(scope: typeof globalThis = globalThis): boolean {
|
|
16
|
+
return !isLocaldbBrowserRuntime(scope);
|
|
17
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev LocalDB CLI Group Descriptor
|
|
3
|
+
*
|
|
4
|
+
* Groups LocalDB development commands under `vibes dev localdb`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { CLIGroupAssetDescriptor } from '@vibesdotdev/cli/schemas/types';
|
|
8
|
+
|
|
9
|
+
const descriptor: CLIGroupAssetDescriptor = {
|
|
10
|
+
kind: 'cli/group',
|
|
11
|
+
id: 'dev.localdb',
|
|
12
|
+
name: 'localdb',
|
|
13
|
+
description: 'Manage local SQLite databases (migrations, connections, status)',
|
|
14
|
+
parent: 'dev',
|
|
15
|
+
surfaces: ['cli'],
|
|
16
|
+
hardware: ['consumer'],
|
|
17
|
+
enabled: true
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default descriptor;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev LocalDB Context Descriptor
|
|
3
|
+
*
|
|
4
|
+
* Provides LocalDB API for dev commands and workflows.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createContextDescriptor } from '@vibesdotdev/runtime';
|
|
8
|
+
|
|
9
|
+
export default createContextDescriptor({
|
|
10
|
+
id: 'dev/localdb',
|
|
11
|
+
description: 'LocalDB API for managing local SQLite databases.',
|
|
12
|
+
hardware: ['consumer']
|
|
13
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Database (LocalDB)
|
|
3
|
+
*
|
|
4
|
+
* Unified SQLite database management for vibes dev toolkit.
|
|
5
|
+
* Provides connection pooling, migration management, and consistent configuration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
LocaldbApi,
|
|
10
|
+
LocaldbRawDatabase,
|
|
11
|
+
LocalDatabase as LocalDatabaseInterface,
|
|
12
|
+
Migration,
|
|
13
|
+
MigrationFunction,
|
|
14
|
+
ConnectionStats,
|
|
15
|
+
ConnectionPoolApi,
|
|
16
|
+
MigrationRegistryApi
|
|
17
|
+
} from './schemas/api';
|
|
18
|
+
|
|
19
|
+
export { LocalDatabase, localdb } from './core/database';
|
|
20
|
+
export { connectionPool } from './core/connection-pool';
|
|
21
|
+
export { migrationRegistry } from './core/migration-registry';
|
|
22
|
+
export {
|
|
23
|
+
loadMigrationSQL,
|
|
24
|
+
loadMigrationSQLSync,
|
|
25
|
+
listMigrationFiles,
|
|
26
|
+
parseVersionFromFilename,
|
|
27
|
+
parseMigrationName
|
|
28
|
+
} from './migrations/load-sql';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalDB Cloud Plugin
|
|
3
|
+
*
|
|
4
|
+
* Cloud-bundle-safe variant of `localdb.plugin.ts`.
|
|
5
|
+
*
|
|
6
|
+
* LocalDB manages on-disk SQLite (libsql / better-sqlite3) for consumer
|
|
7
|
+
* environments. Every loader in `localdb.plugin.ts` is `.impl.consumer.ts`
|
|
8
|
+
* and reaches `bun:sqlite` / `node:fs`. None of it is reachable from a
|
|
9
|
+
* cloudflare-workers bundle.
|
|
10
|
+
*
|
|
11
|
+
* ai-web's plugin chain still loads localdb for shape uniformity per
|
|
12
|
+
* `apps/ai-web (consolidated)/SPEC.md` line 14. This empty cloud variant satisfies
|
|
13
|
+
* the chain without pulling consumer-only code into the cloud bundle.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createRuntimePlugin } from '@vibesdotdev/runtime';
|
|
17
|
+
|
|
18
|
+
export default createRuntimePlugin({
|
|
19
|
+
// Same id as the consumer `localdb` plugin per AGENT-CONSTITUTION
|
|
20
|
+
// §PLUGIN ID PARITY: the bare id is the public capability.
|
|
21
|
+
id: 'localdb',
|
|
22
|
+
name: 'LocalDB (Cloud)',
|
|
23
|
+
description: 'Cloud-safe localdb registration — empty (localdb is consumer-only)'
|
|
24
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalDB Plugin
|
|
3
|
+
*
|
|
4
|
+
* Local SQLite database management — migrations, connections, status.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createRuntimePlugin, loader } from '@vibesdotdev/runtime';
|
|
8
|
+
|
|
9
|
+
// CLI
|
|
10
|
+
import localdbGroup from './dev.localdb.cli-group.descriptor.ts';
|
|
11
|
+
import localdbMigrate from './cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.ts';
|
|
12
|
+
import localdbNamespaces from './cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.ts';
|
|
13
|
+
import localdbPools from './cli/commands/pools/dev.localdb.pools.cli-command.descriptor.ts';
|
|
14
|
+
import localdbQuery from './cli/commands/query/dev.localdb.query.cli-command.descriptor.ts';
|
|
15
|
+
import localdbStatus from './cli/commands/status/dev.localdb.status.cli-command.descriptor.ts';
|
|
16
|
+
|
|
17
|
+
// Context
|
|
18
|
+
import localdbContextDescriptor from './dev.localdb.context.descriptor.ts';
|
|
19
|
+
|
|
20
|
+
export default createRuntimePlugin({
|
|
21
|
+
id: 'localdb',
|
|
22
|
+
name: 'LocalDB',
|
|
23
|
+
description: 'Local SQLite database management — migrations, connections, status',
|
|
24
|
+
|
|
25
|
+
descriptors: [
|
|
26
|
+
localdbGroup,
|
|
27
|
+
localdbMigrate,
|
|
28
|
+
localdbNamespaces,
|
|
29
|
+
localdbPools,
|
|
30
|
+
localdbQuery,
|
|
31
|
+
localdbStatus,
|
|
32
|
+
localdbContextDescriptor
|
|
33
|
+
],
|
|
34
|
+
|
|
35
|
+
loaders: [
|
|
36
|
+
loader('dev.localdb.migrate', 'consumer', () => import('./cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.ts')),
|
|
37
|
+
loader('dev.localdb.namespaces', () => import('./cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.ts')),
|
|
38
|
+
loader('dev.localdb.pools', () => import('./cli/commands/pools/dev.localdb.pools.cli-command.impl.ts')),
|
|
39
|
+
loader('dev.localdb.query', 'consumer', () => import('./cli/commands/query/dev.localdb.query.cli-command.impl.consumer.ts')),
|
|
40
|
+
loader('dev.localdb.status', 'consumer', () => import('./cli/commands/status/dev.localdb.status.cli-command.impl.consumer.ts')),
|
|
41
|
+
loader('dev/localdb', 'consumer', () => import('./dev.localdb.context.impl.consumer.ts'))
|
|
42
|
+
]
|
|
43
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
-- Atlas Migration 001: Initial Schema
|
|
2
|
+
-- Creates all tables for code indexing
|
|
3
|
+
|
|
4
|
+
-- Index configurations
|
|
5
|
+
CREATE TABLE IF NOT EXISTS index_configs (
|
|
6
|
+
id TEXT PRIMARY KEY,
|
|
7
|
+
project_id TEXT NOT NULL,
|
|
8
|
+
branch TEXT NOT NULL DEFAULT 'main',
|
|
9
|
+
git_commit TEXT,
|
|
10
|
+
workspace_path TEXT NOT NULL,
|
|
11
|
+
paths TEXT NOT NULL,
|
|
12
|
+
exclude_paths TEXT NOT NULL,
|
|
13
|
+
languages TEXT NOT NULL,
|
|
14
|
+
storage_backend TEXT NOT NULL,
|
|
15
|
+
runtime TEXT NOT NULL,
|
|
16
|
+
package_manager TEXT NOT NULL,
|
|
17
|
+
enable_embeddings INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
redact_patterns TEXT NOT NULL,
|
|
19
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
20
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX idx_configs_project ON index_configs(project_id);
|
|
24
|
+
|
|
25
|
+
-- Index runs
|
|
26
|
+
CREATE TABLE IF NOT EXISTS index_runs (
|
|
27
|
+
id TEXT PRIMARY KEY,
|
|
28
|
+
config_id TEXT NOT NULL,
|
|
29
|
+
status TEXT NOT NULL CHECK(status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
|
|
30
|
+
started_at TEXT NOT NULL,
|
|
31
|
+
completed_at TEXT,
|
|
32
|
+
files_processed INTEGER NOT NULL DEFAULT 0,
|
|
33
|
+
symbols_captured INTEGER NOT NULL DEFAULT 0,
|
|
34
|
+
errors TEXT NOT NULL DEFAULT '[]',
|
|
35
|
+
worker_job_id TEXT,
|
|
36
|
+
duration_ms INTEGER,
|
|
37
|
+
memory_usage_bytes INTEGER,
|
|
38
|
+
cpu_usage_percent REAL,
|
|
39
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
40
|
+
FOREIGN KEY (config_id) REFERENCES index_configs(id) ON DELETE CASCADE
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX idx_runs_config ON index_runs(config_id);
|
|
44
|
+
CREATE INDEX idx_runs_status ON index_runs(status);
|
|
45
|
+
CREATE INDEX idx_runs_started_at ON index_runs(started_at DESC);
|
|
46
|
+
|
|
47
|
+
-- Symbols
|
|
48
|
+
CREATE TABLE IF NOT EXISTS symbols (
|
|
49
|
+
id TEXT PRIMARY KEY,
|
|
50
|
+
run_id TEXT NOT NULL,
|
|
51
|
+
name TEXT NOT NULL,
|
|
52
|
+
kind TEXT NOT NULL CHECK(kind IN (
|
|
53
|
+
'class', 'function', 'method', 'variable', 'constant',
|
|
54
|
+
'interface', 'type', 'enum', 'namespace', 'module',
|
|
55
|
+
'property', 'parameter', 'import', 'export'
|
|
56
|
+
)),
|
|
57
|
+
language TEXT NOT NULL,
|
|
58
|
+
file_path TEXT NOT NULL,
|
|
59
|
+
start_line INTEGER NOT NULL,
|
|
60
|
+
start_column INTEGER NOT NULL,
|
|
61
|
+
end_line INTEGER NOT NULL,
|
|
62
|
+
end_column INTEGER NOT NULL,
|
|
63
|
+
signature TEXT,
|
|
64
|
+
documentation TEXT,
|
|
65
|
+
exported INTEGER NOT NULL DEFAULT 0,
|
|
66
|
+
parent_symbol_id TEXT,
|
|
67
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
68
|
+
FOREIGN KEY (run_id) REFERENCES index_runs(id) ON DELETE CASCADE,
|
|
69
|
+
FOREIGN KEY (parent_symbol_id) REFERENCES symbols(id) ON DELETE SET NULL
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE INDEX idx_symbols_run ON symbols(run_id);
|
|
73
|
+
CREATE INDEX idx_symbols_name ON symbols(name);
|
|
74
|
+
CREATE INDEX idx_symbols_file ON symbols(file_path);
|
|
75
|
+
CREATE INDEX idx_symbols_kind ON symbols(kind);
|
|
76
|
+
CREATE INDEX idx_symbols_parent ON symbols(parent_symbol_id);
|
|
77
|
+
|
|
78
|
+
-- Full-text search index for symbols
|
|
79
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(
|
|
80
|
+
name,
|
|
81
|
+
signature,
|
|
82
|
+
documentation,
|
|
83
|
+
content=symbols,
|
|
84
|
+
content_rowid=rowid
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
-- Triggers to keep FTS index in sync
|
|
88
|
+
CREATE TRIGGER IF NOT EXISTS symbols_fts_insert AFTER INSERT ON symbols BEGIN
|
|
89
|
+
INSERT INTO symbols_fts(rowid, name, signature, documentation)
|
|
90
|
+
VALUES (new.rowid, new.name, new.signature, new.documentation);
|
|
91
|
+
END;
|
|
92
|
+
|
|
93
|
+
CREATE TRIGGER IF NOT EXISTS symbols_fts_delete AFTER DELETE ON symbols BEGIN
|
|
94
|
+
DELETE FROM symbols_fts WHERE rowid = old.rowid;
|
|
95
|
+
END;
|
|
96
|
+
|
|
97
|
+
CREATE TRIGGER IF NOT EXISTS symbols_fts_update AFTER UPDATE ON symbols BEGIN
|
|
98
|
+
DELETE FROM symbols_fts WHERE rowid = old.rowid;
|
|
99
|
+
INSERT INTO symbols_fts(rowid, name, signature, documentation)
|
|
100
|
+
VALUES (new.rowid, new.name, new.signature, new.documentation);
|
|
101
|
+
END;
|
|
102
|
+
|
|
103
|
+
-- File metadata
|
|
104
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
105
|
+
id TEXT PRIMARY KEY,
|
|
106
|
+
run_id TEXT NOT NULL,
|
|
107
|
+
path TEXT NOT NULL,
|
|
108
|
+
checksum TEXT NOT NULL,
|
|
109
|
+
size_bytes INTEGER NOT NULL,
|
|
110
|
+
language TEXT NOT NULL,
|
|
111
|
+
lines_of_code INTEGER NOT NULL DEFAULT 0,
|
|
112
|
+
imports TEXT NOT NULL DEFAULT '[]',
|
|
113
|
+
exports TEXT NOT NULL DEFAULT '[]',
|
|
114
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
115
|
+
FOREIGN KEY (run_id) REFERENCES index_runs(id) ON DELETE CASCADE
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE INDEX idx_files_run ON files(run_id);
|
|
119
|
+
CREATE INDEX idx_files_path ON files(path);
|
|
120
|
+
CREATE INDEX idx_files_checksum ON files(checksum);
|
|
121
|
+
|
|
122
|
+
-- Dependency edges
|
|
123
|
+
CREATE TABLE IF NOT EXISTS edges (
|
|
124
|
+
id TEXT PRIMARY KEY,
|
|
125
|
+
run_id TEXT NOT NULL,
|
|
126
|
+
source_file_id TEXT,
|
|
127
|
+
source_symbol_id TEXT,
|
|
128
|
+
target_file_id TEXT,
|
|
129
|
+
target_symbol_id TEXT,
|
|
130
|
+
edge_type TEXT NOT NULL CHECK(edge_type IN (
|
|
131
|
+
'imports', 'calls', 'extends', 'implements', 'references'
|
|
132
|
+
)),
|
|
133
|
+
weight INTEGER NOT NULL DEFAULT 1,
|
|
134
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
135
|
+
FOREIGN KEY (run_id) REFERENCES index_runs(id) ON DELETE CASCADE,
|
|
136
|
+
FOREIGN KEY (source_file_id) REFERENCES files(id) ON DELETE CASCADE,
|
|
137
|
+
FOREIGN KEY (source_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE,
|
|
138
|
+
FOREIGN KEY (target_file_id) REFERENCES files(id) ON DELETE CASCADE,
|
|
139
|
+
FOREIGN KEY (target_symbol_id) REFERENCES symbols(id) ON DELETE CASCADE
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
CREATE INDEX idx_edges_run ON edges(run_id);
|
|
143
|
+
CREATE INDEX idx_edges_source_file ON edges(source_file_id);
|
|
144
|
+
CREATE INDEX idx_edges_source_symbol ON edges(source_symbol_id);
|
|
145
|
+
CREATE INDEX idx_edges_target_file ON edges(target_file_id);
|
|
146
|
+
CREATE INDEX idx_edges_target_symbol ON edges(target_symbol_id);
|
|
147
|
+
CREATE INDEX idx_edges_type ON edges(edge_type);
|
|
148
|
+
|
|
149
|
+
-- Embeddings (for semantic search)
|
|
150
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
151
|
+
id TEXT PRIMARY KEY,
|
|
152
|
+
run_id TEXT NOT NULL,
|
|
153
|
+
symbol_id TEXT,
|
|
154
|
+
file_id TEXT,
|
|
155
|
+
vector_id TEXT NOT NULL,
|
|
156
|
+
embedding BLOB,
|
|
157
|
+
metadata TEXT,
|
|
158
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
159
|
+
FOREIGN KEY (run_id) REFERENCES index_runs(id) ON DELETE CASCADE,
|
|
160
|
+
FOREIGN KEY (symbol_id) REFERENCES symbols(id) ON DELETE CASCADE,
|
|
161
|
+
FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
CREATE INDEX idx_embeddings_run ON embeddings(run_id);
|
|
165
|
+
CREATE INDEX idx_embeddings_symbol ON embeddings(symbol_id);
|
|
166
|
+
CREATE INDEX idx_embeddings_file ON embeddings(file_id);
|
|
167
|
+
|
|
168
|
+
-- Performance optimizations
|
|
169
|
+
PRAGMA journal_mode = WAL;
|
|
170
|
+
PRAGMA synchronous = NORMAL;
|
|
171
|
+
PRAGMA cache_size = -64000; -- 64MB cache
|
|
172
|
+
PRAGMA temp_store = memory;
|
|
173
|
+
PRAGMA mmap_size = 30000000000; -- 30GB mmap
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Atlas Migration 002: Language Server Integration Fields
|
|
2
|
+
-- Adds fields for lang server enrichment and validation
|
|
3
|
+
|
|
4
|
+
-- Add lang server config options to index_configs
|
|
5
|
+
ALTER TABLE index_configs ADD COLUMN enable_lang_server_enrichment INTEGER NOT NULL DEFAULT 0;
|
|
6
|
+
ALTER TABLE index_configs ADD COLUMN enable_lang_server_validation INTEGER NOT NULL DEFAULT 1;
|
|
7
|
+
ALTER TABLE index_configs ADD COLUMN lang_server_timeout INTEGER NOT NULL DEFAULT 2000;
|
|
8
|
+
|
|
9
|
+
-- Add lang server enrichment fields to symbols
|
|
10
|
+
ALTER TABLE symbols ADD COLUMN type_signature TEXT;
|
|
11
|
+
ALTER TABLE symbols ADD COLUMN has_errors INTEGER DEFAULT 0;
|
|
12
|
+
ALTER TABLE symbols ADD COLUMN error_messages TEXT;
|