easy-ping 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +17 -11
  2. package/dist/adapter-C3uFRpEN.d.cts +10 -0
  3. package/dist/adapter-DoCRPi9I.d.ts +10 -0
  4. package/dist/adapters/mysql.cjs +7 -4
  5. package/dist/adapters/mysql.cjs.map +1 -1
  6. package/dist/adapters/mysql.d.cts +3 -2
  7. package/dist/adapters/mysql.d.ts +3 -2
  8. package/dist/adapters/mysql.js +1 -1
  9. package/dist/adapters/postgres.js +3 -3
  10. package/dist/adapters/sqlite.cjs +8 -5
  11. package/dist/adapters/sqlite.cjs.map +1 -1
  12. package/dist/adapters/sqlite.d.cts +3 -2
  13. package/dist/adapters/sqlite.d.ts +3 -2
  14. package/dist/adapters/sqlite.js +1 -1
  15. package/dist/chunk-EROWLUL4.js +411 -0
  16. package/dist/chunk-EROWLUL4.js.map +1 -0
  17. package/dist/{chunk-HY47TQMY.js → chunk-HSB5T3LD.js} +11 -8
  18. package/dist/chunk-HSB5T3LD.js.map +1 -0
  19. package/dist/{chunk-M4R77BJD.js → chunk-UM2L26PV.js} +10 -7
  20. package/dist/chunk-UM2L26PV.js.map +1 -0
  21. package/dist/dialect-CvBJSydR.d.cts +10 -0
  22. package/dist/dialect-CvBJSydR.d.ts +10 -0
  23. package/dist/plugins/digests.cjs +19 -12
  24. package/dist/plugins/digests.cjs.map +1 -1
  25. package/dist/plugins/digests.js +5 -12
  26. package/dist/plugins/digests.js.map +1 -1
  27. package/dist/schema.cjs +334 -123
  28. package/dist/schema.cjs.map +1 -1
  29. package/dist/schema.d.cts +26 -1
  30. package/dist/schema.d.ts +26 -1
  31. package/dist/schema.js +3 -3
  32. package/package.json +1 -1
  33. package/dist/adapter-B7R4rXtJ.d.cts +0 -17
  34. package/dist/adapter-B7R4rXtJ.d.ts +0 -17
  35. package/dist/chunk-HY47TQMY.js.map +0 -1
  36. package/dist/chunk-M4R77BJD.js.map +0 -1
  37. package/dist/chunk-XIDVLYNQ.js +0 -209
  38. package/dist/chunk-XIDVLYNQ.js.map +0 -1
package/dist/schema.cjs CHANGED
@@ -60,6 +60,173 @@ var coreSchema = {
60
60
  }
61
61
  };
62
62
 
63
+ // src/schema/render-mysql.ts
64
+ var quote = (value) => `\`${value.split("`").join("``")}\``;
65
+ function stringType(field, table, spec) {
66
+ const keys = [
67
+ ...table.primaryKey ? [table.primaryKey] : [],
68
+ ...(table.indexes ?? []).map((index) => index.on)
69
+ ].filter((columns) => columns.includes(field));
70
+ if (keys.some((columns) => columns.length > 1)) return "VARCHAR(255)";
71
+ if (keys.length > 0) return "VARCHAR(768)";
72
+ if (spec.default !== void 0) return "VARCHAR(255)";
73
+ return "TEXT";
74
+ }
75
+ function columnType(field, table, spec) {
76
+ switch (spec.type) {
77
+ case "string":
78
+ return stringType(field, table, spec);
79
+ case "number":
80
+ return "INT";
81
+ case "boolean":
82
+ return "TINYINT(1)";
83
+ case "date":
84
+ return "DATETIME(3)";
85
+ case "json":
86
+ return "JSON";
87
+ }
88
+ }
89
+ var defaultClause = (spec) => {
90
+ if (spec.defaultNow) return " DEFAULT CURRENT_TIMESTAMP(3)";
91
+ if (spec.default === void 0) return "";
92
+ if (typeof spec.default === "string") return ` DEFAULT '${spec.default.replace(/'/g, "''")}'`;
93
+ if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
94
+ return ` DEFAULT ${spec.default}`;
95
+ };
96
+ function mysqlColumnDefinition(field, table, spec) {
97
+ const nullability = spec.required ? " NOT NULL" : "";
98
+ return `${quote(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause(spec)}`;
99
+ }
100
+ function renderMysqlDdl(schema, prefix = "") {
101
+ const statements = [];
102
+ for (const table of Object.values(schema)) {
103
+ const lines = Object.entries(table.fields).map(
104
+ ([field, spec]) => ` ${mysqlColumnDefinition(field, table, spec)}`
105
+ );
106
+ if (table.primaryKey?.length) {
107
+ lines.push(
108
+ ` PRIMARY KEY (${table.primaryKey.map((f) => quote(toSnakeCase(f))).join(", ")})`
109
+ );
110
+ }
111
+ for (const index of table.indexes ?? []) {
112
+ const name = quote(prefix + (index.name ?? `${table.tableName}_${index.on.join("_")}_idx`));
113
+ const columns = index.on.map((f) => quote(toSnakeCase(f))).join(", ");
114
+ lines.push(` ${index.unique ? "UNIQUE " : ""}INDEX ${name} (${columns})`);
115
+ }
116
+ statements.push(
117
+ `CREATE TABLE IF NOT EXISTS ${quote(prefix + table.tableName)} (
118
+ ${lines.join(",\n")}
119
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
120
+ );
121
+ }
122
+ return statements;
123
+ }
124
+
125
+ // src/schema/migrate-mysql.ts
126
+ var MYSQL_INTROSPECT_COLUMNS_SQL = `
127
+ SELECT TABLE_NAME AS table_name, COLUMN_NAME AS column_name, DATA_TYPE AS data_type
128
+ FROM information_schema.COLUMNS
129
+ WHERE TABLE_SCHEMA = DATABASE()
130
+ `;
131
+ var MYSQL_INTROSPECT_INDEXES_SQL = `
132
+ SELECT DISTINCT TABLE_NAME AS table_name, INDEX_NAME AS index_name
133
+ FROM information_schema.STATISTICS
134
+ WHERE TABLE_SCHEMA = DATABASE()
135
+ `;
136
+ var LIVE_TYPE = {
137
+ string: ["varchar", "char", "text", "tinytext", "mediumtext", "longtext"],
138
+ number: ["int", "integer", "bigint", "smallint", "mediumint", "tinyint"],
139
+ boolean: ["tinyint", "bit", "boolean"],
140
+ date: ["datetime", "timestamp"],
141
+ json: ["json", "longtext"]
142
+ };
143
+ var quote2 = (value) => `\`${value.split("`").join("``")}\``;
144
+ var indexName = (table, declaration) => declaration.name ?? `${table.tableName}_${declaration.on.join("_")}_idx`;
145
+ var lower = (value) => String(value).toLowerCase();
146
+ async function planMysqlMigration(query, schema, prefix = "") {
147
+ const [columnRows, indexRows] = await Promise.all([
148
+ query(MYSQL_INTROSPECT_COLUMNS_SQL, []),
149
+ query(MYSQL_INTROSPECT_INDEXES_SQL, [])
150
+ ]);
151
+ const columns = /* @__PURE__ */ new Map();
152
+ for (const row of columnRows.rows) {
153
+ const table = lower(row.table_name ?? row.TABLE_NAME);
154
+ const byColumn = columns.get(table) ?? /* @__PURE__ */ new Map();
155
+ byColumn.set(lower(row.column_name ?? row.COLUMN_NAME), lower(row.data_type ?? row.DATA_TYPE));
156
+ columns.set(table, byColumn);
157
+ }
158
+ const indexes = new Set(
159
+ indexRows.rows.map(
160
+ (row) => `${lower(row.table_name ?? row.TABLE_NAME)}.${lower(row.index_name ?? row.INDEX_NAME)}`
161
+ )
162
+ );
163
+ const statements = [];
164
+ const unsupported = [];
165
+ const createdTables = [];
166
+ for (const table of Object.values(schema)) {
167
+ const qualified = prefix + table.tableName;
168
+ const existing = columns.get(qualified.toLowerCase());
169
+ if (!existing || existing.size === 0) {
170
+ createdTables.push(qualified);
171
+ statements.push(...renderMysqlDdl({ [table.tableName]: table }, prefix));
172
+ continue;
173
+ }
174
+ for (const [field, spec] of Object.entries(table.fields)) {
175
+ const column = toSnakeCase(field);
176
+ const found = existing.get(column.toLowerCase());
177
+ if (found === void 0) {
178
+ const canFill = spec.defaultNow || spec.default !== void 0;
179
+ const definition = mysqlColumnDefinition(field, table, {
180
+ ...spec,
181
+ // A required column with nothing to fill existing rows with is added nullable.
182
+ required: Boolean(spec.required && canFill)
183
+ });
184
+ statements.push(`ALTER TABLE ${quote2(qualified)} ADD COLUMN ${definition}`);
185
+ if (spec.required && !canFill) {
186
+ unsupported.push(
187
+ `${qualified}.${column} is declared required but has no default; it was added nullable. Backfill it, then ALTER TABLE ... MODIFY ... NOT NULL yourself.`
188
+ );
189
+ }
190
+ continue;
191
+ }
192
+ if (!LIVE_TYPE[spec.type].includes(found)) {
193
+ unsupported.push(
194
+ `${qualified}.${column} is ${found} but ${spec.type} is declared. Changing a column type can lose data \u2014 do it yourself.`
195
+ );
196
+ }
197
+ }
198
+ for (const declaration of table.indexes ?? []) {
199
+ const name = prefix + indexName(table, declaration);
200
+ if (indexes.has(`${qualified.toLowerCase()}.${name.toLowerCase()}`)) continue;
201
+ const textColumn = declaration.on.find((field) => {
202
+ const live = existing.get(toSnakeCase(field).toLowerCase());
203
+ return live?.endsWith("text") ?? false;
204
+ });
205
+ if (textColumn) {
206
+ unsupported.push(
207
+ `${qualified}: index ${name} needs ${toSnakeCase(textColumn)} as VARCHAR, but it is TEXT, which MySQL cannot index without a prefix length. Change the column type, then rerun.`
208
+ );
209
+ continue;
210
+ }
211
+ const cols = declaration.on.map((field) => quote2(toSnakeCase(field))).join(", ");
212
+ statements.push(
213
+ `CREATE ${declaration.unique ? "UNIQUE " : ""}INDEX ${quote2(name)} ON ${quote2(qualified)} (${cols})`
214
+ );
215
+ }
216
+ for (const column of existing.keys()) {
217
+ const declared = Object.keys(table.fields).some(
218
+ (field) => toSnakeCase(field).toLowerCase() === column
219
+ );
220
+ if (!declared) {
221
+ unsupported.push(
222
+ `${qualified}.${column} exists but is not declared. Left alone \u2014 drop it yourself if it is ours and no longer needed.`
223
+ );
224
+ }
225
+ }
226
+ }
227
+ return { statements, unsupported, createdTables };
228
+ }
229
+
63
230
  // src/schema/render-sql.ts
64
231
  var SQL_TYPE = {
65
232
  string: "text",
@@ -68,8 +235,8 @@ var SQL_TYPE = {
68
235
  date: "timestamptz",
69
236
  json: "jsonb"
70
237
  };
71
- var quote = (value) => `"${value.replace(/"/g, '""')}"`;
72
- var defaultClause = (field) => {
238
+ var quote3 = (value) => `"${value.replace(/"/g, '""')}"`;
239
+ var defaultClause2 = (field) => {
73
240
  if (field.defaultNow) return " DEFAULT now()";
74
241
  if (field.default === void 0) return "";
75
242
  if (typeof field.default === "string") return ` DEFAULT '${field.default.replace(/'/g, "''")}'`;
@@ -78,25 +245,25 @@ var defaultClause = (field) => {
78
245
  function renderPostgresDdl(schema, prefix = "") {
79
246
  const statements = [];
80
247
  for (const table of Object.values(schema)) {
81
- const name = quote(prefix + table.tableName);
248
+ const name = quote3(prefix + table.tableName);
82
249
  const columns = Object.entries(table.fields).map(([field, spec]) => {
83
250
  const nullability = spec.required ? " NOT NULL" : "";
84
- return ` ${quote(toSnakeCase(field))} ${SQL_TYPE[spec.type]}${nullability}${defaultClause(spec)}`;
251
+ return ` ${quote3(toSnakeCase(field))} ${SQL_TYPE[spec.type]}${nullability}${defaultClause2(spec)}`;
85
252
  });
86
253
  if (table.primaryKey?.length) {
87
- const key = table.primaryKey.map((field) => quote(toSnakeCase(field))).join(", ");
254
+ const key = table.primaryKey.map((field) => quote3(toSnakeCase(field))).join(", ");
88
255
  columns.push(` PRIMARY KEY (${key})`);
89
256
  }
90
257
  statements.push(`CREATE TABLE IF NOT EXISTS ${name} (
91
258
  ${columns.join(",\n")}
92
259
  )`);
93
260
  for (const declaration of table.indexes ?? []) {
94
- const indexName2 = quote(
261
+ const indexName3 = quote3(
95
262
  prefix + (declaration.name ?? `${table.tableName}_${declaration.on.join("_")}_idx`)
96
263
  );
97
- const columns2 = declaration.on.map((field) => quote(toSnakeCase(field))).join(", ");
264
+ const columns2 = declaration.on.map((field) => quote3(toSnakeCase(field))).join(", ");
98
265
  const unique = declaration.unique ? "UNIQUE " : "";
99
- statements.push(`CREATE ${unique}INDEX IF NOT EXISTS ${indexName2} ON ${name} (${columns2})`);
266
+ statements.push(`CREATE ${unique}INDEX IF NOT EXISTS ${indexName3} ON ${name} (${columns2})`);
100
267
  }
101
268
  }
102
269
  return statements;
@@ -110,21 +277,21 @@ var SQL_TYPE2 = {
110
277
  date: "timestamptz",
111
278
  json: "jsonb"
112
279
  };
113
- var LIVE_TYPE = {
280
+ var LIVE_TYPE2 = {
114
281
  string: ["text", "character varying", "character"],
115
282
  number: ["integer", "bigint", "smallint"],
116
283
  boolean: ["boolean"],
117
284
  date: ["timestamp with time zone", "timestamp without time zone"],
118
285
  json: ["jsonb", "json"]
119
286
  };
120
- var quote2 = (value) => `"${value.replace(/"/g, '""')}"`;
121
- var defaultClause2 = (field) => {
287
+ var quote4 = (value) => `"${value.replace(/"/g, '""')}"`;
288
+ var defaultClause3 = (field) => {
122
289
  if (field.defaultNow) return " DEFAULT now()";
123
290
  if (field.default === void 0) return "";
124
291
  if (typeof field.default === "string") return ` DEFAULT '${field.default.replace(/'/g, "''")}'`;
125
292
  return ` DEFAULT ${field.default}`;
126
293
  };
127
- var indexName = (table, declaration) => declaration.name ?? `${table.tableName}_${declaration.on.join("_")}_idx`;
294
+ var indexName2 = (table, declaration) => declaration.name ?? `${table.tableName}_${declaration.on.join("_")}_idx`;
128
295
  function normaliseColumn(row) {
129
296
  if ("table" in row) return row;
130
297
  return {
@@ -166,7 +333,7 @@ async function planPostgresMigration(introspect, schema, prefix = "") {
166
333
  const canFill = spec.defaultNow || spec.default !== void 0;
167
334
  const nullability = spec.required && canFill ? " NOT NULL" : "";
168
335
  statements.push(
169
- `ALTER TABLE ${quote2(qualified)} ADD COLUMN IF NOT EXISTS ${quote2(column)} ${SQL_TYPE2[spec.type]}${nullability}${defaultClause2(spec)}`
336
+ `ALTER TABLE ${quote4(qualified)} ADD COLUMN IF NOT EXISTS ${quote4(column)} ${SQL_TYPE2[spec.type]}${nullability}${defaultClause3(spec)}`
170
337
  );
171
338
  if (spec.required && !canFill) {
172
339
  unsupported.push(
@@ -175,17 +342,17 @@ async function planPostgresMigration(introspect, schema, prefix = "") {
175
342
  }
176
343
  continue;
177
344
  }
178
- if (!LIVE_TYPE[spec.type].includes(found.type)) {
345
+ if (!LIVE_TYPE2[spec.type].includes(found.type)) {
179
346
  unsupported.push(
180
347
  `${qualified}.${column} is ${found.type} but ${spec.type} is declared (${SQL_TYPE2[spec.type]}). Changing a column type can lose data \u2014 do it yourself.`
181
348
  );
182
349
  }
183
350
  }
184
351
  for (const declaration of table.indexes ?? []) {
185
- const name = quote2(prefix + indexName(table, declaration));
186
- const columns = declaration.on.map((field) => quote2(toSnakeCase(field))).join(", ");
352
+ const name = quote4(prefix + indexName2(table, declaration));
353
+ const columns = declaration.on.map((field) => quote4(toSnakeCase(field))).join(", ");
187
354
  statements.push(
188
- `CREATE ${declaration.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${name} ON ${quote2(qualified)} (${columns})`
355
+ `CREATE ${declaration.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${name} ON ${quote4(qualified)} (${columns})`
189
356
  );
190
357
  }
191
358
  for (const column of existing.keys()) {
@@ -200,6 +367,151 @@ async function planPostgresMigration(introspect, schema, prefix = "") {
200
367
  return { statements, unsupported, createdTables };
201
368
  }
202
369
 
370
+ // src/schema/render-sqlite.ts
371
+ var SQLITE_TYPE = {
372
+ string: "TEXT",
373
+ number: "INTEGER",
374
+ boolean: "INTEGER",
375
+ // ISO-8601 with milliseconds and a trailing Z, the same 24 characters
376
+ // `Date#toISOString` writes, so `<=` on the text is `<=` on the instant.
377
+ date: "TEXT",
378
+ json: "TEXT"
379
+ };
380
+ var quote5 = (value) => `"${value.split('"').join('""')}"`;
381
+ var defaultClause4 = (spec) => {
382
+ if (spec.defaultNow) return " DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))";
383
+ if (spec.default === void 0) return "";
384
+ if (typeof spec.default === "string") return ` DEFAULT '${spec.default.replace(/'/g, "''")}'`;
385
+ if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
386
+ return ` DEFAULT ${spec.default}`;
387
+ };
388
+ function sqliteColumnDefinition(field, spec) {
389
+ const nullability = spec.required ? " NOT NULL" : "";
390
+ return `${quote5(toSnakeCase(field))} ${SQLITE_TYPE[spec.type]}${nullability}${defaultClause4(spec)}`;
391
+ }
392
+ function renderSqliteDdl(schema, prefix = "") {
393
+ const statements = [];
394
+ for (const table of Object.values(schema)) {
395
+ const name = quote5(prefix + table.tableName);
396
+ const columns = Object.entries(table.fields).map(
397
+ ([field, spec]) => ` ${sqliteColumnDefinition(field, spec)}`
398
+ );
399
+ if (table.primaryKey?.length) {
400
+ columns.push(
401
+ ` PRIMARY KEY (${table.primaryKey.map((f) => quote5(toSnakeCase(f))).join(", ")})`
402
+ );
403
+ }
404
+ statements.push(`CREATE TABLE IF NOT EXISTS ${name} (
405
+ ${columns.join(",\n")}
406
+ )`);
407
+ for (const index of table.indexes ?? []) {
408
+ const indexName3 = quote5(
409
+ prefix + (index.name ?? `${table.tableName}_${index.on.join("_")}_idx`)
410
+ );
411
+ const cols = index.on.map((f) => quote5(toSnakeCase(f))).join(", ");
412
+ statements.push(
413
+ `CREATE ${index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${indexName3} ON ${name} (${cols})`
414
+ );
415
+ }
416
+ }
417
+ return statements;
418
+ }
419
+
420
+ // src/schema/migrate-sqlite.ts
421
+ var SQLITE_INTROSPECT_SQL = `
422
+ SELECT m.name AS table_name, p.name AS column_name, p.type AS data_type
423
+ FROM sqlite_master AS m
424
+ JOIN pragma_table_info(m.name) AS p
425
+ WHERE m.type = 'table' AND m.name NOT LIKE 'sqlite_%'
426
+ `;
427
+ var quote6 = (value) => `"${value.split('"').join('""')}"`;
428
+ var REBUILD_SUFFIX = "__easy_ping_rebuild";
429
+ async function planSqliteMigration(query, schema, prefix = "") {
430
+ const { rows } = await query(SQLITE_INTROSPECT_SQL, []);
431
+ const live = /* @__PURE__ */ new Map();
432
+ for (const row of rows) {
433
+ const table = String(row.table_name);
434
+ const columns = live.get(table) ?? /* @__PURE__ */ new Map();
435
+ columns.set(String(row.column_name), String(row.data_type).toUpperCase());
436
+ live.set(table, columns);
437
+ }
438
+ const statements = [];
439
+ const unsupported = [];
440
+ const createdTables = [];
441
+ for (const table of Object.values(schema)) {
442
+ const qualified = prefix + table.tableName;
443
+ const existing = live.get(qualified);
444
+ if (!existing || existing.size === 0) {
445
+ createdTables.push(qualified);
446
+ statements.push(...renderSqliteDdl({ [table.tableName]: table }, prefix));
447
+ continue;
448
+ }
449
+ const missing = Object.entries(table.fields).filter(
450
+ ([field]) => !existing.has(toSnakeCase(field))
451
+ );
452
+ const relaxed = {
453
+ ...table,
454
+ fields: Object.fromEntries(
455
+ Object.entries(table.fields).map(([field, spec]) => {
456
+ const unfillable = !existing.has(toSnakeCase(field)) && spec.required && !spec.defaultNow && spec.default === void 0;
457
+ if (unfillable) {
458
+ unsupported.push(
459
+ `${qualified}.${toSnakeCase(field)} is declared required but has no default; it was added nullable. Backfill it; SQLite cannot add NOT NULL afterwards without a rebuild.`
460
+ );
461
+ }
462
+ return [field, unfillable ? { ...spec, required: false } : spec];
463
+ })
464
+ )
465
+ };
466
+ if (missing.some(([, spec]) => spec.defaultNow)) {
467
+ statements.push(...rebuild(relaxed, prefix, existing));
468
+ } else {
469
+ for (const [field] of missing) {
470
+ const spec = relaxed.fields[field];
471
+ if (!spec) continue;
472
+ statements.push(
473
+ `ALTER TABLE ${quote6(qualified)} ADD COLUMN ${sqliteColumnDefinition(field, spec)}`
474
+ );
475
+ }
476
+ statements.push(...renderSqliteDdl({ [table.tableName]: table }, prefix).slice(1));
477
+ }
478
+ for (const [field, spec] of Object.entries(table.fields)) {
479
+ const found = existing.get(toSnakeCase(field));
480
+ if (found !== void 0 && found !== SQLITE_TYPE[spec.type]) {
481
+ unsupported.push(
482
+ `${qualified}.${toSnakeCase(field)} is ${found || "untyped"} but ${SQLITE_TYPE[spec.type]} is declared. Changing a column type needs a table rebuild \u2014 do it yourself.`
483
+ );
484
+ }
485
+ }
486
+ for (const column of existing.keys()) {
487
+ const declared = Object.keys(table.fields).some((field) => toSnakeCase(field) === column);
488
+ if (!declared) {
489
+ unsupported.push(
490
+ `${qualified}.${column} exists but is not declared. Left alone \u2014 drop it yourself if it is ours and no longer needed.`
491
+ );
492
+ }
493
+ }
494
+ }
495
+ return { statements, unsupported, createdTables };
496
+ }
497
+ function rebuild(table, prefix, existing) {
498
+ const qualified = prefix + table.tableName;
499
+ const temp = qualified + REBUILD_SUFFIX;
500
+ const [createTemp = ""] = renderSqliteDdl(
501
+ { [table.tableName]: { ...table, tableName: table.tableName + REBUILD_SUFFIX, indexes: [] } },
502
+ prefix
503
+ );
504
+ const shared = Object.keys(table.fields).map(toSnakeCase).filter((column) => existing.has(column)).map(quote6).join(", ");
505
+ return [
506
+ `DROP TABLE IF EXISTS ${quote6(temp)}`,
507
+ createTemp,
508
+ `INSERT INTO ${quote6(temp)} (${shared}) SELECT ${shared} FROM ${quote6(qualified)}`,
509
+ `DROP TABLE ${quote6(qualified)}`,
510
+ `ALTER TABLE ${quote6(temp)} RENAME TO ${quote6(qualified)}`,
511
+ ...renderSqliteDdl({ [table.tableName]: table }, prefix).slice(1)
512
+ ];
513
+ }
514
+
203
515
  // src/schema/render-drizzle.ts
204
516
  var COLUMN_BUILDER = {
205
517
  string: "text",
@@ -264,115 +576,14 @@ ${body}
264
576
  `;
265
577
  }
266
578
 
267
- // src/schema/render-mysql.ts
268
- var quote3 = (value) => `\`${value.split("`").join("``")}\``;
269
- function stringType(field, table, spec) {
270
- const keys = [
271
- ...table.primaryKey ? [table.primaryKey] : [],
272
- ...(table.indexes ?? []).map((index) => index.on)
273
- ].filter((columns) => columns.includes(field));
274
- if (keys.some((columns) => columns.length > 1)) return "VARCHAR(255)";
275
- if (keys.length > 0) return "VARCHAR(768)";
276
- if (spec.default !== void 0) return "VARCHAR(255)";
277
- return "TEXT";
278
- }
279
- function columnType(field, table, spec) {
280
- switch (spec.type) {
281
- case "string":
282
- return stringType(field, table, spec);
283
- case "number":
284
- return "INT";
285
- case "boolean":
286
- return "TINYINT(1)";
287
- case "date":
288
- return "DATETIME(3)";
289
- case "json":
290
- return "JSON";
291
- }
292
- }
293
- var defaultClause3 = (spec) => {
294
- if (spec.defaultNow) return " DEFAULT CURRENT_TIMESTAMP(3)";
295
- if (spec.default === void 0) return "";
296
- if (typeof spec.default === "string") return ` DEFAULT '${spec.default.replace(/'/g, "''")}'`;
297
- if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
298
- return ` DEFAULT ${spec.default}`;
299
- };
300
- function renderMysqlDdl(schema, prefix = "") {
301
- const statements = [];
302
- for (const table of Object.values(schema)) {
303
- const lines = Object.entries(table.fields).map(([field, spec]) => {
304
- const nullability = spec.required ? " NOT NULL" : "";
305
- return ` ${quote3(toSnakeCase(field))} ${columnType(field, table, spec)}${nullability}${defaultClause3(spec)}`;
306
- });
307
- if (table.primaryKey?.length) {
308
- lines.push(
309
- ` PRIMARY KEY (${table.primaryKey.map((f) => quote3(toSnakeCase(f))).join(", ")})`
310
- );
311
- }
312
- for (const index of table.indexes ?? []) {
313
- const name = quote3(prefix + (index.name ?? `${table.tableName}_${index.on.join("_")}_idx`));
314
- const columns = index.on.map((f) => quote3(toSnakeCase(f))).join(", ");
315
- lines.push(` ${index.unique ? "UNIQUE " : ""}INDEX ${name} (${columns})`);
316
- }
317
- statements.push(
318
- `CREATE TABLE IF NOT EXISTS ${quote3(prefix + table.tableName)} (
319
- ${lines.join(",\n")}
320
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
321
- );
322
- }
323
- return statements;
324
- }
325
-
326
- // src/schema/render-sqlite.ts
327
- var SQL_TYPE3 = {
328
- string: "TEXT",
329
- number: "INTEGER",
330
- boolean: "INTEGER",
331
- // ISO-8601 with milliseconds and a trailing Z, the same 24 characters
332
- // `Date#toISOString` writes, so `<=` on the text is `<=` on the instant.
333
- date: "TEXT",
334
- json: "TEXT"
335
- };
336
- var quote4 = (value) => `"${value.split('"').join('""')}"`;
337
- var defaultClause4 = (spec) => {
338
- if (spec.defaultNow) return " DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))";
339
- if (spec.default === void 0) return "";
340
- if (typeof spec.default === "string") return ` DEFAULT '${spec.default.replace(/'/g, "''")}'`;
341
- if (typeof spec.default === "boolean") return ` DEFAULT ${spec.default ? 1 : 0}`;
342
- return ` DEFAULT ${spec.default}`;
343
- };
344
- function renderSqliteDdl(schema, prefix = "") {
345
- const statements = [];
346
- for (const table of Object.values(schema)) {
347
- const name = quote4(prefix + table.tableName);
348
- const columns = Object.entries(table.fields).map(([field, spec]) => {
349
- const nullability = spec.required ? " NOT NULL" : "";
350
- return ` ${quote4(toSnakeCase(field))} ${SQL_TYPE3[spec.type]}${nullability}${defaultClause4(spec)}`;
351
- });
352
- if (table.primaryKey?.length) {
353
- columns.push(
354
- ` PRIMARY KEY (${table.primaryKey.map((f) => quote4(toSnakeCase(f))).join(", ")})`
355
- );
356
- }
357
- statements.push(`CREATE TABLE IF NOT EXISTS ${name} (
358
- ${columns.join(",\n")}
359
- )`);
360
- for (const index of table.indexes ?? []) {
361
- const indexName2 = quote4(
362
- prefix + (index.name ?? `${table.tableName}_${index.on.join("_")}_idx`)
363
- );
364
- const cols = index.on.map((f) => quote4(toSnakeCase(f))).join(", ");
365
- statements.push(
366
- `CREATE ${index.unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${indexName2} ON ${name} (${cols})`
367
- );
368
- }
369
- }
370
- return statements;
371
- }
372
-
373
579
  exports.INTROSPECT_SQL = INTROSPECT_SQL;
580
+ exports.MYSQL_INTROSPECT_COLUMNS_SQL = MYSQL_INTROSPECT_COLUMNS_SQL;
581
+ exports.MYSQL_INTROSPECT_INDEXES_SQL = MYSQL_INTROSPECT_INDEXES_SQL;
582
+ exports.SQLITE_INTROSPECT_SQL = SQLITE_INTROSPECT_SQL;
374
583
  exports.coreSchema = coreSchema;
584
+ exports.planMysqlMigration = planMysqlMigration;
375
585
  exports.planPostgresMigration = planPostgresMigration;
586
+ exports.planSqliteMigration = planSqliteMigration;
376
587
  exports.renderDrizzleSchema = renderDrizzleSchema;
377
588
  exports.renderMysqlDdl = renderMysqlDdl;
378
589
  exports.renderPostgresDdl = renderPostgresDdl;