@happyvertical/sql 0.79.0 → 0.80.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/AGENT.md +1 -1
- package/dist/backup/index.js +210 -188
- package/dist/backup/index.js.map +1 -1
- package/dist/chunks/alter-utils-K7tiuj5L.js +135 -0
- package/dist/chunks/alter-utils-K7tiuj5L.js.map +1 -0
- package/dist/chunks/duckdb-DykBBzd6.js +1056 -0
- package/dist/chunks/duckdb-DykBBzd6.js.map +1 -0
- package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js +115 -0
- package/dist/chunks/duckdb-schema-utils-B-XBP8QF.js.map +1 -0
- package/dist/chunks/postgres-cli-DzZcXSIG.js +272 -0
- package/dist/chunks/postgres-cli-DzZcXSIG.js.map +1 -0
- package/dist/chunks/schema-manager-a8RlxIE2.js +450 -0
- package/dist/chunks/schema-manager-a8RlxIE2.js.map +1 -0
- package/dist/chunks/sqlite-YgU9LCVy.js +1045 -0
- package/dist/chunks/sqlite-YgU9LCVy.js.map +1 -0
- package/dist/chunks/sqlite-native-BGtpx7Ro.js +902 -0
- package/dist/chunks/sqlite-native-BGtpx7Ro.js.map +1 -0
- package/dist/cli/claude-context.js +17 -17
- package/dist/cli/claude-context.js.map +1 -1
- package/dist/index.js +3244 -35
- package/dist/index.js.map +1 -1
- package/metadata.json +2 -2
- package/package.json +5 -5
- package/dist/chunks/duckdb-B6m78QdL.js +0 -924
- package/dist/chunks/duckdb-B6m78QdL.js.map +0 -1
- package/dist/chunks/index-CzdYvRFp.js +0 -3692
- package/dist/chunks/index-CzdYvRFp.js.map +0 -1
- package/dist/chunks/postgres-cli-Cr6bBZ3q.js +0 -209
- package/dist/chunks/postgres-cli-Cr6bBZ3q.js.map +0 -1
- package/dist/chunks/sqlite-CPCNeJIH.js +0 -937
- package/dist/chunks/sqlite-CPCNeJIH.js.map +0 -1
- package/dist/chunks/sqlite-native-DqVPKNUu.js +0 -1129
- package/dist/chunks/sqlite-native-DqVPKNUu.js.map +0 -1
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
//#region src/shared/utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Formats a database error to include all relevant details
|
|
4
|
+
*
|
|
5
|
+
* Database drivers often include additional properties beyond just `message`:
|
|
6
|
+
* - PostgreSQL: code, detail, hint, severity
|
|
7
|
+
* - DuckDB: may include additional context
|
|
8
|
+
* - SQLite/LibSQL: may include errno, code
|
|
9
|
+
*
|
|
10
|
+
* This function extracts all available error properties to provide
|
|
11
|
+
* better debugging information.
|
|
12
|
+
*
|
|
13
|
+
* @param error - The caught error object
|
|
14
|
+
* @returns Formatted error string with all available error details
|
|
15
|
+
*/
|
|
16
|
+
function formatDbError(error) {
|
|
17
|
+
const dbError = error;
|
|
18
|
+
const parts = [];
|
|
19
|
+
if (dbError.message) parts.push(dbError.message);
|
|
20
|
+
if (dbError.code) parts.push(`code=${dbError.code}`);
|
|
21
|
+
if (dbError.detail) parts.push(`detail=${dbError.detail}`);
|
|
22
|
+
if (dbError.hint) parts.push(`hint=${dbError.hint}`);
|
|
23
|
+
if (dbError.severity) parts.push(`severity=${dbError.severity}`);
|
|
24
|
+
if (dbError.errno !== void 0) parts.push(`errno=${dbError.errno}`);
|
|
25
|
+
return parts.length > 0 ? parts.join(", ") : String(error);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Map of valid SQL operators for use in WHERE clauses
|
|
29
|
+
*/
|
|
30
|
+
var VALID_OPERATORS = {
|
|
31
|
+
"=": "=",
|
|
32
|
+
">": ">",
|
|
33
|
+
">=": ">=",
|
|
34
|
+
"<": "<",
|
|
35
|
+
"<=": "<=",
|
|
36
|
+
"!=": "!=",
|
|
37
|
+
like: "LIKE",
|
|
38
|
+
in: "IN",
|
|
39
|
+
"not in": "NOT IN"
|
|
40
|
+
};
|
|
41
|
+
function isSimpleSqlIdentifier(field) {
|
|
42
|
+
return /^[a-zA-Z0-9_.]+$/.test(field);
|
|
43
|
+
}
|
|
44
|
+
function parseConditionKey(fullKey) {
|
|
45
|
+
const trimmed = fullKey.trim();
|
|
46
|
+
const lower = trimmed.toLowerCase();
|
|
47
|
+
for (const operator of [
|
|
48
|
+
"not in",
|
|
49
|
+
"like",
|
|
50
|
+
"in",
|
|
51
|
+
"!=",
|
|
52
|
+
">=",
|
|
53
|
+
"<=",
|
|
54
|
+
">",
|
|
55
|
+
"<",
|
|
56
|
+
"="
|
|
57
|
+
]) if (lower.endsWith(` ${operator}`)) return {
|
|
58
|
+
field: trimmed.slice(0, -(operator.length + 1)).trim(),
|
|
59
|
+
operator,
|
|
60
|
+
explicitOperator: true
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
field: trimmed,
|
|
64
|
+
operator: "=",
|
|
65
|
+
explicitOperator: false
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Builds a single condition for a WHERE clause
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
var buildCondition = (fullKey, value, currIndex, adapterType) => {
|
|
73
|
+
const { field, operator, explicitOperator } = parseConditionKey(fullKey);
|
|
74
|
+
if (!explicitOperator && !isSimpleSqlIdentifier(field)) throw new Error(`Invalid SQL identifier: ${field}`);
|
|
75
|
+
const sqlOperator = VALID_OPERATORS[operator] || "=";
|
|
76
|
+
const values = [];
|
|
77
|
+
let sql;
|
|
78
|
+
if (value === null) sql = `${field} IS ${sqlOperator === "=" ? "NULL" : "NOT NULL"}`;
|
|
79
|
+
else if ((sqlOperator === "IN" || sqlOperator === "NOT IN") && Array.isArray(value)) {
|
|
80
|
+
if (value.length === 0) throw new Error(`${sqlOperator} requires at least one value`);
|
|
81
|
+
sql = `${field} ${sqlOperator} (${value.map(() => `$${currIndex.value++}`).join(", ")})`;
|
|
82
|
+
values.push(...value);
|
|
83
|
+
} else if (value instanceof Date) {
|
|
84
|
+
if (adapterType === "duckdb" || adapterType === "json") sql = `${field} ${sqlOperator} CAST($${currIndex.value++} AS TIMESTAMP)`;
|
|
85
|
+
else sql = `${field} ${sqlOperator} $${currIndex.value++}`;
|
|
86
|
+
values.push(value.toISOString());
|
|
87
|
+
} else {
|
|
88
|
+
sql = `${field} ${sqlOperator} $${currIndex.value++}`;
|
|
89
|
+
values.push(value);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
sql,
|
|
93
|
+
values
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Builds a SQL WHERE clause with parameterized values and flexible operators
|
|
98
|
+
*
|
|
99
|
+
* @param where - Conditions as object (AND-only) or 2D array (OR/AND compound)
|
|
100
|
+
* @param startIndex - Starting index for parameter numbering (default: 1)
|
|
101
|
+
* @param adapterType - Database adapter type. Controls Date handling:
|
|
102
|
+
* - `'duckdb'` / `'json'`: Wraps Date values in `CAST($N AS TIMESTAMP)` to
|
|
103
|
+
* prevent DuckDB ANY-type inference issues.
|
|
104
|
+
* - `'sqlite'` / `'postgres'` / `undefined`: Passes ISO strings directly
|
|
105
|
+
* (these adapters handle ISO timestamp strings natively).
|
|
106
|
+
*
|
|
107
|
+
* Keys with explicit operator suffixes are treated as SQL field/expression
|
|
108
|
+
* text. Keep those keys developer-controlled; do not pass end-user input as a
|
|
109
|
+
* condition key.
|
|
110
|
+
*
|
|
111
|
+
* @returns Object containing the SQL clause and array of values
|
|
112
|
+
*
|
|
113
|
+
* @example Basic Usage (Object format - AND-only):
|
|
114
|
+
* ```typescript
|
|
115
|
+
* buildWhere({
|
|
116
|
+
* 'status': 'active', // equals operator is default
|
|
117
|
+
* 'price >': 100, // greater than
|
|
118
|
+
* 'stock <=': 5, // less than or equal
|
|
119
|
+
* 'category in': ['A', 'B'], // IN clause for arrays
|
|
120
|
+
* 'name like': '%shirt%' // LIKE for pattern matching
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @example 2D Array Format (OR/AND compound logic):
|
|
125
|
+
* ```typescript
|
|
126
|
+
* // WHERE (status = 'active' AND price > 100) OR (status = 'pending' AND priority = 'high')
|
|
127
|
+
* buildWhere([
|
|
128
|
+
* [{ status: 'active' }, { 'price >': 100 }],
|
|
129
|
+
* [{ status: 'pending' }, { priority: 'high' }]
|
|
130
|
+
* ]);
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
133
|
+
* @example NULL Handling:
|
|
134
|
+
* ```typescript
|
|
135
|
+
* buildWhere({
|
|
136
|
+
* 'deleted_at': null, // becomes "deleted_at IS NULL"
|
|
137
|
+
* 'updated_at !=': null, // becomes "updated_at IS NOT NULL"
|
|
138
|
+
* 'status': 'active' // regular comparison
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*
|
|
142
|
+
* @example Common Patterns:
|
|
143
|
+
* ```typescript
|
|
144
|
+
* // Price range
|
|
145
|
+
* buildWhere({
|
|
146
|
+
* 'price >=': 10,
|
|
147
|
+
* 'price <': 100
|
|
148
|
+
* });
|
|
149
|
+
*
|
|
150
|
+
* // Date filtering
|
|
151
|
+
* buildWhere({
|
|
152
|
+
* 'created_at >': startDate,
|
|
153
|
+
* 'created_at <=': endDate,
|
|
154
|
+
* 'deleted_at': null
|
|
155
|
+
* });
|
|
156
|
+
*
|
|
157
|
+
* // Search with LIKE
|
|
158
|
+
* buildWhere({
|
|
159
|
+
* 'title like': '%search%',
|
|
160
|
+
* 'description like': '%search%',
|
|
161
|
+
* 'status': 'published'
|
|
162
|
+
* });
|
|
163
|
+
*
|
|
164
|
+
* // Multiple values with IN
|
|
165
|
+
* buildWhere({
|
|
166
|
+
* 'role in': ['admin', 'editor'],
|
|
167
|
+
* 'active': true,
|
|
168
|
+
* 'last_login !=': null
|
|
169
|
+
* });
|
|
170
|
+
* ```
|
|
171
|
+
*
|
|
172
|
+
* The function handles:
|
|
173
|
+
* - Standard comparisons (=, >, >=, <, <=, !=)
|
|
174
|
+
* - NULL checks (IS NULL, IS NOT NULL)
|
|
175
|
+
* - IN clauses for arrays
|
|
176
|
+
* - LIKE for pattern matching
|
|
177
|
+
* - Object format: Multiple conditions combined with AND
|
|
178
|
+
* - 2D array format: Inner arrays ANDed, outer array ORed
|
|
179
|
+
*/
|
|
180
|
+
var buildWhere = (where, startIndex = 1, adapterType) => {
|
|
181
|
+
let sql = "";
|
|
182
|
+
const values = [];
|
|
183
|
+
const currIndex = { value: startIndex };
|
|
184
|
+
if (Array.isArray(where)) {
|
|
185
|
+
if (where.length === 0) return {
|
|
186
|
+
sql: "",
|
|
187
|
+
values: []
|
|
188
|
+
};
|
|
189
|
+
const orGroups = [];
|
|
190
|
+
for (const andGroup of where) {
|
|
191
|
+
if (!Array.isArray(andGroup) || andGroup.length === 0) continue;
|
|
192
|
+
const andConditions = [];
|
|
193
|
+
for (const conditionObj of andGroup) for (const [fullKey, value] of Object.entries(conditionObj)) {
|
|
194
|
+
const result = buildCondition(fullKey, value, currIndex, adapterType);
|
|
195
|
+
andConditions.push(result.sql);
|
|
196
|
+
values.push(...result.values);
|
|
197
|
+
}
|
|
198
|
+
if (andConditions.length > 0) orGroups.push(`(${andConditions.join(" AND ")})`);
|
|
199
|
+
}
|
|
200
|
+
if (orGroups.length > 0) sql = `WHERE ${orGroups.join(" OR ")}`;
|
|
201
|
+
return {
|
|
202
|
+
sql,
|
|
203
|
+
values
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (where && Object.keys(where).length > 0) {
|
|
207
|
+
sql = "WHERE ";
|
|
208
|
+
for (const [fullKey, value] of Object.entries(where)) {
|
|
209
|
+
const result = buildCondition(fullKey, value, currIndex, adapterType);
|
|
210
|
+
if (sql !== "WHERE ") sql += " AND ";
|
|
211
|
+
sql += result.sql;
|
|
212
|
+
values.push(...result.values);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
sql,
|
|
217
|
+
values
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/schema-manager.ts
|
|
222
|
+
/**
|
|
223
|
+
* Manages schema initialization from JSON manifests with dependency resolution
|
|
224
|
+
*
|
|
225
|
+
* Handles topological sorting of schema dependencies, table creation,
|
|
226
|
+
* index and trigger setup, and version tracking to avoid redundant re-initialization.
|
|
227
|
+
*/
|
|
228
|
+
var DatabaseSchemaManager = class DatabaseSchemaManager {
|
|
229
|
+
static initializationLock = /* @__PURE__ */ new Map();
|
|
230
|
+
initializedSchemas = /* @__PURE__ */ new Set();
|
|
231
|
+
schemaVersions = /* @__PURE__ */ new Map();
|
|
232
|
+
/**
|
|
233
|
+
* Initializes database schemas from a JSON manifest with dependency resolution
|
|
234
|
+
*
|
|
235
|
+
* Schemas are topologically sorted by their declared dependencies and
|
|
236
|
+
* initialized in order. Supports legacy raw-SQL schemas via the `schema` option,
|
|
237
|
+
* JSON manifests via the `manifest` option, and schema overrides for extending
|
|
238
|
+
* base schemas.
|
|
239
|
+
*
|
|
240
|
+
* @param db - Database interface to execute DDL against
|
|
241
|
+
* @param options - Schema initialization options (manifest, overrides, force, debug)
|
|
242
|
+
* @returns Result with lists of initialized/skipped schemas, errors, and execution time
|
|
243
|
+
*/
|
|
244
|
+
async initializeSchemas(db, options) {
|
|
245
|
+
const startTime = Date.now();
|
|
246
|
+
const result = {
|
|
247
|
+
initialized: [],
|
|
248
|
+
skipped: [],
|
|
249
|
+
errors: [],
|
|
250
|
+
executionTime: 0
|
|
251
|
+
};
|
|
252
|
+
try {
|
|
253
|
+
let schemas = {};
|
|
254
|
+
if (options.schema) {
|
|
255
|
+
console.log("[schema] Legacy SQL schema provided, converting to manifest format");
|
|
256
|
+
if (db.syncSchema) {
|
|
257
|
+
await db.syncSchema(options.schema);
|
|
258
|
+
result.initialized.push("legacy-sql");
|
|
259
|
+
}
|
|
260
|
+
result.executionTime = Date.now() - startTime;
|
|
261
|
+
return result;
|
|
262
|
+
}
|
|
263
|
+
if (options.manifest) schemas = { ...options.manifest.schemas };
|
|
264
|
+
if (options.overrides) schemas = {
|
|
265
|
+
...schemas,
|
|
266
|
+
...options.overrides
|
|
267
|
+
};
|
|
268
|
+
if (Object.keys(schemas).length === 0) {
|
|
269
|
+
console.warn("[schema] No schemas provided for initialization");
|
|
270
|
+
result.executionTime = Date.now() - startTime;
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
const dependencyGraph = this.buildDependencyGraph(schemas);
|
|
274
|
+
const initializationOrder = this.resolveDependencies(dependencyGraph);
|
|
275
|
+
if (options.debug) console.log("[schema] Initialization order:", initializationOrder);
|
|
276
|
+
for (const schemaName of initializationOrder) {
|
|
277
|
+
const schema = schemas[schemaName];
|
|
278
|
+
if (!schema) continue;
|
|
279
|
+
try {
|
|
280
|
+
await this.initializeSchema(db, schemaName, schema, {
|
|
281
|
+
force: options.force,
|
|
282
|
+
debug: options.debug
|
|
283
|
+
});
|
|
284
|
+
result.initialized.push(schemaName);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
287
|
+
result.errors.push({
|
|
288
|
+
schema: schemaName,
|
|
289
|
+
error: errorMessage
|
|
290
|
+
});
|
|
291
|
+
if (options.debug) console.error(`[schema] Failed to initialize ${schemaName}:`, error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
result.executionTime = Date.now() - startTime;
|
|
295
|
+
if (options.debug) console.log("[schema] Initialization complete:", result);
|
|
296
|
+
return result;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
result.executionTime = Date.now() - startTime;
|
|
299
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
300
|
+
result.errors.push({
|
|
301
|
+
schema: "dependency-resolution",
|
|
302
|
+
error: errorMessage
|
|
303
|
+
});
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Initialize a single schema
|
|
309
|
+
*/
|
|
310
|
+
async initializeSchema(db, schemaName, schema, options) {
|
|
311
|
+
const { tableName, version } = schema;
|
|
312
|
+
const lockKey = `${schemaName}:${tableName}`;
|
|
313
|
+
if (DatabaseSchemaManager.initializationLock.has(lockKey)) {
|
|
314
|
+
await DatabaseSchemaManager.initializationLock.get(lockKey);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (!options.force && this.isSchemaUpToDate(schemaName, version)) {
|
|
318
|
+
if (options.debug) console.log(`[schema] Skipping ${schemaName} - already up to date`);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const initPromise = this.performSchemaInitialization(db, schemaName, schema, options);
|
|
322
|
+
DatabaseSchemaManager.initializationLock.set(lockKey, initPromise);
|
|
323
|
+
try {
|
|
324
|
+
await initPromise;
|
|
325
|
+
this.markSchemaInitialized(schemaName, version);
|
|
326
|
+
} finally {
|
|
327
|
+
DatabaseSchemaManager.initializationLock.delete(lockKey);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Perform the actual schema initialization
|
|
332
|
+
*/
|
|
333
|
+
async performSchemaInitialization(db, schemaName, schema, options) {
|
|
334
|
+
const { tableName, columns, indexes, triggers } = schema;
|
|
335
|
+
if (options.debug) console.log(`[schema] Initializing ${schemaName} (${tableName})`);
|
|
336
|
+
if (!await db.tableExists(tableName)) {
|
|
337
|
+
await this.createTable(db, schema);
|
|
338
|
+
for (const index of indexes) await this.createIndex(db, tableName, index);
|
|
339
|
+
for (const trigger of triggers) await this.createTrigger(db, trigger);
|
|
340
|
+
} else if (options.force) {
|
|
341
|
+
await db.query(`DROP TABLE IF EXISTS ${tableName}`);
|
|
342
|
+
await this.createTable(db, schema);
|
|
343
|
+
for (const index of indexes) await this.createIndex(db, tableName, index);
|
|
344
|
+
for (const trigger of triggers) await this.createTrigger(db, trigger);
|
|
345
|
+
} else await this.updateSchemaIfNeeded(db, schema);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Create table from schema definition
|
|
349
|
+
*/
|
|
350
|
+
async createTable(db, schema) {
|
|
351
|
+
const { tableName, columns, foreignKeys } = schema;
|
|
352
|
+
const columnDefinitions = [];
|
|
353
|
+
for (const [columnName, columnDef] of Object.entries(columns)) {
|
|
354
|
+
let def = `${columnName} ${columnDef.type}`;
|
|
355
|
+
if (columnDef.primaryKey) def += " PRIMARY KEY";
|
|
356
|
+
if (columnDef.unique && !columnDef.primaryKey) def += " UNIQUE";
|
|
357
|
+
if (columnDef.notNull) def += " NOT NULL";
|
|
358
|
+
if (columnDef.defaultValue !== void 0) def += ` DEFAULT ${columnDef.defaultValue}`;
|
|
359
|
+
if (columnDef.check) def += ` CHECK (${columnDef.check})`;
|
|
360
|
+
columnDefinitions.push(def);
|
|
361
|
+
}
|
|
362
|
+
for (const fk of foreignKeys) {
|
|
363
|
+
let fkDef = `FOREIGN KEY (${fk.column}) REFERENCES ${fk.referencesTable}(${fk.referencesColumn})`;
|
|
364
|
+
if (fk.onDelete) fkDef += ` ON DELETE ${fk.onDelete}`;
|
|
365
|
+
if (fk.onUpdate) fkDef += ` ON UPDATE ${fk.onUpdate}`;
|
|
366
|
+
columnDefinitions.push(fkDef);
|
|
367
|
+
}
|
|
368
|
+
const createTableSQL = `CREATE TABLE ${tableName} (\n ${columnDefinitions.join(",\n ")}\n)`;
|
|
369
|
+
await db.query(createTableSQL);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Create index from definition
|
|
373
|
+
*/
|
|
374
|
+
async createIndex(db, tableName, index) {
|
|
375
|
+
const uniqueClause = index.unique ? "UNIQUE " : "";
|
|
376
|
+
const whereClause = index.where ? ` WHERE ${index.where}` : "";
|
|
377
|
+
const createIndexSQL = `CREATE ${uniqueClause}INDEX ${index.name} ON ${tableName} (${index.columns.join(", ")})${whereClause}`;
|
|
378
|
+
await db.query(createIndexSQL);
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Create trigger from definition
|
|
382
|
+
*/
|
|
383
|
+
async createTrigger(db, trigger) {
|
|
384
|
+
const conditionClause = trigger.condition ? ` WHEN ${trigger.condition}` : "";
|
|
385
|
+
const createTriggerSQL = `CREATE TRIGGER ${trigger.name} ${trigger.when} ${trigger.event} ON ${trigger.table}${conditionClause} BEGIN ${trigger.body} END`;
|
|
386
|
+
await db.query(createTriggerSQL);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Update schema if changes are detected
|
|
390
|
+
*/
|
|
391
|
+
async updateSchemaIfNeeded(_db, schema) {
|
|
392
|
+
console.log(`[schema] Schema update logic not yet implemented for ${schema.tableName}`);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Build dependency graph from schemas
|
|
396
|
+
*/
|
|
397
|
+
buildDependencyGraph(schemas) {
|
|
398
|
+
const graph = /* @__PURE__ */ new Map();
|
|
399
|
+
for (const [schemaName, schema] of Object.entries(schemas)) {
|
|
400
|
+
const dependencies = schema.dependencies.filter((dep) => Object.values(schemas).some((s) => s.tableName === dep));
|
|
401
|
+
graph.set(schemaName, dependencies.map((dep) => Object.entries(schemas).find(([_, s]) => s.tableName === dep)?.[0] || dep));
|
|
402
|
+
}
|
|
403
|
+
return graph;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Resolve dependencies using topological sort
|
|
407
|
+
*/
|
|
408
|
+
resolveDependencies(graph) {
|
|
409
|
+
const resolved = [];
|
|
410
|
+
const visited = /* @__PURE__ */ new Set();
|
|
411
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
412
|
+
const visit = (node) => {
|
|
413
|
+
if (visiting.has(node)) throw new Error(`Circular dependency detected involving ${node}`);
|
|
414
|
+
if (visited.has(node)) return;
|
|
415
|
+
visiting.add(node);
|
|
416
|
+
const dependencies = graph.get(node) || [];
|
|
417
|
+
for (const dep of dependencies) if (graph.has(dep)) visit(dep);
|
|
418
|
+
visiting.delete(node);
|
|
419
|
+
visited.add(node);
|
|
420
|
+
resolved.push(node);
|
|
421
|
+
};
|
|
422
|
+
for (const node of graph.keys()) if (!visited.has(node)) visit(node);
|
|
423
|
+
return resolved;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Check if schema is up to date
|
|
427
|
+
*/
|
|
428
|
+
isSchemaUpToDate(schemaName, version) {
|
|
429
|
+
return this.initializedSchemas.has(schemaName) && this.schemaVersions.get(schemaName) === version;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Mark schema as initialized
|
|
433
|
+
*/
|
|
434
|
+
markSchemaInitialized(schemaName, version) {
|
|
435
|
+
this.initializedSchemas.add(schemaName);
|
|
436
|
+
this.schemaVersions.set(schemaName, version);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Reset initialization state (for testing)
|
|
440
|
+
*/
|
|
441
|
+
reset() {
|
|
442
|
+
this.initializedSchemas.clear();
|
|
443
|
+
this.schemaVersions.clear();
|
|
444
|
+
DatabaseSchemaManager.initializationLock.clear();
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
//#endregion
|
|
448
|
+
export { parseConditionKey as i, buildWhere as n, formatDbError as r, DatabaseSchemaManager as t };
|
|
449
|
+
|
|
450
|
+
//# sourceMappingURL=schema-manager-a8RlxIE2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-manager-a8RlxIE2.js","names":[],"sources":["../../src/shared/utils.ts","../../src/schema-manager.ts"],"sourcesContent":["/**\n * Shared SQL utilities that work in both browser and Node.js environments\n */\n\nimport type { WhereClause } from './types.js';\n\n/**\n * Formats a database error to include all relevant details\n *\n * Database drivers often include additional properties beyond just `message`:\n * - PostgreSQL: code, detail, hint, severity\n * - DuckDB: may include additional context\n * - SQLite/LibSQL: may include errno, code\n *\n * This function extracts all available error properties to provide\n * better debugging information.\n *\n * @param error - The caught error object\n * @returns Formatted error string with all available error details\n */\nexport function formatDbError(error: unknown): string {\n const dbError = error as {\n message?: string;\n code?: string;\n detail?: string;\n hint?: string;\n severity?: string;\n errno?: number;\n cause?: unknown;\n };\n\n const parts: string[] = [];\n\n if (dbError.message) parts.push(dbError.message);\n if (dbError.code) parts.push(`code=${dbError.code}`);\n if (dbError.detail) parts.push(`detail=${dbError.detail}`);\n if (dbError.hint) parts.push(`hint=${dbError.hint}`);\n if (dbError.severity) parts.push(`severity=${dbError.severity}`);\n if (dbError.errno !== undefined) parts.push(`errno=${dbError.errno}`);\n\n return parts.length > 0 ? parts.join(', ') : String(error);\n}\n\n/**\n * Map of valid SQL operators for use in WHERE clauses\n */\nconst VALID_OPERATORS = {\n '=': '=',\n '>': '>',\n '>=': '>=',\n '<': '<',\n '<=': '<=',\n '!=': '!=',\n like: 'LIKE',\n in: 'IN',\n 'not in': 'NOT IN',\n} as const;\n\nfunction isSimpleSqlIdentifier(field: string): boolean {\n return /^[a-zA-Z0-9_.]+$/.test(field);\n}\n\nexport function parseConditionKey(fullKey: string): {\n field: string;\n operator: string;\n explicitOperator: boolean;\n} {\n const trimmed = fullKey.trim();\n const lower = trimmed.toLowerCase();\n const operators = ['not in', 'like', 'in', '!=', '>=', '<=', '>', '<', '='];\n\n for (const operator of operators) {\n if (lower.endsWith(` ${operator}`)) {\n return {\n field: trimmed.slice(0, -(operator.length + 1)).trim(),\n operator,\n explicitOperator: true,\n };\n }\n }\n\n return { field: trimmed, operator: '=', explicitOperator: false };\n}\n\n/**\n * SQL adapter type for adapter-specific query generation\n */\nexport type SqlAdapterType = 'sqlite' | 'postgres' | 'duckdb' | 'json';\n\n/**\n * Builds a single condition for a WHERE clause\n * @internal\n */\nconst buildCondition = (\n fullKey: string,\n value: any,\n currIndex: { value: number },\n adapterType?: SqlAdapterType,\n): { sql: string; values: any[] } => {\n const { field, operator, explicitOperator } = parseConditionKey(fullKey);\n if (!explicitOperator && !isSimpleSqlIdentifier(field)) {\n throw new Error(`Invalid SQL identifier: ${field}`);\n }\n const sqlOperator =\n VALID_OPERATORS[operator as keyof typeof VALID_OPERATORS] || '=';\n const values: any[] = [];\n\n let sql: string;\n\n if (value === null) {\n sql = `${field} IS ${sqlOperator === '=' ? 'NULL' : 'NOT NULL'}`;\n } else if (\n (sqlOperator === 'IN' || sqlOperator === 'NOT IN') &&\n Array.isArray(value)\n ) {\n if (value.length === 0) {\n throw new Error(`${sqlOperator} requires at least one value`);\n }\n const placeholders = value.map(() => `$${currIndex.value++}`).join(', ');\n sql = `${field} ${sqlOperator} (${placeholders})`;\n values.push(...value);\n } else if (value instanceof Date) {\n // DuckDB/JSON need CAST to TIMESTAMP to prevent ANY type inference (issue #540)\n // SQLite/PostgreSQL handle ISO strings natively without CAST\n const needsCast = adapterType === 'duckdb' || adapterType === 'json';\n if (needsCast) {\n sql = `${field} ${sqlOperator} CAST($${currIndex.value++} AS TIMESTAMP)`;\n } else {\n sql = `${field} ${sqlOperator} $${currIndex.value++}`;\n }\n values.push(value.toISOString());\n } else {\n sql = `${field} ${sqlOperator} $${currIndex.value++}`;\n values.push(value);\n }\n\n return { sql, values };\n};\n\n/**\n * Builds a SQL WHERE clause with parameterized values and flexible operators\n *\n * @param where - Conditions as object (AND-only) or 2D array (OR/AND compound)\n * @param startIndex - Starting index for parameter numbering (default: 1)\n * @param adapterType - Database adapter type. Controls Date handling:\n * - `'duckdb'` / `'json'`: Wraps Date values in `CAST($N AS TIMESTAMP)` to\n * prevent DuckDB ANY-type inference issues.\n * - `'sqlite'` / `'postgres'` / `undefined`: Passes ISO strings directly\n * (these adapters handle ISO timestamp strings natively).\n *\n * Keys with explicit operator suffixes are treated as SQL field/expression\n * text. Keep those keys developer-controlled; do not pass end-user input as a\n * condition key.\n *\n * @returns Object containing the SQL clause and array of values\n *\n * @example Basic Usage (Object format - AND-only):\n * ```typescript\n * buildWhere({\n * 'status': 'active', // equals operator is default\n * 'price >': 100, // greater than\n * 'stock <=': 5, // less than or equal\n * 'category in': ['A', 'B'], // IN clause for arrays\n * 'name like': '%shirt%' // LIKE for pattern matching\n * });\n * ```\n *\n * @example 2D Array Format (OR/AND compound logic):\n * ```typescript\n * // WHERE (status = 'active' AND price > 100) OR (status = 'pending' AND priority = 'high')\n * buildWhere([\n * [{ status: 'active' }, { 'price >': 100 }],\n * [{ status: 'pending' }, { priority: 'high' }]\n * ]);\n * ```\n *\n * @example NULL Handling:\n * ```typescript\n * buildWhere({\n * 'deleted_at': null, // becomes \"deleted_at IS NULL\"\n * 'updated_at !=': null, // becomes \"updated_at IS NOT NULL\"\n * 'status': 'active' // regular comparison\n * });\n * ```\n *\n * @example Common Patterns:\n * ```typescript\n * // Price range\n * buildWhere({\n * 'price >=': 10,\n * 'price <': 100\n * });\n *\n * // Date filtering\n * buildWhere({\n * 'created_at >': startDate,\n * 'created_at <=': endDate,\n * 'deleted_at': null\n * });\n *\n * // Search with LIKE\n * buildWhere({\n * 'title like': '%search%',\n * 'description like': '%search%',\n * 'status': 'published'\n * });\n *\n * // Multiple values with IN\n * buildWhere({\n * 'role in': ['admin', 'editor'],\n * 'active': true,\n * 'last_login !=': null\n * });\n * ```\n *\n * The function handles:\n * - Standard comparisons (=, >, >=, <, <=, !=)\n * - NULL checks (IS NULL, IS NOT NULL)\n * - IN clauses for arrays\n * - LIKE for pattern matching\n * - Object format: Multiple conditions combined with AND\n * - 2D array format: Inner arrays ANDed, outer array ORed\n */\nexport const buildWhere = (\n where: WhereClause,\n startIndex = 1,\n adapterType?: SqlAdapterType,\n) => {\n let sql = '';\n const values: any[] = [];\n const currIndex = { value: startIndex };\n\n // Handle 2D array format for OR/AND compound logic\n if (Array.isArray(where)) {\n if (where.length === 0) {\n return { sql: '', values: [] };\n }\n\n const orGroups: string[] = [];\n\n for (const andGroup of where) {\n if (!Array.isArray(andGroup) || andGroup.length === 0) {\n continue;\n }\n\n const andConditions: string[] = [];\n\n for (const conditionObj of andGroup) {\n for (const [fullKey, value] of Object.entries(conditionObj)) {\n const result = buildCondition(fullKey, value, currIndex, adapterType);\n andConditions.push(result.sql);\n values.push(...result.values);\n }\n }\n\n if (andConditions.length > 0) {\n // Wrap AND group in parentheses\n orGroups.push(`(${andConditions.join(' AND ')})`);\n }\n }\n\n if (orGroups.length > 0) {\n sql = `WHERE ${orGroups.join(' OR ')}`;\n }\n\n return { sql, values };\n }\n\n // Handle object format (original behavior - AND-only)\n if (where && Object.keys(where).length > 0) {\n sql = 'WHERE ';\n for (const [fullKey, value] of Object.entries(where)) {\n const result = buildCondition(fullKey, value, currIndex, adapterType);\n\n if (sql !== 'WHERE ') {\n sql += ' AND ';\n }\n\n sql += result.sql;\n values.push(...result.values);\n }\n }\n\n return { sql, values };\n};\n","/**\n * Schema management and dependency resolution for JSON manifests\n * Moved from SMRT package to SQL package for proper separation of concerns\n */\n\nimport type {\n DatabaseInterface,\n IndexDefinition,\n SchemaDefinition,\n SchemaInitializationOptions,\n TriggerDefinition,\n} from './shared/types.js';\n\n/**\n * Result returned by DatabaseSchemaManager.initializeSchemas()\n *\n * Contains the list of schemas that were successfully initialized,\n * any that were skipped (already up to date), errors encountered,\n * and the total execution time in milliseconds.\n */\nexport interface SchemaInitializationResult {\n initialized: string[];\n skipped: string[];\n errors: Array<{ schema: string; error: string }>;\n executionTime: number;\n}\n\n/**\n * Manages schema initialization from JSON manifests with dependency resolution\n *\n * Handles topological sorting of schema dependencies, table creation,\n * index and trigger setup, and version tracking to avoid redundant re-initialization.\n */\nexport class DatabaseSchemaManager {\n private static initializationLock = new Map<string, Promise<void>>();\n private initializedSchemas = new Set<string>();\n private schemaVersions = new Map<string, string>();\n\n /**\n * Initializes database schemas from a JSON manifest with dependency resolution\n *\n * Schemas are topologically sorted by their declared dependencies and\n * initialized in order. Supports legacy raw-SQL schemas via the `schema` option,\n * JSON manifests via the `manifest` option, and schema overrides for extending\n * base schemas.\n *\n * @param db - Database interface to execute DDL against\n * @param options - Schema initialization options (manifest, overrides, force, debug)\n * @returns Result with lists of initialized/skipped schemas, errors, and execution time\n */\n async initializeSchemas(\n db: DatabaseInterface,\n options: SchemaInitializationOptions,\n ): Promise<SchemaInitializationResult> {\n const startTime = Date.now();\n const result: SchemaInitializationResult = {\n initialized: [],\n skipped: [],\n errors: [],\n executionTime: 0,\n };\n\n try {\n let schemas: Record<string, SchemaDefinition> = {};\n\n // Handle legacy SQL schema format\n if (options.schema) {\n console.log(\n '[schema] Legacy SQL schema provided, converting to manifest format',\n );\n // For legacy support, we'll still use the original syncSchema method\n if (db.syncSchema) {\n await db.syncSchema(options.schema);\n result.initialized.push('legacy-sql');\n }\n result.executionTime = Date.now() - startTime;\n return result;\n }\n\n // Handle JSON manifest format\n if (options.manifest) {\n schemas = { ...options.manifest.schemas };\n }\n\n // Apply schema overrides\n if (options.overrides) {\n schemas = { ...schemas, ...options.overrides };\n }\n\n if (Object.keys(schemas).length === 0) {\n console.warn('[schema] No schemas provided for initialization');\n result.executionTime = Date.now() - startTime;\n return result;\n }\n\n // Build dependency graph\n const dependencyGraph = this.buildDependencyGraph(schemas);\n\n // Get initialization order\n const initializationOrder = this.resolveDependencies(dependencyGraph);\n\n if (options.debug) {\n console.log('[schema] Initialization order:', initializationOrder);\n }\n\n // Initialize schemas in dependency order\n for (const schemaName of initializationOrder) {\n const schema = schemas[schemaName];\n if (!schema) continue;\n\n try {\n await this.initializeSchema(db, schemaName, schema, {\n force: options.force,\n debug: options.debug,\n });\n result.initialized.push(schemaName);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n result.errors.push({ schema: schemaName, error: errorMessage });\n\n if (options.debug) {\n console.error(\n `[schema] Failed to initialize ${schemaName}:`,\n error,\n );\n }\n }\n }\n\n result.executionTime = Date.now() - startTime;\n\n if (options.debug) {\n console.log('[schema] Initialization complete:', result);\n }\n\n return result;\n } catch (error) {\n result.executionTime = Date.now() - startTime;\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n result.errors.push({\n schema: 'dependency-resolution',\n error: errorMessage,\n });\n return result;\n }\n }\n\n /**\n * Initialize a single schema\n */\n private async initializeSchema(\n db: DatabaseInterface,\n schemaName: string,\n schema: SchemaDefinition,\n options: { force?: boolean; debug?: boolean },\n ): Promise<void> {\n const { tableName, version } = schema;\n const lockKey = `${schemaName}:${tableName}`;\n\n // Check if already being initialized\n if (DatabaseSchemaManager.initializationLock.has(lockKey)) {\n await DatabaseSchemaManager.initializationLock.get(lockKey);\n return;\n }\n\n // Check if already initialized and up to date\n if (!options.force && this.isSchemaUpToDate(schemaName, version)) {\n if (options.debug) {\n console.log(`[schema] Skipping ${schemaName} - already up to date`);\n }\n return;\n }\n\n // Create initialization promise\n const initPromise = this.performSchemaInitialization(\n db,\n schemaName,\n schema,\n options,\n );\n DatabaseSchemaManager.initializationLock.set(lockKey, initPromise);\n\n try {\n await initPromise;\n this.markSchemaInitialized(schemaName, version);\n } finally {\n DatabaseSchemaManager.initializationLock.delete(lockKey);\n }\n }\n\n /**\n * Perform the actual schema initialization\n */\n private async performSchemaInitialization(\n db: DatabaseInterface,\n schemaName: string,\n schema: SchemaDefinition,\n options: { force?: boolean; debug?: boolean },\n ): Promise<void> {\n const { tableName, columns, indexes, triggers } = schema;\n\n if (options.debug) {\n console.log(`[schema] Initializing ${schemaName} (${tableName})`);\n }\n\n // Check if table exists\n const tableExists = await db.tableExists(tableName);\n\n if (!tableExists) {\n // Create table\n await this.createTable(db, schema);\n\n // Create indexes\n for (const index of indexes) {\n await this.createIndex(db, tableName, index);\n }\n\n // Create triggers\n for (const trigger of triggers) {\n await this.createTrigger(db, trigger);\n }\n } else if (options.force) {\n // Recreate table if forced\n await db.query(`DROP TABLE IF EXISTS ${tableName}`);\n await this.createTable(db, schema);\n\n for (const index of indexes) {\n await this.createIndex(db, tableName, index);\n }\n\n for (const trigger of triggers) {\n await this.createTrigger(db, trigger);\n }\n } else {\n // Table exists, check for schema changes\n await this.updateSchemaIfNeeded(db, schema);\n }\n }\n\n /**\n * Create table from schema definition\n */\n private async createTable(\n db: DatabaseInterface,\n schema: SchemaDefinition,\n ): Promise<void> {\n const { tableName, columns, foreignKeys } = schema;\n\n const columnDefinitions: string[] = [];\n\n // Add column definitions\n for (const [columnName, columnDef] of Object.entries(columns)) {\n let def = `${columnName} ${columnDef.type}`;\n\n if (columnDef.primaryKey) def += ' PRIMARY KEY';\n if (columnDef.unique && !columnDef.primaryKey) def += ' UNIQUE';\n if (columnDef.notNull) def += ' NOT NULL';\n if (columnDef.defaultValue !== undefined) {\n def += ` DEFAULT ${columnDef.defaultValue}`;\n }\n if (columnDef.check) def += ` CHECK (${columnDef.check})`;\n\n columnDefinitions.push(def);\n }\n\n // Add foreign key constraints\n for (const fk of foreignKeys) {\n let fkDef = `FOREIGN KEY (${fk.column}) REFERENCES ${fk.referencesTable}(${fk.referencesColumn})`;\n if (fk.onDelete) fkDef += ` ON DELETE ${fk.onDelete}`;\n if (fk.onUpdate) fkDef += ` ON UPDATE ${fk.onUpdate}`;\n columnDefinitions.push(fkDef);\n }\n\n const createTableSQL = `CREATE TABLE ${tableName} (\\n ${columnDefinitions.join(',\\n ')}\\n)`;\n await db.query(createTableSQL);\n }\n\n /**\n * Create index from definition\n */\n private async createIndex(\n db: DatabaseInterface,\n tableName: string,\n index: IndexDefinition,\n ): Promise<void> {\n const uniqueClause = index.unique ? 'UNIQUE ' : '';\n const whereClause = index.where ? ` WHERE ${index.where}` : '';\n const createIndexSQL = `CREATE ${uniqueClause}INDEX ${index.name} ON ${tableName} (${index.columns.join(', ')})${whereClause}`;\n\n await db.query(createIndexSQL);\n }\n\n /**\n * Create trigger from definition\n */\n private async createTrigger(\n db: DatabaseInterface,\n trigger: TriggerDefinition,\n ): Promise<void> {\n const conditionClause = trigger.condition\n ? ` WHEN ${trigger.condition}`\n : '';\n const createTriggerSQL = `CREATE TRIGGER ${trigger.name} ${trigger.when} ${trigger.event} ON ${trigger.table}${conditionClause} BEGIN ${trigger.body} END`;\n\n await db.query(createTriggerSQL);\n }\n\n /**\n * Update schema if changes are detected\n */\n private async updateSchemaIfNeeded(\n _db: DatabaseInterface,\n schema: SchemaDefinition,\n ): Promise<void> {\n // For now, just log that update is needed\n // In future, implement ALTER TABLE logic\n console.log(\n `[schema] Schema update logic not yet implemented for ${schema.tableName}`,\n );\n }\n\n /**\n * Build dependency graph from schemas\n */\n private buildDependencyGraph(\n schemas: Record<string, SchemaDefinition>,\n ): Map<string, string[]> {\n const graph = new Map<string, string[]>();\n\n for (const [schemaName, schema] of Object.entries(schemas)) {\n const dependencies = schema.dependencies.filter((dep) =>\n Object.values(schemas).some((s) => s.tableName === dep),\n );\n graph.set(\n schemaName,\n dependencies.map(\n (dep) =>\n Object.entries(schemas).find(\n ([_, s]) => s.tableName === dep,\n )?.[0] || dep,\n ),\n );\n }\n\n return graph;\n }\n\n /**\n * Resolve dependencies using topological sort\n */\n private resolveDependencies(graph: Map<string, string[]>): string[] {\n const resolved: string[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n const visit = (node: string) => {\n if (visiting.has(node)) {\n throw new Error(`Circular dependency detected involving ${node}`);\n }\n if (visited.has(node)) return;\n\n visiting.add(node);\n const dependencies = graph.get(node) || [];\n\n for (const dep of dependencies) {\n if (graph.has(dep)) {\n visit(dep);\n }\n }\n\n visiting.delete(node);\n visited.add(node);\n resolved.push(node);\n };\n\n for (const node of graph.keys()) {\n if (!visited.has(node)) {\n visit(node);\n }\n }\n\n return resolved;\n }\n\n /**\n * Check if schema is up to date\n */\n private isSchemaUpToDate(schemaName: string, version: string): boolean {\n return (\n this.initializedSchemas.has(schemaName) &&\n this.schemaVersions.get(schemaName) === version\n );\n }\n\n /**\n * Mark schema as initialized\n */\n private markSchemaInitialized(schemaName: string, version: string): void {\n this.initializedSchemas.add(schemaName);\n this.schemaVersions.set(schemaName, version);\n }\n\n /**\n * Reset initialization state (for testing)\n */\n reset(): void {\n this.initializedSchemas.clear();\n this.schemaVersions.clear();\n DatabaseSchemaManager.initializationLock.clear();\n }\n}\n"],"mappings":"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"}
|