nextly 0.0.2-alpha.7 → 0.0.2-alpha.9
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/dist/_dts-chunks/{collections-handler.d-DAGa4oyr.d.ts → collections-handler.d-B3iNFGJv.d.ts} +4 -2
- package/dist/_dts-chunks/{config.d-gCEU-kSb.d.ts → config.d-D8eInFE5.d.ts} +1 -1
- package/dist/_dts-chunks/{define-component.d-B5ffJpKw.d.ts → define-component.d-COeEsUOT.d.ts} +2 -2
- package/dist/_dts-chunks/{index.d-CknEcQSu.d.ts → index.d-qjDq8N63.d.ts} +5 -5
- package/dist/actions/index.mjs +16 -16
- package/dist/api/auth-state.mjs +23 -23
- package/dist/api/collections-schema-detail.mjs +23 -23
- package/dist/api/collections-schema-export.mjs +23 -23
- package/dist/api/collections-schema.mjs +24 -24
- package/dist/api/components-detail.mjs +24 -24
- package/dist/api/components.mjs +24 -24
- package/dist/api/email-providers-default.mjs +24 -24
- package/dist/api/email-providers-detail.mjs +25 -25
- package/dist/api/email-providers-test.mjs +25 -25
- package/dist/api/email-providers.mjs +26 -26
- package/dist/api/email-send-template.mjs +26 -26
- package/dist/api/email-send.mjs +26 -26
- package/dist/api/email-templates-detail.mjs +25 -25
- package/dist/api/email-templates-layout.mjs +25 -25
- package/dist/api/email-templates-preview.mjs +26 -26
- package/dist/api/email-templates.mjs +26 -26
- package/dist/api/health.mjs +2 -2
- package/dist/api/index.mjs +2 -2
- package/dist/api/media-bulk.mjs +20 -20
- package/dist/api/media-folders.mjs +24 -24
- package/dist/api/media-handlers.d.ts +2 -2
- package/dist/api/media-handlers.mjs +25 -25
- package/dist/api/media.mjs +25 -25
- package/dist/api/singles-detail.mjs +25 -25
- package/dist/api/singles-schema-detail.mjs +24 -24
- package/dist/api/singles.mjs +23 -23
- package/dist/api/storage-upload-url.mjs +24 -24
- package/dist/api/uploads.mjs +29 -28
- package/dist/auth/index.mjs +7 -7
- package/dist/{boot-apply-5EFSWAMW.mjs → boot-apply-TDOMT356.mjs} +1 -1
- package/dist/{chunk-UB4CALU5.mjs → chunk-2QSGNGOB.mjs} +1 -1
- package/dist/{chunk-DP3G27G5.mjs → chunk-35LAHTCU.mjs} +12 -2
- package/dist/{chunk-L5FA2FAY.mjs → chunk-463A2UDH.mjs} +6 -3
- package/dist/{chunk-JXZITQPZ.mjs → chunk-4HUQNXVM.mjs} +1 -1
- package/dist/{chunk-CLBPXLO3.mjs → chunk-4LLOFW52.mjs} +27 -23
- package/dist/{chunk-NRUWQ5Z7.mjs → chunk-5WWWJCKI.mjs} +3 -5
- package/dist/{chunk-HVH4J6S6.mjs → chunk-7MA6UATK.mjs} +27 -25
- package/dist/{chunk-LP2CDTCR.mjs → chunk-A75OLKXP.mjs} +1 -1
- package/dist/{chunk-L3SZUFF4.mjs → chunk-AKK5Q5SX.mjs} +1 -1
- package/dist/{chunk-3NUAFSDX.mjs → chunk-DWV7GUKQ.mjs} +1 -1
- package/dist/{chunk-D72XH3BG.mjs → chunk-ERJGWWPB.mjs} +2 -2
- package/dist/{chunk-SSRCS4NR.mjs → chunk-FI2Z2B2J.mjs} +5 -5
- package/dist/{chunk-EBDAFQUX.mjs → chunk-GJIHOHY5.mjs} +2 -2
- package/dist/{chunk-RKXTA5KC.mjs → chunk-HLTSQIP6.mjs} +79 -50
- package/dist/{chunk-UJ2IMJ4W.mjs → chunk-HREB7UR4.mjs} +10 -4
- package/dist/{chunk-66ZNVKTF.mjs → chunk-HSIAXEYF.mjs} +274 -45
- package/dist/{chunk-QOH5I67F.mjs → chunk-JAOH2TTZ.mjs} +3 -3
- package/dist/{chunk-J4KHGYOM.mjs → chunk-JCQMC6HH.mjs} +69 -2
- package/dist/{chunk-LPVOTXNV.mjs → chunk-KLICUQPV.mjs} +1 -1
- package/dist/{chunk-ZVN3JILF.mjs → chunk-KPCSDPFQ.mjs} +103 -21
- package/dist/{chunk-M52VMPGA.mjs → chunk-KZFYCMBL.mjs} +1 -1
- package/dist/{chunk-FQH647CT.mjs → chunk-NJ3LXLSJ.mjs} +53 -4
- package/dist/{chunk-MWW7OUDL.mjs → chunk-OVQCDNU3.mjs} +23 -43
- package/dist/{chunk-INV7QKLG.mjs → chunk-PAWMG5BR.mjs} +1 -1
- package/dist/{chunk-2W3DVD7S.mjs → chunk-PFRMIEJ3.mjs} +1 -1
- package/dist/{chunk-WZBYMYVW.mjs → chunk-QPYR3TC4.mjs} +1 -1
- package/dist/{chunk-NAP3TDS6.mjs → chunk-SWVNFKWD.mjs} +2 -2
- package/dist/{chunk-KSKKIZDP.mjs → chunk-TFCPZ7PG.mjs} +4 -4
- package/dist/{chunk-JWAH6ROD.mjs → chunk-TVG3WU6C.mjs} +1 -1
- package/dist/{chunk-7YKKCBOC.mjs → chunk-V2W7G5GC.mjs} +3 -3
- package/dist/{chunk-RNKZALZE.mjs → chunk-VH4BX2QH.mjs} +5 -5
- package/dist/{chunk-TK76W55J.mjs → chunk-X3OHQBMZ.mjs} +6 -2
- package/dist/{chunk-TO5AFLVQ.mjs → chunk-XIKEJO27.mjs} +1 -1
- package/dist/{chunk-ZE6A3FYH.mjs → chunk-YG2HSZC4.mjs} +1 -1
- package/dist/{chunk-VQJQHVEV.mjs → chunk-YLRZTPSK.mjs} +1 -1
- package/dist/{chunk-TS7GHTG2.mjs → chunk-YXZH65YV.mjs} +2 -2
- package/dist/{chunk-GJNSJU4S.mjs → chunk-Z52OQOZH.mjs} +1 -1
- package/dist/cli/nextly.mjs +1 -1
- package/dist/cli/utils/index.d.ts +2 -2
- package/dist/cli/utils/index.mjs +3 -3
- package/dist/{component-schema-service-JOQIBQGK.mjs → component-schema-service-HUAQQ4H5.mjs} +2 -2
- package/dist/{config-loader-23YEMC3Z.mjs → config-loader-MUFY6UMU.mjs} +2 -2
- package/dist/config.d.ts +3 -3
- package/dist/database/index.d.ts +2 -2
- package/dist/database/index.mjs +4 -4
- package/dist/database/seeders/index.mjs +19 -19
- package/dist/{db-sync-demote-Z2HOXRZN.mjs → db-sync-demote-2DAQZXLD.mjs} +8 -8
- package/dist/{db-sync-promote-FKWZSRYC.mjs → db-sync-promote-SBTPSUEJ.mjs} +7 -7
- package/dist/{dynamic-collection-schema-service-KMOP5PGD.mjs → dynamic-collection-schema-service-FIRWOXZI.mjs} +2 -2
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.mjs +1 -1
- package/dist/{factory-IWMBKUJM.mjs → factory-UGJGOZZ7.mjs} +2 -2
- package/dist/index.d.ts +16 -11
- package/dist/index.mjs +25 -56
- package/dist/observability/index.mjs +1 -1
- package/dist/{permissions-YZSSFHU4.mjs → permissions-YEODB733.mjs} +17 -17
- package/dist/{pipeline-2DWG7LSN.mjs → pipeline-URL33V42.mjs} +11 -11
- package/dist/{preview-H6CJBMCP.mjs → preview-3RSMZITY.mjs} +3 -3
- package/dist/{program-CEFDADQM.mjs → program-TOV63II7.mjs} +29 -29
- package/dist/{register-G6I4N6QM.mjs → register-O2CJDFCK.mjs} +18 -18
- package/dist/reload-config-5P3IUF6O.mjs +23 -0
- package/dist/{routeHandler-ROBRMRZA.mjs → routeHandler-P2KXM7MH.mjs} +30 -30
- package/dist/{runtime-schema-generator-MOPQWGJP.mjs → runtime-schema-generator-NQOLDUDG.mjs} +2 -2
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.mjs +30 -30
- package/dist/{super-admin-WSTXAZSS.mjs → super-admin-6LWRM36J.mjs} +17 -17
- package/dist/{system-table-service-WGSRVEGT.mjs → system-table-service-FIX3AVKW.mjs} +6 -6
- package/dist/{users-NFD7IUFT.mjs → users-Q773BQCU.mjs} +16 -16
- package/package.json +8 -8
- package/dist/reload-config-VUEIQP5W.mjs +0 -23
- /package/dist/{first-run-QIVKWJIF.mjs → first-run-2JTNWFEG.mjs} +0 -0
|
@@ -5,16 +5,16 @@ import {
|
|
|
5
5
|
countRows,
|
|
6
6
|
diffSnapshots,
|
|
7
7
|
introspectLiveSnapshot
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JCQMC6HH.mjs";
|
|
9
9
|
import {
|
|
10
10
|
ComponentSchemaService
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-4HUQNXVM.mjs";
|
|
12
12
|
import {
|
|
13
13
|
generateRuntimeSchema
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2QSGNGOB.mjs";
|
|
15
15
|
import {
|
|
16
16
|
getDialectTables
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-YXZH65YV.mjs";
|
|
18
18
|
|
|
19
19
|
// src/domains/schema/pipeline/errors.ts
|
|
20
20
|
import { UnsupportedDialectVersionError } from "@nextlyhq/adapter-drizzle/version-check";
|
|
@@ -285,9 +285,6 @@ var RealPreCleanupExecutor = class {
|
|
|
285
285
|
}
|
|
286
286
|
};
|
|
287
287
|
|
|
288
|
-
// src/domains/schema/pipeline/pushschema-pipeline.ts
|
|
289
|
-
import { dequal } from "dequal";
|
|
290
|
-
|
|
291
288
|
// src/init/schema-snapshot-cache.ts
|
|
292
289
|
var g = globalThis;
|
|
293
290
|
function getCachedSnapshot() {
|
|
@@ -296,6 +293,27 @@ function getCachedSnapshot() {
|
|
|
296
293
|
function setCachedSnapshot(snapshot) {
|
|
297
294
|
g.__nextly_prevSchemaSnapshot = snapshot;
|
|
298
295
|
}
|
|
296
|
+
function keyOf(tableNames) {
|
|
297
|
+
return [...tableNames].sort().join(" ");
|
|
298
|
+
}
|
|
299
|
+
function bag() {
|
|
300
|
+
return globalThis;
|
|
301
|
+
}
|
|
302
|
+
function getLiveSnapshot(managedTableNames) {
|
|
303
|
+
return bag().__nextly_liveSnapshots?.get(keyOf(managedTableNames));
|
|
304
|
+
}
|
|
305
|
+
function setLiveSnapshot(managedTableNames, snapshot) {
|
|
306
|
+
const b = bag();
|
|
307
|
+
if (!b.__nextly_liveSnapshots) b.__nextly_liveSnapshots = /* @__PURE__ */ new Map();
|
|
308
|
+
b.__nextly_liveSnapshots.set(keyOf(managedTableNames), snapshot);
|
|
309
|
+
}
|
|
310
|
+
function clearLiveSnapshots() {
|
|
311
|
+
const b = bag();
|
|
312
|
+
if (b.__nextly_liveSnapshots) b.__nextly_liveSnapshots.clear();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// src/domains/schema/pipeline/pushschema-pipeline.ts
|
|
316
|
+
import { dequal } from "dequal";
|
|
299
317
|
|
|
300
318
|
// src/runtime/notifications/build-event.ts
|
|
301
319
|
function buildNotificationEvent(args) {
|
|
@@ -323,6 +341,96 @@ function buildNotificationEvent(args) {
|
|
|
323
341
|
};
|
|
324
342
|
}
|
|
325
343
|
|
|
344
|
+
// src/domains/schema/pipeline/ddl-emitter/identifiers.ts
|
|
345
|
+
function quoteIdent(identifier) {
|
|
346
|
+
if (identifier.includes("\0")) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`Invalid identifier (contains NUL byte): ${JSON.stringify(identifier)}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/domains/schema/pipeline/ddl-emitter/postgres.ts
|
|
355
|
+
function columnTail(col) {
|
|
356
|
+
let s = col.type;
|
|
357
|
+
if (col.nullable === false) s += " NOT NULL";
|
|
358
|
+
if (col.default !== void 0) s += ` DEFAULT ${col.default}`;
|
|
359
|
+
return s;
|
|
360
|
+
}
|
|
361
|
+
function createTableColumn(col) {
|
|
362
|
+
if (col.name === "id") {
|
|
363
|
+
return `${quoteIdent(col.name)} ${col.type} PRIMARY KEY NOT NULL`;
|
|
364
|
+
}
|
|
365
|
+
return `${quoteIdent(col.name)} ${columnTail(col)}`;
|
|
366
|
+
}
|
|
367
|
+
function createTableCanonicalIndexes(spec) {
|
|
368
|
+
const colNames = new Set(spec.columns.map((c) => c.name));
|
|
369
|
+
const stmts = [];
|
|
370
|
+
if (colNames.has("slug")) {
|
|
371
|
+
stmts.push(
|
|
372
|
+
`CREATE UNIQUE INDEX ${quoteIdent(`idx_${spec.name}_slug`)} ON ${quoteIdent(spec.name)} USING btree (${quoteIdent("slug")})`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
if (colNames.has("created_at")) {
|
|
376
|
+
stmts.push(
|
|
377
|
+
`CREATE INDEX ${quoteIdent(`idx_${spec.name}_created_at`)} ON ${quoteIdent(spec.name)} USING btree (${quoteIdent("created_at")} DESC)`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
return stmts;
|
|
381
|
+
}
|
|
382
|
+
function emitPostgresDdl(op) {
|
|
383
|
+
switch (op.type) {
|
|
384
|
+
case "rename_table":
|
|
385
|
+
case "rename_column":
|
|
386
|
+
case "drop_column":
|
|
387
|
+
case "drop_table":
|
|
388
|
+
return [];
|
|
389
|
+
case "add_column":
|
|
390
|
+
return [
|
|
391
|
+
`ALTER TABLE ${quoteIdent(op.tableName)} ADD COLUMN ${quoteIdent(op.column.name)} ${columnTail(op.column)}`
|
|
392
|
+
];
|
|
393
|
+
case "add_table": {
|
|
394
|
+
const cols = op.table.columns.map(createTableColumn);
|
|
395
|
+
const createTable = `CREATE TABLE ${quoteIdent(op.table.name)} (
|
|
396
|
+
${cols.join(",\n ")}
|
|
397
|
+
)`;
|
|
398
|
+
return [createTable, ...createTableCanonicalIndexes(op.table)];
|
|
399
|
+
}
|
|
400
|
+
case "change_column_type":
|
|
401
|
+
case "change_column_nullable":
|
|
402
|
+
case "change_column_default":
|
|
403
|
+
throw new Error(
|
|
404
|
+
`emitPostgresDdl: op type "${op.type}" not yet implemented \u2014 canEmitWithoutDrizzleKit should not have routed it here`
|
|
405
|
+
);
|
|
406
|
+
default: {
|
|
407
|
+
const exhaustive = op;
|
|
408
|
+
throw new Error(
|
|
409
|
+
`emitPostgresDdl: unknown op ${JSON.stringify(exhaustive)}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/domains/schema/pipeline/ddl-emitter/index.ts
|
|
416
|
+
var FAST_PATH_OP_TYPES = /* @__PURE__ */ new Set([
|
|
417
|
+
"add_column",
|
|
418
|
+
"add_table"
|
|
419
|
+
]);
|
|
420
|
+
function canEmitWithoutDrizzleKit(ops, dialect) {
|
|
421
|
+
if (dialect !== "postgresql") return false;
|
|
422
|
+
if (ops.length === 0) return false;
|
|
423
|
+
return ops.every((op) => FAST_PATH_OP_TYPES.has(op.type));
|
|
424
|
+
}
|
|
425
|
+
function emitDdl(ops, dialect) {
|
|
426
|
+
if (dialect !== "postgresql") {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`emitDdl: unsupported dialect "${dialect}" (fast path is PostgreSQL-only)`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
return ops.flatMap((op) => emitPostgresDdl(op));
|
|
432
|
+
}
|
|
433
|
+
|
|
326
434
|
// src/domains/schema/pipeline/managed-tables.ts
|
|
327
435
|
var MANAGED_TABLE_PREFIXES_REGEX = /^(dc_|single_|comp_)/;
|
|
328
436
|
function isManagedTable(name) {
|
|
@@ -393,7 +501,7 @@ function isPreResolutionOp(op) {
|
|
|
393
501
|
}
|
|
394
502
|
|
|
395
503
|
// src/domains/schema/pipeline/sql-templates/identifier-quoting.ts
|
|
396
|
-
function
|
|
504
|
+
function quoteIdent2(name, dialect) {
|
|
397
505
|
const q4 = dialect === "mysql" ? "`" : '"';
|
|
398
506
|
if (name.includes(q4)) {
|
|
399
507
|
throw new Error(
|
|
@@ -404,7 +512,7 @@ function quoteIdent(name, dialect) {
|
|
|
404
512
|
}
|
|
405
513
|
|
|
406
514
|
// src/domains/schema/pipeline/sql-templates/mysql.ts
|
|
407
|
-
var q = (n) =>
|
|
515
|
+
var q = (n) => quoteIdent2(n, "mysql");
|
|
408
516
|
function columnDef(c) {
|
|
409
517
|
const nullable = c.nullable ? "" : " NOT NULL";
|
|
410
518
|
const def = c.default !== void 0 ? ` DEFAULT ${c.default}` : "";
|
|
@@ -480,7 +588,7 @@ var MysqlUnsupportedOperationError = class extends Error {
|
|
|
480
588
|
};
|
|
481
589
|
|
|
482
590
|
// src/domains/schema/pipeline/sql-templates/postgres.ts
|
|
483
|
-
var q2 = (n) =>
|
|
591
|
+
var q2 = (n) => quoteIdent2(n, "postgresql");
|
|
484
592
|
function columnDef2(c) {
|
|
485
593
|
const nullable = c.nullable ? "" : " NOT NULL";
|
|
486
594
|
const def = c.default !== void 0 ? ` DEFAULT ${c.default}` : "";
|
|
@@ -547,7 +655,7 @@ function generateChangeColumnDefault2(op) {
|
|
|
547
655
|
}
|
|
548
656
|
|
|
549
657
|
// src/domains/schema/pipeline/sql-templates/sqlite.ts
|
|
550
|
-
var q3 = (n) =>
|
|
658
|
+
var q3 = (n) => quoteIdent2(n, "sqlite");
|
|
551
659
|
function columnDef3(c) {
|
|
552
660
|
const nullable = c.nullable ? "" : " NOT NULL";
|
|
553
661
|
const def = c.default !== void 0 ? ` DEFAULT ${c.default}` : "";
|
|
@@ -777,6 +885,29 @@ var DdlExecutionError = class extends Error {
|
|
|
777
885
|
this.name = "DdlExecutionError";
|
|
778
886
|
}
|
|
779
887
|
};
|
|
888
|
+
var ORPHAN_DROP_PATTERNS = [
|
|
889
|
+
{
|
|
890
|
+
kind: "SEQUENCE",
|
|
891
|
+
re: /^DROP\s+SEQUENCE\s+(?:IF\s+EXISTS\s+)?(?:["`]?\w+["`]?\.)?["`]?(\w+)["`]?/i
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
kind: "INDEX",
|
|
895
|
+
re: /^DROP\s+INDEX\s+(?:IF\s+EXISTS\s+)?(?:["`]?\w+["`]?\.)?["`]?(\w+)["`]?/i
|
|
896
|
+
}
|
|
897
|
+
];
|
|
898
|
+
function logApplyRoute(useFastPath, ops) {
|
|
899
|
+
if (process.env.DEBUG_SCHEMA !== "1") return;
|
|
900
|
+
if (useFastPath) {
|
|
901
|
+
console.debug(
|
|
902
|
+
`[nextly] schema apply: fast-path DDL emitter (${ops.length} op(s))`
|
|
903
|
+
);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const nonAdditive = ops.filter((o) => o.type !== "add_column" && o.type !== "add_table").map((o) => o.type);
|
|
907
|
+
console.debug(
|
|
908
|
+
`[nextly] schema apply: drizzle-kit fallback (${ops.length} op(s); non-additive: ${nonAdditive.length === 0 ? "<none>" : nonAdditive.join(",")})`
|
|
909
|
+
);
|
|
910
|
+
}
|
|
780
911
|
function computeJournalSummaryFromOperations(operations) {
|
|
781
912
|
let added = 0;
|
|
782
913
|
let removed = 0;
|
|
@@ -864,11 +995,17 @@ var PushSchemaPipeline = class {
|
|
|
864
995
|
...Object.values(desired.singles).map((s) => s.tableName),
|
|
865
996
|
...Object.values(desired.components).map((c) => c.tableName)
|
|
866
997
|
];
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
998
|
+
let liveSnapshot;
|
|
999
|
+
if (this.testHooks._introspectSnapshotOverride) {
|
|
1000
|
+
liveSnapshot = await this.testHooks._introspectSnapshotOverride(
|
|
1001
|
+
db,
|
|
1002
|
+
dialect,
|
|
1003
|
+
managedTableNames
|
|
1004
|
+
);
|
|
1005
|
+
} else {
|
|
1006
|
+
const cached2 = getLiveSnapshot(managedTableNames);
|
|
1007
|
+
liveSnapshot = cached2 !== void 0 ? cached2 : await introspectLiveSnapshot(db, dialect, managedTableNames);
|
|
1008
|
+
}
|
|
872
1009
|
const desiredSnapshot = {
|
|
873
1010
|
tables: [
|
|
874
1011
|
...Object.values(desired.collections).map(
|
|
@@ -910,9 +1047,17 @@ var PushSchemaPipeline = class {
|
|
|
910
1047
|
countRows: (table) => countRows(db, dialect, table),
|
|
911
1048
|
dialect
|
|
912
1049
|
});
|
|
913
|
-
const
|
|
1050
|
+
const dropsCoveredByCandidates = /* @__PURE__ */ new Set();
|
|
1051
|
+
for (const c of candidates) {
|
|
1052
|
+
dropsCoveredByCandidates.add(`${c.tableName}::${c.fromColumn}`);
|
|
1053
|
+
}
|
|
1054
|
+
const dispatchEvents = classificationResult.events.filter(
|
|
1055
|
+
(e) => e.kind !== "destructive_drop" || !dropsCoveredByCandidates.has(`${e.tableName}::${e.columnName}`)
|
|
1056
|
+
);
|
|
1057
|
+
const needsPrompt = candidates.length > 0 || dispatchEvents.length > 0;
|
|
1058
|
+
const dispatchResult = needsPrompt ? await this.deps.promptDispatcher.dispatch({
|
|
914
1059
|
candidates,
|
|
915
|
-
events:
|
|
1060
|
+
events: dispatchEvents,
|
|
916
1061
|
classification: classificationResult.level,
|
|
917
1062
|
channel: promptChannel
|
|
918
1063
|
}) : {
|
|
@@ -928,11 +1073,43 @@ var PushSchemaPipeline = class {
|
|
|
928
1073
|
dispatchResult.resolutions,
|
|
929
1074
|
classificationResult.events
|
|
930
1075
|
);
|
|
931
|
-
const resolvedOps = applyResolutionsToOperations(
|
|
1076
|
+
const resolvedOps = this.testHooks._resolvedOpsOverride ?? applyResolutionsToOperations(
|
|
932
1077
|
operations,
|
|
933
1078
|
toRenameResolutions(dispatchResult.confirmedRenames, candidates)
|
|
934
1079
|
);
|
|
935
1080
|
const drizzleSchema = this.testHooks._buildDrizzleSchemaOverride ? this.testHooks._buildDrizzleSchemaOverride(patchedDesired, dialect) : this.buildDrizzleSchema(patchedDesired, dialect);
|
|
1081
|
+
const affectedTableNames = /* @__PURE__ */ new Set();
|
|
1082
|
+
for (const op of resolvedOps) {
|
|
1083
|
+
switch (op.type) {
|
|
1084
|
+
case "add_table":
|
|
1085
|
+
affectedTableNames.add(op.table.name);
|
|
1086
|
+
break;
|
|
1087
|
+
case "rename_table":
|
|
1088
|
+
affectedTableNames.add(op.toName);
|
|
1089
|
+
break;
|
|
1090
|
+
case "drop_table":
|
|
1091
|
+
break;
|
|
1092
|
+
case "add_column":
|
|
1093
|
+
case "drop_column":
|
|
1094
|
+
case "rename_column":
|
|
1095
|
+
case "change_column_type":
|
|
1096
|
+
case "change_column_nullable":
|
|
1097
|
+
case "change_column_default":
|
|
1098
|
+
affectedTableNames.add(op.tableName);
|
|
1099
|
+
break;
|
|
1100
|
+
default: {
|
|
1101
|
+
const _exhaustive = op;
|
|
1102
|
+
void _exhaustive;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
const scopedSchema = {};
|
|
1107
|
+
for (const [tableName, tableObj] of Object.entries(drizzleSchema)) {
|
|
1108
|
+
if (affectedTableNames.has(tableName)) {
|
|
1109
|
+
scopedSchema[tableName] = tableObj;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
const effectiveDrizzleSchema = Object.keys(scopedSchema).length > 0 ? scopedSchema : drizzleSchema;
|
|
936
1113
|
const kit = this.testHooks._kitOverride ? this.testHooks._kitOverride : await this.importDrizzleKit(dialect, databaseName);
|
|
937
1114
|
const isSqlite = dialect === "sqlite";
|
|
938
1115
|
const runApply = async (tx) => {
|
|
@@ -970,22 +1147,30 @@ var PushSchemaPipeline = class {
|
|
|
970
1147
|
err
|
|
971
1148
|
);
|
|
972
1149
|
}
|
|
973
|
-
const desiredTableNames = Object.keys(
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1150
|
+
const desiredTableNames = Object.keys(effectiveDrizzleSchema);
|
|
1151
|
+
const useFastPath = canEmitWithoutDrizzleKit(resolvedOps, dialect);
|
|
1152
|
+
logApplyRoute(useFastPath, resolvedOps);
|
|
1153
|
+
let emittedStatements;
|
|
1154
|
+
if (useFastPath) {
|
|
1155
|
+
emittedStatements = emitDdl(resolvedOps, dialect);
|
|
1156
|
+
} else {
|
|
1157
|
+
let pushResult;
|
|
1158
|
+
try {
|
|
1159
|
+
pushResult = await withCapturedStdout(
|
|
1160
|
+
() => kit.pushSchema(effectiveDrizzleSchema, tx, desiredTableNames),
|
|
1161
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
1162
|
+
process.env.DEBUG_SCHEMA === "1" ? { debug: (msg) => console.debug(msg) } : void 0
|
|
1163
|
+
);
|
|
1164
|
+
} catch (err) {
|
|
1165
|
+
throw new PushSchemaError(
|
|
1166
|
+
err instanceof Error ? err.message : String(err),
|
|
1167
|
+
err
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
emittedStatements = pushResult.statementsToExecute;
|
|
986
1171
|
}
|
|
987
1172
|
const safe = this.filterUnsafeStatements(
|
|
988
|
-
|
|
1173
|
+
emittedStatements,
|
|
989
1174
|
desiredTableNames
|
|
990
1175
|
);
|
|
991
1176
|
try {
|
|
@@ -1066,25 +1251,67 @@ var PushSchemaPipeline = class {
|
|
|
1066
1251
|
}
|
|
1067
1252
|
}
|
|
1068
1253
|
filterUnsafeStatements(statements, desiredTableNames) {
|
|
1069
|
-
const desiredSet = new Set(
|
|
1070
|
-
desiredTableNames.map((t) => t.toLowerCase())
|
|
1071
|
-
);
|
|
1254
|
+
const desiredSet = new Set(desiredTableNames.map((t) => t.toLowerCase()));
|
|
1072
1255
|
return statements.filter((stmt) => {
|
|
1073
1256
|
const dropMatch = stmt.match(
|
|
1074
1257
|
/^DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(?:["`]?\w+["`]?\.)?["`]?(\w+)["`]?/i
|
|
1075
1258
|
);
|
|
1076
|
-
if (
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1259
|
+
if (dropMatch) {
|
|
1260
|
+
const tableName = dropMatch[1] ?? "<unknown>";
|
|
1261
|
+
const isInDesired = desiredSet.has(tableName.toLowerCase());
|
|
1262
|
+
if (isInDesired) {
|
|
1263
|
+
return true;
|
|
1264
|
+
}
|
|
1265
|
+
console.warn(
|
|
1266
|
+
`[Nextly schema] Blocked DROP TABLE "${tableName}" emitted by drizzle-kit pushSchema (table not in current desired schema). If this drop was intentional, route it through the pre-resolution executor with explicit user confirmation. (managed=${isManagedTable(tableName)})`
|
|
1267
|
+
);
|
|
1268
|
+
return false;
|
|
1081
1269
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1270
|
+
for (const { kind, re } of ORPHAN_DROP_PATTERNS) {
|
|
1271
|
+
const m = stmt.match(re);
|
|
1272
|
+
if (!m) continue;
|
|
1273
|
+
const objectName = m[1] ?? "";
|
|
1274
|
+
if (this.inferOwnerTableFromObjectName(objectName, desiredSet) !== null) {
|
|
1275
|
+
return true;
|
|
1276
|
+
}
|
|
1277
|
+
console.warn(
|
|
1278
|
+
`[Nextly schema] Blocked DROP ${kind} "${objectName}" emitted by drizzle-kit pushSchema (owner table not in current desired schema or name is non-conventional). If this drop was intentional, route it through the pre-resolution executor with explicit user confirmation, or drop it manually before re-running if the ${kind.toLowerCase()} name is custom.`
|
|
1279
|
+
);
|
|
1280
|
+
return false;
|
|
1281
|
+
}
|
|
1282
|
+
return true;
|
|
1086
1283
|
});
|
|
1087
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Infers the owner table of a sequence or index from its name using
|
|
1287
|
+
* Postgres's default naming conventions:
|
|
1288
|
+
* - SERIAL / IDENTITY sequences: `<table>_<col>_seq`
|
|
1289
|
+
* - Indexes: `<table>_<col(s)>_idx | _key | _pkey | _unique`
|
|
1290
|
+
*
|
|
1291
|
+
* Strategy: walk underscore-delimited prefixes from longest to shortest
|
|
1292
|
+
* and return the first candidate found in `desiredSet`. Longest-first
|
|
1293
|
+
* ensures that multi-word table names like `email_templates` are
|
|
1294
|
+
* preferred over the shorter prefix `email`.
|
|
1295
|
+
*
|
|
1296
|
+
* Examples:
|
|
1297
|
+
* accounts_id_seq → "accounts" (if in desiredSet)
|
|
1298
|
+
* email_templates_id_seq → "email_templates" (if in desiredSet)
|
|
1299
|
+
* dc_posts_title_idx → "dc_posts" (if in desiredSet)
|
|
1300
|
+
* idx_completely_custom → null (no prefix matches)
|
|
1301
|
+
*
|
|
1302
|
+
* Returns the matched table name (lowercased) or `null` if no prefix
|
|
1303
|
+
* in `desiredSet` was found. A `null` result means we can't identify
|
|
1304
|
+
* the owner, and the caller should treat the statement as unsafe.
|
|
1305
|
+
*/
|
|
1306
|
+
inferOwnerTableFromObjectName(objectName, desiredSet) {
|
|
1307
|
+
const lower = objectName.toLowerCase();
|
|
1308
|
+
const parts = lower.split("_");
|
|
1309
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
1310
|
+
const candidate = parts.slice(0, i).join("_");
|
|
1311
|
+
if (desiredSet.has(candidate)) return candidate;
|
|
1312
|
+
}
|
|
1313
|
+
return null;
|
|
1314
|
+
}
|
|
1088
1315
|
async runSqlitePragma(db, pragma) {
|
|
1089
1316
|
const { sql: sqlTag } = await import("drizzle-orm");
|
|
1090
1317
|
const dbTyped = db;
|
|
@@ -1455,6 +1682,8 @@ export {
|
|
|
1455
1682
|
TTYRequiredError,
|
|
1456
1683
|
PromptCancelledError,
|
|
1457
1684
|
RealPreCleanupExecutor,
|
|
1685
|
+
setLiveSnapshot,
|
|
1686
|
+
clearLiveSnapshots,
|
|
1458
1687
|
generateSQL,
|
|
1459
1688
|
PushSchemaPipeline,
|
|
1460
1689
|
noopPreRenameExecutor,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClackTerminalPromptDispatcher
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NJ3LXLSJ.mjs";
|
|
4
4
|
import {
|
|
5
5
|
getAdapterFromDI,
|
|
6
6
|
getCollectionRegistryFromDI,
|
|
@@ -14,11 +14,11 @@ import {
|
|
|
14
14
|
getProductionNotifier,
|
|
15
15
|
noopMigrationJournal,
|
|
16
16
|
noopPreRenameExecutor
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-HSIAXEYF.mjs";
|
|
18
18
|
import {
|
|
19
19
|
RealClassifier,
|
|
20
20
|
RegexRenameDetector
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JCQMC6HH.mjs";
|
|
22
22
|
|
|
23
23
|
// src/domains/schema/pipeline/snapshot.ts
|
|
24
24
|
function buildDesiredSchemaFromRegistry(registry, overrides) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getColumnDescriptor,
|
|
3
3
|
getSystemColumnDescriptors
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-463A2UDH.mjs";
|
|
5
5
|
|
|
6
6
|
// src/domains/schema/pipeline/classifier/count-helpers.ts
|
|
7
7
|
import { sql } from "drizzle-orm";
|
|
@@ -214,6 +214,17 @@ var RealClassifier = class {
|
|
|
214
214
|
]
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
|
+
} else if (op.type === "drop_column") {
|
|
218
|
+
const tableRowCount = await args.countRows(op.tableName);
|
|
219
|
+
events.push({
|
|
220
|
+
id: formatEventId("destructive_drop", op.tableName, op.columnName),
|
|
221
|
+
kind: "destructive_drop",
|
|
222
|
+
tableName: op.tableName,
|
|
223
|
+
columnName: op.columnName,
|
|
224
|
+
columnType: op.columnType,
|
|
225
|
+
tableRowCount,
|
|
226
|
+
applicableResolutions: ["confirm_drop", "abort"]
|
|
227
|
+
});
|
|
217
228
|
}
|
|
218
229
|
}
|
|
219
230
|
const hasInteractive = events.some((e) => e.kind !== "type_change");
|
|
@@ -430,6 +441,62 @@ function buildDesiredTableFromComponentFields(tableName, fields, dialect) {
|
|
|
430
441
|
return { name: tableName, columns };
|
|
431
442
|
}
|
|
432
443
|
|
|
444
|
+
// src/domains/schema/pipeline/diff/normalize-default.ts
|
|
445
|
+
var PG_REDUNDANT_CAST_TYPES = [
|
|
446
|
+
// Two-word forms come first so the regex prefers the longer match.
|
|
447
|
+
"character varying",
|
|
448
|
+
"character",
|
|
449
|
+
"double precision",
|
|
450
|
+
"timestamp without time zone",
|
|
451
|
+
"timestamp with time zone",
|
|
452
|
+
"time without time zone",
|
|
453
|
+
"time with time zone",
|
|
454
|
+
// Single-word forms.
|
|
455
|
+
"text",
|
|
456
|
+
"varchar",
|
|
457
|
+
"bpchar",
|
|
458
|
+
"integer",
|
|
459
|
+
"bigint",
|
|
460
|
+
"smallint",
|
|
461
|
+
"numeric",
|
|
462
|
+
"real",
|
|
463
|
+
"boolean",
|
|
464
|
+
"uuid",
|
|
465
|
+
"jsonb",
|
|
466
|
+
"json"
|
|
467
|
+
];
|
|
468
|
+
var CAST_SUFFIX_RE = new RegExp(
|
|
469
|
+
`::(?:${PG_REDUNDANT_CAST_TYPES.join("|")})$`,
|
|
470
|
+
"i"
|
|
471
|
+
);
|
|
472
|
+
var NORMALIZE_LOWERCASE_FUNCTIONS = /* @__PURE__ */ new Set(["now()"]);
|
|
473
|
+
function normalizeDefault(expr) {
|
|
474
|
+
if (expr === void 0) return void 0;
|
|
475
|
+
let normalised = stripRedundantCast(expr);
|
|
476
|
+
const lower = normalised.toLowerCase();
|
|
477
|
+
if (NORMALIZE_LOWERCASE_FUNCTIONS.has(lower)) {
|
|
478
|
+
normalised = lower;
|
|
479
|
+
}
|
|
480
|
+
return normalised;
|
|
481
|
+
}
|
|
482
|
+
function stripRedundantCast(expr) {
|
|
483
|
+
const match = expr.match(CAST_SUFFIX_RE);
|
|
484
|
+
if (!match) return expr;
|
|
485
|
+
const beforeCast = expr.slice(0, match.index);
|
|
486
|
+
if (isCompleteLiteral(beforeCast)) {
|
|
487
|
+
return beforeCast;
|
|
488
|
+
}
|
|
489
|
+
return expr;
|
|
490
|
+
}
|
|
491
|
+
function isCompleteLiteral(value) {
|
|
492
|
+
if (value.startsWith("'") && value.endsWith("'") && value.length >= 2) {
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
if (/^-?[0-9]+(?:\.[0-9]+)?$/.test(value)) return true;
|
|
496
|
+
if (value === "true" || value === "false") return true;
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
|
|
433
500
|
// src/domains/schema/pipeline/diff/diff.ts
|
|
434
501
|
function diffSnapshots(prev, cur) {
|
|
435
502
|
const prevByName = /* @__PURE__ */ new Map();
|
|
@@ -509,7 +576,7 @@ function diffColumns(tableName, prev, cur) {
|
|
|
509
576
|
toNullable: curC.nullable
|
|
510
577
|
});
|
|
511
578
|
}
|
|
512
|
-
if (prevC.default !== curC.default) {
|
|
579
|
+
if (normalizeDefault(prevC.default) !== normalizeDefault(curC.default)) {
|
|
513
580
|
defaultChanges.push({
|
|
514
581
|
type: "change_column_default",
|
|
515
582
|
tableName,
|