@softure-ai/db 0.0.0-stage → 0.1.6
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/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +383 -2
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/command.d.ts +8 -0
- package/dist/cli/command.d.ts.map +1 -0
- package/dist/cli/command.js +38 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +27 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +205 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/client.d.ts +73 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +90 -0
- package/dist/client.js.map +1 -0
- package/dist/configured.d.ts +31 -0
- package/dist/configured.d.ts.map +1 -0
- package/dist/configured.js +74 -0
- package/dist/configured.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/adopt.d.ts +33 -0
- package/dist/migrations/adopt.d.ts.map +1 -0
- package/dist/migrations/adopt.js +97 -0
- package/dist/migrations/adopt.js.map +1 -0
- package/dist/migrations/apply.d.ts +35 -0
- package/dist/migrations/apply.d.ts.map +1 -0
- package/dist/migrations/apply.js +115 -0
- package/dist/migrations/apply.js.map +1 -0
- package/dist/migrations/export.d.ts +10 -0
- package/dist/migrations/export.d.ts.map +1 -0
- package/dist/migrations/export.js +58 -0
- package/dist/migrations/export.js.map +1 -0
- package/dist/migrations/exported.d.ts +20 -0
- package/dist/migrations/exported.d.ts.map +1 -0
- package/dist/migrations/exported.js +61 -0
- package/dist/migrations/exported.js.map +1 -0
- package/dist/migrations/files.d.ts +21 -0
- package/dist/migrations/files.d.ts.map +1 -0
- package/dist/migrations/files.js +112 -0
- package/dist/migrations/files.js.map +1 -0
- package/dist/migrations/introspect.d.ts +6 -0
- package/dist/migrations/introspect.d.ts.map +1 -0
- package/dist/migrations/introspect.js +117 -0
- package/dist/migrations/introspect.js.map +1 -0
- package/dist/migrations/ledger.d.ts +27 -0
- package/dist/migrations/ledger.d.ts.map +1 -0
- package/dist/migrations/ledger.js +45 -0
- package/dist/migrations/ledger.js.map +1 -0
- package/dist/migrations/migrator.d.ts +97 -0
- package/dist/migrations/migrator.d.ts.map +1 -0
- package/dist/migrations/migrator.js +301 -0
- package/dist/migrations/migrator.js.map +1 -0
- package/dist/migrations/problems.d.ts +93 -0
- package/dist/migrations/problems.d.ts.map +1 -0
- package/dist/migrations/problems.js +62 -0
- package/dist/migrations/problems.js.map +1 -0
- package/dist/migrations/reference.d.ts +23 -0
- package/dist/migrations/reference.d.ts.map +1 -0
- package/dist/migrations/reference.js +64 -0
- package/dist/migrations/reference.js.map +1 -0
- package/dist/migrations/session.d.ts +15 -0
- package/dist/migrations/session.d.ts.map +1 -0
- package/dist/migrations/session.js +56 -0
- package/dist/migrations/session.js.map +1 -0
- package/dist/migrations/stubs.d.ts +23 -0
- package/dist/migrations/stubs.d.ts.map +1 -0
- package/dist/migrations/stubs.js +99 -0
- package/dist/migrations/stubs.js.map +1 -0
- package/dist/shared.d.ts +9 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +32 -0
- package/dist/shared.js.map +1 -0
- package/dist/testing.d.ts +23 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +62 -0
- package/dist/testing.js.map +1 -0
- package/package.json +73 -4
- package/src/cli/bin.ts +5 -0
- package/src/cli/command.ts +47 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/run.ts +250 -0
- package/src/client.ts +151 -0
- package/src/configured.ts +109 -0
- package/src/index.ts +46 -0
- package/src/migrations/adopt.ts +136 -0
- package/src/migrations/apply.ts +135 -0
- package/src/migrations/export.ts +66 -0
- package/src/migrations/exported.ts +82 -0
- package/src/migrations/files.ts +129 -0
- package/src/migrations/introspect.ts +165 -0
- package/src/migrations/ledger.ts +75 -0
- package/src/migrations/migrator.ts +403 -0
- package/src/migrations/problems.ts +99 -0
- package/src/migrations/reference.ts +78 -0
- package/src/migrations/session.ts +74 -0
- package/src/migrations/stubs.ts +139 -0
- package/src/shared.ts +36 -0
- package/src/testing.ts +80 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const NOT_IN_EXTENSION = (catalog, alias) => `NOT EXISTS (SELECT 1 FROM pg_depend dep WHERE dep.classid = '${catalog}'::regclass AND dep.objid = ${alias}.oid AND dep.deptype = 'e')`;
|
|
2
|
+
const RELATION_KINDS = {
|
|
3
|
+
r: "table",
|
|
4
|
+
p: "partitioned table",
|
|
5
|
+
v: "view",
|
|
6
|
+
m: "materialized view",
|
|
7
|
+
S: "sequence",
|
|
8
|
+
f: "foreign table",
|
|
9
|
+
};
|
|
10
|
+
const QUERIES = {
|
|
11
|
+
relations: `
|
|
12
|
+
SELECT c.relname, c.relkind, c.relpersistence
|
|
13
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
14
|
+
WHERE n.nspname = $1 AND c.relkind IN ('r', 'p', 'v', 'm', 'S', 'f') AND ${NOT_IN_EXTENSION("pg_class", "c")}`,
|
|
15
|
+
columns: `
|
|
16
|
+
SELECT c.relname, a.attname, format_type(a.atttypid, a.atttypmod) AS type, a.attnotnull AS not_null,
|
|
17
|
+
CASE WHEN a.attcollation <> 0 AND a.attcollation <> t.typcollation THEN quote_ident(coll.collname) END AS collation,
|
|
18
|
+
pg_get_expr(d.adbin, d.adrelid) AS default_expr, a.attidentity AS identity, a.attgenerated AS generated
|
|
19
|
+
FROM pg_attribute a
|
|
20
|
+
JOIN pg_class c ON c.oid = a.attrelid
|
|
21
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
22
|
+
JOIN pg_type t ON t.oid = a.atttypid
|
|
23
|
+
LEFT JOIN pg_collation coll ON coll.oid = a.attcollation
|
|
24
|
+
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
25
|
+
WHERE n.nspname = $1 AND c.relkind IN ('r', 'p', 'v', 'm', 'f', 'c') AND a.attnum > 0 AND NOT a.attisdropped
|
|
26
|
+
AND ${NOT_IN_EXTENSION("pg_class", "c")}`,
|
|
27
|
+
constraints: `
|
|
28
|
+
SELECT c.relname, con.conname, pg_get_constraintdef(con.oid, true) AS definition
|
|
29
|
+
FROM pg_constraint con
|
|
30
|
+
JOIN pg_class c ON c.oid = con.conrelid
|
|
31
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
32
|
+
WHERE n.nspname = $1 AND con.contype IN ('p', 'u', 'f', 'c', 'x')`,
|
|
33
|
+
indexes: `
|
|
34
|
+
SELECT i.relname, pg_get_indexdef(i.oid) AS definition
|
|
35
|
+
FROM pg_index x
|
|
36
|
+
JOIN pg_class i ON i.oid = x.indexrelid
|
|
37
|
+
JOIN pg_namespace n ON n.oid = i.relnamespace
|
|
38
|
+
WHERE n.nspname = $1 AND ${NOT_IN_EXTENSION("pg_class", "i")}`,
|
|
39
|
+
sequences: `
|
|
40
|
+
SELECT c.relname, format_type(s.seqtypid, NULL) AS type, s.seqstart::text AS start, s.seqincrement::text AS increment,
|
|
41
|
+
s.seqmin::text AS min, s.seqmax::text AS max, s.seqcache::text AS cache, s.seqcycle AS cycle
|
|
42
|
+
FROM pg_sequence s
|
|
43
|
+
JOIN pg_class c ON c.oid = s.seqrelid
|
|
44
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
45
|
+
WHERE n.nspname = $1 AND ${NOT_IN_EXTENSION("pg_class", "c")}`,
|
|
46
|
+
triggers: `
|
|
47
|
+
SELECT c.relname, t.tgname, pg_get_triggerdef(t.oid, true) AS definition
|
|
48
|
+
FROM pg_trigger t
|
|
49
|
+
JOIN pg_class c ON c.oid = t.tgrelid
|
|
50
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
51
|
+
WHERE n.nspname = $1 AND NOT t.tgisinternal`,
|
|
52
|
+
functions: `
|
|
53
|
+
SELECT p.proname || '(' || pg_get_function_identity_arguments(p.oid) || ')' AS signature,
|
|
54
|
+
md5(pg_get_functiondef(p.oid)) AS definition_hash
|
|
55
|
+
FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
56
|
+
WHERE n.nspname = $1 AND p.prokind IN ('f', 'p') AND ${NOT_IN_EXTENSION("pg_proc", "p")}`,
|
|
57
|
+
types: `
|
|
58
|
+
SELECT t.typname, t.typtype,
|
|
59
|
+
CASE t.typtype
|
|
60
|
+
WHEN 'r' THEN (SELECT format_type(r.rngsubtype, NULL) FROM pg_range r WHERE r.rngtypid = t.oid)
|
|
61
|
+
WHEN 'e' THEN (SELECT string_agg(e.enumlabel, ', ' ORDER BY e.enumsortorder) FROM pg_enum e WHERE e.enumtypid = t.oid)
|
|
62
|
+
WHEN 'd' THEN format_type(t.typbasetype, t.typtypmod)
|
|
63
|
+
END AS definition
|
|
64
|
+
FROM pg_type t JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
65
|
+
WHERE n.nspname = $1 AND ${NOT_IN_EXTENSION("pg_type", "t")}
|
|
66
|
+
AND (t.typtype IN ('e', 'd', 'r')
|
|
67
|
+
OR (t.typtype = 'c' AND (SELECT c.relkind FROM pg_class c WHERE c.oid = t.typrelid) = 'c'))`,
|
|
68
|
+
};
|
|
69
|
+
const TYPE_KINDS = { e: "enum", d: "domain", r: "range", c: "composite type" };
|
|
70
|
+
/** Every object of `schema` as one sorted line each. Runs in its own read-only transaction. */
|
|
71
|
+
export async function describeSchema(session, schema) {
|
|
72
|
+
await session.exec("BEGIN READ ONLY; SET LOCAL search_path = pg_catalog;");
|
|
73
|
+
try {
|
|
74
|
+
const lines = [
|
|
75
|
+
...(await session.query(QUERIES.relations, [schema])).map((row) => `${row.relpersistence === "u" ? "unlogged " : ""}${RELATION_KINDS[row.relkind] ?? row.relkind} ${schema}.${row.relname}`),
|
|
76
|
+
...(await session.query(QUERIES.sequences, [schema])).map((row) => `sequence ${schema}.${row.relname}: ${row.type} start ${row.start} increment ${row.increment} min ${row.min} max ${row.max} cache ${row.cache}${row.cycle ? " cycle" : ""}`),
|
|
77
|
+
...(await session.query(QUERIES.columns, [schema])).map((row) => describeColumn(schema, row)),
|
|
78
|
+
...(await session.query(QUERIES.constraints, [schema])).map((row) => `constraint ${schema}.${row.relname}.${row.conname}: ${row.definition}`),
|
|
79
|
+
...(await session.query(QUERIES.indexes, [schema])).map((row) => `index ${schema}.${row.relname}: ${row.definition}`),
|
|
80
|
+
...(await session.query(QUERIES.triggers, [schema])).map((row) => `trigger ${schema}.${row.relname}.${row.tgname}: ${row.definition}`),
|
|
81
|
+
...(await session.query(QUERIES.functions, [schema])).map((row) => `function ${schema}.${row.signature}: body ${row.definition_hash}`),
|
|
82
|
+
...(await session.query(QUERIES.types, [schema])).map((row) => `${TYPE_KINDS[row.typtype] ?? row.typtype} ${schema}.${row.typname}${row.definition === null ? "" : `: ${row.definition}`}`),
|
|
83
|
+
];
|
|
84
|
+
return lines.sort();
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
await session.exec("ROLLBACK");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** What is in `expected` and not in `actual`, and the other way round, as readable lines. */
|
|
91
|
+
export function diffSchemas(expected, actual) {
|
|
92
|
+
const actualSet = new Set(actual);
|
|
93
|
+
const expectedSet = new Set(expected);
|
|
94
|
+
return [
|
|
95
|
+
...expected.filter((line) => !actualSet.has(line)).map((line) => `missing in database: ${line}`),
|
|
96
|
+
...actual.filter((line) => !expectedSet.has(line)).map((line) => `unexpected in database: ${line}`),
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
function describeColumn(schema, row) {
|
|
100
|
+
const parts = [`column ${schema}.${row.relname}.${row.attname} ${row.type}`];
|
|
101
|
+
if (row.collation !== null)
|
|
102
|
+
parts.push(`collate ${row.collation}`);
|
|
103
|
+
if (row.not_null)
|
|
104
|
+
parts.push("not null");
|
|
105
|
+
if (row.generated === "s" || row.generated === "v") {
|
|
106
|
+
parts.push(`generated as ${row.default_expr ?? ""}${row.generated === "s" ? " stored" : " virtual"}`);
|
|
107
|
+
}
|
|
108
|
+
else if (row.default_expr !== null) {
|
|
109
|
+
parts.push(`default ${row.default_expr}`);
|
|
110
|
+
}
|
|
111
|
+
if (row.identity === "a")
|
|
112
|
+
parts.push("identity always");
|
|
113
|
+
if (row.identity === "d")
|
|
114
|
+
parts.push("identity by default");
|
|
115
|
+
return parts.join(" ");
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/migrations/introspect.ts"],"names":[],"mappings":"AAWA,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAE,KAAa,EAAU,EAAE,CAClE,gEAAgE,OAAO,+BAA+B,KAAK,6BAA6B,CAAC;AAE3I,MAAM,cAAc,GAAqC;IACvD,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,mBAAmB;IACtB,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,mBAAmB;IACtB,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,eAAe;CACnB,CAAC;AAEF,MAAM,OAAO,GAAG;IACd,SAAS,EAAE;;;+EAGkE,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE;IAChH,OAAO,EAAE;;;;;;;;;;;YAWC,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE;IAC7C,WAAW,EAAE;;;;;sEAKuD;IACpE,OAAO,EAAE;;;;;+BAKoB,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE;IAChE,SAAS,EAAE;;;;;;+BAMkB,gBAAgB,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE;IAChE,QAAQ,EAAE;;;;;gDAKoC;IAC9C,SAAS,EAAE;;;;2DAI8C,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE;IAC3F,KAAK,EAAE;;;;;;;;+BAQsB,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC;;oGAEqC;CAC1F,CAAC;AAEX,MAAM,UAAU,GAAqC,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC;AAwBjH,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAyB,EAAE,MAAc;IAC5E,MAAM,OAAO,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG;YACZ,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAA+D,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACrH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,cAAc,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAClI;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAc,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACpE,CAAC,GAAG,EAAE,EAAE,CACN,YAAY,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,KAAK,cAAc,GAAG,CAAC,SAAS,QAAQ,GAAG,CAAC,GAAG,QAAQ,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9K;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAY,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACxG,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAA2D,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACnH,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,UAAU,EAAE,CACjF;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAA0C,OAAO,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAC9F,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,MAAM,IAAI,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,UAAU,EAAE,CAC7D;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAA0D,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAC/G,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,UAAU,EAAE,CAC7E;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAiD,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACvG,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,MAAM,IAAI,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,eAAe,EAAE,CAC5E;YACD,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAkE,OAAO,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CACpH,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,OAAO,IAAI,MAAM,IAAI,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,UAAU,EAAE,EAAE,CACrI;SACF,CAAC;QACF,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,WAAW,CAAC,QAA2B,EAAE,MAAyB;IAChF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,OAAO;QACL,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,wBAAwB,IAAI,EAAE,CAAC;QAChG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,2BAA2B,IAAI,EAAE,CAAC;KACpG,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,MAAc,EAAE,GAAc;IACpD,MAAM,KAAK,GAAG,CAAC,UAAU,MAAM,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,IAAI,GAAG,CAAC,SAAS,KAAK,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;IACnE,IAAI,GAAG,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,GAAG,CAAC,SAAS,KAAK,GAAG,IAAI,GAAG,CAAC,SAAS,KAAK,GAAG,EAAE,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,YAAY,IAAI,EAAE,GAAG,GAAG,CAAC,SAAS,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACxG,CAAC;SAAM,IAAI,GAAG,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACxD,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG;QAAE,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC5D,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type MigrationFile } from "./files.js";
|
|
2
|
+
import type { MigrationSession } from "./session.js";
|
|
3
|
+
export declare const LEDGER_MODULE_ID = "softure";
|
|
4
|
+
export declare const LEDGER_SCHEMA = "softure";
|
|
5
|
+
/** The ledger's own format version, recorded as `module_version` of its rows. */
|
|
6
|
+
export declare const LEDGER_VERSION = "1.0.0";
|
|
7
|
+
export declare const LEDGER_FILES: readonly MigrationFile[];
|
|
8
|
+
export type MigrationMethod = "applied" | "adopted";
|
|
9
|
+
export interface JournalRow {
|
|
10
|
+
readonly module: string;
|
|
11
|
+
readonly version: number;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly checksum: string;
|
|
14
|
+
readonly method: MigrationMethod;
|
|
15
|
+
}
|
|
16
|
+
export interface LedgerEntry {
|
|
17
|
+
readonly module: string;
|
|
18
|
+
readonly version: number;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly checksum: string;
|
|
21
|
+
readonly moduleVersion: string;
|
|
22
|
+
readonly method: MigrationMethod;
|
|
23
|
+
}
|
|
24
|
+
/** Every ledger row, or none while the ledger table does not exist yet. */
|
|
25
|
+
export declare function readJournal(session: MigrationSession): Promise<JournalRow[]>;
|
|
26
|
+
export declare function recordMigration(session: MigrationSession, entry: LedgerEntry): Promise<void>;
|
|
27
|
+
//# sourceMappingURL=ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/migrations/ledger.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAC1C,eAAO,MAAM,aAAa,YAAY,CAAC;AACvC,iFAAiF;AACjF,eAAO,MAAM,cAAc,UAAU,CAAC;AAwBtC,eAAO,MAAM,YAAY,EAAE,SAAS,aAAa,EAAwB,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;CAClC;AAED,2EAA2E;AAC3E,wBAAsB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAQlF;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAKlG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// The migration ledger `softure.migrations` (docs/02-module-standard.md §4). It is owned by the
|
|
2
|
+
// pseudo-module `softure` and created by its own migration, which ships as code so a bundled
|
|
3
|
+
// runner needs no file for it. While the table does not exist the journal reads as empty, so the
|
|
4
|
+
// first run bootstraps through the normal path.
|
|
5
|
+
//
|
|
6
|
+
// NEVER edit LEDGER_MIGRATION: deployed databases hold its checksum (a test pins it). A change to
|
|
7
|
+
// the ledger is a new file, `0002_…`, appended to LEDGER_FILES.
|
|
8
|
+
import { createMigrationFile } from "./files.js";
|
|
9
|
+
export const LEDGER_MODULE_ID = "softure";
|
|
10
|
+
export const LEDGER_SCHEMA = "softure";
|
|
11
|
+
/** The ledger's own format version, recorded as `module_version` of its rows. */
|
|
12
|
+
export const LEDGER_VERSION = "1.0.0";
|
|
13
|
+
const LEDGER_MIGRATION = `-- Rollback: DROP SCHEMA softure CASCADE; this forgets which module migrations ran, the module schemas and their data stay.
|
|
14
|
+
CREATE TABLE softure.migrations (
|
|
15
|
+
id bigint GENERATED ALWAYS AS IDENTITY UNIQUE,
|
|
16
|
+
module text NOT NULL CHECK (module ~ '^[a-z][a-z0-9]*(-[a-z0-9]+)*$'),
|
|
17
|
+
version integer NOT NULL CHECK (version > 0),
|
|
18
|
+
name text NOT NULL CHECK (name ~ '^[a-z0-9]+(_[a-z0-9]+)*$'),
|
|
19
|
+
checksum text NOT NULL CHECK (checksum ~ '^[0-9a-f]{64}$'),
|
|
20
|
+
module_version text NOT NULL,
|
|
21
|
+
method text NOT NULL CHECK (method IN ('applied', 'adopted')),
|
|
22
|
+
applied_at timestamptz NOT NULL DEFAULT now(),
|
|
23
|
+
PRIMARY KEY (module, version)
|
|
24
|
+
);
|
|
25
|
+
`;
|
|
26
|
+
function createLedgerFiles() {
|
|
27
|
+
const file = createMigrationFile("0001_ledger.sql", LEDGER_MIGRATION);
|
|
28
|
+
if (typeof file === "string") {
|
|
29
|
+
throw new Error(`ledger: the built-in migration is invalid: ${file}`);
|
|
30
|
+
}
|
|
31
|
+
return [file];
|
|
32
|
+
}
|
|
33
|
+
export const LEDGER_FILES = createLedgerFiles();
|
|
34
|
+
/** Every ledger row, or none while the ledger table does not exist yet. */
|
|
35
|
+
export async function readJournal(session) {
|
|
36
|
+
const [table] = await session.query("SELECT to_regclass('softure.migrations') IS NOT NULL AS exists");
|
|
37
|
+
if (table?.exists !== true) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return session.query("SELECT module, version, name, checksum, method FROM softure.migrations ORDER BY id");
|
|
41
|
+
}
|
|
42
|
+
export async function recordMigration(session, entry) {
|
|
43
|
+
await session.query("INSERT INTO softure.migrations (module, version, name, checksum, module_version, method) VALUES ($1, $2, $3, $4, $5, $6)", [entry.module, entry.version, entry.name, entry.checksum, entry.moduleVersion, entry.method]);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/migrations/ledger.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,iGAAiG;AACjG,gDAAgD;AAChD,EAAE;AACF,kGAAkG;AAClG,gEAAgE;AAChE,OAAO,EAAE,mBAAmB,EAAsB,MAAM,YAAY,CAAC;AAGrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAC1C,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AACvC,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AAEtC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;CAYxB,CAAC;AAEF,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,mBAAmB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IACtE,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,8CAA8C,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAA6B,iBAAiB,EAAE,CAAC;AAqB1E,2EAA2E;AAC3E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAyB;IACzD,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,KAAK,CAAsB,gEAAgE,CAAC,CAAC;IAC3H,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAClB,oFAAoF,CACrF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAyB,EAAE,KAAkB;IACjF,MAAM,OAAO,CAAC,KAAK,CACjB,0HAA0H,EAC1H,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAC7F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type AnySoftureModule } from "@softure-ai/core";
|
|
2
|
+
import type { DatabaseHandle } from "../client.js";
|
|
3
|
+
import { type MigrationUnit } from "./apply.js";
|
|
4
|
+
import { type JournalRow } from "./ledger.js";
|
|
5
|
+
import { type MigrationProblem, type MigrationResult } from "./problems.js";
|
|
6
|
+
export { applyFile, recordAdoption, rollBack, withMigrationLock, type MigrationUnit } from "./apply.js";
|
|
7
|
+
export interface MigrateOptions {
|
|
8
|
+
/** The enabled modules, in any order; dependencies are applied first. */
|
|
9
|
+
readonly modules: readonly AnySoftureModule[];
|
|
10
|
+
/**
|
|
11
|
+
* Read each module's files from `<migrationsDir>/<module id>/` instead of the module's own
|
|
12
|
+
* folder: the container path, where a bundle cannot locate package folders.
|
|
13
|
+
*/
|
|
14
|
+
readonly migrationsDir?: URL;
|
|
15
|
+
/** Called after each file is committed, e.g. to print progress. */
|
|
16
|
+
readonly onApplied?: (step: MigrationStep) => void;
|
|
17
|
+
/** Called after each file a baseline recorded as adopted, e.g. to print progress. */
|
|
18
|
+
readonly onAdopted?: (step: MigrationStep) => void;
|
|
19
|
+
/**
|
|
20
|
+
* The app's own migrations around the module files, and its baseline. `planMigrations` runs no
|
|
21
|
+
* hook; it reads only the baseline.
|
|
22
|
+
*/
|
|
23
|
+
readonly app?: AppMigrations;
|
|
24
|
+
/** Called after an app hook succeeded, e.g. to print progress. */
|
|
25
|
+
readonly onAppMigrated?: (phase: AppMigrationPhase) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The app's own migration runner, called by `migrate` under the migration lock on every run (it
|
|
29
|
+
* must skip what it already applied, as drizzle's migrator does). A throw or rejection is reported
|
|
30
|
+
* as `db.app_migration_failed`.
|
|
31
|
+
*/
|
|
32
|
+
export type AppMigrationHook = (handle: DatabaseHandle) => Promise<void>;
|
|
33
|
+
export interface AppMigrations {
|
|
34
|
+
/** Runs before the module files: app tables that module SQL references (normally the app's whole history). */
|
|
35
|
+
readonly before?: AppMigrationHook;
|
|
36
|
+
/** Runs after the module files: app migrations that reference tables a module creates. */
|
|
37
|
+
readonly after?: AppMigrationHook;
|
|
38
|
+
/**
|
|
39
|
+
* The module files the app's own history already creates: module id → the last such file (1..n).
|
|
40
|
+
* For a listed module the ledger has never seen, `migrate` compares the module's schema with
|
|
41
|
+
* files 1..n once `before` ran and records them as `adopted`, then applies the later files; an
|
|
42
|
+
* empty schema is migrated normally. Once the module is in the ledger the entry has no effect.
|
|
43
|
+
*/
|
|
44
|
+
readonly baseline?: Readonly<Record<string, number>>;
|
|
45
|
+
}
|
|
46
|
+
export type AppMigrationPhase = "before" | "after";
|
|
47
|
+
export interface MigrationStep {
|
|
48
|
+
readonly module: string;
|
|
49
|
+
readonly schema: string;
|
|
50
|
+
readonly version: number;
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly checksum: string;
|
|
53
|
+
}
|
|
54
|
+
export interface MigrationPlan {
|
|
55
|
+
/** What `migrate` would apply, in order. */
|
|
56
|
+
readonly pending: readonly MigrationStep[];
|
|
57
|
+
/**
|
|
58
|
+
* The pending steps a baseline may adopt instead of applying: they are adopted when the module's
|
|
59
|
+
* schema already holds objects after `before` (which a plan does not run).
|
|
60
|
+
*/
|
|
61
|
+
readonly baseline: readonly MigrationStep[];
|
|
62
|
+
}
|
|
63
|
+
export interface MigrationReport {
|
|
64
|
+
readonly applied: readonly MigrationStep[];
|
|
65
|
+
/** The files a baseline recorded as adopted, in order. */
|
|
66
|
+
readonly adopted: readonly MigrationStep[];
|
|
67
|
+
/** The app hooks that ran, in order. */
|
|
68
|
+
readonly app: readonly AppMigrationPhase[];
|
|
69
|
+
}
|
|
70
|
+
/** The dry run: what `migrate` would apply. Takes no lock and writes nothing. */
|
|
71
|
+
export declare function planMigrations(handle: DatabaseHandle, options: MigrateOptions): Promise<MigrationResult<MigrationPlan>>;
|
|
72
|
+
/**
|
|
73
|
+
* Applies every pending migration under the advisory lock: the ledger, `app.before`, the module
|
|
74
|
+
* files (adopting a baseline where it applies), then `app.after`. Nothing runs when a check fails.
|
|
75
|
+
*/
|
|
76
|
+
export declare function migrate(handle: DatabaseHandle, options: MigrateOptions): Promise<MigrationResult<MigrationReport>>;
|
|
77
|
+
/** `through` must be a whole number from 1 to the unit's file count. */
|
|
78
|
+
export declare function checkThrough(unit: MigrationUnit, through: number): MigrationProblem[];
|
|
79
|
+
export declare function toStep(unit: MigrationUnit, file: MigrationUnit["files"][number]): MigrationStep;
|
|
80
|
+
/**
|
|
81
|
+
* Runs one app hook alone under the migration lock (the CLI's `--adopt` runs `before` first, so the
|
|
82
|
+
* app migration that moves a table and the adoption are one command). Ok when there is no hook.
|
|
83
|
+
*/
|
|
84
|
+
export declare function runAppMigrations(handle: DatabaseHandle, app: AppMigrations, phase: AppMigrationPhase): Promise<MigrationResult<{
|
|
85
|
+
ran: boolean;
|
|
86
|
+
}>>;
|
|
87
|
+
/**
|
|
88
|
+
* Validates the module list and reads every file: the ledger first, then the modules with a
|
|
89
|
+
* schema in dependency order. Fails with every problem found.
|
|
90
|
+
*/
|
|
91
|
+
export declare function prepareUnits(modules: readonly AnySoftureModule[], migrationsDir: URL | undefined): Promise<MigrationResult<MigrationUnit[]>>;
|
|
92
|
+
/** Checks applied files against the folder and lists the pending ones in run order. */
|
|
93
|
+
export declare function compareJournal(units: readonly MigrationUnit[], journal: readonly JournalRow[]): {
|
|
94
|
+
problems: MigrationProblem[];
|
|
95
|
+
pending: MigrationStep[];
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=migrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/migrations/migrator.ts"],"names":[],"mappings":"AAMA,OAAO,EAAiC,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAA+D,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAG7G,OAAO,EAA8E,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1H,OAAO,EAAY,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAKtF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAExG,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,GAAG,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC7D;AAED;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,8GAA8G;IAC9G,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,CAAC;IACnC,0FAA0F;IAC1F,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAC3C,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC5C;AAMD,iFAAiF;AACjF,wBAAsB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAiB7H;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC,CA2ExH;AAwDD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAKrF;AAgBD,wBAAgB,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,GAAG,aAAa,CAE/F;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,eAAe,CAAC;IAAE,GAAG,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,CAM5C;AA2BD;;;GAGG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,aAAa,EAAE,GAAG,GAAG,SAAS,GAC7B,OAAO,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC,CA2B3C;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B;IAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAAC,OAAO,EAAE,aAAa,EAAE,CAAA;CAAE,CA4B5D"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// The module migrator (docs/02-module-standard.md §4). Every enabled module's SQL files run in the
|
|
2
|
+
// module's own schema, dependencies first, one transaction per file together with its ledger row.
|
|
3
|
+
// Everything that can be checked (names, numbering, checksums, order) is checked for all modules
|
|
4
|
+
// before the first file runs, so a problem in one module never leaves another half-migrated.
|
|
5
|
+
// The app's own migrations (drizzle's, usually) plug in through `app.before` / `app.after`, and
|
|
6
|
+
// `app.baseline` names the module files the app's own history already creates (issue #152).
|
|
7
|
+
import { ok, sortModulesByDependencies } from "@softure-ai/core";
|
|
8
|
+
import { applyFile, describeError, recordAdoption, withMigrationLock } from "./apply.js";
|
|
9
|
+
import { readMigrationFiles } from "./files.js";
|
|
10
|
+
import { describeSchema, diffSchemas } from "./introspect.js";
|
|
11
|
+
import { LEDGER_FILES, LEDGER_MODULE_ID, LEDGER_SCHEMA, LEDGER_VERSION, readJournal } from "./ledger.js";
|
|
12
|
+
import { failWith } from "./problems.js";
|
|
13
|
+
import { buildReferenceSchema, listOwnedSchemas } from "./reference.js";
|
|
14
|
+
import { withSession } from "./session.js";
|
|
15
|
+
import { readAppTables } from "./stubs.js";
|
|
16
|
+
export { applyFile, recordAdoption, rollBack, withMigrationLock } from "./apply.js";
|
|
17
|
+
// `drizzle` holds drizzle's own ledger (`drizzle.__drizzle_migrations`) in every app on this stack.
|
|
18
|
+
const RESERVED_SCHEMAS = new Set([LEDGER_SCHEMA, "public", "information_schema", "drizzle"]);
|
|
19
|
+
const MAX_IDENTIFIER_BYTES = 63;
|
|
20
|
+
/** The dry run: what `migrate` would apply. Takes no lock and writes nothing. */
|
|
21
|
+
export async function planMigrations(handle, options) {
|
|
22
|
+
const units = await prepareUnits(options.modules, options.migrationsDir);
|
|
23
|
+
if (!units.ok) {
|
|
24
|
+
return units;
|
|
25
|
+
}
|
|
26
|
+
const baseline = options.app?.baseline ?? {};
|
|
27
|
+
const baselineProblems = checkBaseline(options.modules, units.value, baseline);
|
|
28
|
+
if (baselineProblems.length > 0) {
|
|
29
|
+
return failWith(baselineProblems);
|
|
30
|
+
}
|
|
31
|
+
const journal = await withSession(handle, readJournal);
|
|
32
|
+
const comparison = compareJournal(units.value, journal);
|
|
33
|
+
if (comparison.problems.length > 0) {
|
|
34
|
+
return failWith(comparison.problems);
|
|
35
|
+
}
|
|
36
|
+
const adoptable = comparison.pending.filter((step) => isBaselineStep(step, baseline, journal));
|
|
37
|
+
return ok({ pending: comparison.pending, baseline: adoptable });
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Applies every pending migration under the advisory lock: the ledger, `app.before`, the module
|
|
41
|
+
* files (adopting a baseline where it applies), then `app.after`. Nothing runs when a check fails.
|
|
42
|
+
*/
|
|
43
|
+
export async function migrate(handle, options) {
|
|
44
|
+
const units = await prepareUnits(options.modules, options.migrationsDir);
|
|
45
|
+
if (!units.ok) {
|
|
46
|
+
return units;
|
|
47
|
+
}
|
|
48
|
+
const app = options.app ?? {};
|
|
49
|
+
const baseline = app.baseline ?? {};
|
|
50
|
+
const baselineProblems = checkBaseline(options.modules, units.value, baseline);
|
|
51
|
+
if (baselineProblems.length > 0) {
|
|
52
|
+
return failWith(baselineProblems);
|
|
53
|
+
}
|
|
54
|
+
return withSession(handle, (session) => withMigrationLock(session, async () => {
|
|
55
|
+
// Read after the lock: a runner that waited sees what the first one applied.
|
|
56
|
+
const journal = await readJournal(session);
|
|
57
|
+
const comparison = compareJournal(units.value, journal);
|
|
58
|
+
if (comparison.problems.length > 0) {
|
|
59
|
+
return failWith(comparison.problems);
|
|
60
|
+
}
|
|
61
|
+
const applied = [];
|
|
62
|
+
const adopted = [];
|
|
63
|
+
const ranHooks = [];
|
|
64
|
+
const applySteps = async (steps) => {
|
|
65
|
+
for (const step of steps) {
|
|
66
|
+
const { unit, file } = findFile(units.value, step);
|
|
67
|
+
const failure = await applyFile(session, { unit, file, method: "applied" });
|
|
68
|
+
if (failure !== null) {
|
|
69
|
+
return failure;
|
|
70
|
+
}
|
|
71
|
+
applied.push(step);
|
|
72
|
+
options.onApplied?.(step);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
const runHook = async (phase) => {
|
|
77
|
+
const failure = await runAppHook(handle, app, phase);
|
|
78
|
+
if (failure === null && app[phase] !== undefined) {
|
|
79
|
+
ranHooks.push(phase);
|
|
80
|
+
options.onAppMigrated?.(phase);
|
|
81
|
+
}
|
|
82
|
+
return failure;
|
|
83
|
+
};
|
|
84
|
+
// Unit by unit in dependency order, so a module's dependencies are migrated before its
|
|
85
|
+
// baseline is compared.
|
|
86
|
+
const migrateModules = async () => {
|
|
87
|
+
for (const unit of units.value) {
|
|
88
|
+
if (unit.module === LEDGER_MODULE_ID)
|
|
89
|
+
continue;
|
|
90
|
+
const steps = comparison.pending.filter((step) => step.module === unit.module);
|
|
91
|
+
const through = baseline[unit.module];
|
|
92
|
+
if (through === undefined || journal.some((row) => row.module === unit.module)) {
|
|
93
|
+
const failure = await applySteps(steps);
|
|
94
|
+
if (failure !== null)
|
|
95
|
+
return failure;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const adoption = await adoptBaseline(session, { modules: options.modules, units: units.value, unit, through });
|
|
99
|
+
if (adoption.problem !== null)
|
|
100
|
+
return adoption.problem;
|
|
101
|
+
for (const step of adoption.adopted) {
|
|
102
|
+
adopted.push(step);
|
|
103
|
+
options.onAdopted?.(step);
|
|
104
|
+
}
|
|
105
|
+
const failure = await applySteps(steps.filter((step) => !adoption.adopted.some((done) => done.version === step.version)));
|
|
106
|
+
if (failure !== null)
|
|
107
|
+
return failure;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
// The ledger first, so it exists whatever a hook does; then before, the modules, after.
|
|
112
|
+
const ledgerSteps = comparison.pending.filter((step) => step.module === LEDGER_MODULE_ID);
|
|
113
|
+
const failure = (await applySteps(ledgerSteps)) ?? (await runHook("before")) ?? (await migrateModules()) ?? (await runHook("after"));
|
|
114
|
+
if (failure !== null) {
|
|
115
|
+
return failWith([failure]);
|
|
116
|
+
}
|
|
117
|
+
return ok({ applied, adopted, app: ranHooks });
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Adopt-or-migrate for a baseline module the ledger has never seen: an empty schema adopts
|
|
122
|
+
* nothing (its files are then applied normally); otherwise the schema must equal files 1..through,
|
|
123
|
+
* which are then recorded as adopted in one transaction.
|
|
124
|
+
*/
|
|
125
|
+
async function adoptBaseline(session, input) {
|
|
126
|
+
const { unit, through } = input;
|
|
127
|
+
const live = await describeSchema(session, unit.schema);
|
|
128
|
+
if (live.length === 0) {
|
|
129
|
+
return { adopted: [], problem: null };
|
|
130
|
+
}
|
|
131
|
+
// Built only here, so a database whose modules are all in the ledger never needs PGlite. The app's
|
|
132
|
+
// tables exist by now (`before` ran), so module SQL that references them finds their stubs.
|
|
133
|
+
const appTables = await readAppTables(session, listOwnedSchemas(input.units));
|
|
134
|
+
const reference = await buildReferenceSchema({ modules: input.modules, units: input.units, target: unit, through, appTables });
|
|
135
|
+
if (!reference.ok) {
|
|
136
|
+
return { adopted: [], problem: reference.problems[0] ?? null };
|
|
137
|
+
}
|
|
138
|
+
const differences = diffSchemas(reference.value, live);
|
|
139
|
+
if (differences.length > 0) {
|
|
140
|
+
return { adopted: [], problem: { code: "db.schema_mismatch", module: unit.module, schema: unit.schema, differences } };
|
|
141
|
+
}
|
|
142
|
+
const files = unit.files.filter((file) => file.version <= through);
|
|
143
|
+
const failure = await recordAdoption(session, unit, files);
|
|
144
|
+
if (failure !== null) {
|
|
145
|
+
return { adopted: [], problem: failure };
|
|
146
|
+
}
|
|
147
|
+
return { adopted: files.map((file) => toStep(unit, file)), problem: null };
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* A baseline may name only enabled modules with a schema and migrations, and a last file between
|
|
151
|
+
* 1 and the module's file count. Checked before anything runs.
|
|
152
|
+
*/
|
|
153
|
+
function checkBaseline(modules, units, baseline) {
|
|
154
|
+
return Object.entries(baseline).flatMap(([module, through]) => {
|
|
155
|
+
if (!modules.some((candidate) => candidate.id === module)) {
|
|
156
|
+
return [{ code: "db.adopt_unknown_module", module }];
|
|
157
|
+
}
|
|
158
|
+
const unit = units.find((candidate) => candidate.module === module);
|
|
159
|
+
if (unit === undefined) {
|
|
160
|
+
return [{ code: "db.adopt_no_schema", module }];
|
|
161
|
+
}
|
|
162
|
+
return checkThrough(unit, through);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/** `through` must be a whole number from 1 to the unit's file count. */
|
|
166
|
+
export function checkThrough(unit, through) {
|
|
167
|
+
const files = unit.files.length;
|
|
168
|
+
return Number.isInteger(through) && through >= 1 && through <= files
|
|
169
|
+
? []
|
|
170
|
+
: [{ code: "db.adopt_through_out_of_range", module: unit.module, through, files }];
|
|
171
|
+
}
|
|
172
|
+
function isBaselineStep(step, baseline, journal) {
|
|
173
|
+
const through = baseline[step.module];
|
|
174
|
+
return through !== undefined && step.version <= through && !journal.some((row) => row.module === step.module);
|
|
175
|
+
}
|
|
176
|
+
function findFile(units, step) {
|
|
177
|
+
const unit = units.find((candidate) => candidate.module === step.module);
|
|
178
|
+
const file = unit?.files.find((candidate) => candidate.version === step.version);
|
|
179
|
+
if (unit === undefined || file === undefined) {
|
|
180
|
+
throw new Error(`migrate: step ${step.module} ${step.version} has no file; compareJournal is broken`);
|
|
181
|
+
}
|
|
182
|
+
return { unit, file };
|
|
183
|
+
}
|
|
184
|
+
export function toStep(unit, file) {
|
|
185
|
+
return { module: unit.module, schema: unit.schema, version: file.version, name: file.name, checksum: file.checksum };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Runs one app hook alone under the migration lock (the CLI's `--adopt` runs `before` first, so the
|
|
189
|
+
* app migration that moves a table and the adoption are one command). Ok when there is no hook.
|
|
190
|
+
*/
|
|
191
|
+
export async function runAppMigrations(handle, app, phase) {
|
|
192
|
+
if (app[phase] === undefined) {
|
|
193
|
+
return ok({ ran: false });
|
|
194
|
+
}
|
|
195
|
+
const failure = await withSession(handle, (session) => withMigrationLock(session, () => runAppHook(handle, app, phase)));
|
|
196
|
+
return failure === null ? ok({ ran: true }) : failWith([failure]);
|
|
197
|
+
}
|
|
198
|
+
async function runAppHook(handle, app, phase) {
|
|
199
|
+
const hook = app[phase];
|
|
200
|
+
if (hook === undefined) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
await hook(handle);
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
return { code: "db.app_migration_failed", phase, reason: describeErrorChain(error) };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// drizzle wraps the database error (`Failed query: <sql>`, the Postgres message in `cause`), so the
|
|
212
|
+
// whole chain is the reason.
|
|
213
|
+
function describeErrorChain(error) {
|
|
214
|
+
const messages = [];
|
|
215
|
+
let current = error;
|
|
216
|
+
while (current !== undefined && current !== null && messages.length < 5) {
|
|
217
|
+
messages.push(describeError(current));
|
|
218
|
+
current = current instanceof Error ? current.cause : undefined;
|
|
219
|
+
}
|
|
220
|
+
return messages.join(": ");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Validates the module list and reads every file: the ledger first, then the modules with a
|
|
224
|
+
* schema in dependency order. Fails with every problem found.
|
|
225
|
+
*/
|
|
226
|
+
export async function prepareUnits(modules, migrationsDir) {
|
|
227
|
+
const problems = modules.flatMap(checkModule);
|
|
228
|
+
const sorted = sortModulesByDependencies(modules);
|
|
229
|
+
if (!sorted.ok) {
|
|
230
|
+
problems.push({ code: "db.dependency_cycle", modules: modules.map((module) => module.id) });
|
|
231
|
+
}
|
|
232
|
+
if (problems.length > 0 || !sorted.ok) {
|
|
233
|
+
return failWith(problems);
|
|
234
|
+
}
|
|
235
|
+
const units = [
|
|
236
|
+
{ module: LEDGER_MODULE_ID, schema: LEDGER_SCHEMA, moduleVersion: LEDGER_VERSION, files: LEDGER_FILES },
|
|
237
|
+
];
|
|
238
|
+
for (const module of sorted.value) {
|
|
239
|
+
const schema = module.manifest.dbSchema;
|
|
240
|
+
if (schema === null || module.migrations === null) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const dir = migrationsDir === undefined ? module.migrations.dir : new URL(`${module.id}/`, ensureFolderUrl(migrationsDir));
|
|
244
|
+
const files = await readMigrationFiles(module.id, dir);
|
|
245
|
+
if (files.ok) {
|
|
246
|
+
units.push({ module: module.id, schema, moduleVersion: module.manifest.version, files: files.value });
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
problems.push(...files.problems);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return problems.length > 0 ? failWith(problems) : ok(units);
|
|
253
|
+
}
|
|
254
|
+
/** Checks applied files against the folder and lists the pending ones in run order. */
|
|
255
|
+
export function compareJournal(units, journal) {
|
|
256
|
+
const problems = [];
|
|
257
|
+
const pending = [];
|
|
258
|
+
for (const unit of units) {
|
|
259
|
+
const rows = journal.filter((row) => row.module === unit.module);
|
|
260
|
+
const appliedVersions = new Set(rows.map((row) => row.version));
|
|
261
|
+
const lastApplied = Math.max(0, ...rows.map((row) => row.version));
|
|
262
|
+
for (const row of rows) {
|
|
263
|
+
const file = unit.files.find((candidate) => candidate.version === row.version);
|
|
264
|
+
if (file === undefined) {
|
|
265
|
+
problems.push({ code: "db.migration_missing", module: unit.module, version: row.version, name: row.name });
|
|
266
|
+
}
|
|
267
|
+
else if (file.checksum !== row.checksum || file.name !== row.name) {
|
|
268
|
+
problems.push({ code: "db.migration_changed", module: unit.module, version: row.version, name: row.name });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (const file of unit.files) {
|
|
272
|
+
if (appliedVersions.has(file.version)) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (file.version < lastApplied) {
|
|
276
|
+
problems.push({ code: "db.migration_out_of_order", module: unit.module, version: file.version, appliedVersion: lastApplied });
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
pending.push({ module: unit.module, schema: unit.schema, version: file.version, name: file.name, checksum: file.checksum });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return { problems, pending };
|
|
284
|
+
}
|
|
285
|
+
function checkModule(module) {
|
|
286
|
+
const schema = module.manifest.dbSchema;
|
|
287
|
+
if (module.id === LEDGER_MODULE_ID || (schema !== null && isReservedSchema(schema))) {
|
|
288
|
+
return [{ code: "db.reserved_module", module: module.id }];
|
|
289
|
+
}
|
|
290
|
+
if (schema === null && module.migrations !== null) {
|
|
291
|
+
return [{ code: "db.migrations_without_schema", module: module.id }];
|
|
292
|
+
}
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
function isReservedSchema(schema) {
|
|
296
|
+
return RESERVED_SCHEMAS.has(schema) || schema.startsWith("pg_") || Buffer.byteLength(schema, "utf8") > MAX_IDENTIFIER_BYTES;
|
|
297
|
+
}
|
|
298
|
+
function ensureFolderUrl(url) {
|
|
299
|
+
return url.href.endsWith("/") ? url : new URL(`${url.href}/`);
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=migrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrator.js","sourceRoot":"","sources":["../../src/migrations/migrator.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,kGAAkG;AAClG,iGAAiG;AACjG,6FAA6F;AAC7F,gGAAgG;AAChG,4FAA4F;AAC5F,OAAO,EAAE,EAAE,EAAE,yBAAyB,EAAyB,MAAM,kBAAkB,CAAC;AAExF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAsB,MAAM,YAAY,CAAC;AAC7G,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAmB,MAAM,aAAa,CAAC;AAC1H,OAAO,EAAE,QAAQ,EAA+C,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAyB,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,EAAsB,MAAM,YAAY,CAAC;AAwExG,oGAAoG;AACpG,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,QAAQ,EAAE,oBAAoB,EAAE,SAAS,CAAC,CAAC,CAAC;AAC7F,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,iFAAiF;AACjF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAsB,EAAE,OAAuB;IAClF,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC7C,MAAM,gBAAgB,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/E,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACvD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/F,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,MAAsB,EAAE,OAAuB;IAC3E,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACzE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;IAC9B,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;IACpC,MAAM,gBAAgB,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/E,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,WAAW,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CACrC,iBAAiB,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;QACpC,6EAA6E;QAC7E,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnC,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,MAAM,OAAO,GAAoB,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAwB,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,KAAK,EAAE,KAA+B,EAAoC,EAAE;YAC7F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBACnD,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC5E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,OAAO,OAAO,CAAC;gBACjB,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,KAAK,EAAE,KAAwB,EAAoC,EAAE;YACnF,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YACrD,IAAI,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrB,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC;QACF,uFAAuF;QACvF,wBAAwB;QACxB,MAAM,cAAc,GAAG,KAAK,IAAsC,EAAE;YAClE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB;oBAAE,SAAS;gBAC/C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACtC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC/E,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC;oBACxC,IAAI,OAAO,KAAK,IAAI;wBAAE,OAAO,OAAO,CAAC;oBACrC,SAAS;gBACX,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC/G,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI;oBAAE,OAAO,QAAQ,CAAC,OAAO,CAAC;gBACvD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;oBACpC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACnB,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC5B,CAAC;gBACD,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC1H,IAAI,OAAO,KAAK,IAAI;oBAAE,OAAO,OAAO,CAAC;YACvC,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QAEF,wFAAwF;QACxF,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC;QAC1F,MAAM,OAAO,GAAG,CAAC,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,cAAc,EAAE,CAAC,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACrI,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjD,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,aAAa,CAC1B,OAAyB,EACzB,KAAsH;IAEtH,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACxC,CAAC;IACD,mGAAmG;IACnG,4FAA4F;IAC5F,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9E,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/H,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAClB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACjE,CAAC;IACD,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACvD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,CAAC;IACzH,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CACpB,OAAoC,EACpC,KAA+B,EAC/B,QAA0C;IAE1C,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAsB,EAAE;QAChF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC;YAC1D,OAAO,CAAC,EAAE,IAAI,EAAE,yBAAyB,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;QACpE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,OAAO,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,IAAmB,EAAE,OAAe;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAChC,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,KAAK;QAClE,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,+BAA+B,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;AACvF,CAAC;AAED,SAAS,cAAc,CAAC,IAAmB,EAAE,QAA0C,EAAE,OAA8B;IACrH,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,OAAO,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;AAChH,CAAC;AAED,SAAS,QAAQ,CAAC,KAA+B,EAAE,IAAmB;IACpE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC;IACjF,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,wCAAwC,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAmB,EAAE,IAAoC;IAC9E,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;AACvH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAsB,EACtB,GAAkB,EAClB,KAAwB;IAExB,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACzH,OAAO,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,MAAsB,EAAE,GAAkB,EAAE,KAAwB;IAC5F,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;IACvF,CAAC;AACH,CAAC;AAED,oGAAoG;AACpG,6BAA6B;AAC7B,SAAS,kBAAkB,CAAC,KAAc;IACxC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,OAAO,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,OAAO,GAAG,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACjE,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAoC,EACpC,aAA8B;IAE9B,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACtC,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,KAAK,GAAoB;QAC7B,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE;KACxG,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACxC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAClD,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC;QAC3H,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACvD,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACxG,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAC5B,KAA+B,EAC/B,OAA8B;IAE9B,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAChE,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAEnE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/E,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7G,CAAC;iBAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;gBACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAC7G,CAAC;QACH,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtC,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,2BAA2B,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;YAChI,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC9H,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,WAAW,CAAC,MAAwB;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACxC,IAAI,MAAM,CAAC,EAAE,KAAK,gBAAgB,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACpF,OAAO,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAClD,OAAO,CAAC,EAAE,IAAI,EAAE,8BAA8B,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAc;IACtC,OAAO,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,oBAAoB,CAAC;AAC9H,CAAC;AAED,SAAS,eAAe,CAAC,GAAQ;IAC/B,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;AAChE,CAAC"}
|