@jantstack/adonis-authz 2.4.0-alpha.1 → 2.4.0-alpha.10

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 (158) hide show
  1. package/README.md +496 -29
  2. package/build/commands/authz_catalog_diff.d.ts.map +1 -1
  3. package/build/commands/authz_catalog_diff.js +0 -1
  4. package/build/commands/authz_catalog_diff.js.map +1 -1
  5. package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -1
  6. package/build/commands/authz_catalog_prune_orphans.js +0 -1
  7. package/build/commands/authz_catalog_prune_orphans.js.map +1 -1
  8. package/build/commands/authz_freeze.d.ts.map +1 -1
  9. package/build/commands/authz_freeze.js +1 -2
  10. package/build/commands/authz_freeze.js.map +1 -1
  11. package/build/commands/authz_reconcile.d.ts +15 -6
  12. package/build/commands/authz_reconcile.d.ts.map +1 -1
  13. package/build/commands/authz_reconcile.js +143 -20
  14. package/build/commands/authz_reconcile.js.map +1 -1
  15. package/build/commands/authz_relations_reconcile.d.ts +72 -1
  16. package/build/commands/authz_relations_reconcile.d.ts.map +1 -1
  17. package/build/commands/authz_relations_reconcile.js +234 -21
  18. package/build/commands/authz_relations_reconcile.js.map +1 -1
  19. package/build/commands/authz_scopes_relay.d.ts +50 -0
  20. package/build/commands/authz_scopes_relay.d.ts.map +1 -1
  21. package/build/commands/authz_scopes_relay.js +213 -10
  22. package/build/commands/authz_scopes_relay.js.map +1 -1
  23. package/build/commands/authz_scopes_requeue.d.ts +80 -0
  24. package/build/commands/authz_scopes_requeue.d.ts.map +1 -0
  25. package/build/commands/authz_scopes_requeue.js +230 -0
  26. package/build/commands/authz_scopes_requeue.js.map +1 -0
  27. package/build/commands/authz_unfreeze.d.ts +12 -4
  28. package/build/commands/authz_unfreeze.d.ts.map +1 -1
  29. package/build/commands/authz_unfreeze.js +74 -9
  30. package/build/commands/authz_unfreeze.js.map +1 -1
  31. package/build/commands/main.d.ts +16 -13
  32. package/build/commands/main.d.ts.map +1 -1
  33. package/build/commands/main.js +59 -11
  34. package/build/commands/main.js.map +1 -1
  35. package/build/commands/openfga_provision.d.ts +49 -3
  36. package/build/commands/openfga_provision.d.ts.map +1 -1
  37. package/build/commands/openfga_provision.js +72 -7
  38. package/build/commands/openfga_provision.js.map +1 -1
  39. package/build/index.d.ts +15 -5
  40. package/build/index.d.ts.map +1 -1
  41. package/build/index.js +16 -3
  42. package/build/index.js.map +1 -1
  43. package/build/providers/authz_provider.d.ts +7 -2
  44. package/build/providers/authz_provider.d.ts.map +1 -1
  45. package/build/providers/authz_provider.js +20 -3
  46. package/build/providers/authz_provider.js.map +1 -1
  47. package/build/src/define_config.d.ts +106 -1
  48. package/build/src/define_config.d.ts.map +1 -1
  49. package/build/src/define_config.js +11 -0
  50. package/build/src/define_config.js.map +1 -1
  51. package/build/src/drivers/database_driver.d.ts +53 -14
  52. package/build/src/drivers/database_driver.d.ts.map +1 -1
  53. package/build/src/drivers/database_driver.js +104 -32
  54. package/build/src/drivers/database_driver.js.map +1 -1
  55. package/build/src/drivers/database_relations_driver.d.ts +74 -9
  56. package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
  57. package/build/src/drivers/database_relations_driver.js +391 -93
  58. package/build/src/drivers/database_relations_driver.js.map +1 -1
  59. package/build/src/drivers/openfga_driver.d.ts +71 -10
  60. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  61. package/build/src/drivers/openfga_driver.js +195 -16
  62. package/build/src/drivers/openfga_driver.js.map +1 -1
  63. package/build/src/drivers/openfga_facts.d.ts +15 -0
  64. package/build/src/drivers/openfga_facts.d.ts.map +1 -1
  65. package/build/src/drivers/openfga_facts.js +25 -2
  66. package/build/src/drivers/openfga_facts.js.map +1 -1
  67. package/build/src/drivers/openfga_relations_driver.d.ts +72 -6
  68. package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
  69. package/build/src/drivers/openfga_relations_driver.js +308 -50
  70. package/build/src/drivers/openfga_relations_driver.js.map +1 -1
  71. package/build/src/errors.d.ts +175 -4
  72. package/build/src/errors.d.ts.map +1 -1
  73. package/build/src/errors.js +219 -3
  74. package/build/src/errors.js.map +1 -1
  75. package/build/src/freeze.d.ts +100 -20
  76. package/build/src/freeze.d.ts.map +1 -1
  77. package/build/src/freeze.js +159 -25
  78. package/build/src/freeze.js.map +1 -1
  79. package/build/src/manager.d.ts +239 -7
  80. package/build/src/manager.d.ts.map +1 -1
  81. package/build/src/manager.js +1192 -148
  82. package/build/src/manager.js.map +1 -1
  83. package/build/src/models/authz_assignment.d.ts +2 -2
  84. package/build/src/models/authz_assignment.d.ts.map +1 -1
  85. package/build/src/models/authz_deny.d.ts +2 -2
  86. package/build/src/models/authz_deny.d.ts.map +1 -1
  87. package/build/src/models/authz_permission.d.ts +2 -2
  88. package/build/src/models/authz_permission.d.ts.map +1 -1
  89. package/build/src/models/authz_role.d.ts +2 -2
  90. package/build/src/models/authz_role.d.ts.map +1 -1
  91. package/build/src/models/authz_role_permission.d.ts +2 -2
  92. package/build/src/models/authz_role_permission.d.ts.map +1 -1
  93. package/build/src/relation_partition_trigger.d.ts.map +1 -1
  94. package/build/src/relation_partition_trigger.js +11 -3
  95. package/build/src/relation_partition_trigger.js.map +1 -1
  96. package/build/src/relations/define_relations_config.d.ts +51 -0
  97. package/build/src/relations/define_relations_config.d.ts.map +1 -1
  98. package/build/src/relations/define_relations_config.js +68 -1
  99. package/build/src/relations/define_relations_config.js.map +1 -1
  100. package/build/src/relations/manager.d.ts +106 -6
  101. package/build/src/relations/manager.d.ts.map +1 -1
  102. package/build/src/relations/manager.js +368 -30
  103. package/build/src/relations/manager.js.map +1 -1
  104. package/build/src/relations/reconcile.d.ts +81 -6
  105. package/build/src/relations/reconcile.d.ts.map +1 -1
  106. package/build/src/relations/reconcile.js +233 -77
  107. package/build/src/relations/reconcile.js.map +1 -1
  108. package/build/src/scope_outbox.d.ts +54 -0
  109. package/build/src/scope_outbox.d.ts.map +1 -1
  110. package/build/src/scope_outbox.js +274 -33
  111. package/build/src/scope_outbox.js.map +1 -1
  112. package/build/src/shared/backend_guard.d.ts +28 -0
  113. package/build/src/shared/backend_guard.d.ts.map +1 -1
  114. package/build/src/shared/backend_guard.js +52 -0
  115. package/build/src/shared/backend_guard.js.map +1 -1
  116. package/build/src/shared/sql_expiry.d.ts +2 -0
  117. package/build/src/shared/sql_expiry.d.ts.map +1 -1
  118. package/build/src/shared/sql_expiry.js +4 -0
  119. package/build/src/shared/sql_expiry.js.map +1 -1
  120. package/build/src/shared/transaction_guard.d.ts +53 -0
  121. package/build/src/shared/transaction_guard.d.ts.map +1 -0
  122. package/build/src/shared/transaction_guard.js +74 -0
  123. package/build/src/shared/transaction_guard.js.map +1 -0
  124. package/build/src/sql_descendants.d.ts +8 -0
  125. package/build/src/sql_descendants.d.ts.map +1 -1
  126. package/build/src/sql_descendants.js.map +1 -1
  127. package/build/src/testing/contract.d.ts +55 -4
  128. package/build/src/testing/contract.d.ts.map +1 -1
  129. package/build/src/testing/contract.js +244 -9
  130. package/build/src/testing/contract.js.map +1 -1
  131. package/build/src/testing/main.d.ts +6 -4
  132. package/build/src/testing/main.d.ts.map +1 -1
  133. package/build/src/testing/main.js +3 -2
  134. package/build/src/testing/main.js.map +1 -1
  135. package/build/src/testing/migration_contract.d.ts +9 -0
  136. package/build/src/testing/migration_contract.d.ts.map +1 -1
  137. package/build/src/testing/migration_contract.js +5 -2
  138. package/build/src/testing/migration_contract.js.map +1 -1
  139. package/build/src/testing/relations_contract.d.ts +23 -1
  140. package/build/src/testing/relations_contract.d.ts.map +1 -1
  141. package/build/src/testing/relations_contract.js +603 -19
  142. package/build/src/testing/relations_contract.js.map +1 -1
  143. package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -1
  144. package/build/src/testing/relations_reconcile_contract.js +49 -1
  145. package/build/src/testing/relations_reconcile_contract.js.map +1 -1
  146. package/build/src/testing/scope_outbox_contract.d.ts +98 -0
  147. package/build/src/testing/scope_outbox_contract.d.ts.map +1 -0
  148. package/build/src/testing/scope_outbox_contract.js +677 -0
  149. package/build/src/testing/scope_outbox_contract.js.map +1 -0
  150. package/build/src/traits/has_uuid.d.ts +2 -2
  151. package/build/src/traits/has_uuid.d.ts.map +1 -1
  152. package/build/src/types.d.ts +708 -38
  153. package/build/src/types.d.ts.map +1 -1
  154. package/build/src/types.js +55 -0
  155. package/build/src/types.js.map +1 -1
  156. package/build/stubs/config/authorization.stub +45 -1
  157. package/build/stubs/migration.stub +20 -10
  158. package/package.json +1 -1
@@ -19,8 +19,17 @@
19
19
  * puerto (`../types.js`), los errores y la config de relaciones. NO importa
20
20
  * `manager` ni `drivers/*`: los drivers concretos los inyecta el llamante (la
21
21
  * plataforma), como `manager.reconcile` recibe el destino por su clave.
22
+ *
23
+ * **Es un PRIMITIVO PRIVADO** (cierre del auditor de alpha.10 · R1): no sabe
24
+ * cuál es el driver activo ni toma la ventana durable, así que ya no sale de la
25
+ * raíz del paquete. La vía pública es `runRelationsReconcile`
26
+ * (`commands/authz_relations_reconcile.ts`), que resuelve origen y destino por
27
+ * clave, niega la dirección rancia y congela; la guardia de superficie de
28
+ * `command_flags.spec.ts` impide re-exportarlo por ningún subpath.
22
29
  */
23
- import { MassReconcileRefusedError, UnsupportedOperationError } from '../errors.js';
30
+ import { AuthorizationConfigError, MassReconcileRefusedError, RelationTypeUnknownError, RelationUnknownError, UnsupportedOperationError, } from '../errors.js';
31
+ import { systemClock } from '../clock.js';
32
+ import { isActiveExpiry, sameInstant } from '../expiry.js';
24
33
  import { isRelUserset } from '../types.js';
25
34
  /** La clave estable de una tupla de relación (partición + objeto + relación + sujeto). */
26
35
  function subjectKey(subject) {
@@ -34,28 +43,53 @@ function scopeKeyOf(scope) {
34
43
  function tupleKey(tuple) {
35
44
  return `${scopeKeyOf(tuple.partition)}|${tuple.object.type}:${tuple.object.id}#${tuple.relation}@${subjectKey(tuple.subject)}`;
36
45
  }
37
- /** Enumera TODAS las tuplas de una partición de un driver (paginando el cursor). */
38
- async function enumerateAll(driver, partition, role) {
46
+ /**
47
+ * Enumera TODAS las tuplas de las particiones pedidas, repartidas por clave de
48
+ * partición. UNA pasada con `enumerateRelationsMany` si el driver lo trae
49
+ * (alpha.6: en `openfga`, un barrido del store en vez de K); si no, una
50
+ * llamada de `enumerateRelations` por partición. En los dos casos se pagina el
51
+ * cursor hasta agotarlo, con las mismas guardas ruidosas (un cursor que se
52
+ * repite o más de 100 000 páginas es un error, nunca una enumeración corta).
53
+ */
54
+ async function enumerateAllMany(driver, partitions, role) {
39
55
  if (!driver.enumerateRelations || !driver.capabilities?.enumerateRelations) {
40
56
  throw new UnsupportedOperationError('enumerateRelations', `ser ${role} de authz:reconcile de relaciones`, 'relations', `El driver ${role} no sabe enumerar sus tuplas (capacidad 'enumerateRelations': false). ` +
41
57
  `Sin poder leer ${role === 'destino' ? 'el destino no se ve qué sobra' : 'el origen no hay nada que migrar'}.`);
42
58
  }
43
- const out = [];
44
- const seen = new Set();
45
- let after;
46
- let pages = 0;
47
- do {
48
- const page = await driver.enumerateRelations(partition, after ? { after } : undefined);
49
- out.push(...page.tuples);
50
- after = page.cursor || undefined;
51
- if (after) {
52
- if (seen.has(after))
53
- throw new Error(`enumerateRelations (${role}): el cursor '${after}' se repite; no avanza`);
54
- seen.add(after);
55
- if (++pages > 100_000)
56
- throw new Error(`enumerateRelations (${role}): más de 100000 páginas sin agotar`);
59
+ const out = new Map();
60
+ for (const partition of partitions)
61
+ out.set(scopeKeyOf(partition), []);
62
+ const collect = (tuples) => {
63
+ for (const tuple of tuples) {
64
+ const bucket = out.get(scopeKeyOf(tuple.partition));
65
+ // Una tupla de una partición que no se pidió no es de esta pasada: el
66
+ // puerto promete «solo las pedidas», y si un driver de terceros trae de
67
+ // más, no se migra lo ajeno.
68
+ if (bucket)
69
+ bucket.push(tuple);
57
70
  }
58
- } while (after);
71
+ };
72
+ const many = driver.enumerateRelationsMany;
73
+ const fetch = typeof many === 'function' && partitions.length > 1
74
+ ? [(after) => many.call(driver, partitions, after ? { after } : undefined)]
75
+ : partitions.map((partition) => (after) => driver.enumerateRelations(partition, after ? { after } : undefined));
76
+ for (const pageOf of fetch) {
77
+ const seen = new Set();
78
+ let after;
79
+ let pages = 0;
80
+ do {
81
+ const page = await pageOf(after);
82
+ collect(page.tuples);
83
+ after = page.cursor || undefined;
84
+ if (after) {
85
+ if (seen.has(after))
86
+ throw new Error(`enumerateRelations (${role}): el cursor '${after}' se repite; no avanza`);
87
+ seen.add(after);
88
+ if (++pages > 100_000)
89
+ throw new Error(`enumerateRelations (${role}): más de 100000 páginas sin agotar`);
90
+ }
91
+ } while (after);
92
+ }
59
93
  return out;
60
94
  }
61
95
  /**
@@ -64,75 +98,197 @@ async function enumerateAll(driver, partition, role) {
64
98
  * sentido). Nunca silenciosa: devuelve `written`/`deleted`/`unchanged`/`extra`.
65
99
  */
66
100
  export async function reconcileRelations(options) {
67
- const { from, to, partition, dryRun = false, prune = false, allowMassDelete = false, toConfig } = options;
68
- const sourceTuples = await enumerateAll(from, partition, 'origen');
69
- const destTuples = await enumerateAll(to, partition, 'destino');
101
+ const { from, to, dryRun = false, prune = false, allowMassDelete = false, toConfig } = options;
102
+ const now = (options.now ?? systemClock)();
103
+ const partitions = partitionsOf(options);
104
+ // alpha.6: origen y destino se enumeran UNA vez para todas las particiones.
105
+ const enumeratedByKey = await enumerateAllMany(from, partitions, 'origen');
106
+ const destByKeyAll = await enumerateAllMany(to, partitions, 'destino');
107
+ // Primero se DECIDE todo (K diffs, K seguros R-17) y luego se escribe: un
108
+ // seguro que salte en la partición 3 no deja las dos primeras a medias.
109
+ const plans = [];
110
+ for (const partition of partitions) {
111
+ const enumerated = enumeratedByKey.get(scopeKeyOf(partition)) ?? [];
112
+ const destTuples = destByKeyAll.get(scopeKeyOf(partition)) ?? [];
113
+ plans.push(planPartition(partition, enumerated, destTuples, { now, prune, toConfig }));
114
+ }
115
+ const report = {
116
+ dryRun,
117
+ written: 0,
118
+ updated: 0,
119
+ deleted: 0,
120
+ unchanged: 0,
121
+ extra: 0,
122
+ modelDrift: [],
123
+ massDelete: false,
124
+ skipped: { expired: 0, undeclared: 0 },
125
+ };
126
+ for (const plan of plans) {
127
+ // **El seguro de borrado masivo** (R-17, paridad con `authz:reconcile` de
128
+ // roles / AA2). `prune` + un ORIGEN sin una sola tupla utilizable + un destino
129
+ // con tuplas que borrar es la firma de un `--from` equivocado o de un store
130
+ // vacío: vaciar el destino entero se rechaza con 500 antes de tocar nada,
131
+ // salvo `allowMassDelete`. En `--dry-run` no lanza, lo marca. «Utilizable» =
132
+ // una tupla VIVA del origen que cabe en el destino (las que `skipped`
133
+ // recoge no migran, así que no cuentan como respaldo).
134
+ if (plan.massDelete && !dryRun && !allowMassDelete) {
135
+ throw new MassReconcileRefusedError(`authz:relations:reconcile --prune borraría ${plan.toDelete.length} tupla(s) de relación del destino` +
136
+ `${partitions.length > 1 ? ` (partición ${scopeKeyOf(plan.partition)})` : ''} y el ` +
137
+ `ORIGEN no ha aportado NI UNA utilizable (${plan.enumerated} leídas` +
138
+ `${toConfig ? ', todas de tipos que el destino no declara' : ''}` +
139
+ `${plan.skipped.expired ? `, ${plan.skipped.expired} caducada(s)` : ''}). Eso es la firma de un --from ` +
140
+ `equivocado o de un store vacío, no de un destino que sobra: esta pasada dejaría la partición sin ` +
141
+ `una sola relación. Comprueba el --from y el store; si de verdad quieres vaciarlo, --allow-mass-delete.` +
142
+ `${partitions.length > 1 ? ' No se ha escrito nada en ninguna partición.' : ''}`);
143
+ }
144
+ report.massDelete ||= plan.massDelete;
145
+ report.skipped.expired += plan.skipped.expired;
146
+ report.skipped.undeclared += plan.skipped.undeclared;
147
+ for (const type of plan.modelDrift)
148
+ if (!report.modelDrift.includes(type))
149
+ report.modelDrift.push(type);
150
+ }
151
+ for (const plan of plans) {
152
+ let written = plan.toWrite.length;
153
+ let updated = plan.toUpdate.length;
154
+ if (!dryRun) {
155
+ // La caducidad viaja EXPLÍCITA (`Date` la fija, `null` la quita): un
156
+ // `relate` sin opciones PRESERVARÍA la del destino (invariante 10).
157
+ // L-0: el DRIVER del destino aplica F-05 (`assertRelationDeclared`), así que
158
+ // sin `toConfig` —o con una que no coincida con la del driver— es SU 422 el
159
+ // que embuda la tupla no declarada: se cuenta en `skipped.undeclared` y se
160
+ // descuenta de `written`/`updated`. Nunca un throw a mitad de pasada por
161
+ // una tupla que el reporte puede contar, y nunca un silencio.
162
+ for (const tuple of plan.toWrite) {
163
+ if (await relateOrSkip(to, tuple))
164
+ continue;
165
+ report.skipped.undeclared += 1;
166
+ written -= 1;
167
+ }
168
+ for (const tuple of plan.toUpdate) {
169
+ if (await relateOrSkip(to, tuple))
170
+ continue;
171
+ report.skipped.undeclared += 1;
172
+ updated -= 1;
173
+ }
174
+ if (prune) {
175
+ for (const tuple of plan.toDelete) {
176
+ await to.unrelate(tuple.subject, tuple.relation, tuple.object, tuple.partition);
177
+ }
178
+ }
179
+ }
180
+ report.written += written;
181
+ report.updated += updated;
182
+ report.deleted += prune ? plan.toDelete.length : 0;
183
+ report.unchanged += plan.unchanged;
184
+ report.extra += prune ? 0 : plan.toDelete.length;
185
+ }
186
+ return report;
187
+ }
188
+ /** Exactamente uno de `partition` / `partitions` (y `partitions` no vacío): lo demás es 500 antes de leer nada. */
189
+ function partitionsOf(options) {
190
+ const { partition, partitions } = options;
191
+ if (partition !== undefined && partitions !== undefined) {
192
+ throw new AuthorizationConfigError('reconcileRelations: pasa `partition` O `partitions`, no los dos.');
193
+ }
194
+ // alpha.10 · 5, gemelo en la API: `{ type: 'app', uuid: 'x' }` reconciliaba la
195
+ // RAÍZ en silencio (la clave de `app` ignora el uuid), igual que la pareja
196
+ // `--partition-type=app --partition-uuid=x` del comando.
197
+ for (const p of partition !== undefined ? [partition] : Array.isArray(partitions) ? partitions : []) {
198
+ if (p?.type === 'app' && p.uuid !== null && p.uuid !== undefined) {
199
+ throw new AuthorizationConfigError(`reconcileRelations: la partición raíz no lleva uuid (llegó { type: 'app', uuid: ${JSON.stringify(p.uuid)} }). ` +
200
+ 'O la raíz ({ type: app, uuid: null }) o el tipo del tenant.');
201
+ }
202
+ }
203
+ if (partition !== undefined)
204
+ return [partition];
205
+ if (!Array.isArray(partitions) || partitions.length === 0) {
206
+ throw new AuthorizationConfigError('reconcileRelations: pasa `partition` (una) o `partitions` (varias, al menos una): sin partición no hay nada que migrar, ' +
207
+ 'y una lista vacía no es una pasada en verde.');
208
+ }
209
+ // Dos referencias con la misma clave son la misma partición: se migra una vez.
210
+ const seen = new Set();
211
+ return partitions.filter((p) => {
212
+ const key = scopeKeyOf(p);
213
+ if (seen.has(key))
214
+ return false;
215
+ seen.add(key);
216
+ return true;
217
+ });
218
+ }
219
+ function planPartition(partition, enumerated, destTuples, context) {
220
+ const { now, prune, toConfig } = context;
221
+ // R-15: la caducada LLEGA del origen (el puerto lo exige) y aquí se DESCARTA
222
+ // contándola: no se migra ni respalda nada en el destino. Decidido con UN
223
+ // `now` para toda la pasada.
224
+ // L-0: la tupla cuyo tipo o relación el DESTINO no declara (F-05) se
225
+ // descarta AQUÍ cuando hay `toConfig` —antes del driver, así `--dry-run` y la
226
+ // pasada real dan los mismos números— y tampoco respalda nada en el destino
227
+ // (una que ya estuviera allí es `extra`, y `--prune` la barre). El tipo se
228
+ // reporta además en `modelDrift` (deriva del modelo fusionado).
229
+ const skipped = { expired: 0, undeclared: 0 };
230
+ const modelDrift = [];
231
+ const sourceTuples = [];
232
+ for (const tuple of enumerated) {
233
+ if (!isActiveExpiry(tuple.expiresAt ?? null, now)) {
234
+ skipped.expired += 1;
235
+ continue;
236
+ }
237
+ if (toConfig && !declaredIn(toConfig, tuple.object.type)) {
238
+ skipped.undeclared += 1;
239
+ if (!modelDrift.includes(tuple.object.type))
240
+ modelDrift.push(tuple.object.type);
241
+ continue;
242
+ }
243
+ if (toConfig && !toConfig.isDeclared(tuple.object.type, tuple.relation)) {
244
+ skipped.undeclared += 1;
245
+ continue;
246
+ }
247
+ sourceTuples.push(tuple);
248
+ }
70
249
  const sourceByKey = new Map();
71
250
  for (const tuple of sourceTuples)
72
251
  sourceByKey.set(tupleKey(tuple), tuple);
73
- const destKeys = new Set(destTuples.map((tuple) => tupleKey(tuple)));
252
+ const destByKey = new Map();
253
+ for (const tuple of destTuples)
254
+ destByKey.set(tupleKey(tuple), tuple);
74
255
  const toWrite = [];
256
+ const toUpdate = [];
75
257
  let unchanged = 0;
76
258
  for (const [key, tuple] of sourceByKey) {
77
- if (destKeys.has(key))
78
- unchanged += 1;
79
- else
259
+ const present = destByKey.get(key);
260
+ if (!present)
80
261
  toWrite.push(tuple);
262
+ // Misma clave, OTRA caducidad ⇒ se reescribe con la del origen (R-15).
263
+ else if (!sameInstant(present.expiresAt ?? null, tuple.expiresAt ?? null))
264
+ toUpdate.push(tuple);
265
+ else
266
+ unchanged += 1;
81
267
  }
82
268
  const toDelete = destTuples.filter((tuple) => !sourceByKey.has(tupleKey(tuple)));
83
- // Deriva del modelo fusionado del destino (--dry-run): tipos del origen que
84
- // el destino no declara. `group` es built-in (el generador lo emite siempre).
85
- const modelDrift = [];
86
- if (toConfig) {
87
- const declared = new Set(['group', ...toConfig.objectTypes.map((t) => t.type)]);
88
- const seenDrift = new Set();
89
- for (const tuple of sourceTuples) {
90
- if (!declared.has(tuple.object.type) && !seenDrift.has(tuple.object.type)) {
91
- seenDrift.add(tuple.object.type);
92
- modelDrift.push(tuple.object.type);
93
- }
94
- }
95
- }
96
- // **El seguro de borrado masivo** (R-17, paridad con `authz:reconcile` de
97
- // roles / AA2). `prune` + un ORIGEN sin una sola tupla utilizable + un destino
98
- // con tuplas que borrar es la firma de un `--from` equivocado o de un store
99
- // vacío: vaciar el destino entero se rechaza con 500 antes de tocar nada,
100
- // salvo `allowMassDelete`. En `--dry-run` no lanza, lo marca. «Utilizable» =
101
- // una tupla del origen cuyo tipo cabe en el modelo del destino (los tipos que
102
- // `modelDrift` recoge no migrarían, así que no cuentan como respaldo).
103
- const usableSource = toConfig
104
- ? sourceTuples.filter((tuple) => declaredIn(toConfig, tuple.object.type)).length
105
- : sourceTuples.length;
106
- const massDelete = prune && toDelete.length > 0 && usableSource === 0;
107
- if (massDelete && !dryRun && !allowMassDelete) {
108
- throw new MassReconcileRefusedError(`authz:relations:reconcile --prune borraría ${toDelete.length} tupla(s) de relación del destino y el ` +
109
- `ORIGEN no ha aportado NI UNA utilizable (${sourceTuples.length} leídas` +
110
- `${toConfig ? ', todas de tipos que el destino no declara' : ''}). Eso es la firma de un --from ` +
111
- `equivocado o de un store vacío, no de un destino que sobra: esta pasada dejaría la partición sin ` +
112
- `una sola relación. Comprueba el --from y el store; si de verdad quieres vaciarlo, --allow-mass-delete.`);
113
- }
114
- if (!dryRun) {
115
- for (const tuple of toWrite) {
116
- await to.relate(tuple.subject, tuple.relation, tuple.object, tuple.partition);
117
- }
118
- if (prune) {
119
- for (const tuple of toDelete) {
120
- await to.unrelate(tuple.subject, tuple.relation, tuple.object, tuple.partition);
121
- }
122
- }
123
- }
124
- return {
125
- dryRun,
126
- written: toWrite.length,
127
- deleted: prune ? toDelete.length : 0,
128
- unchanged,
129
- extra: prune ? 0 : toDelete.length,
130
- modelDrift,
131
- massDelete,
132
- };
269
+ const massDelete = prune && toDelete.length > 0 && sourceTuples.length === 0;
270
+ return { partition, enumerated: enumerated.length, toWrite, toUpdate, toDelete, unchanged, skipped, modelDrift, massDelete };
133
271
  }
134
- /** ¿El tipo de objeto cabe en el modelo fusionado del destino? (`group` es built-in.) */
272
+ /** ¿El tipo de objeto cabe en el modelo fusionado del destino? (`group` es built-in: `hasType` lo incluye.) */
135
273
  function declaredIn(config, objectType) {
136
- return objectType === 'group' || config.objectTypes.some((t) => t.type === objectType);
274
+ return config.hasType(objectType);
275
+ }
276
+ /**
277
+ * Escribe la tupla en el destino; `false` si el DRIVER la rechazó por F-05
278
+ * (tipo o relación no declarados: 422 `E_AUTHZ_RELATION_TYPE_UNKNOWN` /
279
+ * `E_AUTHZ_RELATION_UNKNOWN`). Cualquier otro error se propaga tal cual.
280
+ */
281
+ async function relateOrSkip(to, tuple) {
282
+ try {
283
+ await to.relate(tuple.subject, tuple.relation, tuple.object, tuple.partition, {
284
+ expiresAt: tuple.expiresAt ?? null,
285
+ });
286
+ return true;
287
+ }
288
+ catch (error) {
289
+ if (error instanceof RelationTypeUnknownError || error instanceof RelationUnknownError)
290
+ return false;
291
+ throw error;
292
+ }
137
293
  }
138
294
  //# sourceMappingURL=reconcile.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/relations/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACnF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI1C,0FAA0F;AAC1F,SAAS,UAAU,CAAC,OAAmB;IACrC,IAAI,YAAY,CAAC,OAAO,CAAC;QAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAA;IACnG,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAA;AAC1C,CAAC;AAED,SAAS,UAAU,CAAC,KAAe;IACjC,OAAO,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAA;AACrE,CAAC;AAED,SAAS,QAAQ,CAAC,KAAoB;IACpC,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,KAAK,CAAC,QAAQ,IAAI,UAAU,CAC3G,KAAK,CAAC,OAAO,CACd,EAAE,CAAA;AACL,CAAC;AAyDD,oFAAoF;AACpF,KAAK,UAAU,YAAY,CAAC,MAAuB,EAAE,SAAmB,EAAE,IAA0B;IAClG,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,kBAAkB,EAAE,CAAC;QAC3E,MAAM,IAAI,yBAAyB,CACjC,oBAAoB,EACpB,OAAO,IAAI,mCAAmC,EAC9C,WAAW,EACX,aAAa,IAAI,wEAAwE;YACvF,kBAAkB,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,kCAAkC,GAAG,CACjH,CAAA;IACH,CAAC;IACD,MAAM,GAAG,GAAoB,EAAE,CAAA;IAC/B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,IAAI,KAAyB,CAAA;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,GAAG,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACtF,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAA;QACxB,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,SAAS,CAAA;QAChC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,iBAAiB,KAAK,wBAAwB,CAAC,CAAA;YAC/G,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACf,IAAI,EAAE,KAAK,GAAG,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,qCAAqC,CAAC,CAAA;QAC1G,CAAC;IACH,CAAC,QAAQ,KAAK,EAAC;IACf,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAkC;IACzE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,eAAe,GAAG,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAA;IAEzG,MAAM,YAAY,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;IAClE,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;IAE/D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAA;IACpD,KAAK,MAAM,KAAK,IAAI,YAAY;QAAE,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;IACzE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAEpE,MAAM,OAAO,GAAoB,EAAE,CAAA;IACnC,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC;QACvC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS,IAAI,CAAC,CAAA;;YAChC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IACD,MAAM,QAAQ,GAAoB,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IAEjG,4EAA4E;IAC5E,8EAA8E;IAC9E,MAAM,UAAU,GAAa,EAAE,CAAA;IAC/B,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACvF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAA;QACnC,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1E,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBAChC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,+EAA+E;IAC/E,4EAA4E;IAC5E,0EAA0E;IAC1E,6EAA6E;IAC7E,8EAA8E;IAC9E,uEAAuE;IACvE,MAAM,YAAY,GAAG,QAAQ;QAC3B,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;QAChF,CAAC,CAAC,YAAY,CAAC,MAAM,CAAA;IACvB,MAAM,UAAU,GAAG,KAAK,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,YAAY,KAAK,CAAC,CAAA;IACrE,IAAI,UAAU,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,MAAM,IAAI,yBAAyB,CACjC,8CAA8C,QAAQ,CAAC,MAAM,yCAAyC;YACpG,4CAA4C,YAAY,CAAC,MAAM,SAAS;YACxE,GAAG,QAAQ,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,CAAC,EAAE,kCAAkC;YACjG,mGAAmG;YACnG,wGAAwG,CAC3G,CAAA;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;QAC/E,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;gBAC7B,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;YACjF,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM;QACN,OAAO,EAAE,OAAO,CAAC,MAAM;QACvB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpC,SAAS;QACT,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM;QAClC,UAAU;QACV,UAAU;KACX,CAAA;AACH,CAAC;AAED,yFAAyF;AACzF,SAAS,UAAU,CAAC,MAAuB,EAAE,UAAkB;IAC7D,OAAO,UAAU,KAAK,OAAO,IAAI,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAA;AACxF,CAAC"}
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/relations/reconcile.ts"],"names":[],"mappings":"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"}
@@ -58,12 +58,66 @@ export interface SqlScopeOutboxOptions {
58
58
  * 3b-3, o un `scopes.detached`/`attached` nuevo del consumidor).
59
59
  */
60
60
  maxAttempts?: number;
61
+ /**
62
+ * **Encolar sin `{ transaction }` es 500** (2.4.0-alpha.8; panel relay 2,
63
+ * cuestión 6; P13 del tester). Con `true`, `enqueue` sin la transacción
64
+ * ABIERTA del llamante es 500 `E_AUTHZ_CONFIG` ANTES del INSERT: la fila
65
+ * confirmaría sola, fuera de tu unidad de trabajo, y si tu transacción
66
+ * revierte después el relay aplica al store un cambio que en TU base no
67
+ * ocurrió — la fuga S5 por el camino de uso «correcto» (SQL dice un padre y
68
+ * el PDP otro, sin que tu base pueda enseñártelo). Default **`false`** en
69
+ * alpha.8 porque el contrato PUBLICADO (`runScopeOutboxContract`) encola sin
70
+ * transacción en `ctx()` y un default `true` pondría en rojo a todo tercero;
71
+ * **pasa a `true` en 2.5** (decisión del dueño), cuando el contrato pase la
72
+ * transacción en `ctx()`. Ponlo a `true` hoy: es la letra de la mitigación.
73
+ */
74
+ requireTransaction?: boolean;
61
75
  }
62
76
  /** Lo que se necesita del `db` de Lucid; inyectable para probar sin servidor. */
63
77
  interface DatabaseLike {
64
78
  from(table: string): any;
65
79
  table(table: string): any;
66
80
  }
81
+ /**
82
+ * **Los nombres del lease de una cola** (2.4.0-alpha.5 · R6; tester del panel
83
+ * relay). Hasta alpha.4 el nombre era `authz_scope_outbox:<conexión>:<tabla>`
84
+ * recortado a 64 caracteres para MySQL: dos despliegues cuya conexión+tabla
85
+ * coincidieran en los primeros 64 COMPARTÍAN lease y se excluían para siempre
86
+ * — y el modo de fallo era `busy: true`, que no lanza: el relay de uno de los
87
+ * dos no corría nunca, en silencio. Desde alpha.5 el nombre es un hash
88
+ * ESTABLE del nombre completo (`current`, 59 caracteres), que no colisiona.
89
+ *
90
+ * **Transición**: `sqlScopeOutbox.acquire()` toma el lock viejo (`legacy`, el
91
+ * recortado) Y el nuevo durante alpha.5, porque en un rolling deploy un
92
+ * proceso con alpha.4 y otro con alpha.5 tienen que seguir excluyéndose
93
+ * (+0,56 ms medidos por `acquire`). El viejo se retira en 2.5 (CHANGELOG):
94
+ * mientras dure la transición, dos tablas con los mismos 64 primeros
95
+ * caracteres siguen compartiendo el `legacy` — la colisión solo se cierra
96
+ * del todo al retirarlo. Exportado para que el caso lo fije sin MySQL.
97
+ */
98
+ /**
99
+ * **Retirada del lock viejo, atada a la versión (cierre alpha.6).** `acquire()` toma
100
+ * el nombre `legacy` SOLO mientras `SCOPE_OUTBOX_TAKES_LEGACY_LOCK` sea `true`, y
101
+ * `tests/scope_outbox.spec.ts` exige que ese flag valga exactamente
102
+ * «`package.json` ≤ `SCOPE_OUTBOX_LEGACY_LOCK_UNTIL`»: en cuanto alguien suba la
103
+ * versión por encima de la última que necesita la transición, la suite se pone en
104
+ * ROJO hasta que el flag pase a `false` (y el caso MySQL de R6 se ajuste). Nadie
105
+ * tiene que acordarse: el propio bump lo exige. Por qué no se retira en la misma
106
+ * versión que estrena el nombre nuevo: durante un despliegue rodante los procesos
107
+ * de alpha.4 sostienen el viejo y los nuevos el nuevo, y ninguno ve al otro —
108
+ * dos escritores justo entonces.
109
+ */
110
+ export declare const SCOPE_OUTBOX_LEGACY_LOCK_UNTIL = "2.4.0-alpha.6";
111
+ /**
112
+ * `false` desde `2.4.0-alpha.7`: la transición terminó (decisión del dueño 2026-09-10: sin
113
+ * consumidores en MySQL desplegando desde alpha.4, la transición no protegía a nadie). Subir
114
+ * desde ≤ alpha.4 en MySQL exige parada completa del relay, no despliegue rodante.
115
+ */
116
+ export declare const SCOPE_OUTBOX_TAKES_LEGACY_LOCK = false;
117
+ export declare function scopeOutboxLockNames(connection: string | undefined, table: string): {
118
+ legacy: string;
119
+ current: string;
120
+ };
67
121
  export declare function sqlScopeOutbox(options?: SqlScopeOutboxOptions, database?: DatabaseLike): ScopeOutbox;
68
122
  export {};
69
123
  //# sourceMappingURL=scope_outbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope_outbox.d.ts","sourceRoot":"","sources":["../../src/scope_outbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAKH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKvC,OAAO,KAAK,EAEV,WAAW,EAKZ,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,KAAK,CAAA;IACX;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAgBD,iFAAiF;AACjF,UAAU,YAAY;IACpB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAA;IACxB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAA;CAC1B;AAuFD,wBAAgB,cAAc,CAC5B,OAAO,GAAE,qBAA0B,EACnC,QAAQ,GAAE,YAA4C,GACrD,WAAW,CAsLb"}
1
+ {"version":3,"file":"scope_outbox.d.ts","sourceRoot":"","sources":["../../src/scope_outbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAMH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAMvC,OAAO,KAAK,EAEV,WAAW,EAOZ,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,KAAK,CAAA;IACX;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAgBD,iFAAiF;AACjF,UAAU,YAAY;IACpB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAA;IACxB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAA;CAC1B;AAiCD;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,8BAA8B,kBAAkB,CAAA;AAC7D;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAQ,CAAA;AAEnD,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAMvH;AAoGD,wBAAgB,cAAc,CAC5B,OAAO,GAAE,qBAA0B,EACnC,QAAQ,GAAE,YAA4C,GACrD,WAAW,CAqWb"}