@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.
Files changed (210) hide show
  1. package/README.md +462 -35
  2. package/build/commands/authz_catalog_diff.js +1 -1
  3. package/build/commands/authz_catalog_diff.js.map +1 -1
  4. package/build/commands/authz_catalog_prune_orphans.d.ts +78 -0
  5. package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -0
  6. package/build/commands/authz_catalog_prune_orphans.js +136 -0
  7. package/build/commands/authz_catalog_prune_orphans.js.map +1 -0
  8. package/build/commands/authz_catalog_sync.d.ts +17 -0
  9. package/build/commands/authz_catalog_sync.d.ts.map +1 -1
  10. package/build/commands/authz_catalog_sync.js +27 -4
  11. package/build/commands/authz_catalog_sync.js.map +1 -1
  12. package/build/commands/authz_freeze.d.ts +44 -0
  13. package/build/commands/authz_freeze.d.ts.map +1 -0
  14. package/build/commands/authz_freeze.js +95 -0
  15. package/build/commands/authz_freeze.js.map +1 -0
  16. package/build/commands/authz_reconcile.d.ts +102 -0
  17. package/build/commands/authz_reconcile.d.ts.map +1 -0
  18. package/build/commands/authz_reconcile.js +294 -0
  19. package/build/commands/authz_reconcile.js.map +1 -0
  20. package/build/commands/authz_relations_reconcile.d.ts +73 -0
  21. package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
  22. package/build/commands/authz_relations_reconcile.js +225 -0
  23. package/build/commands/authz_relations_reconcile.js.map +1 -0
  24. package/build/commands/authz_scopes_relay.d.ts +47 -0
  25. package/build/commands/authz_scopes_relay.d.ts.map +1 -0
  26. package/build/commands/authz_scopes_relay.js +141 -0
  27. package/build/commands/authz_scopes_relay.js.map +1 -0
  28. package/build/commands/authz_unfreeze.d.ts +37 -0
  29. package/build/commands/authz_unfreeze.d.ts.map +1 -0
  30. package/build/commands/authz_unfreeze.js +92 -0
  31. package/build/commands/authz_unfreeze.js.map +1 -0
  32. package/build/commands/main.d.ts +6 -1
  33. package/build/commands/main.d.ts.map +1 -1
  34. package/build/commands/main.js +6 -1
  35. package/build/commands/main.js.map +1 -1
  36. package/build/commands/openfga_provision.d.ts +46 -4
  37. package/build/commands/openfga_provision.d.ts.map +1 -1
  38. package/build/commands/openfga_provision.js +90 -7
  39. package/build/commands/openfga_provision.js.map +1 -1
  40. package/build/configure.d.ts +11 -0
  41. package/build/configure.d.ts.map +1 -1
  42. package/build/configure.js +37 -1
  43. package/build/configure.js.map +1 -1
  44. package/build/index.d.ts +39 -10
  45. package/build/index.d.ts.map +1 -1
  46. package/build/index.js +35 -6
  47. package/build/index.js.map +1 -1
  48. package/build/providers/authz_provider.d.ts +26 -2
  49. package/build/providers/authz_provider.d.ts.map +1 -1
  50. package/build/providers/authz_provider.js +48 -2
  51. package/build/providers/authz_provider.js.map +1 -1
  52. package/build/services/relations.d.ts +14 -0
  53. package/build/services/relations.d.ts.map +1 -0
  54. package/build/services/relations.js +17 -0
  55. package/build/services/relations.js.map +1 -0
  56. package/build/src/{catalog.d.ts → catalog/catalog.d.ts} +41 -2
  57. package/build/src/catalog/catalog.d.ts.map +1 -0
  58. package/build/src/{catalog.js → catalog/catalog.js} +120 -14
  59. package/build/src/catalog/catalog.js.map +1 -0
  60. package/build/src/{catalog_cache.d.ts → catalog/catalog_cache.d.ts} +46 -22
  61. package/build/src/catalog/catalog_cache.d.ts.map +1 -0
  62. package/build/src/{catalog_cache.js → catalog/catalog_cache.js} +53 -43
  63. package/build/src/catalog/catalog_cache.js.map +1 -0
  64. package/build/src/define_config.d.ts +101 -3
  65. package/build/src/define_config.d.ts.map +1 -1
  66. package/build/src/define_config.js.map +1 -1
  67. package/build/src/drivers/database_driver.d.ts +86 -4
  68. package/build/src/drivers/database_driver.d.ts.map +1 -1
  69. package/build/src/drivers/database_driver.js +425 -11
  70. package/build/src/drivers/database_driver.js.map +1 -1
  71. package/build/src/drivers/database_relations_driver.d.ts +75 -0
  72. package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
  73. package/build/src/drivers/database_relations_driver.js +450 -0
  74. package/build/src/drivers/database_relations_driver.js.map +1 -0
  75. package/build/src/drivers/openfga_driver.d.ts +713 -119
  76. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  77. package/build/src/drivers/openfga_driver.js +2048 -476
  78. package/build/src/drivers/openfga_driver.js.map +1 -1
  79. package/build/src/drivers/openfga_facts.d.ts +369 -0
  80. package/build/src/drivers/openfga_facts.d.ts.map +1 -0
  81. package/build/src/drivers/openfga_facts.js +813 -0
  82. package/build/src/drivers/openfga_facts.js.map +1 -0
  83. package/build/src/drivers/openfga_relations_driver.d.ts +120 -0
  84. package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
  85. package/build/src/drivers/openfga_relations_driver.js +466 -0
  86. package/build/src/drivers/openfga_relations_driver.js.map +1 -0
  87. package/build/src/errors.d.ts +258 -5
  88. package/build/src/errors.d.ts.map +1 -1
  89. package/build/src/errors.js +238 -7
  90. package/build/src/errors.js.map +1 -1
  91. package/build/src/freeze.d.ts +120 -0
  92. package/build/src/freeze.d.ts.map +1 -0
  93. package/build/src/freeze.js +172 -0
  94. package/build/src/freeze.js.map +1 -0
  95. package/build/src/http/app_access_middleware.d.ts.map +1 -0
  96. package/build/src/http/app_access_middleware.js.map +1 -0
  97. package/build/src/http/resource_access_middleware.d.ts +105 -0
  98. package/build/src/http/resource_access_middleware.d.ts.map +1 -0
  99. package/build/src/http/resource_access_middleware.js +81 -0
  100. package/build/src/http/resource_access_middleware.js.map +1 -0
  101. package/build/src/identity.d.ts +74 -1
  102. package/build/src/identity.d.ts.map +1 -1
  103. package/build/src/identity.js +100 -2
  104. package/build/src/identity.js.map +1 -1
  105. package/build/src/manager.d.ts +315 -4
  106. package/build/src/manager.d.ts.map +1 -1
  107. package/build/src/manager.js +1187 -181
  108. package/build/src/manager.js.map +1 -1
  109. package/build/src/models/authz_assignment.d.ts +6 -6
  110. package/build/src/models/authz_assignment.d.ts.map +1 -1
  111. package/build/src/models/authz_deny.d.ts +6 -6
  112. package/build/src/models/authz_deny.d.ts.map +1 -1
  113. package/build/src/models/authz_permission.d.ts +6 -6
  114. package/build/src/models/authz_permission.d.ts.map +1 -1
  115. package/build/src/models/authz_role.d.ts +6 -6
  116. package/build/src/models/authz_role.d.ts.map +1 -1
  117. package/build/src/models/authz_role_permission.d.ts +6 -6
  118. package/build/src/models/authz_role_permission.d.ts.map +1 -1
  119. package/build/src/openfga.d.ts +18 -2
  120. package/build/src/openfga.d.ts.map +1 -1
  121. package/build/src/openfga.js +15 -1
  122. package/build/src/openfga.js.map +1 -1
  123. package/build/src/reconcile.d.ts +37 -0
  124. package/build/src/reconcile.d.ts.map +1 -0
  125. package/build/src/reconcile.js +69 -0
  126. package/build/src/reconcile.js.map +1 -0
  127. package/build/src/relation_partition_trigger.d.ts +8 -0
  128. package/build/src/relation_partition_trigger.d.ts.map +1 -0
  129. package/build/src/relation_partition_trigger.js +85 -0
  130. package/build/src/relation_partition_trigger.js.map +1 -0
  131. package/build/src/relations/define_relations_config.d.ts +58 -0
  132. package/build/src/relations/define_relations_config.d.ts.map +1 -0
  133. package/build/src/relations/define_relations_config.js +144 -0
  134. package/build/src/relations/define_relations_config.js.map +1 -0
  135. package/build/src/relations/manager.d.ts +38 -0
  136. package/build/src/relations/manager.d.ts.map +1 -0
  137. package/build/src/relations/manager.js +156 -0
  138. package/build/src/relations/manager.js.map +1 -0
  139. package/build/src/relations/reconcile.d.ts +62 -0
  140. package/build/src/relations/reconcile.d.ts.map +1 -0
  141. package/build/src/relations/reconcile.js +138 -0
  142. package/build/src/relations/reconcile.js.map +1 -0
  143. package/build/src/relations_config_store.d.ts +22 -0
  144. package/build/src/relations_config_store.d.ts.map +1 -0
  145. package/build/src/relations_config_store.js +74 -0
  146. package/build/src/relations_config_store.js.map +1 -0
  147. package/build/src/scope_outbox.d.ts +69 -0
  148. package/build/src/scope_outbox.d.ts.map +1 -0
  149. package/build/src/scope_outbox.js +291 -0
  150. package/build/src/scope_outbox.js.map +1 -0
  151. package/build/src/{drivers → shared}/backend_guard.d.ts +14 -0
  152. package/build/src/shared/backend_guard.d.ts.map +1 -0
  153. package/build/src/{drivers → shared}/backend_guard.js +26 -1
  154. package/build/src/shared/backend_guard.js.map +1 -0
  155. package/build/src/shared/sql_expiry.d.ts.map +1 -0
  156. package/build/src/shared/sql_expiry.js.map +1 -0
  157. package/build/src/sql_descendants.d.ts +47 -1
  158. package/build/src/sql_descendants.d.ts.map +1 -1
  159. package/build/src/sql_descendants.js +75 -1
  160. package/build/src/sql_descendants.js.map +1 -1
  161. package/build/src/testing/contract.d.ts +74 -0
  162. package/build/src/testing/contract.d.ts.map +1 -1
  163. package/build/src/testing/contract.js +672 -169
  164. package/build/src/testing/contract.js.map +1 -1
  165. package/build/src/testing/main.d.ts +6 -0
  166. package/build/src/testing/main.d.ts.map +1 -1
  167. package/build/src/testing/main.js +3 -0
  168. package/build/src/testing/main.js.map +1 -1
  169. package/build/src/testing/migration_contract.d.ts +284 -0
  170. package/build/src/testing/migration_contract.d.ts.map +1 -0
  171. package/build/src/testing/migration_contract.js +586 -0
  172. package/build/src/testing/migration_contract.js.map +1 -0
  173. package/build/src/testing/relations_contract.d.ts +51 -0
  174. package/build/src/testing/relations_contract.d.ts.map +1 -0
  175. package/build/src/testing/relations_contract.js +654 -0
  176. package/build/src/testing/relations_contract.js.map +1 -0
  177. package/build/src/testing/relations_reconcile_contract.d.ts +24 -0
  178. package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -0
  179. package/build/src/testing/relations_reconcile_contract.js +172 -0
  180. package/build/src/testing/relations_reconcile_contract.js.map +1 -0
  181. package/build/src/traits/authz_scopes.js +1 -1
  182. package/build/src/traits/authz_scopes.js.map +1 -1
  183. package/build/src/traits/has_uuid.d.ts +7 -7
  184. package/build/src/traits/has_uuid.d.ts.map +1 -1
  185. package/build/src/types.d.ts +865 -82
  186. package/build/src/types.d.ts.map +1 -1
  187. package/build/src/types.js +10 -0
  188. package/build/src/types.js.map +1 -1
  189. package/build/stubs/config/authorization.stub +104 -4
  190. package/build/stubs/migration.stub +126 -0
  191. package/build/stubs/scopes_outbox_migration.stub +57 -0
  192. package/package.json +4 -2
  193. package/build/commands/openfga_import.d.ts +0 -34
  194. package/build/commands/openfga_import.d.ts.map +0 -1
  195. package/build/commands/openfga_import.js +0 -97
  196. package/build/commands/openfga_import.js.map +0 -1
  197. package/build/src/catalog.d.ts.map +0 -1
  198. package/build/src/catalog.js.map +0 -1
  199. package/build/src/catalog_cache.d.ts.map +0 -1
  200. package/build/src/catalog_cache.js.map +0 -1
  201. package/build/src/drivers/backend_guard.d.ts.map +0 -1
  202. package/build/src/drivers/backend_guard.js.map +0 -1
  203. package/build/src/drivers/sql_expiry.d.ts.map +0 -1
  204. package/build/src/drivers/sql_expiry.js.map +0 -1
  205. package/build/src/middleware/app_access_middleware.d.ts.map +0 -1
  206. package/build/src/middleware/app_access_middleware.js.map +0 -1
  207. /package/build/src/{middleware → http}/app_access_middleware.d.ts +0 -0
  208. /package/build/src/{middleware → http}/app_access_middleware.js +0 -0
  209. /package/build/src/{drivers → shared}/sql_expiry.d.ts +0 -0
  210. /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,mBAAmB,CAAC,CAAA;QAC5D,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"}
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;AAEzD;;;;;;;;;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;CAmCV"}
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
- const { count, shadowedByGlobal, assignableAtViolations } = await syncCatalogs(catalogs, {
37
- prune: this.keepLinks ? 'none' : 'links',
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;AAGvD;;;;;;;;;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,mBAAmB,CAAC,CAAA;QAC1D,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE;YACvF,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;SACzC,CAAC,CAAA;QACF,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;;AApCO;IADP,KAAK,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,0DAA0D,EAAE,CAAC;;mDACrD"}
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"}