@nifrajs/mcp-db 3.3.0 → 4.0.0

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.
@@ -0,0 +1,580 @@
1
+ /**
2
+ * `@nifrajs/mcp-db/postgres` - a read-only Postgres engine for a development database, on Bun's
3
+ * built-in `Bun.SQL` (no npm driver). Every layer below is enforced on each call, in this order:
4
+ *
5
+ * 1. Host gate ({@link connectPostgres}): loopback, `*.localhost` or a unix socket, else only a host
6
+ * named in `allowHosts`. Connection fields are passed explicitly, so `DATABASE_URL`/`PG*` in the
7
+ * environment can never redirect the connection.
8
+ * 2. Role gate: a superuser, a member of a superuser role or of `pg_execute_server_program` /
9
+ * `pg_read_server_files` / `pg_write_server_files`, or a role that may execute the server-file
10
+ * functions is refused - a read-only transaction does not stop `COPY ... TO PROGRAM` or
11
+ * `pg_read_file`. {@link postgresRoleSql} prints the role to use instead.
12
+ * 3. Extension gate: `dblink`, `postgres_fdw`, `file_fdw` or an untrusted language the role can use
13
+ * is refused unless `allowExtensions` names it - `dblink_exec` writes to another database from a
14
+ * read-only transaction.
15
+ * 4. Transaction: every connection starts with `default_transaction_read_only=on`; each call runs in
16
+ * `BEGIN READ ONLY` with `statement_timeout`, `lock_timeout` and
17
+ * `idle_in_transaction_session_timeout` set locally, and always rolls back.
18
+ * 5. Statement gate: the query goes over the extended protocol (the server refuses a second
19
+ * statement) as `DECLARE ... NO SCROLL CURSOR` (the server accepts only a query there), and at most
20
+ * `maxRows + 1` rows are fetched. A tokenizer in front refuses side-effect functions.
21
+ * 6. Table scope: the plan's relations (and every table they inherit from) must sit in the allowed
22
+ * schemas and outside `exclude`. A function can still read a table internally; hard isolation
23
+ * comes from the role's grants, which {@link postgresRoleSql} writes with a REVOKE per exclusion.
24
+ *
25
+ * const target = parsePostgresUrl("postgres://nifra_dev_reader@localhost:5432/app")
26
+ * const client = connectPostgres(target, { timeoutMs: 5000 })
27
+ * const rows = await queryPostgres(client, "SELECT status, count(*) FROM orders GROUP BY 1", {
28
+ * timeoutMs: 5000, maxRows: 100, maxResultBytes: 100 * 1024,
29
+ * })
30
+ */
31
+ import { explainThroughServer, fetchThroughCursor, inReadOnlyTransaction, records, } from "./internal/pg-exec.js";
32
+ import { DENIED_FUNCTION_PREFIXES, lintPostgres, withoutTerminator } from "./internal/pg-lexer.js";
33
+ import { dbRefusal, fitToBytes, isDbRefusal, shapeRows, } from "./internal/shared.js";
34
+ const SSL_MODES = ["disable", "allow", "prefer", "require", "verify-ca", "verify-full"];
35
+ /**
36
+ * Parse a `postgres://` (or `postgresql://`) URL. Only the host, port, user, password, database and
37
+ * `sslmode` are read - any other parameter (`options=-c ...` included) is ignored, so a URL cannot
38
+ * switch off the read-only session settings.
39
+ */
40
+ export function parsePostgresUrl(url) {
41
+ let parsed;
42
+ try {
43
+ parsed = new URL(url);
44
+ }
45
+ catch {
46
+ return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url is not a valid URL");
47
+ }
48
+ if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:") {
49
+ return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url must start with postgres:// or postgresql://");
50
+ }
51
+ const params = parsed.searchParams;
52
+ const queryHost = params.get("host") ?? undefined;
53
+ const host = (queryHost ?? decodeURIComponent(parsed.hostname)).replace(/^\[(.*)\]$/, "$1");
54
+ if (host.includes(",")) {
55
+ return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url names several hosts; name one");
56
+ }
57
+ const port = Number(parsed.port || params.get("port") || 5432);
58
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
59
+ return dbRefusal("NIFRA_DB_CONFIG", "devDatabase.url has an invalid port");
60
+ }
61
+ const username = decodeURIComponent(parsed.username) || params.get("user") || "postgres";
62
+ const password = decodeURIComponent(parsed.password) || params.get("password") || undefined;
63
+ const database = decodeURIComponent(parsed.pathname.replace(/^\//, "")) || params.get("dbname") || username;
64
+ const sslmode = params.get("sslmode") ?? undefined;
65
+ const tls = SSL_MODES.find((mode) => mode === sslmode);
66
+ if (sslmode !== undefined && tls === undefined) {
67
+ return dbRefusal("NIFRA_DB_CONFIG", `devDatabase.url has an unknown sslmode ${sslmode}`);
68
+ }
69
+ const location = host === "" || host.startsWith("/")
70
+ ? { socket: `${host === "" ? "/tmp" : host.replace(/\/$/, "")}/.s.PGSQL.${port}` }
71
+ : { host };
72
+ return {
73
+ ...location,
74
+ port,
75
+ username,
76
+ database,
77
+ ...(password === undefined ? {} : { password }),
78
+ ...(tls === undefined ? {} : { tls }),
79
+ };
80
+ }
81
+ /** True for `localhost`, `*.localhost`, 127.0.0.0/8 and `::1` (also as an IPv4-mapped address). */
82
+ export function isLocalPostgresHost(host) {
83
+ const name = host.toLowerCase().replace(/^\[(.*)\]$/, "$1");
84
+ if (name === "localhost" || name.endsWith(".localhost"))
85
+ return true;
86
+ const v4 = /^(?:::ffff:)?(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(name);
87
+ if (v4 !== null) {
88
+ const octets = v4.slice(1).map(Number);
89
+ return octets.every((octet) => octet <= 255) && octets[0] === 127;
90
+ }
91
+ return name === "::1" || name === "0:0:0:0:0:0:0:1";
92
+ }
93
+ /** Refuse a TCP host that is neither local nor named in `allowHosts`. */
94
+ export function postgresHostRefusal(target, allowHosts = []) {
95
+ if (target.socket !== undefined || target.host === undefined)
96
+ return undefined;
97
+ if (isLocalPostgresHost(target.host))
98
+ return undefined;
99
+ const host = target.host.toLowerCase();
100
+ if (allowHosts.some((allowed) => allowed.toLowerCase() === host))
101
+ return undefined;
102
+ return dbRefusal("NIFRA_DB_REMOTE_HOST", `${target.host} is not a local host, and devDatabase.allowHosts does not name it`);
103
+ }
104
+ /**
105
+ * Open a one-connection `Bun.SQL` client for `target` after the host gate. Every session starts
106
+ * read-only with `standard_conforming_strings` on (the statement tokenizer reads strings that way).
107
+ */
108
+ export function connectPostgres(target, options) {
109
+ const refusal = postgresHostRefusal(target, options.allowHosts);
110
+ if (refusal !== undefined)
111
+ return refusal;
112
+ if (typeof Bun === "undefined") {
113
+ return dbRefusal("NIFRA_DB_DRIVER", "the Postgres engine needs Bun (it uses Bun.SQL)");
114
+ }
115
+ return new Bun.SQL({
116
+ adapter: "postgres",
117
+ ...(target.socket === undefined ? { hostname: target.host } : { path: target.socket }),
118
+ port: target.port,
119
+ username: target.username,
120
+ ...(target.password === undefined ? {} : { password: target.password }),
121
+ database: target.database,
122
+ ...(target.tls === undefined ? {} : { tls: target.tls }),
123
+ max: 1,
124
+ bigint: true,
125
+ connectionTimeout: Math.max(1, Math.ceil(options.timeoutMs / 1000)),
126
+ connection: {
127
+ application_name: "nifra-db",
128
+ default_transaction_read_only: "on",
129
+ standard_conforming_strings: "on",
130
+ statement_timeout: options.timeoutMs,
131
+ idle_in_transaction_session_timeout: options.timeoutMs,
132
+ },
133
+ });
134
+ }
135
+ const asStrings = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
136
+ async function readRole(connection) {
137
+ const [row] = records(await connection `
138
+ SELECT current_user::text AS role,
139
+ r.rolsuper AS superuser,
140
+ pg_catalog.current_setting('server_version_num')::int AS version,
141
+ to_json(ARRAY(SELECT s.rolname::text FROM pg_catalog.pg_roles s
142
+ WHERE s.rolsuper AND s.rolname <> current_user
143
+ AND pg_catalog.pg_has_role(current_user, s.oid, 'MEMBER') ORDER BY 1)) AS superuser_roles,
144
+ to_json(ARRAY(SELECT p.rolname::text FROM pg_catalog.pg_roles p
145
+ WHERE p.rolname IN ('pg_execute_server_program', 'pg_read_server_files', 'pg_write_server_files')
146
+ AND pg_catalog.pg_has_role(current_user, p.oid, 'MEMBER') ORDER BY 1)) AS server_roles,
147
+ to_json(ARRAY(SELECT DISTINCT f.proname::text FROM pg_catalog.pg_proc f
148
+ WHERE f.pronamespace = 'pg_catalog'::regnamespace
149
+ AND f.proname IN ('pg_read_file', 'pg_read_binary_file', 'pg_ls_dir', 'pg_stat_file', 'lo_import', 'lo_export')
150
+ AND pg_catalog.has_function_privilege(f.oid, 'EXECUTE') ORDER BY 1)) AS file_functions,
151
+ to_json(ARRAY(
152
+ SELECT e.extname::text FROM pg_catalog.pg_extension e
153
+ WHERE e.extname = 'dblink' AND EXISTS (
154
+ SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_proc p ON p.oid = d.objid
155
+ WHERE d.classid = 'pg_catalog.pg_proc'::regclass AND d.refclassid = 'pg_catalog.pg_extension'::regclass
156
+ AND d.refobjid = e.oid AND d.deptype = 'e' AND pg_catalog.has_function_privilege(p.oid, 'EXECUTE'))
157
+ UNION
158
+ SELECT w.fdwname::text FROM pg_catalog.pg_foreign_data_wrapper w
159
+ WHERE w.fdwname IN ('postgres_fdw', 'file_fdw') AND (
160
+ pg_catalog.has_foreign_data_wrapper_privilege(w.oid, 'USAGE') OR EXISTS (
161
+ SELECT 1 FROM pg_catalog.pg_foreign_table ft JOIN pg_catalog.pg_foreign_server fs ON fs.oid = ft.ftserver
162
+ WHERE fs.srvfdw = w.oid AND pg_catalog.has_table_privilege(ft.ftrelid, 'SELECT')))
163
+ ORDER BY 1)) AS extensions,
164
+ to_json(ARRAY(SELECT l.lanname::text FROM pg_catalog.pg_language l
165
+ WHERE NOT l.lanpltrusted AND l.lanname NOT IN ('c', 'internal') AND (
166
+ pg_catalog.has_language_privilege(l.oid, 'USAGE') OR EXISTS (
167
+ SELECT 1 FROM pg_catalog.pg_proc p WHERE p.prolang = l.oid
168
+ AND pg_catalog.has_function_privilege(p.oid, 'EXECUTE')))
169
+ ORDER BY 1)) AS languages
170
+ FROM pg_catalog.pg_roles r WHERE r.rolname = current_user`);
171
+ if (row === undefined)
172
+ throw new Error("the connected role is not in pg_roles");
173
+ return {
174
+ role: String(row.role),
175
+ superuser: row.superuser === true,
176
+ serverVersionNum: Number(row.version),
177
+ superuserRoles: asStrings(row.superuser_roles),
178
+ serverRoles: asStrings(row.server_roles),
179
+ fileFunctions: asStrings(row.file_functions),
180
+ extensions: asStrings(row.extensions),
181
+ languages: asStrings(row.languages),
182
+ };
183
+ }
184
+ /** Read the connected role's privileges, in a read-only transaction. */
185
+ export function inspectPostgresRole(client, options) {
186
+ return inReadOnlyTransaction(client, options.timeoutMs, readRole);
187
+ }
188
+ /** The role gate and the extension gate, as one decision over a {@link PostgresRoleReport}. */
189
+ export function postgresRoleRefusal(report, options = {}) {
190
+ const role = JSON.stringify(report.role);
191
+ if (report.superuser)
192
+ return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a superuser`);
193
+ if (report.superuserRoles.length > 0) {
194
+ return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a member of the superuser role ${report.superuserRoles.join(", ")}`);
195
+ }
196
+ if (report.serverRoles.length > 0) {
197
+ return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} is a member of ${report.serverRoles.join(", ")}, which reach server programs or files`);
198
+ }
199
+ if (report.fileFunctions.length > 0) {
200
+ return dbRefusal("NIFRA_DB_SUPERUSER", `role ${role} may execute ${report.fileFunctions.join(", ")}, which read or write server files`);
201
+ }
202
+ const allowed = new Set((options.allowExtensions ?? []).map((name) => name.toLowerCase()));
203
+ const usable = [...report.extensions, ...report.languages].filter((name) => !allowed.has(name.toLowerCase()));
204
+ if (usable.length > 0) {
205
+ return dbRefusal("NIFRA_DB_EXTENSION", `role ${role} can use ${usable.join(", ")}, which can reach outside this database`);
206
+ }
207
+ return undefined;
208
+ }
209
+ /** Refuse what the tokenizer layer refuses: a non-query, several statements, a denied function. */
210
+ export function lintPostgresStatement(sql) {
211
+ const refusal = lintPostgres(sql);
212
+ if (refusal === undefined)
213
+ return undefined;
214
+ return dbRefusal(refusal.kind === "function" ? "NIFRA_DB_FUNCTION_REFUSED" : "NIFRA_DB_WRITE_REFUSED", refusal.message);
215
+ }
216
+ function scopeMatcher(scope) {
217
+ const schemas = new Set((scope.schemas ?? ["public"]).map((name) => name.toLowerCase()));
218
+ const excluded = new Set((scope.exclude ?? []).map((name) => name.toLowerCase()));
219
+ return ({ schema, name }) => {
220
+ const qualified = `${schema}.${name}`;
221
+ if (!schemas.has(schema.toLowerCase())) {
222
+ return `the query reads ${qualified}, outside the allowed schemas (${[...schemas].join(", ")})`;
223
+ }
224
+ if (excluded.has(name.toLowerCase()) || excluded.has(qualified.toLowerCase())) {
225
+ return `the query reads ${qualified}, which devDatabase.exclude leaves out`;
226
+ }
227
+ return undefined;
228
+ };
229
+ }
230
+ /**
231
+ * The plan-level table check: each relation, and every table it inherits from (partitions too). A
232
+ * `pg_*` function in FROM reads server state the way a catalog does - a system view such as
233
+ * `pg_stat_activity` plans as one once the planner drops its catalog joins - so it is out of scope too.
234
+ * With `masked`, a plan expression that uses a masked column, or a whole row holding one, is refused.
235
+ */
236
+ function checkScope(scope, masked) {
237
+ const outOfScope = scopeMatcher(scope);
238
+ return async (connection, reads) => {
239
+ for (const fn of reads.functions) {
240
+ const name = fn.name.toLowerCase();
241
+ if (fn.schema === "pg_catalog" && name.startsWith("pg_")) {
242
+ return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", `the query reads server state through ${fn.schema}.${fn.name}(), a system catalog function`);
243
+ }
244
+ if (DENIED_FUNCTION_PREFIXES.some((prefix) => name.startsWith(prefix))) {
245
+ return dbRefusal("NIFRA_DB_FUNCTION_REFUSED", `the query calls ${fn.schema}.${fn.name}(), which reaches outside a read-only query`);
246
+ }
247
+ }
248
+ const { relations } = reads;
249
+ for (const relation of relations) {
250
+ const reason = outOfScope(relation);
251
+ if (reason !== undefined)
252
+ return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", reason);
253
+ }
254
+ if (relations.length === 0)
255
+ return undefined;
256
+ const ancestors = records(await connection `
257
+ WITH RECURSIVE planned AS (
258
+ SELECT pg_catalog.to_regclass(pg_catalog.quote_ident(r.schema) || '.' || pg_catalog.quote_ident(r.name))::oid AS oid
259
+ FROM pg_catalog.json_to_recordset(${[...relations]}::json) AS r(schema text, name text)
260
+ ), up(oid) AS (
261
+ SELECT i.inhparent FROM pg_catalog.pg_inherits i JOIN planned p ON i.inhrelid = p.oid
262
+ UNION
263
+ SELECT i.inhparent FROM pg_catalog.pg_inherits i JOIN up ON i.inhrelid = up.oid
264
+ )
265
+ SELECT n.nspname::text AS schema, c.relname::text AS name
266
+ FROM up JOIN pg_catalog.pg_class c ON c.oid = up.oid
267
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace`);
268
+ for (const row of ancestors) {
269
+ const reason = outOfScope({ schema: String(row.schema), name: String(row.name) });
270
+ if (reason !== undefined)
271
+ return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", reason);
272
+ }
273
+ if (masked === undefined)
274
+ return undefined;
275
+ const hits = new Set();
276
+ const wholeRows = [];
277
+ for (const { relation, column } of reads.columns) {
278
+ if (column === "*")
279
+ wholeRows.push(relation);
280
+ else if (masked(column))
281
+ hits.add(`${relation.name}.${column}`);
282
+ }
283
+ const listed = [...wholeRows, ...reads.outputs.map((output) => output.relation)];
284
+ if (listed.length > 0) {
285
+ const live = new Map();
286
+ const rows = records(await connection `
287
+ SELECT r.schema, r.name, a.attname::text AS column
288
+ FROM pg_catalog.json_to_recordset(${listed}::json) AS r(schema text, name text)
289
+ JOIN pg_catalog.pg_attribute a
290
+ ON a.attrelid = pg_catalog.to_regclass(pg_catalog.quote_ident(r.schema) || '.' || pg_catalog.quote_ident(r.name))
291
+ WHERE a.attnum > 0 AND NOT a.attisdropped`);
292
+ for (const row of rows) {
293
+ const key = `${String(row.schema)}.${String(row.name)}`;
294
+ live.set(key, [...(live.get(key) ?? []), String(row.column)]);
295
+ }
296
+ const columnsOf = (relation) => live.get(`${relation.schema}.${relation.name}`) ?? [];
297
+ for (const relation of wholeRows) {
298
+ for (const column of columnsOf(relation))
299
+ if (masked(column))
300
+ hits.add(`${relation.name}.${column}`);
301
+ }
302
+ for (const output of reads.outputs) {
303
+ const all = columnsOf(output.relation);
304
+ // Every column is the physical target list: the parent's expressions say what it uses.
305
+ if (all.length > 0 && all.every((column) => output.columns.includes(column)))
306
+ continue;
307
+ for (const column of output.columns)
308
+ if (masked(column))
309
+ hits.add(`${output.relation.name}.${column}`);
310
+ }
311
+ }
312
+ if (hits.size === 0)
313
+ return undefined;
314
+ return dbRefusal("NIFRA_DB_COLUMN_REFUSED", `the query reads ${[...hits].join(", ")}, masked as ${hits.size === 1 ? "a credential" : "credentials"}`);
315
+ };
316
+ }
317
+ async function gated(client, sql, options, work) {
318
+ const linted = lintPostgresStatement(sql);
319
+ if (linted !== undefined)
320
+ return linted;
321
+ return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
322
+ const refusal = postgresRoleRefusal(await readRole(connection), options);
323
+ if (refusal !== undefined)
324
+ return refusal;
325
+ return work(connection, withoutTerminator(sql.trim()));
326
+ });
327
+ }
328
+ /**
329
+ * Run one read-only query through every layer and return capped rows. A column `redaction.column`
330
+ * masks is refused wherever the plan uses it (selected under any alias, inside an expression, in a
331
+ * filter, or within a whole row); a matching result name is masked as well.
332
+ */
333
+ export async function queryPostgres(client, sql, options) {
334
+ const fetched = await gated(client, sql, options, (connection, statement) => fetchThroughCursor(connection, statement, {
335
+ maxRows: options.maxRows,
336
+ scope: checkScope(options, options.redaction?.column),
337
+ }));
338
+ if (isDbRefusal(fetched))
339
+ return fetched;
340
+ return shapeRows(fetched.columns, fetched.rows, options);
341
+ }
342
+ /** Drop plan children below `depth` so an oversized plan still says what its top does. */
343
+ function prunePlan(node, depth) {
344
+ if (Array.isArray(node))
345
+ return node.map((item) => prunePlan(item, depth));
346
+ if (typeof node !== "object" || node === null)
347
+ return node;
348
+ const out = {};
349
+ for (const [key, value] of Object.entries(node)) {
350
+ if (key === "Plans") {
351
+ if (depth > 0)
352
+ out[key] = prunePlan(value, depth - 1);
353
+ }
354
+ else
355
+ out[key] = key === "Plan" ? prunePlan(value, depth) : value;
356
+ }
357
+ return out;
358
+ }
359
+ /** `EXPLAIN (FORMAT JSON)` for one query, after the same layers as {@link queryPostgres}. */
360
+ export async function explainPostgres(client, sql, options) {
361
+ const explained = await gated(client, sql, options, (connection, statement) => explainThroughServer(connection, statement, {
362
+ analyze: options.analyze === true,
363
+ scope: checkScope(options),
364
+ }));
365
+ if (isDbRefusal(explained))
366
+ return explained;
367
+ const full = JSON.stringify(explained.plan);
368
+ if (new TextEncoder().encode(full).byteLength <= options.maxResultBytes) {
369
+ return { plan: explained.plan, analyzed: options.analyze === true, truncated: false };
370
+ }
371
+ const fit = fitToBytes(32, (depth) => prunePlan(explained.plan, depth), options.maxResultBytes);
372
+ return { plan: fit?.value ?? null, analyzed: options.analyze === true, truncated: true };
373
+ }
374
+ const RELKINDS = {
375
+ r: "table",
376
+ p: "partitioned table",
377
+ v: "view",
378
+ m: "materialized view",
379
+ f: "foreign table",
380
+ };
381
+ const jsonValues = (value) => {
382
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
383
+ return Array.isArray(parsed) ? parsed : [];
384
+ };
385
+ const jsonStrings = (value) => asStrings(jsonValues(value));
386
+ /**
387
+ * Describe the readable tables and views in the allowed schemas from `pg_catalog`, with queries this
388
+ * module writes (no caller SQL runs), so it works on any role: columns, primary keys, foreign keys,
389
+ * indexes and the planner's row estimate.
390
+ */
391
+ export async function readPostgresSchema(client, options) {
392
+ const schemas = [...(options.schemas ?? ["public"])];
393
+ const excluded = new Set((options.exclude ?? []).map((name) => name.toLowerCase()));
394
+ const isExcluded = (schema, name) => excluded.has(name.toLowerCase()) || excluded.has(`${schema}.${name}`.toLowerCase());
395
+ const wanted = options.table?.toLowerCase();
396
+ const matchesWanted = (schema, name) => wanted === undefined ||
397
+ name.toLowerCase() === wanted ||
398
+ `${schema}.${name}`.toLowerCase() === wanted;
399
+ return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
400
+ const relations = records(await connection `
401
+ SELECT n.nspname::text AS schema, c.relname::text AS name, c.relkind::text AS kind,
402
+ c.reltuples::float8 AS estimate,
403
+ to_json(ARRAY(
404
+ WITH RECURSIVE reach(oid) AS (
405
+ SELECT c.oid
406
+ UNION
407
+ SELECT next.oid FROM reach, LATERAL (
408
+ SELECT i.inhparent AS oid FROM pg_catalog.pg_inherits i WHERE i.inhrelid = reach.oid
409
+ UNION ALL
410
+ SELECT d.refobjid FROM pg_catalog.pg_rewrite w
411
+ JOIN pg_catalog.pg_depend d ON d.classid = 'pg_catalog.pg_rewrite'::regclass
412
+ AND d.objid = w.oid AND d.refclassid = 'pg_catalog.pg_class'::regclass
413
+ WHERE w.ev_class = reach.oid
414
+ ) AS next
415
+ )
416
+ SELECT pg_catalog.json_build_array(pn.nspname::text, pc.relname::text) FROM reach
417
+ JOIN pg_catalog.pg_class pc ON pc.oid = reach.oid
418
+ JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
419
+ WHERE reach.oid <> c.oid)) AS reaches
420
+ FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
421
+ WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f') AND NOT c.relispartition
422
+ AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
423
+ AND pg_catalog.has_table_privilege(c.oid, 'SELECT')
424
+ ORDER BY 1, 2`);
425
+ const columns = records(await connection `
426
+ SELECT n.nspname::text AS schema, c.relname::text AS table, a.attname::text AS name,
427
+ pg_catalog.format_type(a.atttypid, a.atttypmod) AS type, NOT a.attnotnull AS nullable,
428
+ pg_catalog.pg_get_expr(d.adbin, d.adrelid) AS default_value,
429
+ EXISTS (SELECT 1 FROM pg_catalog.pg_constraint k
430
+ WHERE k.conrelid = c.oid AND k.contype = 'p' AND a.attnum = ANY(k.conkey)) AS primary_key
431
+ FROM pg_catalog.pg_attribute a
432
+ JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
433
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
434
+ LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
435
+ WHERE a.attnum > 0 AND NOT a.attisdropped AND c.relkind IN ('r', 'p', 'v', 'm', 'f')
436
+ AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
437
+ ORDER BY 1, 2, a.attnum`);
438
+ const foreignKeys = records(await connection `
439
+ SELECT n.nspname::text AS schema, c.relname::text AS table,
440
+ to_json(ARRAY(SELECT a.attname::text FROM unnest(k.conkey) WITH ORDINALITY AS u(num, ord)
441
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = k.conrelid AND a.attnum = u.num ORDER BY u.ord)) AS columns,
442
+ fn.nspname::text AS ref_schema, fc.relname::text AS ref_table,
443
+ to_json(ARRAY(SELECT a.attname::text FROM unnest(k.confkey) WITH ORDINALITY AS u(num, ord)
444
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = k.confrelid AND a.attnum = u.num ORDER BY u.ord)) AS ref_columns
445
+ FROM pg_catalog.pg_constraint k
446
+ JOIN pg_catalog.pg_class c ON c.oid = k.conrelid
447
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
448
+ JOIN pg_catalog.pg_class fc ON fc.oid = k.confrelid
449
+ JOIN pg_catalog.pg_namespace fn ON fn.oid = fc.relnamespace
450
+ WHERE k.contype = 'f'
451
+ AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
452
+ ORDER BY 1, 2, k.conname`);
453
+ const indexes = records(await connection `
454
+ SELECT n.nspname::text AS schema, t.relname::text AS table, i.relname::text AS name,
455
+ x.indisunique AS is_unique,
456
+ to_json(ARRAY(SELECT pg_catalog.pg_get_indexdef(x.indexrelid, k, true)
457
+ FROM generate_series(1, x.indnkeyatts) AS k ORDER BY k)) AS columns
458
+ FROM pg_catalog.pg_index x
459
+ JOIN pg_catalog.pg_class i ON i.oid = x.indexrelid
460
+ JOIN pg_catalog.pg_class t ON t.oid = x.indrelid
461
+ JOIN pg_catalog.pg_namespace n ON n.oid = t.relnamespace
462
+ WHERE n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${schemas}::json)))
463
+ ORDER BY 1, 2, 3`);
464
+ const key = (schema, name) => `${String(schema)}.${String(name)}`;
465
+ const text = (value) => options.redaction?.text?.(value) ?? value;
466
+ // What the query scope would refuse is hidden too: a table inheriting from an excluded one, and a
467
+ // view reading one (or reading outside the allowed schemas).
468
+ const allowedSchemas = new Set(schemas.map((name) => name.toLowerCase()));
469
+ const outOfScope = (ancestor) => {
470
+ const [schema, name] = asStrings(ancestor);
471
+ return (schema === undefined ||
472
+ name === undefined ||
473
+ !allowedSchemas.has(schema.toLowerCase()) ||
474
+ isExcluded(schema, name));
475
+ };
476
+ const visible = relations.filter((row) => !isExcluded(String(row.schema), String(row.name)) &&
477
+ !jsonValues(row.reaches).some(outOfScope));
478
+ const shown = visible.filter((row) => matchesWanted(String(row.schema), String(row.name)));
479
+ if (wanted !== undefined && shown.length === 0) {
480
+ return dbRefusal("NIFRA_DB_TABLE_EXCLUDED", `${JSON.stringify(options.table)} is not an exposed table or view of this database`);
481
+ }
482
+ const tables = shown.map((row) => {
483
+ const id = key(row.schema, row.name);
484
+ const kind = RELKINDS[String(row.kind)] ?? "table";
485
+ const estimate = Number(row.estimate);
486
+ return {
487
+ schema: String(row.schema),
488
+ name: String(row.name),
489
+ kind,
490
+ rowEstimate: kind === "view" || !Number.isFinite(estimate) || estimate < 0
491
+ ? null
492
+ : Math.round(estimate),
493
+ columns: columns
494
+ .filter((column) => key(column.schema, column.table) === id)
495
+ .map((column) => ({
496
+ name: String(column.name),
497
+ type: String(column.type),
498
+ nullable: column.nullable === true,
499
+ default: typeof column.default_value === "string" ? text(column.default_value) : null,
500
+ primaryKey: column.primary_key === true,
501
+ redacted: options.redaction?.column?.(String(column.name)) === true,
502
+ })),
503
+ foreignKeys: foreignKeys
504
+ .filter((fk) => key(fk.schema, fk.table) === id)
505
+ .map((fk) => ({
506
+ columns: jsonStrings(fk.columns),
507
+ references: {
508
+ schema: String(fk.ref_schema),
509
+ table: String(fk.ref_table),
510
+ columns: jsonStrings(fk.ref_columns),
511
+ },
512
+ })),
513
+ indexes: indexes
514
+ .filter((index) => key(index.schema, index.table) === id)
515
+ .map((index) => ({
516
+ name: String(index.name),
517
+ unique: index.is_unique === true,
518
+ columns: jsonStrings(index.columns),
519
+ })),
520
+ };
521
+ });
522
+ return { tables, excludedCount: relations.length - visible.length };
523
+ });
524
+ }
525
+ const quoteIdent = (name) => `"${name.replaceAll('"', '""')}"`;
526
+ /**
527
+ * Write the SQL for a login role that can read the allowed schemas and nothing else: `pg_read_all_data`
528
+ * on Postgres 14+ when nothing is excluded; otherwise `GRANT SELECT` per schema plus default
529
+ * privileges, and a `REVOKE` for each excluded table and its partitions (`pg_read_all_data` would
530
+ * override a REVOKE, so it is not used then). Nothing is executed.
531
+ */
532
+ export async function postgresRoleSql(client, options) {
533
+ const role = options.role ?? "nifra_dev_reader";
534
+ const schemas = options.schemas ?? ["public"];
535
+ const exclude = (options.exclude ?? []).map((name) => name.toLowerCase());
536
+ return inReadOnlyTransaction(client, options.timeoutMs, async (connection) => {
537
+ const [facts] = records(await connection `
538
+ SELECT pg_catalog.current_database()::text AS database,
539
+ pg_catalog.current_setting('server_version_num')::int AS version`);
540
+ const excluded = exclude.length === 0
541
+ ? []
542
+ : records(await connection `
543
+ WITH RECURSIVE picked AS (
544
+ SELECT c.oid FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
545
+ WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f')
546
+ AND n.nspname = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${[...schemas]}::json)))
547
+ AND (lower(c.relname) = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${exclude}::json)))
548
+ OR lower(n.nspname || '.' || c.relname) = ANY(ARRAY(SELECT pg_catalog.json_array_elements_text(${exclude}::json))))
549
+ UNION
550
+ SELECT i.inhrelid FROM pg_catalog.pg_inherits i JOIN picked p ON i.inhparent = p.oid
551
+ )
552
+ SELECT n.nspname::text AS schema, c.relname::text AS name
553
+ FROM picked JOIN pg_catalog.pg_class c ON c.oid = picked.oid
554
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
555
+ ORDER BY 1, 2`);
556
+ const database = String(facts?.database ?? "");
557
+ const version = Number(facts?.version ?? 0);
558
+ const who = quoteIdent(role);
559
+ const statements = [
560
+ `-- Run as the owner of the app's tables (or a superuser). Then set the password from psql with \\password ${role}`,
561
+ `-- and point devDatabase.url at postgres://${role}:<password>@localhost/${database}`,
562
+ `CREATE ROLE ${who} LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION NOBYPASSRLS;`,
563
+ `ALTER ROLE ${who} SET default_transaction_read_only = on;`,
564
+ `GRANT CONNECT ON DATABASE ${quoteIdent(database)} TO ${who};`,
565
+ ];
566
+ if (version >= 140000 && excluded.length === 0) {
567
+ statements.push(`GRANT pg_read_all_data TO ${who};`);
568
+ }
569
+ else {
570
+ for (const schema of schemas) {
571
+ statements.push(`GRANT USAGE ON SCHEMA ${quoteIdent(schema)} TO ${who};`, `GRANT SELECT ON ALL TABLES IN SCHEMA ${quoteIdent(schema)} TO ${who};`, `ALTER DEFAULT PRIVILEGES IN SCHEMA ${quoteIdent(schema)} GRANT SELECT ON TABLES TO ${who};`);
572
+ }
573
+ for (const row of excluded) {
574
+ statements.push(`REVOKE SELECT ON TABLE ${quoteIdent(String(row.schema))}.${quoteIdent(String(row.name))} FROM ${who};`);
575
+ }
576
+ }
577
+ return { role, database, serverVersionNum: version, statements };
578
+ });
579
+ }
580
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EAKrB,OAAO,GACR,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAClG,OAAO,EAOL,SAAS,EACT,UAAU,EACV,WAAW,EAEX,SAAS,GACV,MAAM,sBAAsB,CAAA;AAiB7B,MAAM,SAAS,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,CAAU,CAAA;AAEhG;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,MAAW,CAAA;IACf,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC,iBAAiB,EAAE,oCAAoC,CAAC,CAAA;IAC3E,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QACzE,OAAO,SAAS,CACd,iBAAiB,EACjB,8DAA8D,CAC/D,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAA;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAA;IACjD,MAAM,IAAI,GAAG,CAAC,SAAS,IAAI,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;IAC3F,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,SAAS,CAAC,iBAAiB,EAAE,+CAA+C,CAAC,CAAA;IACtF,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAA;IAC9D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,OAAO,SAAS,CAAC,iBAAiB,EAAE,qCAAqC,CAAC,CAAA;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,UAAU,CAAA;IACxF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,SAAS,CAAA;IAC3F,MAAM,QAAQ,GACZ,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAA;IAC5F,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,CAAA;IAClD,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;IACtD,IAAI,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC,iBAAiB,EAAE,0CAA0C,OAAO,EAAE,CAAC,CAAA;IAC1F,CAAC;IACD,MAAM,QAAQ,GACZ,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACjC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,IAAI,EAAE,EAAE;QAClF,CAAC,CAAC,EAAE,IAAI,EAAE,CAAA;IACd,OAAO;QACL,GAAG,QAAQ;QACX,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;QAC/C,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;KACtC,CAAA;AACH,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;IAC3D,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAA;IACpE,MAAM,EAAE,GAAG,0DAA0D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChF,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QACtC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAA;IACnE,CAAC;IACD,OAAO,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,iBAAiB,CAAA;AACrD,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CACjC,MAAsB,EACtB,aAAgC,EAAE;IAElC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC9E,IAAI,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;IACtC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IAClF,OAAO,SAAS,CACd,sBAAsB,EACtB,GAAG,MAAM,CAAC,IAAI,mEAAmE,CAClF,CAAA;AACH,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAsB,EACtB,OAA+B;IAE/B,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/D,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,OAAO,CAAA;IACzC,IAAI,OAAO,GAAG,KAAK,WAAW,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC,iBAAiB,EAAE,iDAAiD,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC;QACjB,OAAO,EAAE,UAAU;QACnB,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACtF,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvE,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;QACxD,GAAG,EAAE,CAAC;QACN,MAAM,EAAE,IAAI;QACZ,iBAAiB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QACnE,UAAU,EAAE;YACV,gBAAgB,EAAE,UAAU;YAC5B,6BAA6B,EAAE,IAAI;YACnC,2BAA2B,EAAE,IAAI;YACjC,iBAAiB,EAAE,OAAO,CAAC,SAAS;YACpC,mCAAmC,EAAE,OAAO,CAAC,SAAS;SACvD;KACF,CAAC,CAAA;AACJ,CAAC;AAmBD,MAAM,SAAS,GAAG,CAAC,KAAc,EAAY,EAAE,CAC7C,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;AAE9F,KAAK,UAAU,QAAQ,CAAC,UAAwB;IAC9C,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CACnB,MAAM,UAAU,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8DAiC0C,CAC3D,CAAA;IACD,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;IAC/E,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,SAAS,EAAE,GAAG,CAAC,SAAS,KAAK,IAAI;QACjC,gBAAgB,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QACrC,cAAc,EAAE,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC;QAC9C,WAAW,EAAE,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC;QACxC,aAAa,EAAE,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC;QAC5C,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;QACrC,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,SAAS,CAAC;KACpC,CAAA;AACH,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CACjC,MAAsB,EACtB,OAAuC;IAEvC,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;AACnE,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,mBAAmB,CACjC,MAA0B,EAC1B,UAA4D,EAAE;IAE9D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACxC,IAAI,MAAM,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC,oBAAoB,EAAE,QAAQ,IAAI,iBAAiB,CAAC,CAAA;IAC3F,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,SAAS,CACd,oBAAoB,EACpB,QAAQ,IAAI,sCAAsC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACrF,CAAA;IACH,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,OAAO,SAAS,CACd,oBAAoB,EACpB,QAAQ,IAAI,mBAAmB,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC,CACrG,CAAA;IACH,CAAC;IACD,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,SAAS,CACd,oBAAoB,EACpB,QAAQ,IAAI,gBAAgB,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,oCAAoC,CAChG,CAAA;IACH,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IAC1F,MAAM,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAC/D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAC3C,CAAA;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,SAAS,CACd,oBAAoB,EACpB,QAAQ,IAAI,YAAY,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC,CACnF,CAAA;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IACjC,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC3C,OAAO,SAAS,CACd,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,wBAAwB,EACpF,OAAO,CAAC,OAAO,CAChB,CAAA;AACH,CAAC;AAUD,SAAS,YAAY,CAAC,KAAoB;IACxC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IACxF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IACjF,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1B,MAAM,SAAS,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAA;QACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACvC,OAAO,mBAAmB,SAAS,kCAAkC,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;QACjG,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC9E,OAAO,mBAAmB,SAAS,wCAAwC,CAAA;QAC7E,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,KAAoB,EAAE,MAAoC;IAC5E,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;IACtC,OAAO,KAAK,EAAE,UAAwB,EAAE,KAAgB,EAAkC,EAAE;QAC1F,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;YAClC,IAAI,EAAE,CAAC,MAAM,KAAK,YAAY,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzD,OAAO,SAAS,CACd,yBAAyB,EACzB,wCAAwC,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,+BAA+B,CAC5F,CAAA;YACH,CAAC;YACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBACvE,OAAO,SAAS,CACd,2BAA2B,EAC3B,mBAAmB,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,6CAA6C,CACrF,CAAA;YACH,CAAC;QACH,CAAC;QACD,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;QAC3B,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;YACnC,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAAA;QAC/E,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAC5C,MAAM,SAAS,GAAG,OAAO,CACvB,MAAM,UAAU,CAAA;;;4CAGsB,CAAC,GAAG,SAAS,CAAC;;;;;;;;+DAQK,CAC1D,CAAA;QACD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACjF,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC,yBAAyB,EAAE,MAAM,CAAC,CAAA;QAC/E,CAAC;QACD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC1C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;QAC9B,MAAM,SAAS,GAAmB,EAAE,CAAA;QACpC,KAAK,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACjD,IAAI,MAAM,KAAK,GAAG;gBAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;iBACvC,IAAI,MAAM,CAAC,MAAM,CAAC;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;QACjE,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;QAChF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAA;YACxC,MAAM,IAAI,GAAG,OAAO,CAClB,MAAM,UAAU,CAAA;;4CAEoB,MAAM;;;kDAGA,CAC3C,CAAA;YACD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAA;gBACvD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YAC/D,CAAC;YACD,MAAM,SAAS,GAAG,CAAC,QAAsB,EAAqB,EAAE,CAC9D,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;YACvD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC;oBACtC,IAAI,MAAM,CAAC,MAAM,CAAC;wBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;YAC9D,CAAC;YACD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;gBACtC,uFAAuF;gBACvF,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;oBAAE,SAAQ;gBACtF,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO;oBACjC,IAAI,MAAM,CAAC,MAAM,CAAC;wBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAA;YACrE,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QACrC,OAAO,SAAS,CACd,yBAAyB,EACzB,mBAAmB,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,EAAE,CACzG,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AASD,KAAK,UAAU,KAAK,CAClB,MAAsB,EACtB,GAAW,EACX,OAA6B,EAC7B,IAA6E;IAE7E,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAA;IACzC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACvC,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAC3E,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;QACxE,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,OAAO,CAAA;QACzC,OAAO,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAsB,EACtB,GAAW,EACX,OAA6B;IAE7B,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,CAC1E,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE;QACxC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;KACtD,CAAC,CACH,CAAA;IACD,IAAI,WAAW,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IACxC,OAAO,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC1D,CAAC;AAED,0FAA0F;AAC1F,SAAS,SAAS,CAAC,IAAa,EAAE,KAAa;IAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAA;IAC1E,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC1D,MAAM,GAAG,GAA4B,EAAE,CAAA;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACpB,IAAI,KAAK,GAAG,CAAC;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;QACvD,CAAC;;YAAM,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IACpE,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAsB,EACtB,GAAW,EACX,OAA8D;IAE9D,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,CAC5E,oBAAoB,CAAC,UAAU,EAAE,SAAS,EAAE;QAC1C,OAAO,EAAE,OAAO,CAAC,OAAO,KAAK,IAAI;QACjC,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC;KAC3B,CAAC,CACH,CAAA;IACD,IAAI,WAAW,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC3C,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IACvF,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAC/F,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,OAAO,KAAK,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;AAC1F,CAAC;AAED,MAAM,QAAQ,GAAoD;IAChE,CAAC,EAAE,OAAO;IACV,CAAC,EAAE,mBAAmB;IACtB,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,mBAAmB;IACtB,CAAC,EAAE,eAAe;CACnB,CAAA;AAUD,MAAM,UAAU,GAAG,CAAC,KAAc,EAAa,EAAE;IAC/C,MAAM,MAAM,GAAY,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC7E,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAA;AAC5C,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,KAAc,EAAY,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;AAE9E;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAsB,EACtB,OAA8B;IAE9B,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IACnF,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,IAAY,EAAW,EAAE,CAC3D,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;IACrF,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,CAAA;IAC3C,MAAM,aAAa,GAAG,CAAC,MAAc,EAAE,IAAY,EAAW,EAAE,CAC9D,MAAM,KAAK,SAAS;QACpB,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM;QAC7B,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAA;IAC9C,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAC3E,MAAM,SAAS,GAAG,OAAO,CACvB,MAAM,UAAU,CAAA;;;;;;;;;;;;;;;;;;;;;;+EAsByD,OAAO;;oBAElE,CACf,CAAA;QACD,MAAM,OAAO,GAAG,OAAO,CACrB,MAAM,UAAU,CAAA;;;;;;;;;;;+EAWyD,OAAO;8BACxD,CACzB,CAAA;QACD,MAAM,WAAW,GAAG,OAAO,CACzB,MAAM,UAAU,CAAA;;;;;;;;;;;;;+EAayD,OAAO;+BACvD,CAC1B,CAAA;QACD,MAAM,OAAO,GAAG,OAAO,CACrB,MAAM,UAAU,CAAA;;;;;;;;;+EASyD,OAAO;uBAC/D,CAClB,CAAA;QACD,MAAM,GAAG,GAAG,CAAC,MAAe,EAAE,IAAa,EAAU,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAA;QAC3F,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAA;QACjF,kGAAkG;QAClG,6DAA6D;QAC7D,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;QACzE,MAAM,UAAU,GAAG,CAAC,QAAiB,EAAW,EAAE;YAChD,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAA;YAC1C,OAAO,CACL,MAAM,KAAK,SAAS;gBACpB,IAAI,KAAK,SAAS;gBAClB,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBACzC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CACzB,CAAA;QACH,CAAC,CAAA;QACD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAC9B,CAAC,GAAG,EAAE,EAAE,CACN,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACjD,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAC5C,CAAA;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC1F,IAAI,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,SAAS,CACd,yBAAyB,EACzB,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,mDAAmD,CACpF,CAAA;QACH,CAAC;QACD,MAAM,MAAM,GAAoB,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAChD,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;YACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,OAAO,CAAA;YAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YACrC,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC1B,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBACtB,IAAI;gBACJ,WAAW,EACT,IAAI,KAAK,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC;oBAC3D,CAAC,CAAC,IAAI;oBACN,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBAC1B,OAAO,EAAE,OAAO;qBACb,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;qBAC3D,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;oBAChB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACzB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;oBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,IAAI;oBAClC,OAAO,EAAE,OAAO,MAAM,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI;oBACrF,UAAU,EAAE,MAAM,CAAC,WAAW,KAAK,IAAI;oBACvC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI;iBACpE,CAAC,CAAC;gBACL,WAAW,EAAE,WAAW;qBACrB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;qBAC/C,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;oBACZ,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,OAAO,CAAC;oBAChC,UAAU,EAAE;wBACV,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC;wBAC7B,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC;wBAC3B,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,WAAW,CAAC;qBACrC;iBACF,CAAC,CAAC;gBACL,OAAO,EAAE,OAAO;qBACb,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;qBACxD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACf,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;oBACxB,MAAM,EAAE,KAAK,CAAC,SAAS,KAAK,IAAI;oBAChC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC;iBACpC,CAAC,CAAC;aACN,CAAA;QACH,CAAC,CAAC,CAAA;QACF,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAA;IACrE,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAA;AAiB9E;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAsB,EACtB,OAA+B;IAE/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,kBAAkB,CAAA;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC7C,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACzE,OAAO,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE;QAC3E,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CACrB,MAAM,UAAU,CAAA;;yEAEmD,CACpE,CAAA;QACD,MAAM,QAAQ,GACZ,OAAO,CAAC,MAAM,KAAK,CAAC;YAClB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,OAAO,CACL,MAAM,UAAU,CAAA;;;;iFAIqD,CAAC,GAAG,OAAO,CAAC;yFACJ,OAAO;6GACa,OAAO;;;;;;;oBAOhG,CACT,CAAA;QACP,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAA;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,CAAC,CAAC,CAAA;QAC3C,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAC5B,MAAM,UAAU,GAAG;YACjB,6GAA6G,IAAI,EAAE;YACnH,8CAA8C,IAAI,yBAAyB,QAAQ,EAAE;YACrF,eAAe,GAAG,uEAAuE;YACzF,cAAc,GAAG,0CAA0C;YAC3D,6BAA6B,UAAU,CAAC,QAAQ,CAAC,OAAO,GAAG,GAAG;SAC/D,CAAA;QACD,IAAI,OAAO,IAAI,MAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,UAAU,CAAC,IAAI,CAAC,6BAA6B,GAAG,GAAG,CAAC,CAAA;QACtD,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,UAAU,CAAC,IAAI,CACb,yBAAyB,UAAU,CAAC,MAAM,CAAC,OAAO,GAAG,GAAG,EACxD,wCAAwC,UAAU,CAAC,MAAM,CAAC,OAAO,GAAG,GAAG,EACvE,sCAAsC,UAAU,CAAC,MAAM,CAAC,8BAA8B,GAAG,GAAG,CAC7F,CAAA;YACH,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,UAAU,CAAC,IAAI,CACb,0BAA0B,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG,CACxG,CAAA;YACH,CAAC;QACH,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;IAClE,CAAC,CAAC,CAAA;AACJ,CAAC"}