@jantstack/adonis-authz 1.1.0 → 2.4.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +837 -51
- package/build/commands/authz_catalog_diff.d.ts +28 -0
- package/build/commands/authz_catalog_diff.d.ts.map +1 -0
- package/build/commands/authz_catalog_diff.js +67 -0
- package/build/commands/authz_catalog_diff.js.map +1 -0
- package/build/commands/authz_catalog_prune_orphans.d.ts +78 -0
- package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -0
- package/build/commands/authz_catalog_prune_orphans.js +136 -0
- package/build/commands/authz_catalog_prune_orphans.js.map +1 -0
- package/build/commands/authz_catalog_sync.d.ts +37 -0
- package/build/commands/authz_catalog_sync.d.ts.map +1 -0
- package/build/commands/authz_catalog_sync.js +81 -0
- package/build/commands/authz_catalog_sync.js.map +1 -0
- package/build/commands/authz_freeze.d.ts +44 -0
- package/build/commands/authz_freeze.d.ts.map +1 -0
- package/build/commands/authz_freeze.js +95 -0
- package/build/commands/authz_freeze.js.map +1 -0
- package/build/commands/authz_reconcile.d.ts +102 -0
- package/build/commands/authz_reconcile.d.ts.map +1 -0
- package/build/commands/authz_reconcile.js +294 -0
- package/build/commands/authz_reconcile.js.map +1 -0
- package/build/commands/authz_relations_reconcile.d.ts +73 -0
- package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
- package/build/commands/authz_relations_reconcile.js +225 -0
- package/build/commands/authz_relations_reconcile.js.map +1 -0
- package/build/commands/authz_scopes_relay.d.ts +47 -0
- package/build/commands/authz_scopes_relay.d.ts.map +1 -0
- package/build/commands/authz_scopes_relay.js +141 -0
- package/build/commands/authz_scopes_relay.js.map +1 -0
- package/build/commands/authz_unfreeze.d.ts +37 -0
- package/build/commands/authz_unfreeze.d.ts.map +1 -0
- package/build/commands/authz_unfreeze.js +92 -0
- package/build/commands/authz_unfreeze.js.map +1 -0
- package/build/commands/main.d.ts +8 -1
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +8 -1
- package/build/commands/main.js.map +1 -1
- package/build/commands/openfga_provision.d.ts +46 -4
- package/build/commands/openfga_provision.d.ts.map +1 -1
- package/build/commands/openfga_provision.js +91 -7
- package/build/commands/openfga_provision.js.map +1 -1
- package/build/configure.d.ts +11 -0
- package/build/configure.d.ts.map +1 -1
- package/build/configure.js +37 -1
- package/build/configure.js.map +1 -1
- package/build/index.d.ts +75 -7
- package/build/index.d.ts.map +1 -1
- package/build/index.js +67 -5
- package/build/index.js.map +1 -1
- package/build/providers/authz_provider.d.ts +26 -2
- package/build/providers/authz_provider.d.ts.map +1 -1
- package/build/providers/authz_provider.js +48 -2
- package/build/providers/authz_provider.js.map +1 -1
- package/build/services/relations.d.ts +14 -0
- package/build/services/relations.d.ts.map +1 -0
- package/build/services/relations.js +17 -0
- package/build/services/relations.js.map +1 -0
- package/build/src/catalog/catalog.d.ts +289 -0
- package/build/src/catalog/catalog.d.ts.map +1 -0
- package/build/src/catalog/catalog.js +859 -0
- package/build/src/catalog/catalog.js.map +1 -0
- package/build/src/catalog/catalog_cache.d.ts +324 -0
- package/build/src/catalog/catalog_cache.d.ts.map +1 -0
- package/build/src/catalog/catalog_cache.js +666 -0
- package/build/src/catalog/catalog_cache.js.map +1 -0
- package/build/src/clock.d.ts +24 -0
- package/build/src/clock.d.ts.map +1 -0
- package/build/src/clock.js +7 -0
- package/build/src/clock.js.map +1 -0
- package/build/src/define_config.d.ts +201 -7
- package/build/src/define_config.d.ts.map +1 -1
- package/build/src/define_config.js.map +1 -1
- package/build/src/drivers/database_driver.d.ts +324 -15
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +1107 -128
- package/build/src/drivers/database_driver.js.map +1 -1
- package/build/src/drivers/database_relations_driver.d.ts +75 -0
- package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/database_relations_driver.js +450 -0
- package/build/src/drivers/database_relations_driver.js.map +1 -0
- package/build/src/drivers/openfga_driver.d.ts +963 -55
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2693 -372
- package/build/src/drivers/openfga_driver.js.map +1 -1
- package/build/src/drivers/openfga_facts.d.ts +369 -0
- package/build/src/drivers/openfga_facts.d.ts.map +1 -0
- package/build/src/drivers/openfga_facts.js +813 -0
- package/build/src/drivers/openfga_facts.js.map +1 -0
- package/build/src/drivers/openfga_relations_driver.d.ts +120 -0
- package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
- package/build/src/drivers/openfga_relations_driver.js +466 -0
- package/build/src/drivers/openfga_relations_driver.js.map +1 -0
- package/build/src/errors.d.ts +586 -0
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +583 -0
- package/build/src/errors.js.map +1 -1
- package/build/src/expiry.d.ts +27 -0
- package/build/src/expiry.d.ts.map +1 -0
- package/build/src/expiry.js +50 -0
- package/build/src/expiry.js.map +1 -0
- package/build/src/freeze.d.ts +120 -0
- package/build/src/freeze.d.ts.map +1 -0
- package/build/src/freeze.js +172 -0
- package/build/src/freeze.js.map +1 -0
- package/build/src/hierarchical_resolver.d.ts +56 -0
- package/build/src/hierarchical_resolver.d.ts.map +1 -0
- package/build/src/hierarchical_resolver.js +87 -0
- package/build/src/hierarchical_resolver.js.map +1 -0
- package/build/src/{middleware → http}/app_access_middleware.d.ts +8 -6
- package/build/src/http/app_access_middleware.d.ts.map +1 -0
- package/build/src/{middleware → http}/app_access_middleware.js +10 -26
- package/build/src/http/app_access_middleware.js.map +1 -0
- package/build/src/http/resource_access_middleware.d.ts +105 -0
- package/build/src/http/resource_access_middleware.d.ts.map +1 -0
- package/build/src/http/resource_access_middleware.js +81 -0
- package/build/src/http/resource_access_middleware.js.map +1 -0
- package/build/src/identity.d.ts +228 -0
- package/build/src/identity.d.ts.map +1 -0
- package/build/src/identity.js +457 -0
- package/build/src/identity.js.map +1 -0
- package/build/src/manager.d.ts +536 -7
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +2676 -23
- package/build/src/manager.js.map +1 -1
- package/build/src/memoize_ancestors.d.ts +23 -0
- package/build/src/memoize_ancestors.d.ts.map +1 -0
- package/build/src/memoize_ancestors.js +42 -0
- package/build/src/memoize_ancestors.js.map +1 -0
- package/build/src/models/authz_assignment.d.ts +11 -11
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +11 -11
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +17 -11
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_permission.js +4 -0
- package/build/src/models/authz_permission.js.map +1 -1
- package/build/src/models/authz_role.d.ts +19 -12
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role.js +6 -1
- package/build/src/models/authz_role.js.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +11 -11
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/openfga.d.ts +29 -0
- package/build/src/openfga.d.ts.map +1 -0
- package/build/src/openfga.js +26 -0
- package/build/src/openfga.js.map +1 -0
- package/build/src/reconcile.d.ts +37 -0
- package/build/src/reconcile.d.ts.map +1 -0
- package/build/src/reconcile.js +69 -0
- package/build/src/reconcile.js.map +1 -0
- package/build/src/relation_partition_trigger.d.ts +8 -0
- package/build/src/relation_partition_trigger.d.ts.map +1 -0
- package/build/src/relation_partition_trigger.js +85 -0
- package/build/src/relation_partition_trigger.js.map +1 -0
- package/build/src/relations/define_relations_config.d.ts +58 -0
- package/build/src/relations/define_relations_config.d.ts.map +1 -0
- package/build/src/relations/define_relations_config.js +144 -0
- package/build/src/relations/define_relations_config.js.map +1 -0
- package/build/src/relations/manager.d.ts +38 -0
- package/build/src/relations/manager.d.ts.map +1 -0
- package/build/src/relations/manager.js +156 -0
- package/build/src/relations/manager.js.map +1 -0
- package/build/src/relations/reconcile.d.ts +62 -0
- package/build/src/relations/reconcile.d.ts.map +1 -0
- package/build/src/relations/reconcile.js +138 -0
- package/build/src/relations/reconcile.js.map +1 -0
- package/build/src/relations_config_store.d.ts +22 -0
- package/build/src/relations_config_store.d.ts.map +1 -0
- package/build/src/relations_config_store.js +74 -0
- package/build/src/relations_config_store.js.map +1 -0
- package/build/src/scope_outbox.d.ts +69 -0
- package/build/src/scope_outbox.d.ts.map +1 -0
- package/build/src/scope_outbox.js +291 -0
- package/build/src/scope_outbox.js.map +1 -0
- package/build/src/shared/backend_guard.d.ts +106 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -0
- package/build/src/shared/backend_guard.js +246 -0
- package/build/src/shared/backend_guard.js.map +1 -0
- package/build/src/shared/sql_expiry.d.ts +53 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/shared/sql_expiry.js +66 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/sql_descendants.d.ts +97 -0
- package/build/src/sql_descendants.d.ts.map +1 -0
- package/build/src/sql_descendants.js +203 -0
- package/build/src/sql_descendants.js.map +1 -0
- package/build/src/testing/contract.d.ts +212 -7
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +3449 -24
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +10 -2
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +5 -1
- package/build/src/testing/main.js.map +1 -1
- package/build/src/testing/migration_contract.d.ts +284 -0
- package/build/src/testing/migration_contract.d.ts.map +1 -0
- package/build/src/testing/migration_contract.js +586 -0
- package/build/src/testing/migration_contract.js.map +1 -0
- package/build/src/testing/relations_contract.d.ts +51 -0
- package/build/src/testing/relations_contract.d.ts.map +1 -0
- package/build/src/testing/relations_contract.js +654 -0
- package/build/src/testing/relations_contract.js.map +1 -0
- package/build/src/testing/relations_reconcile_contract.d.ts +24 -0
- package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -0
- package/build/src/testing/relations_reconcile_contract.js +172 -0
- package/build/src/testing/relations_reconcile_contract.js.map +1 -0
- package/build/src/testing/scope_tree.d.ts +65 -0
- package/build/src/testing/scope_tree.d.ts.map +1 -0
- package/build/src/testing/scope_tree.js +145 -0
- package/build/src/testing/scope_tree.js.map +1 -0
- package/build/src/traits/authz_scopes.d.ts +30 -6
- package/build/src/traits/authz_scopes.d.ts.map +1 -1
- package/build/src/traits/authz_scopes.js +30 -18
- package/build/src/traits/authz_scopes.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +12 -12
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +1313 -28
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +20 -4
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/app_acl.stub +4 -2
- package/build/stubs/config/authorization.stub +168 -14
- package/build/stubs/migration.stub +183 -13
- package/build/stubs/scopes_outbox_migration.stub +57 -0
- package/package.json +14 -7
- package/build/commands/openfga_import.d.ts +0 -28
- package/build/commands/openfga_import.d.ts.map +0 -1
- package/build/commands/openfga_import.js +0 -74
- package/build/commands/openfga_import.js.map +0 -1
- package/build/src/catalog.d.ts +0 -3
- package/build/src/catalog.d.ts.map +0 -1
- package/build/src/catalog.js +0 -103
- package/build/src/catalog.js.map +0 -1
- package/build/src/middleware/app_access_middleware.d.ts.map +0 -1
- package/build/src/middleware/app_access_middleware.js.map +0 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import db from '@adonisjs/lucid/services/db';
|
|
2
|
+
import { AuthorizationConfigError, TooManyScopesError, UnsupportedDialectError } from './errors.js';
|
|
3
|
+
import { assertScope, assertScopeType } from './identity.js';
|
|
4
|
+
import { guardSql } from './shared/backend_guard.js';
|
|
5
|
+
import { APP_SCOPE_TYPE } from './types.js';
|
|
6
|
+
import { APP_SCOPE } from './types.js';
|
|
7
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8
|
+
/** Dialecto → carácter con el que cita identificadores (MySQL: backtick; el resto: comilla doble ANSI). */
|
|
9
|
+
const QUOTE_BY_DIALECT = {
|
|
10
|
+
'postgres': '"',
|
|
11
|
+
'sqlite3': '"',
|
|
12
|
+
'better-sqlite3': '"',
|
|
13
|
+
'mysql': '`',
|
|
14
|
+
'mysql2': '`',
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
17
|
+
function identifier(kind, value) {
|
|
18
|
+
if (typeof value !== 'string' || !IDENTIFIER.test(value)) {
|
|
19
|
+
throw new AuthorizationConfigError(`sqlDescendantsOf: ${kind} '${String(value)}' no es un identificador SQL simple (letras, dígitos y _); ` +
|
|
20
|
+
`nada que no lo sea se interpola en la consulta.`);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
export function sqlDescendantsOf(options, database = db) {
|
|
25
|
+
const table = identifier('table', options.table);
|
|
26
|
+
const uuidCol = identifier('uuidColumn', options.uuidColumn);
|
|
27
|
+
const parentCol = identifier('parentColumn', options.parentColumn);
|
|
28
|
+
if ((options.typeColumn === undefined) === (options.scopeType === undefined)) {
|
|
29
|
+
throw new AuthorizationConfigError('sqlDescendantsOf: declara exactamente uno de typeColumn (el tipo viene de la fila) o scopeType (tipo fijo).');
|
|
30
|
+
}
|
|
31
|
+
const typeCol = options.typeColumn === undefined ? null : identifier('typeColumn', options.typeColumn);
|
|
32
|
+
const fixedType = options.scopeType ?? null;
|
|
33
|
+
if (fixedType !== null) {
|
|
34
|
+
// Es identidad de scope (minúsculas, ≤ 20, sin separadores) y nunca la
|
|
35
|
+
// raíz: `app` no tiene fila ni uuid.
|
|
36
|
+
try {
|
|
37
|
+
assertScopeType(fixedType);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new AuthorizationConfigError(`sqlDescendantsOf: scopeType inválido: ${error.message}`);
|
|
41
|
+
}
|
|
42
|
+
if (fixedType === APP_SCOPE_TYPE) {
|
|
43
|
+
throw new AuthorizationConfigError("sqlDescendantsOf: scopeType no puede ser 'app' (la raíz no tiene fila)");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (options.maxNodes !== undefined && !(Number.isInteger(options.maxNodes) && options.maxNodes >= 1)) {
|
|
47
|
+
throw new AuthorizationConfigError(`sqlDescendantsOf: maxNodes debe ser un entero >= 1 (llegó ${String(options.maxNodes)})`);
|
|
48
|
+
}
|
|
49
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
50
|
+
const connectionName = options.connection;
|
|
51
|
+
/** El SQL por carácter de cita (se construye una vez por dialecto). */
|
|
52
|
+
const statements = new Map();
|
|
53
|
+
function statementsFor(quote) {
|
|
54
|
+
let built = statements.get(quote);
|
|
55
|
+
if (built)
|
|
56
|
+
return built;
|
|
57
|
+
const q = (name) => `${quote}${name}${quote}`;
|
|
58
|
+
const typeSelect = typeCol ? `t.${q(typeCol)}` : 'NULL';
|
|
59
|
+
// MySQL corta una CTE recursiva a `cte_max_recursion_depth` iteraciones
|
|
60
|
+
// (1000 por defecto) con el error 3636: con una cota mayor, un ciclo
|
|
61
|
+
// salía como 503 «backend caído» en vez del 422 «posible ciclo» del
|
|
62
|
+
// contrato (2.5 · J3, hallazgo). El hint `SET_VAR` sube el tope SOLO para
|
|
63
|
+
// esta sentencia, a la cota + 1 que la propia consulta ya impone con
|
|
64
|
+
// `depth < ?`; el placeholder `%DEPTH%` se rellena con ese entero (nunca
|
|
65
|
+
// con entrada del consumidor). Los demás dialectos no tienen tope.
|
|
66
|
+
const hint = quote === '`' ? '/*+ SET_VAR(cte_max_recursion_depth = %DEPTH%) */ ' : '';
|
|
67
|
+
built = {
|
|
68
|
+
exists: `SELECT 1 AS ${q('ok')} FROM ${q(table)} t WHERE %WHERE% LIMIT 1`,
|
|
69
|
+
walk: `WITH RECURSIVE ${q('authz_descendants')}(${q('uuid')}, ${q('type')}, ${q('depth')}) AS (` +
|
|
70
|
+
` SELECT t.${q(uuidCol)}, ${typeSelect}, 1 FROM ${q(table)} t WHERE ` +
|
|
71
|
+
`%ANCHOR%` +
|
|
72
|
+
` UNION ALL` +
|
|
73
|
+
` SELECT t.${q(uuidCol)}, ${typeSelect}, d.${q('depth')} + 1 FROM ${q(table)} t` +
|
|
74
|
+
` JOIN ${q('authz_descendants')} d ON t.${q(parentCol)} = d.${q('uuid')} WHERE d.${q('depth')} < ?` +
|
|
75
|
+
`) SELECT ${hint}${q('uuid')}, ${q('type')} FROM ${q('authz_descendants')} LIMIT ?`,
|
|
76
|
+
};
|
|
77
|
+
statements.set(quote, built);
|
|
78
|
+
return built;
|
|
79
|
+
}
|
|
80
|
+
return async (scope, { maxNodes }) => {
|
|
81
|
+
assertScope(scope);
|
|
82
|
+
const cap = Math.min(maxNodes, options.maxNodes ?? Infinity);
|
|
83
|
+
if (!(Number.isInteger(cap) && cap >= 1)) {
|
|
84
|
+
throw new AuthorizationConfigError(`sqlDescendantsOf: maxNodes debe ser un entero >= 1 (llegó ${String(maxNodes)})`);
|
|
85
|
+
}
|
|
86
|
+
const connection = database.connection(connectionName);
|
|
87
|
+
const dialect = connection?.dialect?.name ?? connection?.client?.driverName ?? 'desconocido';
|
|
88
|
+
const quote = QUOTE_BY_DIALECT[dialect];
|
|
89
|
+
if (!quote) {
|
|
90
|
+
throw new UnsupportedDialectError(`sqlDescendantsOf: dialecto '${dialect}' sin observación en la suite (hoy: PostgreSQL, MySQL 8 y SQLite). ` +
|
|
91
|
+
`Implementa scopes.descendantsOf a mano para este motor.`);
|
|
92
|
+
}
|
|
93
|
+
const q = (name) => `${quote}${name}${quote}`;
|
|
94
|
+
const sql = statementsFor(quote);
|
|
95
|
+
const query = (operation, text, bindings) => guardSql('sqlDescendantsOf', operation, timeoutMs, () => connection.rawQuery(text, bindings));
|
|
96
|
+
// Forma del resultado crudo por cliente: pg `{ rows }`, sqlite `rows[]`,
|
|
97
|
+
// mysql2 `[rows[], fields[]]` (2.5 · J3).
|
|
98
|
+
const rowsOf = (result) => {
|
|
99
|
+
if (Array.isArray(result)) {
|
|
100
|
+
return result.length === 2 && Array.isArray(result[0]) && Array.isArray(result[1]) ? result[0] : result;
|
|
101
|
+
}
|
|
102
|
+
return result?.rows ?? [];
|
|
103
|
+
};
|
|
104
|
+
let anchor;
|
|
105
|
+
let bindings;
|
|
106
|
+
if (scope.type === APP_SCOPE_TYPE) {
|
|
107
|
+
anchor = `t.${q(parentCol)} IS NULL`;
|
|
108
|
+
bindings = [];
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
if (fixedType !== null && scope.type !== fixedType)
|
|
112
|
+
return null;
|
|
113
|
+
const where = `t.${q(uuidCol)} = ?` + (typeCol ? ` AND t.${q(typeCol)} = ?` : '');
|
|
114
|
+
const args = typeCol ? [scope.uuid, scope.type] : [scope.uuid];
|
|
115
|
+
const exists = rowsOf(await query('descendantsOf.exists', sql.exists.replace('%WHERE%', where), args));
|
|
116
|
+
if (exists.length === 0)
|
|
117
|
+
return null;
|
|
118
|
+
anchor = `t.${q(parentCol)} = ?`;
|
|
119
|
+
bindings = [scope.uuid];
|
|
120
|
+
}
|
|
121
|
+
const walk = sql.walk.replace('%ANCHOR%', anchor).replace('%DEPTH%', String(cap + 2));
|
|
122
|
+
const rows = rowsOf(await query('descendantsOf', walk, [...bindings, cap + 1, cap + 1]));
|
|
123
|
+
if (rows.length > cap) {
|
|
124
|
+
throw new TooManyScopesError(`sqlDescendantsOf: ${scope.type}:${scope.uuid ?? ''} tiene más de ${cap} descendientes — o la tabla tiene un ` +
|
|
125
|
+
`posible ciclo (un ciclo alcanzable produce una fila por nivel hasta la cota); no se devuelve una lista parcial.`);
|
|
126
|
+
}
|
|
127
|
+
return rows.map((row) => ({ type: typeCol ? row.type : fixedType, uuid: row.uuid }));
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
export function sqlScopeEdges(options, database = db) {
|
|
131
|
+
const table = identifier('table', options.table);
|
|
132
|
+
const uuidCol = identifier('uuidColumn', options.uuidColumn);
|
|
133
|
+
const parentCol = identifier('parentColumn', options.parentColumn);
|
|
134
|
+
if ((options.typeColumn === undefined) === (options.scopeType === undefined)) {
|
|
135
|
+
throw new AuthorizationConfigError('sqlScopeEdges: declara exactamente uno de typeColumn (el tipo viene de la fila) o scopeType (tipo fijo).');
|
|
136
|
+
}
|
|
137
|
+
const typeCol = options.typeColumn === undefined ? null : identifier('typeColumn', options.typeColumn);
|
|
138
|
+
const fixedType = options.scopeType ?? null;
|
|
139
|
+
if (fixedType !== null) {
|
|
140
|
+
try {
|
|
141
|
+
assertScopeType(fixedType);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
throw new AuthorizationConfigError(`sqlScopeEdges: scopeType inválido: ${error.message}`);
|
|
145
|
+
}
|
|
146
|
+
if (fixedType === APP_SCOPE_TYPE) {
|
|
147
|
+
throw new AuthorizationConfigError("sqlScopeEdges: scopeType no puede ser 'app' (la raíz no tiene fila)");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
151
|
+
const connectionName = options.connection;
|
|
152
|
+
return async ({ limit, after }) => {
|
|
153
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
154
|
+
throw new AuthorizationConfigError(`sqlScopeEdges: limit debe ser un entero >= 1 (llegó ${String(limit)})`);
|
|
155
|
+
}
|
|
156
|
+
const connection = database.connection(connectionName);
|
|
157
|
+
const dialect = connection?.dialect?.name ?? connection?.client?.driverName ?? 'desconocido';
|
|
158
|
+
const quote = QUOTE_BY_DIALECT[dialect];
|
|
159
|
+
if (!quote) {
|
|
160
|
+
throw new UnsupportedDialectError(`sqlScopeEdges: dialecto '${dialect}' sin observación en la suite (hoy: PostgreSQL, MySQL 8 y SQLite). ` +
|
|
161
|
+
`Implementa scopes.enumerateEdges a mano para este motor.`);
|
|
162
|
+
}
|
|
163
|
+
const q = (name) => `${quote}${name}${quote}`;
|
|
164
|
+
const childType = typeCol ? `t.${q(typeCol)}` : 'NULL';
|
|
165
|
+
const parentType = typeCol ? `p.${q(typeCol)}` : 'NULL';
|
|
166
|
+
const text = `SELECT t.${q(uuidCol)} AS ${q('child_uuid')}, ${childType} AS ${q('child_type')},` +
|
|
167
|
+
` t.${q(parentCol)} AS ${q('parent_uuid')}, ${parentType} AS ${q('parent_type')},` +
|
|
168
|
+
` p.${q(uuidCol)} AS ${q('parent_row')}` +
|
|
169
|
+
` FROM ${q(table)} t LEFT JOIN ${q(table)} p ON t.${q(parentCol)} = p.${q(uuidCol)}` +
|
|
170
|
+
(after === undefined ? '' : ` WHERE t.${q(uuidCol)} > ?`) +
|
|
171
|
+
` ORDER BY t.${q(uuidCol)} LIMIT ?`;
|
|
172
|
+
const bindings = after === undefined ? [limit] : [after, limit];
|
|
173
|
+
const result = await guardSql('sqlScopeEdges', 'enumerateEdges', timeoutMs, () => connection.rawQuery(text, bindings));
|
|
174
|
+
const rows = Array.isArray(result)
|
|
175
|
+
? result.length === 2 && Array.isArray(result[0]) && Array.isArray(result[1])
|
|
176
|
+
? result[0]
|
|
177
|
+
: result
|
|
178
|
+
: (result?.rows ?? []);
|
|
179
|
+
const edges = [];
|
|
180
|
+
for (const row of rows) {
|
|
181
|
+
const child = { type: typeCol ? row.child_type : fixedType, uuid: String(row.child_uuid) };
|
|
182
|
+
if (row.parent_uuid === null || row.parent_uuid === undefined) {
|
|
183
|
+
edges.push({ child, parent: APP_SCOPE });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// Padre declarado que no tiene fila: el nodo NO está en el árbol
|
|
187
|
+
// (`resolveChain` devuelve `null` ahí). No se inventa la arista.
|
|
188
|
+
if (row.parent_row === null || row.parent_row === undefined)
|
|
189
|
+
continue;
|
|
190
|
+
edges.push({
|
|
191
|
+
child,
|
|
192
|
+
parent: { type: typeCol ? row.parent_type : fixedType, uuid: String(row.parent_uuid) },
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
// El cursor es la ÚLTIMA FILA LEÍDA, no la última arista emitida: si no,
|
|
196
|
+
// una fila con el padre colgando pararía la paginación en seco.
|
|
197
|
+
const last = rows[rows.length - 1];
|
|
198
|
+
return rows.length < limit
|
|
199
|
+
? { edges }
|
|
200
|
+
: { edges, cursor: String(last.child_uuid) };
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=sql_descendants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql_descendants.js","sourceRoot":"","sources":["../../src/sql_descendants.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,27 +1,232 @@
|
|
|
1
1
|
import '@japa/assert';
|
|
2
|
+
import type { Assert } from '@japa/assert';
|
|
2
3
|
import type { AuthorizationDriver, CatalogSpec } from '../types.js';
|
|
4
|
+
import type { ContractScopeTree } from './scope_tree.js';
|
|
3
5
|
/**
|
|
4
6
|
* Suite de contrato del sistema de autorización — EL JUEZ.
|
|
5
7
|
*
|
|
6
8
|
* Cualquier driver (database, openfga, custom del consumidor) debe pasar
|
|
7
9
|
* estos casos sin modificarlos: son la definición ejecutable de la semántica
|
|
8
|
-
* documentada en
|
|
10
|
+
* documentada en `../types.js`. Para enchufar un driver:
|
|
9
11
|
*
|
|
10
12
|
* runAuthorizationDriverContract({
|
|
11
13
|
* name: 'mi-driver',
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* level: '2.0', // omitido = 'core': solo los casos de 1.x
|
|
15
|
+
* capabilities: { hierarchyFacts: false, transactions: false, truncationSignal: false,
|
|
16
|
+
* singleCheckAuthorize: false, injectableClock: false },
|
|
17
|
+
* seedCatalog: (catalog) => ..., // materializa roles/permisos
|
|
18
|
+
* makeDriver: (tree) => new MiDriver({ resolveChain: ... }), // recibe el árbol
|
|
19
|
+
* cleanup: () => ..., // borra hechos + catálogo entre tests
|
|
15
20
|
* })
|
|
16
21
|
*
|
|
22
|
+
* Llámala en el TOP LEVEL del spec (no dentro de un `test` ni de un hook):
|
|
23
|
+
* Japa registra los grupos al importar el archivo y descarta los tardíos.
|
|
24
|
+
*
|
|
17
25
|
* `seedCatalog`/`cleanup` son del harness (no del contrato) porque cada
|
|
18
26
|
* backend materializa el catálogo a su manera (tablas propias, FGA model...).
|
|
27
|
+
*
|
|
28
|
+
* El ÁRBOL de scopes también es del harness (`makeTree`, por defecto
|
|
29
|
+
* `memoryScopeTree()`): la suite lo construye caso a caso con `tree.attach` y
|
|
30
|
+
* el driver lo ve a través de lo que el harness le inyecte (un
|
|
31
|
+
* `resolveChain` que lo camina, o hechos en el backend). Así el
|
|
32
|
+
* invariante 1 se prueba con cadenas reales y no con el resolutor plano.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Lo que un driver DECLARA poder hacer. Cada capacidad tiene en la suite un
|
|
36
|
+
* par de casos `{ whenTrue, whenFalse }` — nunca un `skip`. Hoy hay par para
|
|
37
|
+
* `truncationSignal: false` (con `exhaustiveLists`) y `hierarchyFacts: false`;
|
|
38
|
+
* los demás llegan con su fase, y mientras tanto declararlos `true` hace que
|
|
39
|
+
* la suite lance al registrarse: una promesa sin juez no pasa.
|
|
19
40
|
*/
|
|
41
|
+
export interface DriverCapabilities {
|
|
42
|
+
/** El driver materializa el árbol como hechos propios (Fase 3b). */
|
|
43
|
+
hierarchyFacts: boolean;
|
|
44
|
+
/** Acepta `{ trx }` externa en las escrituras (Fase 2.5). */
|
|
45
|
+
transactions: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Los `list*` lanzan si el backend trunca. Ningún driver del paquete trunca
|
|
48
|
+
* (L0.7 se cerró enumerando con `Read` paginado), así que no hay caso para
|
|
49
|
+
* `true`: es el par que un driver de terceros con backend con tope tendría
|
|
50
|
+
* que traer.
|
|
51
|
+
*/
|
|
52
|
+
truncationSignal: boolean;
|
|
53
|
+
/** `authorize` = una sola llamada al backend (modo facts, Fase 3b). */
|
|
54
|
+
singleCheckAuthorize: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* El driver acepta un reloj inyectado (2.5 · J1): `withClock(now)` en el
|
|
57
|
+
* puerto devuelve una vista que evalúa la caducidad con ESE `now` (SQL,
|
|
58
|
+
* `current_time` de FGA, filtros en cliente y los tres estados del
|
|
59
|
+
* re-grant). Con `true` el juez fija el instante y observa la caducidad
|
|
60
|
+
* exacta (T−1 ms concede, T no) sin dormir; con `false` solo puede
|
|
61
|
+
* observarla en tiempo real (el caso de los tres estados espera 1,5 s).
|
|
62
|
+
*/
|
|
63
|
+
injectableClock: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Los `list*` devuelven TODO sin tope. Es la verdad de los dos drivers del
|
|
66
|
+
* paquete: `database` (SQL sin límite) y `openfga` (enumeración por `Read`
|
|
67
|
+
* paginado, L0.7). Con `false` el harness debe dar `limits.listMaxResults`
|
|
68
|
+
* y el juez prueba solo la frontera exacta: es la declaración honesta de
|
|
69
|
+
* un driver de terceros cuyo backend trunca y que no lo señala.
|
|
70
|
+
*/
|
|
71
|
+
exhaustiveLists: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* El driver implementa el método opcional `listDenies` (2.1). Solo tiene
|
|
74
|
+
* casos en `level: '2.1'` (2E · I5): con `true` se juzgan `listDenies`,
|
|
75
|
+
* `effectivePermissions`, `authorizedScopes` y la versión compartida del
|
|
76
|
+
* catálogo; con `false` se juzga que esas primitivas lo digan (500
|
|
77
|
+
* `E_AUTHZ_UNSUPPORTED` nombrándolo) en vez de simular «sin denies». En un
|
|
78
|
+
* harness `core`/`'2.0'` no hay caso que lo observe: declara `false`.
|
|
79
|
+
*/
|
|
80
|
+
listDenies: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* `purgeRole(roleUuid)` está implementado de verdad (3B · B4): revoca
|
|
83
|
+
* todas las asignaciones del rol en todos los scopes, borra sus vínculos
|
|
84
|
+
* y el rol, atómicamente, y recrear el slug no revive nada. Solo tiene
|
|
85
|
+
* casos en `level: '2.2'`: con `true` se juzga la purga; con `false` se
|
|
86
|
+
* juzga que el driver lo DIGA (500 `E_AUTHZ_UNSUPPORTED`) y que no deje
|
|
87
|
+
* nada a medias — el driver `openfga` del paquete hasta 3b (`facts` +
|
|
88
|
+
* `reconcile`): sus bindings no son enumerables por rol sin leer el
|
|
89
|
+
* store entero. En un harness por debajo de `'2.2'` declara `false`.
|
|
90
|
+
*/
|
|
91
|
+
purgeRole: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* `countRoleAssignments(uuids)` está implementado de verdad (3b-2j): el
|
|
94
|
+
* driver sabe decir cuántos hechos VIGENTES tiene cada rol, que es lo que
|
|
95
|
+
* hace verdadero el `stillGranting` de `pruneOrphanRoles` —el campo que se
|
|
96
|
+
* lee justo antes de un borrado destructivo—. Con `true` se juzga el
|
|
97
|
+
* conteo (caducidad estricta, por posición, 422 por uuid mal formado); con
|
|
98
|
+
* `false`, que el barrido diga `undefined` y **nunca** `false`: «no lo sé»
|
|
99
|
+
* no puede degradar a «no concede». Solo tiene casos en `level: '2.2'`.
|
|
100
|
+
*
|
|
101
|
+
* **Es la capacidad que este lote estrena y es breaking**: un driver de
|
|
102
|
+
* terceros de 2.2 la declara `false` hasta que traiga el método.
|
|
103
|
+
*/
|
|
104
|
+
countRoleAssignments: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Las escrituras del CATÁLOGO se serializan de verdad en el motor (3E ·
|
|
107
|
+
* R2, tester): dos `defineScopedRole` del mismo `(slug, nivel)` en
|
|
108
|
+
* paralelo terminan con EXACTAMENTE uno confirmado y el otro con 422
|
|
109
|
+
* `E_AUTHZ_CATALOG_CONFLICT` — nunca un fallo de backend. Es lo que da el
|
|
110
|
+
* cerrojo sobre la fila de `authz_catalog_version` (3D · M2) en PostgreSQL
|
|
111
|
+
* y MySQL, y es la promesa que el README hace.
|
|
112
|
+
*
|
|
113
|
+
* Con `false` (SQLite, que serializa bloqueando la BASE ENTERA) el juez
|
|
114
|
+
* solo puede exigir lo innegociable: nunca dos ganadores y el perdedor no
|
|
115
|
+
* escribe — su transacción puede morir con `SQLITE_BUSY`, que es un 503
|
|
116
|
+
* legítimo. Declararlo `true` en un motor así convierte un flake en un
|
|
117
|
+
* fallo; declararlo `false` en PG/MySQL deja pasar un mutante que
|
|
118
|
+
* convierte la colisión en 503. Solo tiene casos en `level: '2.2'`.
|
|
119
|
+
*/
|
|
120
|
+
serializedCatalogWrites: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* El backend resuelve la MEMBRESÍA por sí mismo (3b-2e · E2). **`false` en
|
|
123
|
+
* los dos drivers del paquete, también en el modo `facts`** (panel 2, cruce
|
|
124
|
+
* 6): `hasRole`, `listRoles`, `listRoleScopes`, `listSubjects` y
|
|
125
|
+
* `listScopes` siguen usando el `resolveChain` del consumidor. Con `false`
|
|
126
|
+
* el juez lo DEMUESTRA con un espía sobre el árbol, que es lo que impide
|
|
127
|
+
* vender «sin SQL en el camino caliente» a secas: lo cierto es «sin SQL por
|
|
128
|
+
* request en `authorize`». La cara `true` no tiene caso todavía: no hay
|
|
129
|
+
* driver que lo cumpla.
|
|
130
|
+
*/
|
|
131
|
+
roleInheritanceNative: boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Los `list*` enumeran también lo HEREDADO (3b-2e · E2). **`false` siempre
|
|
134
|
+
* en este paquete** (invariante 7): enumerar descendientes sería abierto, y
|
|
135
|
+
* en `openfga` obligaría a `ListObjects`, que trunca al tope del servidor
|
|
136
|
+
* SIN señal (S16, y `Check` y `ListObjects` ni siquiera coinciden en
|
|
137
|
+
* profundidad). Con `false` el juez fija que un grant en el ANCESTRO no
|
|
138
|
+
* aparece en las listas del descendiente aunque `authorize` diga `true`.
|
|
139
|
+
*/
|
|
140
|
+
listObjectsInherited: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* **Las LECTURAS canonizan la ortografía del scope contra el árbol del
|
|
143
|
+
* consumidor antes de buscar los hechos** (3b-2k · K1 · R2 (c)). El árbol
|
|
144
|
+
* del consumidor puede fundir dos formas del mismo id —el tipo `uuid` de
|
|
145
|
+
* PostgreSQL y la collation `*_ci` de MySQL leen `bbbb…` con y sin guiones
|
|
146
|
+
* como la MISMA fila—, y `authz_*` compara por bytes.
|
|
147
|
+
*
|
|
148
|
+
* Con `true` (driver `database`) `authorize` resuelve la cadena y usa
|
|
149
|
+
* `chain[0]`, la identidad CANÓNICA (invariante 17): un alias que el árbol
|
|
150
|
+
* funde con la fila real encuentra exactamente los mismos hechos que la
|
|
151
|
+
* forma canónica.
|
|
152
|
+
*
|
|
153
|
+
* Con `false` (`openfga` en modo `facts`) la DECISIÓN no pasa por el árbol
|
|
154
|
+
* —es la contrapartida de `singleCheckAuthorize`— así que el objeto del
|
|
155
|
+
* store se compone con la ortografía del LLAMANTE y un alias no encuentra
|
|
156
|
+
* nada: `authorize` responde `false` donde la forma canónica concede. Es
|
|
157
|
+
* fail-**CLOSED** y no evade ningún deny, pero **no es la misma respuesta
|
|
158
|
+
* que `database`** y por eso se declara. Lo que NO cambia es la ESCRITURA:
|
|
159
|
+
* `grant`/`revoke`/`removeDeny`/`purgeScope` sí canonizan en los dos
|
|
160
|
+
* drivers (3b-2h · 🟠 3 cerró la cara fail-open), y el juez lo fija en las
|
|
161
|
+
* DOS caras del par para que nadie lea esta capacidad como «en `facts` la
|
|
162
|
+
* ortografía da igual».
|
|
163
|
+
*/
|
|
164
|
+
canonicalScopeReads: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* El driver sabe ser el **ORIGEN** de `authz:reconcile` (3b-3b):
|
|
167
|
+
* `enumerateFacts` entrega sus hechos por páginas, con su caducidad y **sin
|
|
168
|
+
* filtrar** (una asignación caducada tiene que LLEGAR para que el destino
|
|
169
|
+
* la cuente en `skipped`; filtrarla la haría desaparecer sin rastro).
|
|
170
|
+
*
|
|
171
|
+
* Con `true` el juez pasea las páginas, comprueba que el cursor avanza,
|
|
172
|
+
* que no se devuelven más de `limit` hechos y que la caducada sale con su
|
|
173
|
+
* instante. Con `false` juzga que el driver no puede ser origen y que
|
|
174
|
+
* `authz:reconcile` lo DICE —500 `E_AUTHZ_UNSUPPORTED` nombrando el
|
|
175
|
+
* método— en vez de leer cero hechos y vaciar el destino con `--prune`.
|
|
176
|
+
*
|
|
177
|
+
* `false` en el driver `database` a propósito: sus hechos son
|
|
178
|
+
* `authz_assignments`/`authz_denies`, el esquema publicado del paquete, y
|
|
179
|
+
* el destino los lee de ahí. Solo tiene casos en `level: '2.2'`.
|
|
180
|
+
*/
|
|
181
|
+
enumerateFacts: boolean;
|
|
182
|
+
}
|
|
183
|
+
export type ContractLevel = 'core' | '2.0' | '2.1' | '2.2';
|
|
20
184
|
export interface DriverContractHarness {
|
|
21
185
|
name: string;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
186
|
+
/** `core` (default): los casos de 1.x. `'2.0'`: además los de la Fase 0/1. `'2.1'`: además las primitivas de la Fase 2. `'2.2'`: además los roles locales a un scope (Fase 3). */
|
|
187
|
+
level?: ContractLevel;
|
|
188
|
+
capabilities: DriverCapabilities;
|
|
189
|
+
/** Tope de resultados del backend de test. Obligatorio con `exhaustiveLists: false`. */
|
|
190
|
+
limits?: {
|
|
191
|
+
listMaxResults?: number;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Materializa el catálogo del juez. Devuelve lo que quiera (3E: el
|
|
195
|
+
* `syncAuthzCatalog` del paquete devuelve su reporte): el juez no lo mira.
|
|
196
|
+
*/
|
|
197
|
+
seedCatalog(catalog: CatalogSpec): Promise<unknown>;
|
|
198
|
+
/** Árbol que usará la suite. Default: `memoryScopeTree()`. */
|
|
199
|
+
makeTree?(): Promise<ContractScopeTree>;
|
|
200
|
+
makeDriver(tree: ContractScopeTree): AuthorizationDriver | Promise<AuthorizationDriver>;
|
|
201
|
+
/**
|
|
202
|
+
* OTRA instancia del driver sobre el MISMO backend de hechos y con su
|
|
203
|
+
* propio memo del catálogo — lo que sería el mismo driver en otro proceso
|
|
204
|
+
* (2D · F1). Default: si el driver expone `catalog` (`CatalogCache`, los
|
|
205
|
+
* dos del paquete), una vista por prototipo con un memo nuevo; si no, el
|
|
206
|
+
* mismo driver (un driver que lea el catálogo en vivo pasa igual).
|
|
207
|
+
*/
|
|
208
|
+
makeTwin?(driver: AuthorizationDriver, tree: ContractScopeTree): AuthorizationDriver | Promise<AuthorizationDriver>;
|
|
209
|
+
cleanup(): Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Lo mínimo que el juez necesita del runner. En producción es el `test` de
|
|
213
|
+
* Japa; en `tests/contract_harness.spec.ts` es una API falsa que solo anota
|
|
214
|
+
* títulos, porque Japa no permite registrar grupos desde un test en marcha y
|
|
215
|
+
* la regla de capacidades hay que poder probarla.
|
|
216
|
+
*/
|
|
217
|
+
export interface ContractTestApi {
|
|
218
|
+
group(title: string, define: (group: {
|
|
219
|
+
each: {
|
|
220
|
+
setup(fn: () => Promise<void>): unknown;
|
|
221
|
+
};
|
|
222
|
+
teardown(fn: () => Promise<void>): unknown;
|
|
223
|
+
}) => void): unknown;
|
|
224
|
+
test(title: string, fn: (ctx: {
|
|
225
|
+
assert: Assert;
|
|
226
|
+
}) => Promise<void>): {
|
|
227
|
+
timeout(ms: number): unknown;
|
|
228
|
+
} | undefined | void;
|
|
25
229
|
}
|
|
26
230
|
export declare function runAuthorizationDriverContract(harness: DriverContractHarness): void;
|
|
231
|
+
export declare function registerAuthorizationDriverContract(harness: DriverContractHarness, api: ContractTestApi): void;
|
|
27
232
|
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/testing/contract.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/testing/contract.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAG1C,OAAO,KAAK,EACV,mBAAmB,EAEnB,WAAW,EAKZ,MAAM,aAAa,CAAA;AAQpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAA;IACvB,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,gBAAgB,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,eAAe,EAAE,OAAO,CAAA;IACxB;;;;;;OAMG;IACH,eAAe,EAAE,OAAO,CAAA;IACxB;;;;;;;OAOG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;;;;OASG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,oBAAoB,EAAE,OAAO,CAAA;IAC7B;;;;;;;;;;;;;;OAcG;IACH,uBAAuB,EAAE,OAAO,CAAA;IAChC;;;;;;;;;OASG;IACH,qBAAqB,EAAE,OAAO,CAAA;IAC9B;;;;;;;OAOG;IACH,oBAAoB,EAAE,OAAO,CAAA;IAC7B;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,mBAAmB,EAAE,OAAO,CAAA;IAC5B;;;;;;;;;;;;;;;OAeG;IACH,cAAc,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAE1D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,kLAAkL;IAClL,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,YAAY,EAAE,kBAAkB,CAAA;IAChC,wFAAwF;IACxF,MAAM,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACpC;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACnD,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACvC,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACvF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACnH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAoPD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CACH,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,KAAK,EAAE;QACd,IAAI,EAAE;YAAE,KAAK,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;SAAE,CAAA;QACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;KAC3C,KAAK,IAAI,GACT,OAAO,CAAA;IACV,IAAI,CACF,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C;QAAE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI,CAAA;CACvD;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qBAAqB,QAK5E;AAED,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,eAAe,QAo1HrB"}
|