@happyvertical/smrt-cli 0.40.20 → 0.40.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/{commands-mHvAZYmi.js → commands-DrK9PHS4.js} +30 -2
- package/dist/index.js +8 -8
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -55,6 +55,27 @@ reconciles live schema drift. Global `--force` remains available by itself for
|
|
|
55
55
|
backward compatibility but intentionally overrides guards for the whole pending
|
|
56
56
|
batch.
|
|
57
57
|
|
|
58
|
+
### Audited PostgreSQL timestamp conversion
|
|
59
|
+
|
|
60
|
+
`timestamp without time zone` values do not carry enough information for SMRT
|
|
61
|
+
to infer their original instant. After auditing every historical writer,
|
|
62
|
+
database default, trigger, and raw SQL path, an operator may confirm that the
|
|
63
|
+
legacy values are UTC wall times and include the exact opt-in:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
smrt db:migrate --postgres-timestamp-legacy-timezone UTC
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The option has no default and rejects every value other than `UTC`. On
|
|
70
|
+
PostgreSQL, it allows the manifest-owned timestamp columns to be converted to
|
|
71
|
+
`timestamptz` with `USING column AT TIME ZONE 'UTC'`, preserving the proven UTC
|
|
72
|
+
instants. It is not safe for a database with any local-time writer; use an
|
|
73
|
+
application-owned, provenance-aware migration in that case. Rehearse against a
|
|
74
|
+
restored clone and keep a verified backup because type upgrades have no
|
|
75
|
+
automatic down migration. `smrt db:diff --postgres-timestamp-legacy-timezone
|
|
76
|
+
UTC` and `smrt db:migrate --dry-run --postgres-timestamp-legacy-timezone UTC`
|
|
77
|
+
both preview the same conversion without writing it.
|
|
78
|
+
|
|
58
79
|
### Code Generation
|
|
59
80
|
|
|
60
81
|
| Command | Description |
|
|
@@ -408,6 +408,20 @@ async function autoDiscoverAndLoad(projectRoot = process.cwd()) {
|
|
|
408
408
|
};
|
|
409
409
|
}
|
|
410
410
|
//#endregion
|
|
411
|
+
//#region src/commands/postgres-timestamp-migration.ts
|
|
412
|
+
/**
|
|
413
|
+
* Fail-closed confirmation shared by PostgreSQL schema preview and migration.
|
|
414
|
+
*
|
|
415
|
+
* A `timestamp without time zone` value cannot establish its own original
|
|
416
|
+
* instant. Callers must therefore make the same explicit UTC provenance
|
|
417
|
+
* confirmation whether they are previewing or applying the conversion.
|
|
418
|
+
*/
|
|
419
|
+
function resolvePostgresTimestampMigration(legacyTimezone) {
|
|
420
|
+
if (legacyTimezone === void 0) return;
|
|
421
|
+
if (legacyTimezone !== "UTC") throw new Error("--postgres-timestamp-legacy-timezone must be exactly UTC; refusing to infer the offset of legacy PostgreSQL timestamps");
|
|
422
|
+
return { legacyTimezone: "UTC" };
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
411
425
|
//#region src/commands/db-diff.ts
|
|
412
426
|
/**
|
|
413
427
|
* db:diff Command
|
|
@@ -463,11 +477,16 @@ var dbDiffCommand = {
|
|
|
463
477
|
type: "boolean",
|
|
464
478
|
description: "Include orphan-index drops in the diff (indexes in DB but not in the manifest, excluding *_pkey/*_key implicit-from-constraint indexes). Off by default for safety.",
|
|
465
479
|
default: false
|
|
480
|
+
},
|
|
481
|
+
"postgres-timestamp-legacy-timezone": {
|
|
482
|
+
type: "string",
|
|
483
|
+
description: "Confirm that legacy PostgreSQL timestamp-without-time-zone values are UTC wall times before previewing their conversion to timestamptz. Exact value required: UTC; omitted by default."
|
|
466
484
|
}
|
|
467
485
|
},
|
|
468
486
|
handler: async (_args, options) => {
|
|
469
487
|
let db;
|
|
470
488
|
try {
|
|
489
|
+
const postgresTimestampMigration = resolvePostgresTimestampMigration(options["postgres-timestamp-legacy-timezone"]);
|
|
471
490
|
const unsupportedFileOptions = [
|
|
472
491
|
"generate",
|
|
473
492
|
"name",
|
|
@@ -516,7 +535,8 @@ var dbDiffCommand = {
|
|
|
516
535
|
const diff = await new SchemaComparer(db, {
|
|
517
536
|
includeDroppedTables: false,
|
|
518
537
|
includeDroppedColumns: false,
|
|
519
|
-
includeDroppedIndexes: Boolean(options["drop-indexes"])
|
|
538
|
+
includeDroppedIndexes: Boolean(options["drop-indexes"]),
|
|
539
|
+
postgresTimestampMigration
|
|
520
540
|
}).compare(schemaDefinitions);
|
|
521
541
|
if (options.json) {
|
|
522
542
|
console.log(JSON.stringify({
|
|
@@ -7074,6 +7094,10 @@ export default testManifest;
|
|
|
7074
7094
|
description: "Use PostgreSQL-safe operations (CONCURRENTLY for indexes, lock_timeout)",
|
|
7075
7095
|
default: false
|
|
7076
7096
|
},
|
|
7097
|
+
"postgres-timestamp-legacy-timezone": {
|
|
7098
|
+
type: "string",
|
|
7099
|
+
description: "Confirm that legacy PostgreSQL timestamp-without-time-zone values are UTC wall times before converting them to timestamptz. Exact value required: UTC; omitted by default."
|
|
7100
|
+
},
|
|
7077
7101
|
force: {
|
|
7078
7102
|
type: "boolean",
|
|
7079
7103
|
description: "Force re-apply even if already applied (skip checksum validation)",
|
|
@@ -7111,6 +7135,7 @@ export default testManifest;
|
|
|
7111
7135
|
let db;
|
|
7112
7136
|
try {
|
|
7113
7137
|
const forceSelection = resolveForceMigrationSelection(options.force, options["force-migration"]);
|
|
7138
|
+
const postgresTimestampMigration = resolvePostgresTimestampMigration(options["postgres-timestamp-legacy-timezone"]);
|
|
7114
7139
|
const { getPackageConfig } = await import("@happyvertical/smrt-config");
|
|
7115
7140
|
const { DEFAULT_CLI_CONFIG } = await import("./config-BwrFRL8L.js");
|
|
7116
7141
|
const config = getPackageConfig("cli", DEFAULT_CLI_CONFIG);
|
|
@@ -7211,7 +7236,10 @@ export default testManifest;
|
|
|
7211
7236
|
console.warn(" Use --repair-data instead.\n");
|
|
7212
7237
|
}
|
|
7213
7238
|
console.log("🔍 Comparing schemas...\n");
|
|
7214
|
-
const diff = await new SchemaComparer(db, {
|
|
7239
|
+
const diff = await new SchemaComparer(db, {
|
|
7240
|
+
includeDroppedIndexes: Boolean(options["drop-indexes"]),
|
|
7241
|
+
postgresTimestampMigration
|
|
7242
|
+
}).compare(manifestSchemas);
|
|
7215
7243
|
const getClassForTable = (tableName) => {
|
|
7216
7244
|
for (const className of initOrder) if (ObjectRegistry.getTableName(className) === tableName) return className;
|
|
7217
7245
|
return tableName;
|
package/dist/index.js
CHANGED
|
@@ -48,56 +48,56 @@ var _docsCommands = null;
|
|
|
48
48
|
var _playgroundCommands = null;
|
|
49
49
|
async function getGnodeCommands() {
|
|
50
50
|
if (!_gnodeCommands) {
|
|
51
|
-
const { gnodeCommands } = await import("./commands-
|
|
51
|
+
const { gnodeCommands } = await import("./commands-DrK9PHS4.js");
|
|
52
52
|
_gnodeCommands = gnodeCommands;
|
|
53
53
|
}
|
|
54
54
|
return _gnodeCommands;
|
|
55
55
|
}
|
|
56
56
|
async function getGitCommands() {
|
|
57
57
|
if (!_gitCommands) {
|
|
58
|
-
const { gitCommands } = await import("./commands-
|
|
58
|
+
const { gitCommands } = await import("./commands-DrK9PHS4.js");
|
|
59
59
|
_gitCommands = gitCommands;
|
|
60
60
|
}
|
|
61
61
|
return _gitCommands;
|
|
62
62
|
}
|
|
63
63
|
async function getGenerateCommands() {
|
|
64
64
|
if (!_generateCommands) {
|
|
65
|
-
const { generateCommands } = await import("./commands-
|
|
65
|
+
const { generateCommands } = await import("./commands-DrK9PHS4.js");
|
|
66
66
|
_generateCommands = generateCommands;
|
|
67
67
|
}
|
|
68
68
|
return _generateCommands;
|
|
69
69
|
}
|
|
70
70
|
async function getInitCommands() {
|
|
71
71
|
if (!_initCommands) {
|
|
72
|
-
const { initCommands } = await import("./commands-
|
|
72
|
+
const { initCommands } = await import("./commands-DrK9PHS4.js");
|
|
73
73
|
_initCommands = initCommands;
|
|
74
74
|
}
|
|
75
75
|
return _initCommands;
|
|
76
76
|
}
|
|
77
77
|
async function getUtilityCommands() {
|
|
78
78
|
if (!_utilityCommands) {
|
|
79
|
-
const { utilityCommands } = await import("./commands-
|
|
79
|
+
const { utilityCommands } = await import("./commands-DrK9PHS4.js");
|
|
80
80
|
_utilityCommands = utilityCommands;
|
|
81
81
|
}
|
|
82
82
|
return _utilityCommands;
|
|
83
83
|
}
|
|
84
84
|
async function getDispatchCommands() {
|
|
85
85
|
if (!_dispatchCommands) {
|
|
86
|
-
const { dispatchCommands } = await import("./commands-
|
|
86
|
+
const { dispatchCommands } = await import("./commands-DrK9PHS4.js");
|
|
87
87
|
_dispatchCommands = dispatchCommands;
|
|
88
88
|
}
|
|
89
89
|
return _dispatchCommands;
|
|
90
90
|
}
|
|
91
91
|
async function getDocsCommands() {
|
|
92
92
|
if (!_docsCommands) {
|
|
93
|
-
const { docsCommands } = await import("./commands-
|
|
93
|
+
const { docsCommands } = await import("./commands-DrK9PHS4.js");
|
|
94
94
|
_docsCommands = docsCommands;
|
|
95
95
|
}
|
|
96
96
|
return _docsCommands;
|
|
97
97
|
}
|
|
98
98
|
async function getPlaygroundCommands() {
|
|
99
99
|
if (!_playgroundCommands) {
|
|
100
|
-
const { playgroundCommands } = await import("./commands-
|
|
100
|
+
const { playgroundCommands } = await import("./commands-DrK9PHS4.js");
|
|
101
101
|
_playgroundCommands = playgroundCommands;
|
|
102
102
|
}
|
|
103
103
|
return _playgroundCommands;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-cli",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.22",
|
|
4
4
|
"description": "Developer CLI for SMRT framework - introspection, testing, and project management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"acorn": "^8.17.0",
|
|
33
33
|
"fast-glob": "3.3.3",
|
|
34
34
|
"tar": "^7.5.19",
|
|
35
|
-
"@happyvertical/smrt-agents": "0.40.
|
|
36
|
-
"@happyvertical/smrt-
|
|
37
|
-
"@happyvertical/smrt-
|
|
38
|
-
"@happyvertical/smrt-dev-mcp": "0.40.
|
|
39
|
-
"@happyvertical/smrt-playground": "0.40.
|
|
40
|
-
"@happyvertical/smrt-types": "0.40.
|
|
35
|
+
"@happyvertical/smrt-agents": "0.40.22",
|
|
36
|
+
"@happyvertical/smrt-core": "0.40.22",
|
|
37
|
+
"@happyvertical/smrt-config": "0.40.22",
|
|
38
|
+
"@happyvertical/smrt-dev-mcp": "0.40.22",
|
|
39
|
+
"@happyvertical/smrt-playground": "0.40.22",
|
|
40
|
+
"@happyvertical/smrt-types": "0.40.22"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "24.13.2",
|