@lyku/lockstep-pg 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/LLMs.md +86 -0
  2. package/README.md +93 -0
  3. package/package.json +32 -0
  4. package/src/buildTableIndexCommand.d.ts +3 -0
  5. package/src/buildTableIndexCommand.d.ts.map +1 -0
  6. package/src/buildTableIndexCommand.js +46 -0
  7. package/src/buildTableIndexCommand.js.map +1 -0
  8. package/src/buildTableTriggerCommands.d.ts +3 -0
  9. package/src/buildTableTriggerCommands.d.ts.map +1 -0
  10. package/src/buildTableTriggerCommands.js +23 -0
  11. package/src/buildTableTriggerCommands.js.map +1 -0
  12. package/src/createTable.d.ts +3 -0
  13. package/src/createTable.d.ts.map +1 -0
  14. package/src/createTable.js +46 -0
  15. package/src/createTable.js.map +1 -0
  16. package/src/dateToPostgresString.d.ts +2 -0
  17. package/src/dateToPostgresString.d.ts.map +1 -0
  18. package/src/dateToPostgresString.js +7 -0
  19. package/src/dateToPostgresString.js.map +1 -0
  20. package/src/diff.d.ts +82 -0
  21. package/src/diff.d.ts.map +1 -0
  22. package/src/diff.js +218 -0
  23. package/src/diff.js.map +1 -0
  24. package/src/drift.d.ts +16 -0
  25. package/src/drift.d.ts.map +1 -0
  26. package/src/drift.js +299 -0
  27. package/src/drift.js.map +1 -0
  28. package/src/form.d.ts +3 -0
  29. package/src/form.d.ts.map +1 -0
  30. package/src/form.js +9 -0
  31. package/src/form.js.map +1 -0
  32. package/src/generateSql.d.ts +16 -0
  33. package/src/generateSql.d.ts.map +1 -0
  34. package/src/generateSql.js +306 -0
  35. package/src/generateSql.js.map +1 -0
  36. package/src/index.d.ts +16 -0
  37. package/src/index.d.ts.map +1 -0
  38. package/src/index.js +21 -0
  39. package/src/index.js.map +1 -0
  40. package/src/introspect.d.ts +56 -0
  41. package/src/introspect.d.ts.map +1 -0
  42. package/src/introspect.js +435 -0
  43. package/src/introspect.js.map +1 -0
  44. package/src/mapArrayType.d.ts +3 -0
  45. package/src/mapArrayType.d.ts.map +1 -0
  46. package/src/mapArrayType.js +41 -0
  47. package/src/mapArrayType.js.map +1 -0
  48. package/src/mapBigintType.d.ts +3 -0
  49. package/src/mapBigintType.d.ts.map +1 -0
  50. package/src/mapBigintType.js +17 -0
  51. package/src/mapBigintType.js.map +1 -0
  52. package/src/mapBigserialType.d.ts +3 -0
  53. package/src/mapBigserialType.d.ts.map +1 -0
  54. package/src/mapBigserialType.js +12 -0
  55. package/src/mapBigserialType.js.map +1 -0
  56. package/src/mapCharType.d.ts +3 -0
  57. package/src/mapCharType.d.ts.map +1 -0
  58. package/src/mapCharType.js +22 -0
  59. package/src/mapCharType.js.map +1 -0
  60. package/src/mapColumnType.d.ts +3 -0
  61. package/src/mapColumnType.d.ts.map +1 -0
  62. package/src/mapColumnType.js +63 -0
  63. package/src/mapColumnType.js.map +1 -0
  64. package/src/mapIntegerType.d.ts +3 -0
  65. package/src/mapIntegerType.d.ts.map +1 -0
  66. package/src/mapIntegerType.js +12 -0
  67. package/src/mapIntegerType.js.map +1 -0
  68. package/src/mapSerialType.d.ts +3 -0
  69. package/src/mapSerialType.d.ts.map +1 -0
  70. package/src/mapSerialType.js +12 -0
  71. package/src/mapSerialType.js.map +1 -0
  72. package/src/mapTextType.d.ts +3 -0
  73. package/src/mapTextType.d.ts.map +1 -0
  74. package/src/mapTextType.js +44 -0
  75. package/src/mapTextType.js.map +1 -0
  76. package/src/mapTimestamptzType.d.ts +3 -0
  77. package/src/mapTimestamptzType.d.ts.map +1 -0
  78. package/src/mapTimestamptzType.js +13 -0
  79. package/src/mapTimestamptzType.js.map +1 -0
  80. package/src/mapVarcharType.d.ts +3 -0
  81. package/src/mapVarcharType.d.ts.map +1 -0
  82. package/src/mapVarcharType.js +44 -0
  83. package/src/mapVarcharType.js.map +1 -0
  84. package/src/migrate.d.ts +13 -0
  85. package/src/migrate.d.ts.map +1 -0
  86. package/src/migrate.js +350 -0
  87. package/src/migrate.js.map +1 -0
  88. package/src/numberChecks.d.ts +11 -0
  89. package/src/numberChecks.d.ts.map +1 -0
  90. package/src/numberChecks.js +22 -0
  91. package/src/numberChecks.js.map +1 -0
  92. package/src/seed.d.ts +3 -0
  93. package/src/seed.d.ts.map +1 -0
  94. package/src/seed.js +56 -0
  95. package/src/seed.js.map +1 -0
  96. package/src/setupTable.d.ts +3 -0
  97. package/src/setupTable.d.ts.map +1 -0
  98. package/src/setupTable.js +41 -0
  99. package/src/setupTable.js.map +1 -0
  100. package/src/timestampChecks.d.ts +11 -0
  101. package/src/timestampChecks.d.ts.map +1 -0
  102. package/src/timestampChecks.js +22 -0
  103. package/src/timestampChecks.js.map +1 -0
package/LLMs.md ADDED
@@ -0,0 +1,86 @@
1
+ # @lyku/lockstep-pg - LLMs Guide
2
+
3
+ ## Purpose
4
+
5
+ Generic PostgreSQL migration toolkit that operates on `@lyku/lockstep-core` schema definitions. It bridges the gap between code-defined table models and a live database by introspecting, diffing, and generating SQL.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ Code Models (PostgresTableModel)
11
+ |
12
+ v
13
+ modelToIntrospected() --- normalize to IntrospectedTable
14
+ | |
15
+ v v
16
+ introspectDatabase() ---------> diffDatabase()
17
+ | |
18
+ v v
19
+ IntrospectedTable (from DB) DiffOperation[]
20
+ | |
21
+ v v
22
+ detectDrift() ---------> driftToSql() / generateMigrationSql()
23
+ | |
24
+ v v
25
+ Drift[] SQL strings (safe + destructive)
26
+ ```
27
+
28
+ Two parallel paths exist:
29
+
30
+ 1. **Legacy drift path**: `detectDrift()` -> `driftToSql()` -- single function connects to DB, returns flat drift list
31
+ 2. **Structured diff path**: `introspectDatabase()` -> `diffDatabase()` -> `generateMigrationSql()` -- composable pipeline with typed operations
32
+
33
+ ## Key Types
34
+
35
+ - `DataformConfig` -- `{ tables: Record<string, PostgresTableModel> }`
36
+ - `Drift` -- flat drift record with type, table, details, column, expected/actual
37
+ - `IntrospectedTable` -- normalized table representation (columns as Map, indexes, PKs, unique constraints, FKs)
38
+ - `IntrospectedColumn` -- column with normalized type, nullable, default, enum values, array item type
39
+ - `DiffOperation` -- discriminated union of 14 operation types (create_table, add_column, alter_column_type, etc.)
40
+
41
+ ## File Structure
42
+
43
+ ```
44
+ src/
45
+ index.ts -- Public API re-exports
46
+ drift.ts -- detectDrift() - connects to DB, returns Drift[]
47
+ introspect.ts -- introspectTable/Database(), modelToIntrospected(), normalizeDbType()
48
+ diff.ts -- diffTable/Database(), categorizeOperations()
49
+ migrate.ts -- driftToSql(), generateMigration() (legacy drift path)
50
+ generateSql.ts -- operationToSql(), generateMigrationSql() (structured diff path)
51
+ form.ts -- generateCreateTablesSql()
52
+ seed.ts -- generateSeedSql()
53
+ setupTable.ts -- Full CREATE TABLE + indexes + triggers for one table
54
+ createTable.ts -- buildTableCreationCommand()
55
+ buildTableIndexCommand.ts -- buildTableIndexCommands()
56
+ buildTableTriggerCommands.ts -- buildTableTriggerCommands()
57
+ mapColumnType.ts -- Master column type mapper
58
+ map*Type.ts -- Per-type mappers (bigint, serial, varchar, array, etc.)
59
+ dateToPostgresString.ts -- Date formatting
60
+ numberChecks.ts -- CHECK constraint SQL for numeric bounds
61
+ timestampChecks.ts -- CHECK constraint SQL for timestamp bounds
62
+ ```
63
+
64
+ ## Key Patterns
65
+
66
+ - **Type normalization**: Both DB types and schema types are normalized to canonical forms before comparison (e.g., `int8` -> `bigint`, `character varying` -> `varchar`, `user-defined` -> `text`)
67
+ - **Enum as TEXT + CHECK**: Enums are stored as `TEXT` columns with `CHECK (col IN ('a', 'b'))` constraints, not PostgreSQL enum types
68
+ - **Safe vs destructive**: All operations are categorized -- additive changes (ADD COLUMN, CREATE INDEX) are safe; DROP operations are destructive and output separately
69
+ - **Serial detection**: `bigserial`/`serial` columns are stored as `bigint`/`integer` in Postgres with `nextval()` defaults -- the introspector detects this pattern
70
+ - **Array type inference**: Array columns carry `arrayItemType` metadata to generate correct `TYPE[]` SQL
71
+
72
+ ## Gotchas
73
+
74
+ - `detectDrift()` creates its own `pg.Client` with `ssl: true` hardcoded -- won't work with non-SSL connections
75
+ - The legacy `Drift` type and the structured `DiffOperation` type are parallel systems that don't share code
76
+ - `modelToIntrospected()` generates synthetic index names (`idx_tableName_col`) that won't match actual DB index names -- comparison is column-based, not name-based
77
+ - Foreign key diff can generate drops for manually-created constraints not in the schema
78
+ - `escapeString` in migrate.ts and seed.ts is duplicated
79
+
80
+ ## Monorepo Connections
81
+
82
+ - **Input**: `@lyku/lockstep-core` (PostgresTableModel, PostgresRecordModel, PostgresColumnModel types)
83
+ - **Consumer**: `apps/dataform` (CLI scripts that call these functions)
84
+ - **Data source**: `@lyku/pg-config` (the actual table registry passed as DataformConfig)
85
+ - **Peer dep**: `@lyku/lockstep-core >= 0.2.0`
86
+ - **Runtime dep**: `pg` for database connections
package/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # @lyku/lockstep-pg
2
+
3
+ Schema-driven PostgreSQL migration toolkit for [`@lyku/lockstep-core`](../from-schema) models. Detects drift between your code-defined schemas and the live database, then generates safe (and optionally destructive) SQL migrations.
4
+
5
+ ## Features
6
+
7
+ - **Drift detection** - Compare `PostgresTableModel` definitions against a live PostgreSQL database
8
+ - **Introspection** - Read table structure, indexes, constraints, and foreign keys from any Postgres database
9
+ - **Diff engine** - Structural diff between introspected tables and code models, categorized into safe vs. destructive operations
10
+ - **SQL generation** - Produces migration SQL from diffs or drift reports, including `CREATE TABLE`, `ALTER COLUMN`, `ADD INDEX`, enum `CHECK` constraint updates, and stock document seeding
11
+ - **Seed data** - Generate `INSERT ... ON CONFLICT DO NOTHING` statements for stock/fixture documents defined in table models
12
+
13
+ ## Usage
14
+
15
+ ### Detect drift
16
+
17
+ ```typescript
18
+ import { detectDrift } from '@lyku/lockstep-pg';
19
+ import { tables } from '@lyku/pg-config';
20
+
21
+ const drifts = await detectDrift(process.env.PG_CONNECTION_STRING, { tables });
22
+ // Returns Drift[] - missing tables, extra columns, type mismatches, missing indexes, etc.
23
+ ```
24
+
25
+ ### Generate a migration
26
+
27
+ ```typescript
28
+ import { generateMigration } from '@lyku/lockstep-pg';
29
+ import { tables } from '@lyku/pg-config';
30
+
31
+ const { safe, destructive, driftCount } = await generateMigration(process.env.PG_CONNECTION_STRING, { tables });
32
+ // safe: SQL string wrapped in BEGIN/COMMIT (additive changes)
33
+ // destructive: SQL string for DROP operations (review carefully)
34
+ ```
35
+
36
+ ### Introspect a database
37
+
38
+ ```typescript
39
+ import { Client } from 'pg';
40
+ import { introspectDatabase, introspectTable } from '@lyku/lockstep-pg';
41
+
42
+ const client = new Client({ connectionString: '...' });
43
+ await client.connect();
44
+
45
+ // Single table
46
+ const table = await introspectTable(client, 'users');
47
+
48
+ // All tables
49
+ const allTables = await introspectDatabase(client);
50
+
51
+ await client.end();
52
+ ```
53
+
54
+ ### Diff and generate SQL
55
+
56
+ ```typescript
57
+ import { diffDatabase, categorizeOperations } from '@lyku/lockstep-pg';
58
+ import { generateMigrationSql } from '@lyku/lockstep-pg';
59
+
60
+ const ops = diffDatabase(dbTables, codeTables);
61
+ const { safe, destructive } = categorizeOperations(ops);
62
+ const sql = generateMigrationSql(ops);
63
+ ```
64
+
65
+ ### Generate seed SQL
66
+
67
+ ```typescript
68
+ import { generateSeedSql } from '@lyku/lockstep-pg';
69
+ import { tables } from '@lyku/pg-config';
70
+
71
+ const sql = generateSeedSql({ tables });
72
+ // INSERT ... ON CONFLICT DO NOTHING for each table's `docs` array
73
+ ```
74
+
75
+ ## Drift types
76
+
77
+ | Type | Description |
78
+ | --------------------------- | ------------------------------------------------ |
79
+ | `missing_table` | Table defined in schema but absent from database |
80
+ | `extra_table` | Table in database but not in schema |
81
+ | `missing_column` | Column defined in schema but absent from table |
82
+ | `extra_column` | Column in table but not in schema |
83
+ | `column_type_mismatch` | Column type differs between schema and database |
84
+ | `nullable_mismatch` | NOT NULL constraint differs |
85
+ | `missing_index` | Index defined in schema but absent from database |
86
+ | `extra_index` | Index in database but not in schema |
87
+ | `missing_constraint` | CHECK constraint missing for enum column |
88
+ | `check_constraint_mismatch` | Enum CHECK constraint values differ |
89
+ | `missing_stock_doc` | Fixture/seed record missing from table |
90
+
91
+ ## License
92
+
93
+ GPL-3.0
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@lyku/lockstep-pg",
3
+ "version": "0.1.1",
4
+ "description": "Schema-driven PostgreSQL migration toolkit: drift detection, introspection, and SQL generation for @lyku/lockstep-core models",
5
+ "main": "./src/index.js",
6
+ "types": "./src/index.d.ts",
7
+ "type": "module",
8
+ "license": "GPL-3.0",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./src/index.d.ts",
12
+ "import": "./src/index.js",
13
+ "default": "./src/index.js"
14
+ }
15
+ },
16
+ "peerDependencies": {
17
+ "@lyku/lockstep-core": ">=0.2.0"
18
+ },
19
+ "dependencies": {
20
+ "pg": "^8.0.0"
21
+ },
22
+ "keywords": [
23
+ "postgresql",
24
+ "migration",
25
+ "schema",
26
+ "drift",
27
+ "introspection",
28
+ "lockstep",
29
+ "lockstep-core"
30
+ ],
31
+ "module": "./src/index.js"
32
+ }
@@ -0,0 +1,3 @@
1
+ import { PostgresRecordModel, PostgresTableModel } from '@lyku/lockstep-core';
2
+ export declare function buildTableIndexCommands<T extends PostgresTableModel<PostgresRecordModel>>(tableName: string, model: T): string[];
3
+ //# sourceMappingURL=buildTableIndexCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTableIndexCommand.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/buildTableIndexCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,wBAAgB,uBAAuB,CACtC,CAAC,SAAS,kBAAkB,CAAC,mBAAmB,CAAC,EAChD,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,YAmD5B"}
@@ -0,0 +1,46 @@
1
+ export function buildTableIndexCommands(tableName, model) {
2
+ const indexQueries = [];
3
+ if (model.indexes) {
4
+ model.indexes.forEach((index) => {
5
+ let indexColumns;
6
+ let indexName;
7
+ let whereClause;
8
+ if (typeof index === 'string') {
9
+ // Single column index
10
+ indexColumns = [`"${index}"`];
11
+ indexName = `idx_${tableName}_${index.split(' ')[0]}`;
12
+ }
13
+ else if (Array.isArray(index)) {
14
+ // Multi-column index
15
+ indexColumns = index.map((col) => {
16
+ const parts = col.split(' ');
17
+ const colName = parts[0];
18
+ const modifiers = parts.slice(1).join(' '); // e.g., "DESC NULLS LAST"
19
+ return modifiers ? `"${colName}" ${modifiers}` : `"${colName}"`;
20
+ });
21
+ indexName = `idx_${tableName}_${index.map((c) => c.split(' ')[0]).join('_')}`;
22
+ }
23
+ else {
24
+ // Object format with optional custom name and where clause
25
+ const objIndex = index;
26
+ const cols = Array.isArray(objIndex.columns)
27
+ ? objIndex.columns
28
+ : [objIndex.columns];
29
+ indexColumns = cols.map((col) => `"${col}"`);
30
+ indexName = objIndex.name ?? `idx_${tableName}_${cols.join('_')}`;
31
+ // Support partial indexes with WHERE clause
32
+ if (objIndex.where) {
33
+ whereClause = objIndex.where;
34
+ }
35
+ }
36
+ let indexQuery = `CREATE INDEX IF NOT EXISTS "${indexName}" ON "${tableName}" (${indexColumns.join(', ')})`;
37
+ if (whereClause) {
38
+ indexQuery += `\nWHERE ${whereClause}`;
39
+ }
40
+ indexQuery += ';';
41
+ indexQueries.push(indexQuery);
42
+ });
43
+ }
44
+ return indexQueries;
45
+ }
46
+ //# sourceMappingURL=buildTableIndexCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTableIndexCommand.js","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/buildTableIndexCommand.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAErC,SAAiB,EAAE,KAAQ;IAC5B,MAAM,YAAY,GAAa,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,YAAsB,CAAC;YAC3B,IAAI,SAAiB,CAAC;YACtB,IAAI,WAA+B,CAAC;YAEpC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC/B,sBAAsB;gBACtB,YAAY,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;gBAC9B,SAAS,GAAG,OAAO,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,CAAC;iBAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,qBAAqB;gBACrB,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;oBAChC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;oBACzB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,0BAA0B;oBACtE,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC;gBACjE,CAAC,CAAC,CAAC;gBACH,SAAS,GAAG,OAAO,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/E,CAAC;iBAAM,CAAC;gBACP,2DAA2D;gBAC3D,MAAM,QAAQ,GAAG,KAIhB,CAAC;gBACF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;oBAC3C,CAAC,CAAC,QAAQ,CAAC,OAAO;oBAClB,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACtB,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;gBAC7C,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClE,4CAA4C;gBAC5C,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBACpB,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC;gBAC9B,CAAC;YACF,CAAC;YAED,IAAI,UAAU,GAAG,+BAA+B,SAAS,SAAS,SAAS,MAAM,YAAY,CAAC,IAAI,CACjG,IAAI,CACJ,GAAG,CAAC;YACL,IAAI,WAAW,EAAE,CAAC;gBACjB,UAAU,IAAI,WAAW,WAAW,EAAE,CAAC;YACxC,CAAC;YACD,UAAU,IAAI,GAAG,CAAC;YAClB,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC;AACrB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { PostgresRecordModel, PostgresTableModel } from '@lyku/lockstep-core';
2
+ export declare function buildTableTriggerCommands<T extends PostgresTableModel<PostgresRecordModel>>(tableName: string, model: T): string[];
3
+ //# sourceMappingURL=buildTableTriggerCommands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTableTriggerCommands.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/buildTableTriggerCommands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,wBAAgB,yBAAyB,CACxC,CAAC,SAAS,kBAAkB,CAAC,mBAAmB,CAAC,EAChD,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,YA2B5B"}
@@ -0,0 +1,23 @@
1
+ export function buildTableTriggerCommands(tableName, model) {
2
+ const triggerQueries = [];
3
+ if (model.triggers) {
4
+ model.triggers.forEach((trigger, i) => {
5
+ const triggerName = trigger.name ?? `${tableName}_trigger_${i + 1}`;
6
+ const timing = 'before' in trigger ? 'BEFORE' : 'AFTER';
7
+ const event = 'before' in trigger ? trigger.before : trigger.after;
8
+ triggerQueries.push(`CREATE OR REPLACE FUNCTION ${triggerName}_fn()\n` +
9
+ `RETURNS TRIGGER AS $$\n` +
10
+ `BEGIN\n` +
11
+ `${trigger.sql}\n` +
12
+ `END;\n` +
13
+ `$$ LANGUAGE plpgsql;\n`, `DROP TRIGGER IF EXISTS ${triggerName} ON "${tableName}";\n` +
14
+ `CREATE TRIGGER ${triggerName}\n` +
15
+ `${timing} ${event.toUpperCase()}\n` +
16
+ `ON "${tableName}"\n` +
17
+ `FOR EACH ROW\n` +
18
+ `EXECUTE FUNCTION ${triggerName}_fn();`);
19
+ });
20
+ }
21
+ return triggerQueries;
22
+ }
23
+ //# sourceMappingURL=buildTableTriggerCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTableTriggerCommands.js","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/buildTableTriggerCommands.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,yBAAyB,CAEvC,SAAiB,EAAE,KAAQ;IAC5B,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;YACrC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,SAAS,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;YACpE,MAAM,MAAM,GAAG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YACxD,MAAM,KAAK,GAAG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;YAEnE,cAAc,CAAC,IAAI,CAClB,8BAA8B,WAAW,SAAS;gBACjD,yBAAyB;gBACzB,SAAS;gBACT,GAAG,OAAO,CAAC,GAAG,IAAI;gBAClB,QAAQ;gBACR,wBAAwB,EAEzB,0BAA0B,WAAW,QAAQ,SAAS,MAAM;gBAC3D,kBAAkB,WAAW,IAAI;gBACjC,GAAG,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI;gBACpC,OAAO,SAAS,KAAK;gBACrB,gBAAgB;gBAChB,oBAAoB,WAAW,QAAQ,CACxC,CAAC;QACH,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO,cAAc,CAAC;AACvB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { PostgresRecordModel, PostgresTableModel } from '@lyku/lockstep-core';
2
+ export declare const buildTableCreationCommand: (tableName: string, model: PostgresTableModel<PostgresRecordModel>) => string;
3
+ //# sourceMappingURL=createTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTable.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/createTable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAa9E,eAAO,MAAM,yBAAyB,GACrC,WAAW,MAAM,EACjB,OAAO,kBAAkB,CAAC,mBAAmB,CAAC,WAkC9C,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { mapColumnType } from './mapColumnType';
2
+ function formatDefault(def) {
3
+ if (def === null)
4
+ return 'NULL';
5
+ if (typeof def === 'object' && def !== null && 'sql' in def) {
6
+ return def.sql;
7
+ }
8
+ if (typeof def === 'string')
9
+ return `'${def}'`;
10
+ if (typeof def === 'boolean')
11
+ return def ? 'TRUE' : 'FALSE';
12
+ return String(def);
13
+ }
14
+ export const buildTableCreationCommand = (tableName, model) => {
15
+ const { schema } = model;
16
+ const required = 'required' in schema ? schema.required : [];
17
+ const columns = 'properties' in schema
18
+ ? Object.entries(schema.properties)
19
+ .map(([columnName, columnSchema]) => {
20
+ const columnType = mapColumnType(columnName, columnSchema);
21
+ const notNull = required.includes(columnName) ? ' NOT NULL' : '';
22
+ const hasDefault = 'default' in columnSchema && columnSchema.default !== undefined;
23
+ const defaultClause = hasDefault
24
+ ? ` DEFAULT ${formatDefault(columnSchema.default)}`
25
+ : '';
26
+ return `"${columnName}" ${columnType}${defaultClause}${notNull}`;
27
+ })
28
+ .join(', ')
29
+ : '';
30
+ const { primaryKey, unique } = model;
31
+ const primary = primaryKey
32
+ ? `, PRIMARY KEY (${[primaryKey]
33
+ .flat()
34
+ .map((k) => `"${k}"`)
35
+ .join(', ')})`
36
+ : '';
37
+ const uni = unique
38
+ ? `, UNIQUE (${[unique]
39
+ .flat()
40
+ .map((k) => `"${k}"`)
41
+ .join(', ')})`
42
+ : '';
43
+ const createTableQuery = `CREATE TABLE IF NOT EXISTS "${tableName}" (${columns} ${primary} ${uni});`;
44
+ return createTableQuery;
45
+ };
46
+ //# sourceMappingURL=createTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTable.js","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/createTable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,SAAS,aAAa,CAAC,GAAY;IAClC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;QAC7D,OAAQ,GAAuB,CAAC,GAAG,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,GAAG,GAAG,CAAC;IAC/C,IAAI,OAAO,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC5D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACxC,SAAiB,EACjB,KAA8C,EAC7C,EAAE;IACH,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,MAAM,OAAO,GACZ,YAAY,IAAI,MAAM;QACrB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,EAAE;YACnC,MAAM,UAAU,GAAG,aAAa,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;YAC3D,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,MAAM,UAAU,GACf,SAAS,IAAI,YAAY,IAAI,YAAY,CAAC,OAAO,KAAK,SAAS,CAAC;YACjE,MAAM,aAAa,GAAG,UAAU;gBAC/B,CAAC,CAAC,YAAY,aAAa,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE;gBACnD,CAAC,CAAC,EAAE,CAAC;YACN,OAAO,IAAI,UAAU,KAAK,UAAU,GAAG,aAAa,GAAG,OAAO,EAAE,CAAC;QAClE,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC;QACb,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACrC,MAAM,OAAO,GAAG,UAAU;QACzB,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC;aAC7B,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,GAAG;QAChB,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,GAAG,GAAG,MAAM;QACjB,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC;aACpB,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;aACpB,IAAI,CAAC,IAAI,CAAC,GAAG;QAChB,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,gBAAgB,GAAG,+BAA+B,SAAS,MAAM,OAAO,IAAI,OAAO,IAAI,GAAG,IAAI,CAAC;IACrG,OAAO,gBAAgB,CAAC;AACzB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const dateToPostgresString: (date: Date | string) => string;
2
+ //# sourceMappingURL=dateToPostgresString.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateToPostgresString.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/dateToPostgresString.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,GAAI,MAAM,IAAI,GAAG,MAAM,WAMvD,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const dateToPostgresString = (date) => {
2
+ // Ensure we're working with a Date object
3
+ const dateObj = date instanceof Date ? date : new Date(date);
4
+ // Format: YYYY-MM-DD HH:MM:SS.sss
5
+ return dateObj.toISOString().slice(0, 19).replace('T', ' ');
6
+ };
7
+ //# sourceMappingURL=dateToPostgresString.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateToPostgresString.js","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/dateToPostgresString.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAAmB,EAAE,EAAE;IAC3D,0CAA0C;IAC1C,MAAM,OAAO,GAAG,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC;IAE7D,kCAAkC;IAClC,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC7D,CAAC,CAAC"}
package/src/diff.d.ts ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Diff two table models and generate migration operations
3
+ */
4
+ import type { IntrospectedTable, IntrospectedColumn, IntrospectedIndex, IntrospectedForeignKey } from './introspect';
5
+ export type DiffOperation = {
6
+ type: 'create_table';
7
+ table: IntrospectedTable;
8
+ } | {
9
+ type: 'drop_table';
10
+ tableName: string;
11
+ } | {
12
+ type: 'add_column';
13
+ tableName: string;
14
+ column: IntrospectedColumn;
15
+ } | {
16
+ type: 'drop_column';
17
+ tableName: string;
18
+ columnName: string;
19
+ } | {
20
+ type: 'alter_column_type';
21
+ tableName: string;
22
+ columnName: string;
23
+ fromType: string;
24
+ toType: string;
25
+ toArrayItemType: string | null;
26
+ } | {
27
+ type: 'alter_column_nullable';
28
+ tableName: string;
29
+ columnName: string;
30
+ nullable: boolean;
31
+ } | {
32
+ type: 'alter_column_default';
33
+ tableName: string;
34
+ columnName: string;
35
+ defaultValue: string | null;
36
+ } | {
37
+ type: 'update_check_constraint';
38
+ tableName: string;
39
+ columnName: string;
40
+ oldValues: string[];
41
+ newValues: string[];
42
+ } | {
43
+ type: 'add_index';
44
+ tableName: string;
45
+ index: IntrospectedIndex;
46
+ } | {
47
+ type: 'drop_index';
48
+ tableName: string;
49
+ indexName: string;
50
+ } | {
51
+ type: 'add_unique_constraint';
52
+ tableName: string;
53
+ columns: string[];
54
+ } | {
55
+ type: 'drop_unique_constraint';
56
+ tableName: string;
57
+ columns: string[];
58
+ } | {
59
+ type: 'add_foreign_key';
60
+ tableName: string;
61
+ fk: IntrospectedForeignKey;
62
+ } | {
63
+ type: 'drop_foreign_key';
64
+ tableName: string;
65
+ constraintName: string;
66
+ };
67
+ /**
68
+ * Diff two tables and generate operations needed to migrate DB to match code
69
+ */
70
+ export declare function diffTable(dbTable: IntrospectedTable | undefined, codeTable: IntrospectedTable | undefined): DiffOperation[];
71
+ /**
72
+ * Diff entire database against code models
73
+ */
74
+ export declare function diffDatabase(dbTables: Map<string, IntrospectedTable>, codeTables: Map<string, IntrospectedTable>): DiffOperation[];
75
+ /**
76
+ * Categorize operations into safe and destructive
77
+ */
78
+ export declare function categorizeOperations(ops: DiffOperation[]): {
79
+ safe: DiffOperation[];
80
+ destructive: DiffOperation[];
81
+ };
82
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../../libs/lockstep-pg/src/diff.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACX,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,aAAa,GACtB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9D;IACA,IAAI,EAAE,mBAAmB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GACD;IACA,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACjB,GACD;IACA,IAAI,EAAE,sBAAsB,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GACD;IACA,IAAI,EAAE,yBAAyB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,iBAAiB,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC5D;IACA,IAAI,EAAE,uBAAuB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACA,IAAI,EAAE,wBAAwB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACA,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,sBAAsB,CAAC;CAC1B,GACD;IACA,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACtB,CAAC;AA4IL;;GAEG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,iBAAiB,GAAG,SAAS,EACtC,SAAS,EAAE,iBAAiB,GAAG,SAAS,GACtC,aAAa,EAAE,CA+EjB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACxC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GACxC,aAAa,EAAE,CAajB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG;IAC3D,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,WAAW,EAAE,aAAa,EAAE,CAAC;CAC7B,CAgCA"}