@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.
- package/LLMs.md +86 -0
- package/README.md +93 -0
- package/package.json +32 -0
- package/src/buildTableIndexCommand.d.ts +3 -0
- package/src/buildTableIndexCommand.d.ts.map +1 -0
- package/src/buildTableIndexCommand.js +46 -0
- package/src/buildTableIndexCommand.js.map +1 -0
- package/src/buildTableTriggerCommands.d.ts +3 -0
- package/src/buildTableTriggerCommands.d.ts.map +1 -0
- package/src/buildTableTriggerCommands.js +23 -0
- package/src/buildTableTriggerCommands.js.map +1 -0
- package/src/createTable.d.ts +3 -0
- package/src/createTable.d.ts.map +1 -0
- package/src/createTable.js +46 -0
- package/src/createTable.js.map +1 -0
- package/src/dateToPostgresString.d.ts +2 -0
- package/src/dateToPostgresString.d.ts.map +1 -0
- package/src/dateToPostgresString.js +7 -0
- package/src/dateToPostgresString.js.map +1 -0
- package/src/diff.d.ts +82 -0
- package/src/diff.d.ts.map +1 -0
- package/src/diff.js +218 -0
- package/src/diff.js.map +1 -0
- package/src/drift.d.ts +16 -0
- package/src/drift.d.ts.map +1 -0
- package/src/drift.js +299 -0
- package/src/drift.js.map +1 -0
- package/src/form.d.ts +3 -0
- package/src/form.d.ts.map +1 -0
- package/src/form.js +9 -0
- package/src/form.js.map +1 -0
- package/src/generateSql.d.ts +16 -0
- package/src/generateSql.d.ts.map +1 -0
- package/src/generateSql.js +306 -0
- package/src/generateSql.js.map +1 -0
- package/src/index.d.ts +16 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +21 -0
- package/src/index.js.map +1 -0
- package/src/introspect.d.ts +56 -0
- package/src/introspect.d.ts.map +1 -0
- package/src/introspect.js +435 -0
- package/src/introspect.js.map +1 -0
- package/src/mapArrayType.d.ts +3 -0
- package/src/mapArrayType.d.ts.map +1 -0
- package/src/mapArrayType.js +41 -0
- package/src/mapArrayType.js.map +1 -0
- package/src/mapBigintType.d.ts +3 -0
- package/src/mapBigintType.d.ts.map +1 -0
- package/src/mapBigintType.js +17 -0
- package/src/mapBigintType.js.map +1 -0
- package/src/mapBigserialType.d.ts +3 -0
- package/src/mapBigserialType.d.ts.map +1 -0
- package/src/mapBigserialType.js +12 -0
- package/src/mapBigserialType.js.map +1 -0
- package/src/mapCharType.d.ts +3 -0
- package/src/mapCharType.d.ts.map +1 -0
- package/src/mapCharType.js +22 -0
- package/src/mapCharType.js.map +1 -0
- package/src/mapColumnType.d.ts +3 -0
- package/src/mapColumnType.d.ts.map +1 -0
- package/src/mapColumnType.js +63 -0
- package/src/mapColumnType.js.map +1 -0
- package/src/mapIntegerType.d.ts +3 -0
- package/src/mapIntegerType.d.ts.map +1 -0
- package/src/mapIntegerType.js +12 -0
- package/src/mapIntegerType.js.map +1 -0
- package/src/mapSerialType.d.ts +3 -0
- package/src/mapSerialType.d.ts.map +1 -0
- package/src/mapSerialType.js +12 -0
- package/src/mapSerialType.js.map +1 -0
- package/src/mapTextType.d.ts +3 -0
- package/src/mapTextType.d.ts.map +1 -0
- package/src/mapTextType.js +44 -0
- package/src/mapTextType.js.map +1 -0
- package/src/mapTimestamptzType.d.ts +3 -0
- package/src/mapTimestamptzType.d.ts.map +1 -0
- package/src/mapTimestamptzType.js +13 -0
- package/src/mapTimestamptzType.js.map +1 -0
- package/src/mapVarcharType.d.ts +3 -0
- package/src/mapVarcharType.d.ts.map +1 -0
- package/src/mapVarcharType.js +44 -0
- package/src/mapVarcharType.js.map +1 -0
- package/src/migrate.d.ts +13 -0
- package/src/migrate.d.ts.map +1 -0
- package/src/migrate.js +350 -0
- package/src/migrate.js.map +1 -0
- package/src/numberChecks.d.ts +11 -0
- package/src/numberChecks.d.ts.map +1 -0
- package/src/numberChecks.js +22 -0
- package/src/numberChecks.js.map +1 -0
- package/src/seed.d.ts +3 -0
- package/src/seed.d.ts.map +1 -0
- package/src/seed.js +56 -0
- package/src/seed.js.map +1 -0
- package/src/setupTable.d.ts +3 -0
- package/src/setupTable.d.ts.map +1 -0
- package/src/setupTable.js +41 -0
- package/src/setupTable.js.map +1 -0
- package/src/timestampChecks.d.ts +11 -0
- package/src/timestampChecks.d.ts.map +1 -0
- package/src/timestampChecks.js +22 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|