@jantstack/adonis-authz 2.0.0-alpha.1 → 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/README.md +462 -35
- package/build/commands/authz_catalog_diff.js +1 -1
- package/build/commands/authz_catalog_diff.js.map +1 -1
- 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 +17 -0
- package/build/commands/authz_catalog_sync.d.ts.map +1 -1
- package/build/commands/authz_catalog_sync.js +27 -4
- package/build/commands/authz_catalog_sync.js.map +1 -1
- 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 +6 -1
- package/build/commands/main.d.ts.map +1 -1
- package/build/commands/main.js +6 -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 +90 -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 +39 -10
- package/build/index.d.ts.map +1 -1
- package/build/index.js +35 -6
- 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.d.ts → catalog/catalog.d.ts} +41 -2
- package/build/src/catalog/catalog.d.ts.map +1 -0
- package/build/src/{catalog.js → catalog/catalog.js} +120 -14
- package/build/src/catalog/catalog.js.map +1 -0
- package/build/src/{catalog_cache.d.ts → catalog/catalog_cache.d.ts} +46 -22
- package/build/src/catalog/catalog_cache.d.ts.map +1 -0
- package/build/src/{catalog_cache.js → catalog/catalog_cache.js} +53 -43
- package/build/src/catalog/catalog_cache.js.map +1 -0
- package/build/src/define_config.d.ts +101 -3
- 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 +86 -4
- package/build/src/drivers/database_driver.d.ts.map +1 -1
- package/build/src/drivers/database_driver.js +425 -11
- 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 +713 -119
- package/build/src/drivers/openfga_driver.d.ts.map +1 -1
- package/build/src/drivers/openfga_driver.js +2048 -476
- 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 +258 -5
- package/build/src/errors.d.ts.map +1 -1
- package/build/src/errors.js +238 -7
- package/build/src/errors.js.map +1 -1
- 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/http/app_access_middleware.d.ts.map +1 -0
- 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 +74 -1
- package/build/src/identity.d.ts.map +1 -1
- package/build/src/identity.js +100 -2
- package/build/src/identity.js.map +1 -1
- package/build/src/manager.d.ts +315 -4
- package/build/src/manager.d.ts.map +1 -1
- package/build/src/manager.js +1187 -181
- package/build/src/manager.js.map +1 -1
- package/build/src/models/authz_assignment.d.ts +6 -6
- package/build/src/models/authz_assignment.d.ts.map +1 -1
- package/build/src/models/authz_deny.d.ts +6 -6
- package/build/src/models/authz_deny.d.ts.map +1 -1
- package/build/src/models/authz_permission.d.ts +6 -6
- package/build/src/models/authz_permission.d.ts.map +1 -1
- package/build/src/models/authz_role.d.ts +6 -6
- package/build/src/models/authz_role.d.ts.map +1 -1
- package/build/src/models/authz_role_permission.d.ts +6 -6
- package/build/src/models/authz_role_permission.d.ts.map +1 -1
- package/build/src/openfga.d.ts +18 -2
- package/build/src/openfga.d.ts.map +1 -1
- package/build/src/openfga.js +15 -1
- package/build/src/openfga.js.map +1 -1
- 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/{drivers → shared}/backend_guard.d.ts +14 -0
- package/build/src/shared/backend_guard.d.ts.map +1 -0
- package/build/src/{drivers → shared}/backend_guard.js +26 -1
- package/build/src/shared/backend_guard.js.map +1 -0
- package/build/src/shared/sql_expiry.d.ts.map +1 -0
- package/build/src/shared/sql_expiry.js.map +1 -0
- package/build/src/sql_descendants.d.ts +47 -1
- package/build/src/sql_descendants.d.ts.map +1 -1
- package/build/src/sql_descendants.js +75 -1
- package/build/src/sql_descendants.js.map +1 -1
- package/build/src/testing/contract.d.ts +74 -0
- package/build/src/testing/contract.d.ts.map +1 -1
- package/build/src/testing/contract.js +672 -169
- package/build/src/testing/contract.js.map +1 -1
- package/build/src/testing/main.d.ts +6 -0
- package/build/src/testing/main.d.ts.map +1 -1
- package/build/src/testing/main.js +3 -0
- 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/traits/authz_scopes.js +1 -1
- package/build/src/traits/authz_scopes.js.map +1 -1
- package/build/src/traits/has_uuid.d.ts +7 -7
- package/build/src/traits/has_uuid.d.ts.map +1 -1
- package/build/src/types.d.ts +865 -82
- package/build/src/types.d.ts.map +1 -1
- package/build/src/types.js +10 -0
- package/build/src/types.js.map +1 -1
- package/build/stubs/config/authorization.stub +104 -4
- package/build/stubs/migration.stub +126 -0
- package/build/stubs/scopes_outbox_migration.stub +57 -0
- package/package.json +4 -2
- package/build/commands/openfga_import.d.ts +0 -34
- package/build/commands/openfga_import.d.ts.map +0 -1
- package/build/commands/openfga_import.js +0 -97
- package/build/commands/openfga_import.js.map +0 -1
- package/build/src/catalog.d.ts.map +0 -1
- package/build/src/catalog.js.map +0 -1
- package/build/src/catalog_cache.d.ts.map +0 -1
- package/build/src/catalog_cache.js.map +0 -1
- package/build/src/drivers/backend_guard.d.ts.map +0 -1
- package/build/src/drivers/backend_guard.js.map +0 -1
- package/build/src/drivers/sql_expiry.d.ts.map +0 -1
- package/build/src/drivers/sql_expiry.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
- /package/build/src/{middleware → http}/app_access_middleware.d.ts +0 -0
- /package/build/src/{middleware → http}/app_access_middleware.js +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.d.ts +0 -0
- /package/build/src/{drivers → shared}/sql_expiry.js +0 -0
|
@@ -40,7 +40,7 @@ export default class AuthzCatalogDiff extends BaseCommand {
|
|
|
40
40
|
this.exitCode = 1;
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
const { runCatalogDiff } = await import('../src/catalog.js');
|
|
43
|
+
const { runCatalogDiff } = await import('../src/catalog/catalog.js');
|
|
44
44
|
// Con el resolutor del config el diff puede juzgar además si dos roles
|
|
45
45
|
// LOCALES homónimos son visibles en la misma cadena (3D · M2 d).
|
|
46
46
|
const { inSync, lines } = await runCatalogDiff(catalogs, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz_catalog_diff.js","sourceRoot":"","sources":["../../commands/authz_catalog_diff.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,GAAG,oBAAoB,CAAA;IACzC,MAAM,CAAC,WAAW,GAAG,mFAAmF,CAAA;IAExG,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAA;IAQD,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAQ,CAAA;QAC1D,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAA;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,8FAA8F,CAC/F,CAAA;YACD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"authz_catalog_diff.js","sourceRoot":"","sources":["../../commands/authz_catalog_diff.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,GAAG,oBAAoB,CAAA;IACzC,MAAM,CAAC,WAAW,GAAG,mFAAmF,CAAA;IAExG,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAA;IAQD,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAQ,CAAA;QAC1D,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAA;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,8FAA8F,CAC/F,CAAA;YACD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAA;QACpE,uEAAuE;QACvE,iEAAiE;QACjE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE;YACvD,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY;YAC1C,aAAa,EAAE,IAAI,CAAC,aAAa,KAAK,IAAI;SAC3C,CAAC,CAAA;QACF,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC/C,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAA;YACpD,OAAM;QACR,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,qEAAqE,CAAC,CAAA;QACxF,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;IACnB,CAAC;;AA3BO;IAJP,KAAK,CAAC,OAAO,CAAC;QACb,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE,2FAA2F;KACzG,CAAC;;uDAC4B"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
/** Un huérfano tal como lo reporta `manager.pruneOrphanRoles()`. */
|
|
4
|
+
interface OrphanRow {
|
|
5
|
+
role: {
|
|
6
|
+
uuid: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
scopeType: string;
|
|
9
|
+
rank: number;
|
|
10
|
+
};
|
|
11
|
+
owner: {
|
|
12
|
+
type: string;
|
|
13
|
+
uuid?: string | null;
|
|
14
|
+
};
|
|
15
|
+
permissions: string[];
|
|
16
|
+
/** Hechos vigentes del rol, o `undefined` si el driver no sabe contarlos (3b-2j). */
|
|
17
|
+
assignments: number | undefined;
|
|
18
|
+
/** `true` concede, `false` no concede SEGURO, `undefined` **no se sabe** (3b-2j). */
|
|
19
|
+
stillGranting: boolean | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Las líneas del listado, en TRES cubos (3b-2j, decisión del dueño del
|
|
23
|
+
* 2026-08-31 (3) · consecuencia 2). Es una función pura para que el reparto
|
|
24
|
+
* —lo único del comando que decide algo— tenga su caso.
|
|
25
|
+
*
|
|
26
|
+
* - `stillGranting === false`: el driver ha contado sus hechos y son cero.
|
|
27
|
+
* Es el único cubo del que se puede decir «no concede»: línea normal.
|
|
28
|
+
* - `true`: concede hoy. Aparte y con aviso, desde 3b-0b · AA1.
|
|
29
|
+
* - `undefined`: el driver no implementa `countRoleAssignments`, así que
|
|
30
|
+
* NADIE lo sabe. Va con los anteriores —aparte y con aviso—, porque «no
|
|
31
|
+
* lo sé» no puede leerse como «no concede» justo antes de un borrado.
|
|
32
|
+
*/
|
|
33
|
+
export declare function orphanLines(orphans: OrphanRow[], purged: Set<string>, skipped: Set<string>): Array<{
|
|
34
|
+
level: 'log' | 'warning';
|
|
35
|
+
message: string;
|
|
36
|
+
}>;
|
|
37
|
+
/**
|
|
38
|
+
* Lista —y con `--force` borra— los roles LOCALES cuyo owner el árbol del
|
|
39
|
+
* consumidor YA NO conoce (3b-0 · Z2).
|
|
40
|
+
*
|
|
41
|
+
* Un rol así está DORMIDO, y «dormido» significa exactamente esto
|
|
42
|
+
* (3b-0b · AA1): no es visible desde ningún scope vivo cuya cadena NO pase
|
|
43
|
+
* por su owner. NO significa que no conceda: un descendiente vivo cuya ruta
|
|
44
|
+
* materializada siga pasando por el owner cumple la regla de visibilidad
|
|
45
|
+
* (invariante 18), y desde ahí el rol concede, es membresía y se puede
|
|
46
|
+
* asignar. Por eso este comando puede estar revocando permisos VIVOS, y por
|
|
47
|
+
* eso los huérfanos que aún tienen asignaciones vigentes se listan aparte,
|
|
48
|
+
* con aviso — y desde 3b-2j también los que el driver no sabe contar (un
|
|
49
|
+
* driver sin `countRoleAssignments`): «no lo sé» no es «no concede».
|
|
50
|
+
*
|
|
51
|
+
* Lo que el rol dormido hace en todo caso es ocupar su
|
|
52
|
+
* `(slug, nivel)` allí donde todavía se le vea, y `deleteScopedRole` no lo
|
|
53
|
+
* alcanza (resuelve el owner en fresco: 422 `E_AUTHZ_UNKNOWN_SCOPE`). Esta
|
|
54
|
+
* es su salida.
|
|
55
|
+
*
|
|
56
|
+
* node ace authz:catalog:prune-orphans # --dry-run: solo lista
|
|
57
|
+
* node ace authz:catalog:prune-orphans --force # purga de verdad
|
|
58
|
+
* node ace authz:catalog:prune-orphans --force --allow-mass-purge
|
|
59
|
+
*
|
|
60
|
+
* Es una operación de PLATAFORMA, no una escritura que dispare un tenant: no
|
|
61
|
+
* lleva actor ni mide rangos. Hasta 3G esta limpieza la arrastraba
|
|
62
|
+
* `scopes.detached` —que SÍ dispara un tenant, sobre un scope que ya no
|
|
63
|
+
* resuelve—, y de ahí salieron tres de las cuatro regresiones de la Fase 3.
|
|
64
|
+
*
|
|
65
|
+
* `--allow-mass-purge` (3b-0b · AA2) desbloquea la cota que rechaza una
|
|
66
|
+
* pasada con la firma de un resolutor ciego (todos los owners huérfanos, o
|
|
67
|
+
* más del 50 % de los roles locales): es una decisión humana, no un default.
|
|
68
|
+
*/
|
|
69
|
+
export default class AuthzCatalogPruneOrphans extends BaseCommand {
|
|
70
|
+
static commandName: string;
|
|
71
|
+
static description: string;
|
|
72
|
+
static options: CommandOptions;
|
|
73
|
+
force: boolean | undefined;
|
|
74
|
+
allowMassPurge: boolean | undefined;
|
|
75
|
+
run(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=authz_catalog_prune_orphans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_catalog_prune_orphans.d.ts","sourceRoot":"","sources":["../../commands/authz_catalog_prune_orphans.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD,oEAAoE;AACpE,UAAU,SAAS;IACjB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC7C,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,qFAAqF;IACrF,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;IAC/B,qFAAqF;IACrF,aAAa,EAAE,OAAO,GAAG,SAAS,CAAA;CACnC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,EAAE,EACpB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,EACnB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GACnB,KAAK,CAAC;IAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCtD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,OAAO,OAAO,wBAAyB,SAAQ,WAAW;IAC/D,MAAM,CAAC,WAAW,SAAgC;IAClD,MAAM,CAAC,WAAW,SAAkF;IAEpG,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAM1B,cAAc,EAAE,OAAO,GAAG,SAAS,CAAA;IAErC,GAAG;CAuCV"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
|
+
/**
|
|
12
|
+
* Las líneas del listado, en TRES cubos (3b-2j, decisión del dueño del
|
|
13
|
+
* 2026-08-31 (3) · consecuencia 2). Es una función pura para que el reparto
|
|
14
|
+
* —lo único del comando que decide algo— tenga su caso.
|
|
15
|
+
*
|
|
16
|
+
* - `stillGranting === false`: el driver ha contado sus hechos y son cero.
|
|
17
|
+
* Es el único cubo del que se puede decir «no concede»: línea normal.
|
|
18
|
+
* - `true`: concede hoy. Aparte y con aviso, desde 3b-0b · AA1.
|
|
19
|
+
* - `undefined`: el driver no implementa `countRoleAssignments`, así que
|
|
20
|
+
* NADIE lo sabe. Va con los anteriores —aparte y con aviso—, porque «no
|
|
21
|
+
* lo sé» no puede leerse como «no concede» justo antes de un borrado.
|
|
22
|
+
*/
|
|
23
|
+
export function orphanLines(orphans, purged, skipped) {
|
|
24
|
+
const estado = (o) => skipped.has(o.role.uuid) ? 'saltado (el owner volvió)' : purged.has(o.role.uuid) ? 'purgado' : 'huérfano';
|
|
25
|
+
const linea = (o) => `${o.role.slug}@${o.role.scopeType} (uuid ${o.role.uuid}, rank ${o.role.rank}) — owner ` +
|
|
26
|
+
`${o.owner.type}:${o.owner.uuid ?? ''} ya no existe en el árbol; permisos: ${o.permissions.join(', ') || '—'}` +
|
|
27
|
+
(o.stillGranting === true ? `; asignaciones VIGENTES: ${o.assignments}` : '');
|
|
28
|
+
const lines = [];
|
|
29
|
+
for (const o of orphans.filter((x) => x.stillGranting === false)) {
|
|
30
|
+
lines.push({ level: 'log', message: `${estado(o)}: ${linea(o)}` });
|
|
31
|
+
}
|
|
32
|
+
const vivos = orphans.filter((x) => x.stillGranting === true);
|
|
33
|
+
for (const o of vivos)
|
|
34
|
+
lines.push({ level: 'warning', message: `${estado(o)} · TODAVÍA CONCEDE: ${linea(o)}` });
|
|
35
|
+
const dudosos = orphans.filter((x) => x.stillGranting === undefined);
|
|
36
|
+
for (const o of dudosos) {
|
|
37
|
+
lines.push({ level: 'warning', message: `${estado(o)} · NO SE SABE SI CONCEDE: ${linea(o)}` });
|
|
38
|
+
}
|
|
39
|
+
if (vivos.length > 0) {
|
|
40
|
+
lines.push({
|
|
41
|
+
level: 'warning',
|
|
42
|
+
message: `${vivos.length} rol(es) huérfano(s) siguen teniendo asignaciones vigentes: un rol dormido NO deja de conceder si ` +
|
|
43
|
+
`algún scope vivo conserva al owner en su cadena (rutas materializadas, borrado en dos pasos). Purgarlos revoca ` +
|
|
44
|
+
`permisos que hoy funcionan.`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (dudosos.length > 0) {
|
|
48
|
+
lines.push({
|
|
49
|
+
level: 'warning',
|
|
50
|
+
message: `${dudosos.length} rol(es) huérfano(s) SIN demostración de que no concedan: el driver configurado no implementa ` +
|
|
51
|
+
`'countRoleAssignments' (el método del puerto que cuenta los hechos vigentes de un rol), así que nadie sabe si ` +
|
|
52
|
+
`están concediendo. Se listan aparte a propósito: 'no lo sé' no es 'no concede', y --force los borra igual.`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return lines;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Lista —y con `--force` borra— los roles LOCALES cuyo owner el árbol del
|
|
59
|
+
* consumidor YA NO conoce (3b-0 · Z2).
|
|
60
|
+
*
|
|
61
|
+
* Un rol así está DORMIDO, y «dormido» significa exactamente esto
|
|
62
|
+
* (3b-0b · AA1): no es visible desde ningún scope vivo cuya cadena NO pase
|
|
63
|
+
* por su owner. NO significa que no conceda: un descendiente vivo cuya ruta
|
|
64
|
+
* materializada siga pasando por el owner cumple la regla de visibilidad
|
|
65
|
+
* (invariante 18), y desde ahí el rol concede, es membresía y se puede
|
|
66
|
+
* asignar. Por eso este comando puede estar revocando permisos VIVOS, y por
|
|
67
|
+
* eso los huérfanos que aún tienen asignaciones vigentes se listan aparte,
|
|
68
|
+
* con aviso — y desde 3b-2j también los que el driver no sabe contar (un
|
|
69
|
+
* driver sin `countRoleAssignments`): «no lo sé» no es «no concede».
|
|
70
|
+
*
|
|
71
|
+
* Lo que el rol dormido hace en todo caso es ocupar su
|
|
72
|
+
* `(slug, nivel)` allí donde todavía se le vea, y `deleteScopedRole` no lo
|
|
73
|
+
* alcanza (resuelve el owner en fresco: 422 `E_AUTHZ_UNKNOWN_SCOPE`). Esta
|
|
74
|
+
* es su salida.
|
|
75
|
+
*
|
|
76
|
+
* node ace authz:catalog:prune-orphans # --dry-run: solo lista
|
|
77
|
+
* node ace authz:catalog:prune-orphans --force # purga de verdad
|
|
78
|
+
* node ace authz:catalog:prune-orphans --force --allow-mass-purge
|
|
79
|
+
*
|
|
80
|
+
* Es una operación de PLATAFORMA, no una escritura que dispare un tenant: no
|
|
81
|
+
* lleva actor ni mide rangos. Hasta 3G esta limpieza la arrastraba
|
|
82
|
+
* `scopes.detached` —que SÍ dispara un tenant, sobre un scope que ya no
|
|
83
|
+
* resuelve—, y de ahí salieron tres de las cuatro regresiones de la Fase 3.
|
|
84
|
+
*
|
|
85
|
+
* `--allow-mass-purge` (3b-0b · AA2) desbloquea la cota que rechaza una
|
|
86
|
+
* pasada con la firma de un resolutor ciego (todos los owners huérfanos, o
|
|
87
|
+
* más del 50 % de los roles locales): es una decisión humana, no un default.
|
|
88
|
+
*/
|
|
89
|
+
export default class AuthzCatalogPruneOrphans extends BaseCommand {
|
|
90
|
+
static commandName = 'authz:catalog:prune-orphans';
|
|
91
|
+
static description = 'List (or, with --force, purge) local roles whose owner scope no longer exists';
|
|
92
|
+
static options = {
|
|
93
|
+
startApp: true,
|
|
94
|
+
};
|
|
95
|
+
async run() {
|
|
96
|
+
const { default: authorization } = await import('../services/main.js');
|
|
97
|
+
const { orphans, purged, skipped, massPurge, dryRun } = await authorization.pruneOrphanRoles({
|
|
98
|
+
force: this.force === true,
|
|
99
|
+
allowMassPurge: this.allowMassPurge === true,
|
|
100
|
+
});
|
|
101
|
+
// Los que todavía conceden —y los que NADIE puede demostrar que no
|
|
102
|
+
// concedan— van aparte, con aviso: purgarlos es revocar permisos vivos,
|
|
103
|
+
// no recoger basura (3b-0b · AA1 + 3b-2j).
|
|
104
|
+
for (const { level, message } of orphanLines(orphans, new Set(purged.map((role) => role.uuid)), new Set(skipped.map(({ role }) => role.uuid)))) {
|
|
105
|
+
this.logger[level](message);
|
|
106
|
+
}
|
|
107
|
+
if (orphans.length === 0) {
|
|
108
|
+
this.logger.success('No hay roles locales huérfanos: todos los owners resuelven.');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (dryRun) {
|
|
112
|
+
if (massPurge) {
|
|
113
|
+
this.logger.warning(`${orphans.length} de los roles locales quedarían huérfanos: eso es la firma de un 'scopes.resolveChain' ciego. ` +
|
|
114
|
+
`Con --force esto se rechaza (E_AUTHZ_MASS_PURGE_REFUSED); comprueba el resolutor antes de añadir --allow-mass-purge.`);
|
|
115
|
+
}
|
|
116
|
+
this.logger.warning(`${orphans.length} rol(es) local(es) con el owner fuera del árbol. No se ha borrado nada: repite con --force.`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (skipped.length > 0) {
|
|
120
|
+
this.logger.warning(`${skipped.length} rol(es) saltado(s): su owner volvió al árbol durante la pasada.`);
|
|
121
|
+
}
|
|
122
|
+
this.logger.success(`${purged.length} rol(es) local(es) huérfano(s) purgado(s).`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
__decorate([
|
|
126
|
+
flags.boolean({ description: 'Actually purge the orphan roles (default is a dry run)' }),
|
|
127
|
+
__metadata("design:type", Object)
|
|
128
|
+
], AuthzCatalogPruneOrphans.prototype, "force", void 0);
|
|
129
|
+
__decorate([
|
|
130
|
+
flags.boolean({
|
|
131
|
+
name: 'allow-mass-purge',
|
|
132
|
+
description: 'Allow a pass that would purge every owner (or more than half the local roles) — check your resolveChain first',
|
|
133
|
+
}),
|
|
134
|
+
__metadata("design:type", Object)
|
|
135
|
+
], AuthzCatalogPruneOrphans.prototype, "allowMassPurge", void 0);
|
|
136
|
+
//# sourceMappingURL=authz_catalog_prune_orphans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_catalog_prune_orphans.js","sourceRoot":"","sources":["../../commands/authz_catalog_prune_orphans.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
2
|
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
import type { AuthorizationDriver } from '../src/types.js';
|
|
4
|
+
import type { SyncCatalogOptions } from '../src/catalog/catalog.js';
|
|
5
|
+
/**
|
|
6
|
+
* Las opciones con las que este comando sincroniza, en una función PURA
|
|
7
|
+
* (mismo patrón que `reconcileLines`/`unfreezePlan`): es la única decisión
|
|
8
|
+
* del comando y así tiene su caso sin montar un ace.
|
|
9
|
+
*
|
|
10
|
+
* **La proyección del driver ACTIVO viaja con el sync** (3b-8 · A1). En modo
|
|
11
|
+
* `facts` el store guarda un espejo del catálogo (`role:<uuid>#permits_<P>`)
|
|
12
|
+
* y el README promete que este comando lo reescribe — es el camino de
|
|
13
|
+
* recuperación documentado. Hasta 3b-8 el comando llamaba a `syncCatalogs`
|
|
14
|
+
* SIN la proyección: `authz_*` quedaba bien y el espejo sin tocar, o sea que
|
|
15
|
+
* un permiso quitado del catálogo SEGUÍA concediendo (fail-open) y un rol
|
|
16
|
+
* nuevo no concedía nada. Un driver sin espejo (`database`) no declara
|
|
17
|
+
* `catalogProjection` y el sync va sin ella, como siempre.
|
|
18
|
+
*/
|
|
19
|
+
export declare function catalogSyncOptions(driver: AuthorizationDriver, keepLinks: boolean | undefined): SyncCatalogOptions;
|
|
3
20
|
/**
|
|
4
21
|
* Sincroniza a las tablas `authz_*` los catálogos declarados en
|
|
5
22
|
* `config/authorization.ts` (`catalogs`), en orden. Transaccional por
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz_catalog_sync.d.ts","sourceRoot":"","sources":["../../commands/authz_catalog_sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"authz_catalog_sync.d.ts","sourceRoot":"","sources":["../../commands/authz_catalog_sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAEnE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,OAAO,GAAG,SAAS,GAC7B,kBAAkB,CAOpB;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,SAAuB;IACzC,MAAM,CAAC,WAAW,SAAiE;IAEnF,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,SAAS,EAAE,OAAO,GAAG,SAAS,CAAA;IAEhC,GAAG;CAuCV"}
|
|
@@ -8,6 +8,28 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
|
+
/**
|
|
12
|
+
* Las opciones con las que este comando sincroniza, en una función PURA
|
|
13
|
+
* (mismo patrón que `reconcileLines`/`unfreezePlan`): es la única decisión
|
|
14
|
+
* del comando y así tiene su caso sin montar un ace.
|
|
15
|
+
*
|
|
16
|
+
* **La proyección del driver ACTIVO viaja con el sync** (3b-8 · A1). En modo
|
|
17
|
+
* `facts` el store guarda un espejo del catálogo (`role:<uuid>#permits_<P>`)
|
|
18
|
+
* y el README promete que este comando lo reescribe — es el camino de
|
|
19
|
+
* recuperación documentado. Hasta 3b-8 el comando llamaba a `syncCatalogs`
|
|
20
|
+
* SIN la proyección: `authz_*` quedaba bien y el espejo sin tocar, o sea que
|
|
21
|
+
* un permiso quitado del catálogo SEGUÍA concediendo (fail-open) y un rol
|
|
22
|
+
* nuevo no concedía nada. Un driver sin espejo (`database`) no declara
|
|
23
|
+
* `catalogProjection` y el sync va sin ella, como siempre.
|
|
24
|
+
*/
|
|
25
|
+
export function catalogSyncOptions(driver, keepLinks) {
|
|
26
|
+
return {
|
|
27
|
+
prune: keepLinks ? 'none' : 'links',
|
|
28
|
+
...(typeof driver.catalogProjection === 'function'
|
|
29
|
+
? { projection: driver.catalogProjection() }
|
|
30
|
+
: {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
11
33
|
/**
|
|
12
34
|
* Sincroniza a las tablas `authz_*` los catálogos declarados en
|
|
13
35
|
* `config/authorization.ts` (`catalogs`), en orden. Transaccional por
|
|
@@ -32,10 +54,11 @@ export default class AuthzCatalogSync extends BaseCommand {
|
|
|
32
54
|
this.exitCode = 1;
|
|
33
55
|
return;
|
|
34
56
|
}
|
|
35
|
-
const { syncCatalogs } = await import('../src/catalog.js');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
57
|
+
const { syncCatalogs } = await import('../src/catalog/catalog.js');
|
|
58
|
+
// El driver ACTIVO decide si el sync lleva proyección (3b-8 · A1): en
|
|
59
|
+
// `facts` sin ella este comando NO reescribía el espejo del store.
|
|
60
|
+
const { default: authorization } = await import('../services/main.js');
|
|
61
|
+
const { count, shadowedByGlobal, assignableAtViolations } = await syncCatalogs(catalogs, catalogSyncOptions(await authorization.driver(), this.keepLinks));
|
|
39
62
|
// 3E · P1 b / P6: el sync ya no aborta por lo que un tenant hizo en su
|
|
40
63
|
// scope, pero tampoco lo silencia. Un despliegue que ensombrece roles de
|
|
41
64
|
// tenants —o que deja vínculos fuera del `assignableAt` nuevo— tiene que
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authz_catalog_sync.js","sourceRoot":"","sources":["../../commands/authz_catalog_sync.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"authz_catalog_sync.js","sourceRoot":"","sources":["../../commands/authz_catalog_sync.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAKvD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA2B,EAC3B,SAA8B;IAE9B,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;QACnC,GAAG,CAAC,OAAO,MAAM,CAAC,iBAAiB,KAAK,UAAU;YAChD,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,iBAAiB,EAAE,EAAE;YAC5C,CAAC,CAAC,EAAE,CAAC;KACR,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW;IACvD,MAAM,CAAC,WAAW,GAAG,oBAAoB,CAAA;IACzC,MAAM,CAAC,WAAW,GAAG,8DAA8D,CAAA;IAEnF,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAA;IAKD,KAAK,CAAC,GAAG;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAQ,CAAA;QAC1D,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAA;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,8FAA8F,CAC/F,CAAA;YACD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAA;QAClE,sEAAsE;QACtE,mEAAmE;QACnE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAA;QACtE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,GAAG,MAAM,YAAY,CAC5E,QAAQ,EACR,kBAAkB,CAAC,MAAM,aAAa,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CACjE,CAAA;QACD,uEAAuE;QACvE,yEAAyE;QACzE,yEAAyE;QACzE,oDAAoD;QACpD,KAAK,MAAM,IAAI,IAAI,gBAAgB,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,OAAO,CACjB,kDAAkD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,UAAU,IAAI,CAAC,IAAI,WAAW,IAAI,CAAC,KAAK,KAAK;gBACxH,kGAAkG,CACrG,CAAA;QACH,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,sBAAsB,EAAE,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,CACjB,kCAAkC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,WAAW,SAAS,CAAC,IAAI,CAAC,KAAK,MAAM;gBACpH,GAAG,SAAS,CAAC,UAAU,uFAAuF,CACjH,CAAA;QACH,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,OAAO,CACjB,GAAG,KAAK,8BAA8B,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,GAAG,CAAC,CAC7F,CAAA;IACH,CAAC;;AAxCO;IADP,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;;mDACrD"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
/**
|
|
4
|
+
* **Abre la ventana de CUTOVER: congela las escrituras del motor** (3b-7;
|
|
5
|
+
* condición explícita del juez del panel 3 — el intervalo entre la última
|
|
6
|
+
* pasada de `authz:reconcile` y el cambio de `config.default` es donde
|
|
7
|
+
* ocurre la pérdida, lo decide un humano y dura minutos u horas).
|
|
8
|
+
*
|
|
9
|
+
* node ace authz:freeze --reason="cutover a openfga"
|
|
10
|
+
* node ace authz:freeze --reason="cutover" --lease-ms=600000 # opt-in: caduca sola
|
|
11
|
+
* … node ace authz:reconcile --to=openfga --from=database … # corre DENTRO de la ventana
|
|
12
|
+
* … cambiar config.default y redesplegar …
|
|
13
|
+
* node ace authz:unfreeze
|
|
14
|
+
*
|
|
15
|
+
* Mientras la ventana está abierta, toda ESCRITURA del manager —en TODOS los
|
|
16
|
+
* procesos que comparten `authz_*`— es 503 `E_AUTHZ_FROZEN` reintentable;
|
|
17
|
+
* las lecturas siguen. `authz:reconcile` reconoce esta ventana como contexto
|
|
18
|
+
* propio: corre dentro sin tomar otro freeze y NO la levanta al terminar —
|
|
19
|
+
* cerrar el intervalo pasada→cutover es exactamente para lo que existe.
|
|
20
|
+
*
|
|
21
|
+
* **La ventana del operador NO caduca por defecto** (decisión de este lote,
|
|
22
|
+
* justificada en el informe): un cutover no tiene duración conocida de
|
|
23
|
+
* antemano —duración de la ventana y tolerancia a la parada son magnitudes
|
|
24
|
+
* independientes, el argumento que tumbó el TTL fijo— y una ventana que
|
|
25
|
+
* caduca en mitad del cutover devuelve en SILENCIO el fail-open que este
|
|
26
|
+
* mecanismo existe para cerrar. El precio, declarado: si te olvidas de
|
|
27
|
+
* `authz:unfreeze`, la flota no escribe hasta que alguien lo ejecute — pero
|
|
28
|
+
* es un incidente RUIDOSO (cada 503 nombra el motivo y el comando que lo
|
|
29
|
+
* levanta), no una pérdida invisible. `--lease-ms` es el opt-in contrario:
|
|
30
|
+
* la ventana caduca sola pasados esos ms (no hay proceso que renueve: este
|
|
31
|
+
* comando termina), con SU contra declarado — si el cutover tarda más que el
|
|
32
|
+
* lease, los escritores vuelven SOLOS y sin aviso a mitad del cutover.
|
|
33
|
+
*
|
|
34
|
+
* Operación de PLATAFORMA: sin actor, sin rangos, no se expone por HTTP.
|
|
35
|
+
*/
|
|
36
|
+
export default class AuthzFreeze extends BaseCommand {
|
|
37
|
+
static commandName: string;
|
|
38
|
+
static description: string;
|
|
39
|
+
static options: CommandOptions;
|
|
40
|
+
reason: string | undefined;
|
|
41
|
+
leaseMs: number | undefined;
|
|
42
|
+
run(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=authz_freeze.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_freeze.d.ts","sourceRoot":"","sources":["../../commands/authz_freeze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,MAAM,CAAC,WAAW,SAAiB;IACnC,MAAM,CAAC,WAAW,SAAuG;IAEzH,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAO1B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IAE7B,GAAG;CAuCV"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
|
+
/**
|
|
12
|
+
* **Abre la ventana de CUTOVER: congela las escrituras del motor** (3b-7;
|
|
13
|
+
* condición explícita del juez del panel 3 — el intervalo entre la última
|
|
14
|
+
* pasada de `authz:reconcile` y el cambio de `config.default` es donde
|
|
15
|
+
* ocurre la pérdida, lo decide un humano y dura minutos u horas).
|
|
16
|
+
*
|
|
17
|
+
* node ace authz:freeze --reason="cutover a openfga"
|
|
18
|
+
* node ace authz:freeze --reason="cutover" --lease-ms=600000 # opt-in: caduca sola
|
|
19
|
+
* … node ace authz:reconcile --to=openfga --from=database … # corre DENTRO de la ventana
|
|
20
|
+
* … cambiar config.default y redesplegar …
|
|
21
|
+
* node ace authz:unfreeze
|
|
22
|
+
*
|
|
23
|
+
* Mientras la ventana está abierta, toda ESCRITURA del manager —en TODOS los
|
|
24
|
+
* procesos que comparten `authz_*`— es 503 `E_AUTHZ_FROZEN` reintentable;
|
|
25
|
+
* las lecturas siguen. `authz:reconcile` reconoce esta ventana como contexto
|
|
26
|
+
* propio: corre dentro sin tomar otro freeze y NO la levanta al terminar —
|
|
27
|
+
* cerrar el intervalo pasada→cutover es exactamente para lo que existe.
|
|
28
|
+
*
|
|
29
|
+
* **La ventana del operador NO caduca por defecto** (decisión de este lote,
|
|
30
|
+
* justificada en el informe): un cutover no tiene duración conocida de
|
|
31
|
+
* antemano —duración de la ventana y tolerancia a la parada son magnitudes
|
|
32
|
+
* independientes, el argumento que tumbó el TTL fijo— y una ventana que
|
|
33
|
+
* caduca en mitad del cutover devuelve en SILENCIO el fail-open que este
|
|
34
|
+
* mecanismo existe para cerrar. El precio, declarado: si te olvidas de
|
|
35
|
+
* `authz:unfreeze`, la flota no escribe hasta que alguien lo ejecute — pero
|
|
36
|
+
* es un incidente RUIDOSO (cada 503 nombra el motivo y el comando que lo
|
|
37
|
+
* levanta), no una pérdida invisible. `--lease-ms` es el opt-in contrario:
|
|
38
|
+
* la ventana caduca sola pasados esos ms (no hay proceso que renueve: este
|
|
39
|
+
* comando termina), con SU contra declarado — si el cutover tarda más que el
|
|
40
|
+
* lease, los escritores vuelven SOLOS y sin aviso a mitad del cutover.
|
|
41
|
+
*
|
|
42
|
+
* Operación de PLATAFORMA: sin actor, sin rangos, no se expone por HTTP.
|
|
43
|
+
*/
|
|
44
|
+
export default class AuthzFreeze extends BaseCommand {
|
|
45
|
+
static commandName = 'authz:freeze';
|
|
46
|
+
static description = 'Open the cutover window: freeze every engine write (503 retryable) fleet-wide until authz:unfreeze';
|
|
47
|
+
static options = {
|
|
48
|
+
startApp: true,
|
|
49
|
+
};
|
|
50
|
+
async run() {
|
|
51
|
+
if (!this.reason) {
|
|
52
|
+
this.logger.error("Falta --reason: 'node ace authz:freeze --reason=\"cutover a openfga\"'. Cada escritura rechazada se lo " +
|
|
53
|
+
'enseña a quien la intentó: una ventana sin motivo es un 503 misterioso para todos tus admins.');
|
|
54
|
+
this.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const { default: authorization } = await import('../services/main.js');
|
|
58
|
+
try {
|
|
59
|
+
const token = await authorization.freeze(this.reason, {
|
|
60
|
+
kind: 'operator',
|
|
61
|
+
leaseMs: this.leaseMs === undefined ? null : this.leaseMs,
|
|
62
|
+
});
|
|
63
|
+
this.logger.success(`Ventana de cutover ABIERTA (fence ${token.fence}): las escrituras del motor responden 503 reintentable ` +
|
|
64
|
+
`en toda la flota; las lecturas siguen.`);
|
|
65
|
+
if (this.leaseMs === undefined) {
|
|
66
|
+
this.logger.info('La ventana NO caduca sola: ciérrala con `node ace authz:unfreeze` cuando el cutover termine.');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.logger.warning(`La ventana caduca SOLA en ${this.leaseMs} ms y nadie la renueva: si el cutover tarda más, ` +
|
|
70
|
+
'los escritores vuelven sin aviso. Ciérrala antes con `node ace authz:unfreeze`.');
|
|
71
|
+
}
|
|
72
|
+
this.logger.info('Lo que la ventana NO congela: syncAuthzCatalog, manager.driver() y el árbol SQL del consumidor.');
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
if (error?.code === 'E_AUTHZ_FREEZE_HELD') {
|
|
76
|
+
this.logger.error(error.message);
|
|
77
|
+
this.exitCode = 1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
__decorate([
|
|
85
|
+
flags.string({ description: 'Why the window is open — every rejected write shows it to its caller' }),
|
|
86
|
+
__metadata("design:type", Object)
|
|
87
|
+
], AuthzFreeze.prototype, "reason", void 0);
|
|
88
|
+
__decorate([
|
|
89
|
+
flags.number({
|
|
90
|
+
name: 'lease-ms',
|
|
91
|
+
description: 'Opt-in expiry: the window lifts ITSELF after this many ms (nobody renews it — beware a cutover slower than the lease)',
|
|
92
|
+
}),
|
|
93
|
+
__metadata("design:type", Object)
|
|
94
|
+
], AuthzFreeze.prototype, "leaseMs", void 0);
|
|
95
|
+
//# sourceMappingURL=authz_freeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_freeze.js","sourceRoot":"","sources":["../../commands/authz_freeze.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,MAAM,CAAC,WAAW,GAAG,cAAc,CAAA;IACnC,MAAM,CAAC,WAAW,GAAG,oGAAoG,CAAA;IAEzH,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,IAAI;KACf,CAAA;IAYD,KAAK,CAAC,GAAG;QACP,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,yGAAyG;gBACvG,+FAA+F,CAClG,CAAA;YACD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAA;QACtE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE;gBACpD,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO;aAC1D,CAAC,CAAA;YACF,IAAI,CAAC,MAAM,CAAC,OAAO,CACjB,qCAAqC,KAAK,CAAC,KAAK,yDAAyD;gBACvG,wCAAwC,CAC3C,CAAA;YACD,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8FAA8F,CAAC,CAAA;YAClH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,OAAO,CACjB,6BAA6B,IAAI,CAAC,OAAO,mDAAmD;oBAC1F,iFAAiF,CACpF,CAAA;YACH,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,iGAAiG,CAClG,CAAA;QACH,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,KAAK,EAAE,IAAI,KAAK,qBAAqB,EAAE,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBAChC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;gBACjB,OAAM;YACR,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;;AA/CO;IADP,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;;2CACpE;AAO1B;IALP,KAAK,CAAC,MAAM,CAAC;QACZ,IAAI,EAAE,UAAU;QAChB,WAAW,EACT,uHAAuH;KAC1H,CAAC;;4CACiC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
import type { ReconcileReport } from '../src/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Las líneas del reporte y si la pasada es «limpia», en una función PURA
|
|
6
|
+
* (mismo patrón que `orphanLines`): es lo único del comando que decide algo,
|
|
7
|
+
* y así tiene su caso sin montar un ace.
|
|
8
|
+
*
|
|
9
|
+
* Qué cuenta como deriva —y por tanto como exit ≠ 0—:
|
|
10
|
+
* - en `--dry-run`, **cualquier cosa que la pasada haría** (escribir,
|
|
11
|
+
* actualizar o borrar): es el verificador, y para eso está en CI;
|
|
12
|
+
* - siempre, lo que una pasada NO puede arreglar sola: **ciclos** en el
|
|
13
|
+
* árbol del consumidor, **entradas aparcadas** de la outbox y hechos que
|
|
14
|
+
* se quedan fuera (`skipped`) — incluidos los del scope que ya no
|
|
15
|
+
* resuelve, que solo se van con `--prune`.
|
|
16
|
+
*
|
|
17
|
+
* La ventana del relay (`pendingRelay`) se AVISA pero no tumba la pasada: es
|
|
18
|
+
* una ventana de segundos por diseño, no una divergencia. Y **`expired`
|
|
19
|
+
* tampoco tumba** (3b-8 · B2): es la pérdida DECLARADA de la migración, no
|
|
20
|
+
* concede en ningún driver y nada barre las filas caducadas del origen, así
|
|
21
|
+
* que contarla como deriva dejaba el exit 1 clavado para siempre a la
|
|
22
|
+
* primera caducidad real.
|
|
23
|
+
*/
|
|
24
|
+
export declare function reconcileLines(report: ReconcileReport): {
|
|
25
|
+
lines: Array<{
|
|
26
|
+
level: 'log' | 'warning' | 'error' | 'success';
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
29
|
+
clean: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* **Migra —y verifica— entre drivers** (3b-3a). Es la única primitiva de
|
|
33
|
+
* migración del paquete: `openfga:import` se borró en 3b-2k · K2 porque
|
|
34
|
+
* llenaba el store con las tuplas de un modelo que ya no existe.
|
|
35
|
+
*
|
|
36
|
+
* node ace authz:reconcile --to=openfga --dry-run # el VERIFICADOR (CI)
|
|
37
|
+
* node ace authz:reconcile --to=openfga # migra, o MANTIENE (ver abajo)
|
|
38
|
+
* node ace authz:reconcile --to=openfga --prune # y borra lo que el origen ya no respalda
|
|
39
|
+
*
|
|
40
|
+
* `--to` nombra una clave de `drivers` en `config/authorization.ts`, no el
|
|
41
|
+
* driver activo: migrar es llenar el destino mientras el motor sigue
|
|
42
|
+
* corriendo con el otro.
|
|
43
|
+
*
|
|
44
|
+
* **De dónde salen los HECHOS** (3b-5): de quien sea su fuente de verdad, y
|
|
45
|
+
* la pasada lo dice en su primera línea (`report.factsFrom`).
|
|
46
|
+
* - Si `--to` es el driver ACTIVO y sus hechos viven en su backend
|
|
47
|
+
* (`capabilities.hierarchyFacts`, el `openfga` de esta versión), los
|
|
48
|
+
* hechos son los SUYOS: la pasada es de MANTENIMIENTO —rehace lo derivado
|
|
49
|
+
* y aplica el barrido de visibilidad del invariante 18— y no escribe ni
|
|
50
|
+
* borra un solo hecho. `authz_assignments`/`authz_denies` no son su
|
|
51
|
+
* fuente: después de un cutover a `facts` están congeladas, y
|
|
52
|
+
* reconstruir desde ellas resucitaba lo revocado y —con `--prune`—
|
|
53
|
+
* borraba los denies vivos.
|
|
54
|
+
* - Con `--from=<driver>` mandas tú: si ese driver es el `database` del
|
|
55
|
+
* paquete, los hechos son sus tablas y `--to=openfga` es la MIGRACIÓN de
|
|
56
|
+
* un solo sentido de siempre.
|
|
57
|
+
*
|
|
58
|
+
* Qué migra hacia `openfga`: el marcador de raíz, la proyección del catálogo,
|
|
59
|
+
* el árbol (desde `scopes.enumerateEdges`) y los hechos de `authz_*`.
|
|
60
|
+
*
|
|
61
|
+
* Qué migra hacia `database` (3b-3b, la VUELTA): **solo los hechos**, leídos
|
|
62
|
+
* del origen por el puerto `enumerateFacts` (páginas con cursor). El ÁRBOL no
|
|
63
|
+
* se migra —el driver `database` lo lee de tus tablas en cada pregunta, que
|
|
64
|
+
* son su fuente de verdad— y el CATÁLOGO tampoco, que es propiedad local
|
|
65
|
+
* siempre. Por eso en esa dirección las fases `marcador de raíz`,
|
|
66
|
+
* `proyección del catálogo` y `árbol` son cero: no hay nada derivado que
|
|
67
|
+
* rehacer, y el cero lo dice.
|
|
68
|
+
*
|
|
69
|
+
* El origen se elige solo si es inequívoco: con dos drivers registrados es el
|
|
70
|
+
* que no es el destino; con más de un candidato hay que decirlo con
|
|
71
|
+
* `--from=<driver>`, porque de dónde salen los hechos decide lo que va a
|
|
72
|
+
* quedar escrito.
|
|
73
|
+
*
|
|
74
|
+
* Las dos direcciones son IDEMPOTENTES (la segunda pasada escribe cero),
|
|
75
|
+
* reanudables (leen por lotes con cursor y convergen si se repiten) y **nunca
|
|
76
|
+
* silenciosas**: cada cosa que no se migra sale contada y con su motivo.
|
|
77
|
+
*
|
|
78
|
+
* `--dry-run` es el verificador y es **read-only por contrato** (panel 2,
|
|
79
|
+
* cruce 4 · S18): mismo recorrido, cero escrituras y los mismos números. **No
|
|
80
|
+
* hay ni habrá un `--fix`**: sería un mecanismo de concesión.
|
|
81
|
+
*
|
|
82
|
+
* Durante la pasada que ESCRIBE, las escrituras del motor están congeladas
|
|
83
|
+
* (503 reintentable) y las lecturas siguen. **Con `--dry-run` NO se congela**
|
|
84
|
+
* (3b-6): el verificador no escribe nada, así que no tiene nada que proteger,
|
|
85
|
+
* y está publicado para correrlo en CI y en un cron — justo el sitio desde el
|
|
86
|
+
* que un mecanismo de indisponibilidad se dispara solo. Es una operación de
|
|
87
|
+
* PLATAFORMA: no lleva actor, no mide rangos y no se expone por HTTP.
|
|
88
|
+
*/
|
|
89
|
+
export default class AuthzReconcile extends BaseCommand {
|
|
90
|
+
static commandName: string;
|
|
91
|
+
static description: string;
|
|
92
|
+
static options: CommandOptions;
|
|
93
|
+
to: string | undefined;
|
|
94
|
+
from: string | undefined;
|
|
95
|
+
dryRun: boolean | undefined;
|
|
96
|
+
prune: boolean | undefined;
|
|
97
|
+
allowMassDelete: boolean | undefined;
|
|
98
|
+
batchSize: number | undefined;
|
|
99
|
+
maxTuples: number | undefined;
|
|
100
|
+
run(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=authz_reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz_reconcile.d.ts","sourceRoot":"","sources":["../../commands/authz_reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG;IACvD,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,KAAK,EAAE,OAAO,CAAA;CACf,CAyIA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,WAAW;IACrD,MAAM,CAAC,WAAW,SAAoB;IACtC,MAAM,CAAC,WAAW,SAAqG;IAEvH,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;IAKtB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IAGxB,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAG3B,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAM1B,eAAe,EAAE,OAAO,GAAG,SAAS,CAAA;IAGpC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAM7B,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAE/B,GAAG;CA6CV"}
|