@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.
Files changed (130) hide show
  1. package/README.md +121 -0
  2. package/SPEC.md +119 -0
  3. package/dist/cli/commands/_shared.consumer.d.ts +5 -0
  4. package/dist/cli/commands/_shared.consumer.d.ts.map +1 -0
  5. package/dist/cli/commands/_shared.consumer.js +17 -0
  6. package/dist/cli/commands/_shared.consumer.js.map +1 -0
  7. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.d.ts +19 -0
  8. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.d.ts.map +1 -0
  9. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.js +19 -0
  10. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.js.map +1 -0
  11. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.d.ts +5 -0
  12. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.d.ts.map +1 -0
  13. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.js +80 -0
  14. package/dist/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.js.map +1 -0
  15. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.d.ts +19 -0
  16. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.d.ts.map +1 -0
  17. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.js +16 -0
  18. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.js.map +1 -0
  19. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.d.ts +5 -0
  20. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.d.ts.map +1 -0
  21. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.js +33 -0
  22. package/dist/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.js.map +1 -0
  23. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.d.ts +15 -0
  24. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.d.ts.map +1 -0
  25. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.js +15 -0
  26. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.js.map +1 -0
  27. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.d.ts +5 -0
  28. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.d.ts.map +1 -0
  29. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.js +28 -0
  30. package/dist/cli/commands/pools/dev.localdb.pools.cli-command.impl.js.map +1 -0
  31. package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.d.ts +24 -0
  32. package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.d.ts.map +1 -0
  33. package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.js +20 -0
  34. package/dist/cli/commands/query/dev.localdb.query.cli-command.descriptor.js.map +1 -0
  35. package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.d.ts +5 -0
  36. package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.d.ts.map +1 -0
  37. package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.js +63 -0
  38. package/dist/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.js.map +1 -0
  39. package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.d.ts +19 -0
  40. package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.d.ts.map +1 -0
  41. package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.js +19 -0
  42. package/dist/cli/commands/status/dev.localdb.status.cli-command.descriptor.js.map +1 -0
  43. package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.d.ts +5 -0
  44. package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.d.ts.map +1 -0
  45. package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.js +73 -0
  46. package/dist/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.js.map +1 -0
  47. package/dist/core/connection-pool.d.ts +84 -0
  48. package/dist/core/connection-pool.d.ts.map +1 -0
  49. package/dist/core/connection-pool.js +191 -0
  50. package/dist/core/connection-pool.js.map +1 -0
  51. package/dist/core/database.d.ts +137 -0
  52. package/dist/core/database.d.ts.map +1 -0
  53. package/dist/core/database.js +347 -0
  54. package/dist/core/database.js.map +1 -0
  55. package/dist/core/error-context.d.ts +2 -0
  56. package/dist/core/error-context.d.ts.map +1 -0
  57. package/dist/core/error-context.js +17 -0
  58. package/dist/core/error-context.js.map +1 -0
  59. package/dist/core/migration-registry.d.ts +89 -0
  60. package/dist/core/migration-registry.d.ts.map +1 -0
  61. package/dist/core/migration-registry.js +226 -0
  62. package/dist/core/migration-registry.js.map +1 -0
  63. package/dist/core/runtime.d.ts +3 -0
  64. package/dist/core/runtime.d.ts.map +1 -0
  65. package/dist/core/runtime.js +17 -0
  66. package/dist/core/runtime.js.map +1 -0
  67. package/dist/dev.localdb.cli-group.descriptor.d.ts +9 -0
  68. package/dist/dev.localdb.cli-group.descriptor.d.ts.map +1 -0
  69. package/dist/dev.localdb.cli-group.descriptor.js +17 -0
  70. package/dist/dev.localdb.cli-group.descriptor.js.map +1 -0
  71. package/dist/dev.localdb.context.descriptor.d.ts +21 -0
  72. package/dist/dev.localdb.context.descriptor.d.ts.map +1 -0
  73. package/dist/dev.localdb.context.descriptor.js +12 -0
  74. package/dist/dev.localdb.context.descriptor.js.map +1 -0
  75. package/dist/dev.localdb.context.impl.consumer.d.ts +9 -0
  76. package/dist/dev.localdb.context.impl.consumer.d.ts.map +1 -0
  77. package/dist/dev.localdb.context.impl.consumer.js +10 -0
  78. package/dist/dev.localdb.context.impl.consumer.js.map +1 -0
  79. package/dist/index.d.ts +12 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +11 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/localdb.cloud.plugin.d.ts +17 -0
  84. package/dist/localdb.cloud.plugin.d.ts.map +1 -0
  85. package/dist/localdb.cloud.plugin.js +23 -0
  86. package/dist/localdb.cloud.plugin.js.map +1 -0
  87. package/dist/localdb.plugin.d.ts +8 -0
  88. package/dist/localdb.plugin.d.ts.map +1 -0
  89. package/dist/localdb.plugin.js +38 -0
  90. package/dist/localdb.plugin.js.map +1 -0
  91. package/dist/migrations/load-sql.d.ts +45 -0
  92. package/dist/migrations/load-sql.d.ts.map +1 -0
  93. package/dist/migrations/load-sql.js +117 -0
  94. package/dist/migrations/load-sql.js.map +1 -0
  95. package/dist/schemas/api.d.ts +82 -0
  96. package/dist/schemas/api.d.ts.map +1 -0
  97. package/dist/schemas/api.js +2 -0
  98. package/dist/schemas/api.js.map +1 -0
  99. package/package.json +146 -0
  100. package/src/cli/commands/_shared.consumer.ts +20 -0
  101. package/src/cli/commands/migrate/dev.localdb.migrate.cli-command.descriptor.ts +20 -0
  102. package/src/cli/commands/migrate/dev.localdb.migrate.cli-command.impl.consumer.ts +97 -0
  103. package/src/cli/commands/namespaces/dev.localdb.namespaces.cli-command.descriptor.ts +17 -0
  104. package/src/cli/commands/namespaces/dev.localdb.namespaces.cli-command.impl.ts +46 -0
  105. package/src/cli/commands/pools/dev.localdb.pools.cli-command.descriptor.ts +16 -0
  106. package/src/cli/commands/pools/dev.localdb.pools.cli-command.impl.ts +37 -0
  107. package/src/cli/commands/query/dev.localdb.query.cli-command.descriptor.ts +21 -0
  108. package/src/cli/commands/query/dev.localdb.query.cli-command.impl.consumer.ts +69 -0
  109. package/src/cli/commands/status/dev.localdb.status.cli-command.descriptor.ts +20 -0
  110. package/src/cli/commands/status/dev.localdb.status.cli-command.impl.consumer.ts +93 -0
  111. package/src/core/connection-pool.ts +240 -0
  112. package/src/core/database.ts +419 -0
  113. package/src/core/error-context.ts +19 -0
  114. package/src/core/migration-registry.ts +321 -0
  115. package/src/core/runtime.ts +17 -0
  116. package/src/dev.localdb.cli-group.descriptor.ts +20 -0
  117. package/src/dev.localdb.context.descriptor.ts +13 -0
  118. package/src/dev.localdb.context.impl.consumer.ts +12 -0
  119. package/src/index.ts +28 -0
  120. package/src/localdb.cloud.plugin.ts +24 -0
  121. package/src/localdb.plugin.ts +43 -0
  122. package/src/migrations/atlas/001-initial-schema.sql +173 -0
  123. package/src/migrations/atlas/002-lang-server-fields.sql +12 -0
  124. package/src/migrations/atlas/003-config-id-dedup.sql +31 -0
  125. package/src/migrations/atlas/004-fix-on-conflict-constraints.sql +25 -0
  126. package/src/migrations/atlas/005-diagnostics.sql +66 -0
  127. package/src/migrations/atlas/006-diagnostic-summaries.sql +65 -0
  128. package/src/migrations/atlas/007-diagnostic-slice.sql +83 -0
  129. package/src/migrations/load-sql.ts +133 -0
  130. package/src/schemas/api.ts +92 -0
@@ -0,0 +1,31 @@
1
+ -- Atlas Migration 003: Deduplicated Indexing by Config ID
2
+ -- Symbols, files, and edges now belong to config_id (project+branch) not run_id
3
+ -- This enables incremental indexing and prevents duplicate results
4
+
5
+ -- Add config_id to symbols for direct config association
6
+ ALTER TABLE symbols ADD COLUMN config_id TEXT REFERENCES index_configs(id) ON DELETE CASCADE;
7
+
8
+ -- Add config_id to files for direct config association
9
+ ALTER TABLE files ADD COLUMN config_id TEXT REFERENCES index_configs(id) ON DELETE CASCADE;
10
+
11
+ -- Add last_indexed_at to files to track when file was last processed
12
+ ALTER TABLE files ADD COLUMN last_indexed_at TEXT;
13
+
14
+ -- Add config_id to edges for direct config association
15
+ ALTER TABLE edges ADD COLUMN config_id TEXT REFERENCES index_configs(id) ON DELETE CASCADE;
16
+
17
+ -- Backfill config_id from run_id for existing data
18
+ UPDATE symbols SET config_id = (SELECT config_id FROM index_runs WHERE index_runs.id = symbols.run_id);
19
+ UPDATE files SET config_id = (SELECT config_id FROM index_runs WHERE index_runs.id = files.run_id);
20
+ UPDATE files SET last_indexed_at = created_at WHERE last_indexed_at IS NULL;
21
+ UPDATE edges SET config_id = (SELECT config_id FROM index_runs WHERE index_runs.id = edges.run_id);
22
+
23
+ -- Create indexes for config_id lookups
24
+ CREATE INDEX IF NOT EXISTS idx_symbols_config ON symbols(config_id);
25
+ CREATE INDEX IF NOT EXISTS idx_files_config ON files(config_id);
26
+ CREATE INDEX IF NOT EXISTS idx_edges_config ON edges(config_id);
27
+
28
+ -- Create unique constraint to prevent duplicate files per config
29
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_files_config_path ON files(config_id, path);
30
+
31
+ -- Note: We keep run_id for audit/history but queries should use config_id
@@ -0,0 +1,25 @@
1
+ -- Atlas Migration 004: Fix ON CONFLICT Constraint for Files Table
2
+ -- Migration 3 created a UNIQUE INDEX which doesn't work with ON CONFLICT
3
+ -- SQLite requires a table-level UNIQUE constraint for ON CONFLICT to work
4
+
5
+ -- Step 1: Delete duplicate files, keeping only the most recently indexed one
6
+ DELETE FROM files WHERE rowid NOT IN (
7
+ SELECT MAX(rowid) FROM files GROUP BY config_id, path
8
+ );
9
+
10
+ -- Step 2: Drop the broken unique index if it exists (may not exist due to duplicates)
11
+ DROP INDEX IF EXISTS idx_files_config_path;
12
+
13
+ -- Step 3: Create proper unique index now that duplicates are removed
14
+ -- Note: In SQLite, a UNIQUE INDEX does work with ON CONFLICT when created on
15
+ -- a table that already has the constraint enforced (no duplicates exist)
16
+ CREATE UNIQUE INDEX idx_files_config_path ON files(config_id, path);
17
+
18
+ -- Step 4: Same fix for symbols - dedupe by (config_id, file_path, name, kind, start_line)
19
+ DELETE FROM symbols WHERE rowid NOT IN (
20
+ SELECT MAX(rowid) FROM symbols GROUP BY config_id, file_path, name, kind, start_line
21
+ );
22
+
23
+ -- Step 5: Create unique index for symbols deduplication
24
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_symbols_config_unique
25
+ ON symbols(config_id, file_path, name, kind, start_line);
@@ -0,0 +1,66 @@
1
+ -- 005-diagnostics.sql
2
+ -- Atlas diagnostics + signals storage
3
+
4
+ -- Diagnostics captured from LSP / tooling output.
5
+ -- Stored per config_id (index_configs.id) and file_path (workspace-relative).
6
+
7
+ CREATE TABLE IF NOT EXISTS diagnostics (
8
+ id TEXT PRIMARY KEY,
9
+ config_id TEXT NOT NULL,
10
+ file_path TEXT NOT NULL,
11
+ file_id TEXT,
12
+ file_mtime INTEGER,
13
+
14
+ line INTEGER NOT NULL,
15
+ column INTEGER,
16
+ end_line INTEGER,
17
+ end_column INTEGER,
18
+
19
+ source TEXT NOT NULL,
20
+ severity TEXT NOT NULL,
21
+ code TEXT,
22
+ message TEXT NOT NULL,
23
+ raw_line TEXT,
24
+
25
+ detected_at TEXT NOT NULL,
26
+ stale INTEGER NOT NULL DEFAULT 0,
27
+
28
+ FOREIGN KEY(config_id) REFERENCES index_configs(id)
29
+ );
30
+
31
+ CREATE INDEX IF NOT EXISTS diagnostics_by_config_file ON diagnostics (config_id, file_path);
32
+ CREATE INDEX IF NOT EXISTS diagnostics_by_config_severity ON diagnostics (config_id, severity);
33
+ CREATE INDEX IF NOT EXISTS diagnostics_by_config_stale ON diagnostics (config_id, stale);
34
+
35
+ -- De-duplication key: same diagnostic in same file range.
36
+ CREATE UNIQUE INDEX IF NOT EXISTS diagnostics_dedupe
37
+ ON diagnostics (
38
+ config_id,
39
+ file_path,
40
+ line,
41
+ COALESCE(column, 1),
42
+ COALESCE(end_line, line),
43
+ COALESCE(end_column, COALESCE(column, 1)),
44
+ source,
45
+ severity,
46
+ COALESCE(code, ''),
47
+ message
48
+ );
49
+
50
+ -- Full-text search on diagnostic message.
51
+ CREATE VIRTUAL TABLE IF NOT EXISTS diagnostics_fts
52
+ USING fts5(message, content='diagnostics', content_rowid='rowid');
53
+
54
+ CREATE TRIGGER IF NOT EXISTS diagnostics_ai AFTER INSERT ON diagnostics BEGIN
55
+ INSERT INTO diagnostics_fts(rowid, message) VALUES (new.rowid, new.message);
56
+ END;
57
+
58
+ CREATE TRIGGER IF NOT EXISTS diagnostics_ad AFTER DELETE ON diagnostics BEGIN
59
+ INSERT INTO diagnostics_fts(diagnostics_fts, rowid, message) VALUES ('delete', old.rowid, old.message);
60
+ END;
61
+
62
+ CREATE TRIGGER IF NOT EXISTS diagnostics_au AFTER UPDATE ON diagnostics BEGIN
63
+ INSERT INTO diagnostics_fts(diagnostics_fts, rowid, message) VALUES ('delete', old.rowid, old.message);
64
+ INSERT INTO diagnostics_fts(rowid, message) VALUES (new.rowid, new.message);
65
+ END;
66
+
@@ -0,0 +1,65 @@
1
+ -- 006-diagnostic-summaries.sql
2
+ -- Aggregated diagnostics snapshot for fast workspace-wide cache reads.
3
+
4
+ CREATE TABLE IF NOT EXISTS diagnostic_file_summaries (
5
+ config_id TEXT NOT NULL,
6
+ file_path TEXT NOT NULL,
7
+ file_mtime INTEGER,
8
+
9
+ diagnostic_count INTEGER NOT NULL DEFAULT 0,
10
+ error_count INTEGER NOT NULL DEFAULT 0,
11
+ warning_count INTEGER NOT NULL DEFAULT 0,
12
+ info_count INTEGER NOT NULL DEFAULT 0,
13
+ hint_count INTEGER NOT NULL DEFAULT 0,
14
+
15
+ detected_at TEXT NOT NULL,
16
+ stale INTEGER NOT NULL DEFAULT 0,
17
+
18
+ PRIMARY KEY (config_id, file_path),
19
+ FOREIGN KEY(config_id) REFERENCES index_configs(id)
20
+ );
21
+
22
+ CREATE INDEX IF NOT EXISTS diagnostic_file_summaries_by_config
23
+ ON diagnostic_file_summaries (config_id);
24
+
25
+ CREATE INDEX IF NOT EXISTS diagnostic_file_summaries_by_config_stale
26
+ ON diagnostic_file_summaries (config_id, stale);
27
+
28
+ -- Backfill summaries from existing diagnostics table.
29
+ INSERT INTO diagnostic_file_summaries (
30
+ config_id,
31
+ file_path,
32
+ file_mtime,
33
+ diagnostic_count,
34
+ error_count,
35
+ warning_count,
36
+ info_count,
37
+ hint_count,
38
+ detected_at,
39
+ stale
40
+ )
41
+ SELECT
42
+ d.config_id,
43
+ d.file_path,
44
+ MAX(CASE WHEN d.stale = 0 THEN d.file_mtime END) AS file_mtime,
45
+ SUM(CASE WHEN d.stale = 0 THEN 1 ELSE 0 END) AS diagnostic_count,
46
+ SUM(CASE WHEN d.stale = 0 AND d.severity = 'error' THEN 1 ELSE 0 END) AS error_count,
47
+ SUM(CASE WHEN d.stale = 0 AND d.severity = 'warning' THEN 1 ELSE 0 END) AS warning_count,
48
+ SUM(CASE WHEN d.stale = 0 AND d.severity = 'info' THEN 1 ELSE 0 END) AS info_count,
49
+ SUM(CASE WHEN d.stale = 0 AND d.severity = 'hint' THEN 1 ELSE 0 END) AS hint_count,
50
+ MAX(d.detected_at) AS detected_at,
51
+ CASE
52
+ WHEN SUM(CASE WHEN d.stale = 0 THEN 1 ELSE 0 END) > 0 THEN 0
53
+ ELSE 1
54
+ END AS stale
55
+ FROM diagnostics d
56
+ GROUP BY d.config_id, d.file_path
57
+ ON CONFLICT(config_id, file_path) DO UPDATE SET
58
+ file_mtime = excluded.file_mtime,
59
+ diagnostic_count = excluded.diagnostic_count,
60
+ error_count = excluded.error_count,
61
+ warning_count = excluded.warning_count,
62
+ info_count = excluded.info_count,
63
+ hint_count = excluded.hint_count,
64
+ detected_at = excluded.detected_at,
65
+ stale = excluded.stale;
@@ -0,0 +1,83 @@
1
+ -- 007-diagnostic-slice.sql
2
+ -- Slice-level aggregates for diagnostics backlog and feature-level health.
3
+
4
+ ALTER TABLE diagnostic_file_summaries
5
+ ADD COLUMN slice_path TEXT;
6
+
7
+ CREATE INDEX IF NOT EXISTS diagnostic_file_summaries_by_config_slice
8
+ ON diagnostic_file_summaries (config_id, slice_path);
9
+
10
+ CREATE INDEX IF NOT EXISTS diagnostic_file_summaries_by_config_slice_stale
11
+ ON diagnostic_file_summaries (config_id, slice_path, stale);
12
+
13
+ CREATE TABLE IF NOT EXISTS diagnostic_slice_summaries (
14
+ config_id TEXT NOT NULL,
15
+ slice_path TEXT NOT NULL,
16
+
17
+ file_count INTEGER NOT NULL DEFAULT 0,
18
+ stale_file_count INTEGER NOT NULL DEFAULT 0,
19
+ diagnostic_file_count INTEGER NOT NULL DEFAULT 0,
20
+ diagnostic_count INTEGER NOT NULL DEFAULT 0,
21
+ error_count INTEGER NOT NULL DEFAULT 0,
22
+ warning_count INTEGER NOT NULL DEFAULT 0,
23
+ info_count INTEGER NOT NULL DEFAULT 0,
24
+ hint_count INTEGER NOT NULL DEFAULT 0,
25
+
26
+ detected_at TEXT,
27
+ updated_at TEXT NOT NULL,
28
+
29
+ PRIMARY KEY (config_id, slice_path),
30
+ FOREIGN KEY(config_id) REFERENCES index_configs(id)
31
+ );
32
+
33
+ CREATE INDEX IF NOT EXISTS diagnostic_slice_summaries_by_config
34
+ ON diagnostic_slice_summaries (config_id);
35
+
36
+ CREATE INDEX IF NOT EXISTS diagnostic_slice_summaries_by_config_errors
37
+ ON diagnostic_slice_summaries (config_id, error_count DESC);
38
+
39
+ CREATE INDEX IF NOT EXISTS diagnostic_slice_summaries_by_config_stale
40
+ ON diagnostic_slice_summaries (config_id, stale_file_count DESC);
41
+
42
+ -- Best-effort backfill from already-sliced file summaries.
43
+ INSERT INTO diagnostic_slice_summaries (
44
+ config_id,
45
+ slice_path,
46
+ file_count,
47
+ stale_file_count,
48
+ diagnostic_file_count,
49
+ diagnostic_count,
50
+ error_count,
51
+ warning_count,
52
+ info_count,
53
+ hint_count,
54
+ detected_at,
55
+ updated_at
56
+ )
57
+ SELECT
58
+ config_id,
59
+ slice_path,
60
+ COUNT(*) AS file_count,
61
+ SUM(CASE WHEN stale = 1 THEN 1 ELSE 0 END) AS stale_file_count,
62
+ SUM(CASE WHEN stale = 0 AND diagnostic_count > 0 THEN 1 ELSE 0 END) AS diagnostic_file_count,
63
+ SUM(CASE WHEN stale = 0 THEN diagnostic_count ELSE 0 END) AS diagnostic_count,
64
+ SUM(CASE WHEN stale = 0 THEN error_count ELSE 0 END) AS error_count,
65
+ SUM(CASE WHEN stale = 0 THEN warning_count ELSE 0 END) AS warning_count,
66
+ SUM(CASE WHEN stale = 0 THEN info_count ELSE 0 END) AS info_count,
67
+ SUM(CASE WHEN stale = 0 THEN hint_count ELSE 0 END) AS hint_count,
68
+ MAX(detected_at) AS detected_at,
69
+ CURRENT_TIMESTAMP AS updated_at
70
+ FROM diagnostic_file_summaries
71
+ WHERE slice_path IS NOT NULL AND slice_path != ''
72
+ GROUP BY config_id, slice_path
73
+ ON CONFLICT(config_id, slice_path) DO UPDATE SET
74
+ file_count = excluded.file_count,
75
+ stale_file_count = excluded.stale_file_count,
76
+ diagnostic_file_count = excluded.diagnostic_file_count,
77
+ diagnostic_count = excluded.diagnostic_count,
78
+ error_count = excluded.error_count,
79
+ warning_count = excluded.warning_count,
80
+ info_count = excluded.info_count,
81
+ hint_count = excluded.hint_count,
82
+ detected_at = excluded.detected_at,
83
+ updated_at = excluded.updated_at;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * SQL Migration Loader
3
+ *
4
+ * Utility for loading SQL migration files from the filesystem.
5
+ * Uses Bun's file API for fast synchronous reads.
6
+ */
7
+
8
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { isLocaldbServerRuntime } from '../core/runtime.ts';
12
+
13
+ const isServerRuntime = isLocaldbServerRuntime();
14
+ const __dirname = isServerRuntime ? dirname(fileURLToPath(import.meta.url)) : '';
15
+
16
+ function throwIfBrowser(): void {
17
+ if (!isServerRuntime) {
18
+ throw new Error('SQL migrations are not available in browser builds');
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Load a SQL migration file by namespace and filename (async)
24
+ *
25
+ * @param namespace - The migration namespace (atlas, session, external)
26
+ * @param filename - The SQL filename (e.g., '001-initial-schema.sql')
27
+ * @returns Promise resolving to the SQL content as a string
28
+ */
29
+ export function loadMigrationSQL(namespace: string, filename: string): Promise<string> {
30
+ throwIfBrowser();
31
+ const namespaceDir = resolveMigrationNamespaceDir(namespace);
32
+ if (!namespaceDir) {
33
+ throw new Error(`Migration namespace not found: ${namespace}`);
34
+ }
35
+ const filePath = join(namespaceDir, filename);
36
+
37
+ if (!existsSync(filePath)) {
38
+ throw new Error(`Migration file not found: ${filePath}`);
39
+ }
40
+
41
+ return Bun.file(filePath).text();
42
+ }
43
+
44
+ /**
45
+ * Synchronously load a SQL migration file
46
+ * Uses Node's fs.readFileSync for truly synchronous reads
47
+ *
48
+ * @param namespace - The migration namespace (atlas, session, external)
49
+ * @param filename - The SQL filename (e.g., '001-initial-schema.sql')
50
+ * @returns The SQL content as a string
51
+ */
52
+ export function loadMigrationSQLSync(namespace: string, filename: string): string {
53
+ throwIfBrowser();
54
+ const namespaceDir = resolveMigrationNamespaceDir(namespace);
55
+ if (!namespaceDir) {
56
+ throw new Error(`Migration namespace not found: ${namespace}`);
57
+ }
58
+ const filePath = join(namespaceDir, filename);
59
+
60
+ if (!existsSync(filePath)) {
61
+ throw new Error(`Migration file not found: ${filePath}`);
62
+ }
63
+
64
+ return readFileSync(filePath, 'utf-8');
65
+ }
66
+
67
+ /**
68
+ * List all SQL files in a namespace directory
69
+ *
70
+ * @param namespace - The migration namespace (atlas, session, external)
71
+ * @returns Array of SQL filenames sorted by version number
72
+ */
73
+ export function listMigrationFiles(namespace: string): string[] {
74
+ throwIfBrowser();
75
+ const dirPath = resolveMigrationNamespaceDir(namespace);
76
+
77
+ if (!dirPath || !existsSync(dirPath)) {
78
+ return [];
79
+ }
80
+
81
+ return readdirSync(dirPath)
82
+ .filter((f) => f.endsWith('.sql') && !f.startsWith('.'))
83
+ .sort((a, b) => {
84
+ // Extract version numbers (e.g., '001' from '001-initial-schema.sql')
85
+ const versionA = parseInt(a.split('-')[0], 10);
86
+ const versionB = parseInt(b.split('-')[0], 10);
87
+ return versionA - versionB;
88
+ });
89
+ }
90
+
91
+ function resolveMigrationNamespaceDir(namespace: string): string | null {
92
+ // If the namespace is already an absolute path that exists, use it directly.
93
+ // External packages (e.g. PM) pass their own migration directory as a full path.
94
+ if (namespace.startsWith('/') && existsSync(namespace)) {
95
+ return namespace;
96
+ }
97
+
98
+ // Bundled migrations are loaded from the package-local migrations directory.
99
+ const bundledPath = join(__dirname, namespace);
100
+ if (existsSync(bundledPath)) {
101
+ return bundledPath;
102
+ }
103
+
104
+ return null;
105
+ }
106
+
107
+ /**
108
+ * Parse version number from a migration filename
109
+ *
110
+ * @param filename - The SQL filename (e.g., '001-initial-schema.sql')
111
+ * @returns The version number
112
+ */
113
+ export function parseVersionFromFilename(filename: string): number {
114
+ const match = filename.match(/^(\d+)-/);
115
+ if (!match) {
116
+ throw new Error(`Invalid migration filename format: ${filename}`);
117
+ }
118
+ return parseInt(match[1], 10);
119
+ }
120
+
121
+ /**
122
+ * Parse migration name from a filename
123
+ *
124
+ * @param filename - The SQL filename (e.g., '001-initial-schema.sql')
125
+ * @returns The migration name (e.g., 'initial_schema')
126
+ */
127
+ export function parseMigrationName(filename: string): string {
128
+ // Remove version prefix and .sql extension
129
+ const withoutVersion = filename.replace(/^\d+-/, '');
130
+ const withoutExtension = withoutVersion.replace(/\.sql$/, '');
131
+ // Convert hyphens to underscores
132
+ return withoutExtension.replace(/-/g, '_');
133
+ }
@@ -0,0 +1,92 @@
1
+ import type { Database } from 'bun:sqlite';
2
+
3
+ export type LocaldbBinding = string | number | bigint | boolean | null | Uint8Array;
4
+
5
+ export interface LocaldbQuery<T> {
6
+ all(...params: LocaldbBinding[]): T[];
7
+ get(...params: LocaldbBinding[]): T | null;
8
+ }
9
+
10
+ export interface LocaldbStatementResult {
11
+ changes: number;
12
+ lastInsertRowid: number | bigint | null;
13
+ }
14
+
15
+ export interface LocaldbStatement {
16
+ run(...params: LocaldbBinding[]): LocaldbStatementResult;
17
+ get<T>(...params: LocaldbBinding[]): T;
18
+ all<T>(...params: LocaldbBinding[]): T[];
19
+ }
20
+
21
+ export interface LocaldbRawDatabase {
22
+ exec(sql: string): void;
23
+ query<T>(sql: string): LocaldbQuery<T>;
24
+ prepare(sql: string): LocaldbStatement;
25
+ run(sql: string, ...params: LocaldbBinding[]): LocaldbStatementResult;
26
+ }
27
+
28
+ export type MigrationFunction = (db: Database) => void | Promise<void>;
29
+
30
+ export interface Migration {
31
+ version: number;
32
+ name: string;
33
+ up: MigrationFunction;
34
+ down?: MigrationFunction;
35
+ }
36
+
37
+ export interface LocalDatabase {
38
+ initialize(): Promise<void>;
39
+ getDatabase(): Database;
40
+ close(): void;
41
+ }
42
+
43
+ export interface ConnectionStats {
44
+ total: number;
45
+ active: number;
46
+ idle: number;
47
+ connections: Array<{
48
+ path: string;
49
+ refCount: number;
50
+ ageMs: number;
51
+ consumer: string;
52
+ }>;
53
+ }
54
+
55
+ export interface MigrationRegistryApi {
56
+ listNamespaces(): string[];
57
+ getMigrations(namespace: string): Migration[];
58
+ getDatabaseStatus(
59
+ db: Database,
60
+ namespace: string
61
+ ): {
62
+ namespace: string;
63
+ currentVersion: number;
64
+ targetVersion: number;
65
+ pendingMigrations: number;
66
+ migrations: Array<{ version: number; name: string; applied: boolean }>;
67
+ };
68
+ needsMigration(db: Database, namespace: string): boolean;
69
+ runMigrations(db: Database, namespace: string): Promise<void>;
70
+ getStatus(): Array<{
71
+ namespace: string;
72
+ currentVersion: number;
73
+ targetVersion: number;
74
+ pendingMigrations: number;
75
+ }>;
76
+ }
77
+
78
+ export interface ConnectionPoolApi {
79
+ getStats(): ConnectionStats;
80
+ closeAll(): void;
81
+ }
82
+
83
+ export interface LocaldbApi {
84
+ registerMigrations(namespace: string, migrations: Migration[]): void;
85
+ getDatabase(namespace: string, dbPath: string, consumer?: string): Promise<LocalDatabase>;
86
+ getRawDatabase(dbPath: string, consumer?: string): Database;
87
+ releaseDatabase(namespace: string, dbPath: string): Promise<void>;
88
+ getPoolStats(): ConnectionStats;
89
+ getMigrationStatus(): ReturnType<MigrationRegistryApi['getStatus']>;
90
+ listNamespaces(): string[];
91
+ closeAll(): Promise<void>;
92
+ }