@jantstack/adonis-authz 2.0.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 (224) hide show
  1. package/README.md +934 -40
  2. package/build/commands/authz_catalog_diff.d.ts.map +1 -1
  3. package/build/commands/authz_catalog_diff.js +1 -2
  4. package/build/commands/authz_catalog_diff.js.map +1 -1
  5. package/build/commands/authz_catalog_prune_orphans.d.ts +78 -0
  6. package/build/commands/authz_catalog_prune_orphans.d.ts.map +1 -0
  7. package/build/commands/authz_catalog_prune_orphans.js +135 -0
  8. package/build/commands/authz_catalog_prune_orphans.js.map +1 -0
  9. package/build/commands/authz_catalog_sync.d.ts +17 -0
  10. package/build/commands/authz_catalog_sync.d.ts.map +1 -1
  11. package/build/commands/authz_catalog_sync.js +27 -4
  12. package/build/commands/authz_catalog_sync.js.map +1 -1
  13. package/build/commands/authz_freeze.d.ts +44 -0
  14. package/build/commands/authz_freeze.d.ts.map +1 -0
  15. package/build/commands/authz_freeze.js +94 -0
  16. package/build/commands/authz_freeze.js.map +1 -0
  17. package/build/commands/authz_reconcile.d.ts +111 -0
  18. package/build/commands/authz_reconcile.d.ts.map +1 -0
  19. package/build/commands/authz_reconcile.js +417 -0
  20. package/build/commands/authz_reconcile.js.map +1 -0
  21. package/build/commands/authz_relations_reconcile.d.ts +144 -0
  22. package/build/commands/authz_relations_reconcile.d.ts.map +1 -0
  23. package/build/commands/authz_relations_reconcile.js +438 -0
  24. package/build/commands/authz_relations_reconcile.js.map +1 -0
  25. package/build/commands/authz_scopes_relay.d.ts +97 -0
  26. package/build/commands/authz_scopes_relay.d.ts.map +1 -0
  27. package/build/commands/authz_scopes_relay.js +344 -0
  28. package/build/commands/authz_scopes_relay.js.map +1 -0
  29. package/build/commands/authz_scopes_requeue.d.ts +80 -0
  30. package/build/commands/authz_scopes_requeue.d.ts.map +1 -0
  31. package/build/commands/authz_scopes_requeue.js +230 -0
  32. package/build/commands/authz_scopes_requeue.js.map +1 -0
  33. package/build/commands/authz_unfreeze.d.ts +45 -0
  34. package/build/commands/authz_unfreeze.d.ts.map +1 -0
  35. package/build/commands/authz_unfreeze.js +157 -0
  36. package/build/commands/authz_unfreeze.js.map +1 -0
  37. package/build/commands/main.d.ts +16 -8
  38. package/build/commands/main.d.ts.map +1 -1
  39. package/build/commands/main.js +59 -6
  40. package/build/commands/main.js.map +1 -1
  41. package/build/commands/openfga_provision.d.ts +92 -4
  42. package/build/commands/openfga_provision.d.ts.map +1 -1
  43. package/build/commands/openfga_provision.js +155 -7
  44. package/build/commands/openfga_provision.js.map +1 -1
  45. package/build/configure.d.ts +11 -0
  46. package/build/configure.d.ts.map +1 -1
  47. package/build/configure.js +37 -1
  48. package/build/configure.js.map +1 -1
  49. package/build/index.d.ts +50 -11
  50. package/build/index.d.ts.map +1 -1
  51. package/build/index.js +49 -7
  52. package/build/index.js.map +1 -1
  53. package/build/providers/authz_provider.d.ts +31 -2
  54. package/build/providers/authz_provider.d.ts.map +1 -1
  55. package/build/providers/authz_provider.js +65 -2
  56. package/build/providers/authz_provider.js.map +1 -1
  57. package/build/services/relations.d.ts +14 -0
  58. package/build/services/relations.d.ts.map +1 -0
  59. package/build/services/relations.js +17 -0
  60. package/build/services/relations.js.map +1 -0
  61. package/build/src/{catalog.d.ts → catalog/catalog.d.ts} +41 -2
  62. package/build/src/catalog/catalog.d.ts.map +1 -0
  63. package/build/src/{catalog.js → catalog/catalog.js} +120 -14
  64. package/build/src/catalog/catalog.js.map +1 -0
  65. package/build/src/{catalog_cache.d.ts → catalog/catalog_cache.d.ts} +46 -22
  66. package/build/src/catalog/catalog_cache.d.ts.map +1 -0
  67. package/build/src/{catalog_cache.js → catalog/catalog_cache.js} +53 -43
  68. package/build/src/catalog/catalog_cache.js.map +1 -0
  69. package/build/src/define_config.d.ts +206 -3
  70. package/build/src/define_config.d.ts.map +1 -1
  71. package/build/src/define_config.js +11 -0
  72. package/build/src/define_config.js.map +1 -1
  73. package/build/src/drivers/database_driver.d.ts +128 -7
  74. package/build/src/drivers/database_driver.d.ts.map +1 -1
  75. package/build/src/drivers/database_driver.js +510 -24
  76. package/build/src/drivers/database_driver.js.map +1 -1
  77. package/build/src/drivers/database_relations_driver.d.ts +140 -0
  78. package/build/src/drivers/database_relations_driver.d.ts.map +1 -0
  79. package/build/src/drivers/database_relations_driver.js +748 -0
  80. package/build/src/drivers/database_relations_driver.js.map +1 -0
  81. package/build/src/drivers/openfga_driver.d.ts +777 -122
  82. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  83. package/build/src/drivers/openfga_driver.js +2228 -477
  84. package/build/src/drivers/openfga_driver.js.map +1 -1
  85. package/build/src/drivers/openfga_facts.d.ts +384 -0
  86. package/build/src/drivers/openfga_facts.d.ts.map +1 -0
  87. package/build/src/drivers/openfga_facts.js +836 -0
  88. package/build/src/drivers/openfga_facts.js.map +1 -0
  89. package/build/src/drivers/openfga_relations_driver.d.ts +186 -0
  90. package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -0
  91. package/build/src/drivers/openfga_relations_driver.js +724 -0
  92. package/build/src/drivers/openfga_relations_driver.js.map +1 -0
  93. package/build/src/errors.d.ts +430 -6
  94. package/build/src/errors.d.ts.map +1 -1
  95. package/build/src/errors.js +453 -6
  96. package/build/src/errors.js.map +1 -1
  97. package/build/src/freeze.d.ts +200 -0
  98. package/build/src/freeze.d.ts.map +1 -0
  99. package/build/src/freeze.js +306 -0
  100. package/build/src/freeze.js.map +1 -0
  101. package/build/src/http/app_access_middleware.d.ts.map +1 -0
  102. package/build/src/http/app_access_middleware.js.map +1 -0
  103. package/build/src/http/resource_access_middleware.d.ts +105 -0
  104. package/build/src/http/resource_access_middleware.d.ts.map +1 -0
  105. package/build/src/http/resource_access_middleware.js +81 -0
  106. package/build/src/http/resource_access_middleware.js.map +1 -0
  107. package/build/src/identity.d.ts +74 -1
  108. package/build/src/identity.d.ts.map +1 -1
  109. package/build/src/identity.js +100 -2
  110. package/build/src/identity.js.map +1 -1
  111. package/build/src/manager.d.ts +549 -6
  112. package/build/src/manager.d.ts.map +1 -1
  113. package/build/src/manager.js +2243 -193
  114. package/build/src/manager.js.map +1 -1
  115. package/build/src/models/authz_assignment.d.ts +7 -7
  116. package/build/src/models/authz_assignment.d.ts.map +1 -1
  117. package/build/src/models/authz_deny.d.ts +7 -7
  118. package/build/src/models/authz_deny.d.ts.map +1 -1
  119. package/build/src/models/authz_permission.d.ts +7 -7
  120. package/build/src/models/authz_permission.d.ts.map +1 -1
  121. package/build/src/models/authz_role.d.ts +7 -7
  122. package/build/src/models/authz_role.d.ts.map +1 -1
  123. package/build/src/models/authz_role_permission.d.ts +7 -7
  124. package/build/src/models/authz_role_permission.d.ts.map +1 -1
  125. package/build/src/openfga.d.ts +18 -2
  126. package/build/src/openfga.d.ts.map +1 -1
  127. package/build/src/openfga.js +15 -1
  128. package/build/src/openfga.js.map +1 -1
  129. package/build/src/reconcile.d.ts +37 -0
  130. package/build/src/reconcile.d.ts.map +1 -0
  131. package/build/src/reconcile.js +69 -0
  132. package/build/src/reconcile.js.map +1 -0
  133. package/build/src/relation_partition_trigger.d.ts +8 -0
  134. package/build/src/relation_partition_trigger.d.ts.map +1 -0
  135. package/build/src/relation_partition_trigger.js +93 -0
  136. package/build/src/relation_partition_trigger.js.map +1 -0
  137. package/build/src/relations/define_relations_config.d.ts +109 -0
  138. package/build/src/relations/define_relations_config.d.ts.map +1 -0
  139. package/build/src/relations/define_relations_config.js +211 -0
  140. package/build/src/relations/define_relations_config.js.map +1 -0
  141. package/build/src/relations/manager.d.ts +138 -0
  142. package/build/src/relations/manager.d.ts.map +1 -0
  143. package/build/src/relations/manager.js +494 -0
  144. package/build/src/relations/manager.js.map +1 -0
  145. package/build/src/relations/reconcile.d.ts +137 -0
  146. package/build/src/relations/reconcile.d.ts.map +1 -0
  147. package/build/src/relations/reconcile.js +294 -0
  148. package/build/src/relations/reconcile.js.map +1 -0
  149. package/build/src/relations_config_store.d.ts +22 -0
  150. package/build/src/relations_config_store.d.ts.map +1 -0
  151. package/build/src/relations_config_store.js +74 -0
  152. package/build/src/relations_config_store.js.map +1 -0
  153. package/build/src/scope_outbox.d.ts +123 -0
  154. package/build/src/scope_outbox.d.ts.map +1 -0
  155. package/build/src/scope_outbox.js +532 -0
  156. package/build/src/scope_outbox.js.map +1 -0
  157. package/build/src/{drivers → shared}/backend_guard.d.ts +42 -0
  158. package/build/src/shared/backend_guard.d.ts.map +1 -0
  159. package/build/src/{drivers → shared}/backend_guard.js +78 -1
  160. package/build/src/shared/backend_guard.js.map +1 -0
  161. package/build/src/{drivers → shared}/sql_expiry.d.ts +2 -0
  162. package/build/src/shared/sql_expiry.d.ts.map +1 -0
  163. package/build/src/{drivers → shared}/sql_expiry.js +4 -0
  164. package/build/src/shared/sql_expiry.js.map +1 -0
  165. package/build/src/shared/transaction_guard.d.ts +53 -0
  166. package/build/src/shared/transaction_guard.d.ts.map +1 -0
  167. package/build/src/shared/transaction_guard.js +74 -0
  168. package/build/src/shared/transaction_guard.js.map +1 -0
  169. package/build/src/sql_descendants.d.ts +55 -1
  170. package/build/src/sql_descendants.d.ts.map +1 -1
  171. package/build/src/sql_descendants.js +75 -1
  172. package/build/src/sql_descendants.js.map +1 -1
  173. package/build/src/testing/contract.d.ts +129 -4
  174. package/build/src/testing/contract.d.ts.map +1 -1
  175. package/build/src/testing/contract.js +915 -177
  176. package/build/src/testing/contract.js.map +1 -1
  177. package/build/src/testing/main.d.ts +10 -2
  178. package/build/src/testing/main.d.ts.map +1 -1
  179. package/build/src/testing/main.js +5 -1
  180. package/build/src/testing/main.js.map +1 -1
  181. package/build/src/testing/migration_contract.d.ts +293 -0
  182. package/build/src/testing/migration_contract.d.ts.map +1 -0
  183. package/build/src/testing/migration_contract.js +589 -0
  184. package/build/src/testing/migration_contract.js.map +1 -0
  185. package/build/src/testing/relations_contract.d.ts +73 -0
  186. package/build/src/testing/relations_contract.d.ts.map +1 -0
  187. package/build/src/testing/relations_contract.js +1238 -0
  188. package/build/src/testing/relations_contract.js.map +1 -0
  189. package/build/src/testing/relations_reconcile_contract.d.ts +24 -0
  190. package/build/src/testing/relations_reconcile_contract.d.ts.map +1 -0
  191. package/build/src/testing/relations_reconcile_contract.js +220 -0
  192. package/build/src/testing/relations_reconcile_contract.js.map +1 -0
  193. package/build/src/testing/scope_outbox_contract.d.ts +98 -0
  194. package/build/src/testing/scope_outbox_contract.d.ts.map +1 -0
  195. package/build/src/testing/scope_outbox_contract.js +677 -0
  196. package/build/src/testing/scope_outbox_contract.js.map +1 -0
  197. package/build/src/traits/authz_scopes.js +1 -1
  198. package/build/src/traits/authz_scopes.js.map +1 -1
  199. package/build/src/traits/has_uuid.d.ts +8 -8
  200. package/build/src/traits/has_uuid.d.ts.map +1 -1
  201. package/build/src/types.d.ts +1537 -84
  202. package/build/src/types.d.ts.map +1 -1
  203. package/build/src/types.js +65 -0
  204. package/build/src/types.js.map +1 -1
  205. package/build/stubs/config/authorization.stub +149 -5
  206. package/build/stubs/migration.stub +136 -0
  207. package/build/stubs/scopes_outbox_migration.stub +57 -0
  208. package/package.json +4 -2
  209. package/build/commands/openfga_import.d.ts +0 -34
  210. package/build/commands/openfga_import.d.ts.map +0 -1
  211. package/build/commands/openfga_import.js +0 -97
  212. package/build/commands/openfga_import.js.map +0 -1
  213. package/build/src/catalog.d.ts.map +0 -1
  214. package/build/src/catalog.js.map +0 -1
  215. package/build/src/catalog_cache.d.ts.map +0 -1
  216. package/build/src/catalog_cache.js.map +0 -1
  217. package/build/src/drivers/backend_guard.d.ts.map +0 -1
  218. package/build/src/drivers/backend_guard.js.map +0 -1
  219. package/build/src/drivers/sql_expiry.d.ts.map +0 -1
  220. package/build/src/drivers/sql_expiry.js.map +0 -1
  221. package/build/src/middleware/app_access_middleware.d.ts.map +0 -1
  222. package/build/src/middleware/app_access_middleware.js.map +0 -1
  223. /package/build/src/{middleware → http}/app_access_middleware.d.ts +0 -0
  224. /package/build/src/{middleware → http}/app_access_middleware.js +0 -0
@@ -0,0 +1,532 @@
1
+ /**
2
+ * **La outbox del árbol sobre Lucid** (3b-2d; panel 2, cruce 4 · S5).
3
+ *
4
+ * El puerto es `ScopeOutbox` (`src/types.ts`) y el paquete NO impone tabla:
5
+ * cualquier implementación vale. Esta es la que se publica para quien no
6
+ * quiera escribir la suya, junto con su migración
7
+ * (`stubs/scopes_outbox_migration.stub`, que hay que COPIAR al proyecto:
8
+ * `configure` no la publica sola porque la outbox es opt-in).
9
+ *
10
+ * import { sqlScopeOutbox } from '@jantstack/adonis-authz'
11
+ *
12
+ * const outbox = sqlScopeOutbox()
13
+ * export default defineConfig({
14
+ * scopes: { resolveChain, outbox },
15
+ * ...
16
+ * })
17
+ *
18
+ * // y en el sitio donde mueves tu árbol:
19
+ * await db.transaction(async (trx) => {
20
+ * await authorization.scopes.moved(unit, nuevaOrg, { within, actor, transaction: trx })
21
+ * await unit.useTransaction(trx).merge({ organizationId: nuevaOrg.uuid }).save()
22
+ * })
23
+ *
24
+ * Lo único que tiene que cumplir una implementación del puerto es que
25
+ * `enqueue` escriba DENTRO de la transacción que le pasan: es lo que hace
26
+ * que el cambio del árbol y su propagación al backend confirmen —o se vayan—
27
+ * juntos. Todo lo demás (nombres de columna, si las filas aplicadas se
28
+ * borran o se marcan, si hay reintento con backoff) es del consumidor.
29
+ *
30
+ * Aquí las filas aplicadas se MARCAN (`applied_at`), no se borran: la cola
31
+ * es también el registro de lo que se propagó y cuándo, y `authz:scopes:relay`
32
+ * no puede ser silencioso.
33
+ */
34
+ import { createHash } from 'node:crypto';
35
+ import db from '@adonisjs/lucid/services/db';
36
+ import { AuthorizationConfigError } from './errors.js';
37
+ import { systemClock } from './clock.js';
38
+ import { assertScope, assertSubject } from './identity.js';
39
+ import { guardSql } from './shared/backend_guard.js';
40
+ import { assertCallerTransaction } from './shared/transaction_guard.js';
41
+ import { dialectOf, isSqliteDialect } from './shared/sql_expiry.js';
42
+ import { APP_SCOPE, APP_SCOPE_TYPE } from './types.js';
43
+ const DEFAULT_TABLE = 'authz_scope_outbox';
44
+ const DEFAULT_TIMEOUT_MS = 5_000;
45
+ /** Intentos antes de aparcar una entrada (3b-2h · 🔴 2). */
46
+ const DEFAULT_MAX_ATTEMPTS = 5;
47
+ /**
48
+ * Leases tomados por ESTE proceso (SQLite y cualquier dialecto sin cerrojo de
49
+ * sesión): la exclusión es entonces de proceso, no de despliegue, y va escrita
50
+ * en el README. Con PostgreSQL y MySQL el cerrojo es del servidor.
51
+ */
52
+ const localLeases = new Set();
53
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
54
+ /** Las tres operaciones del árbol, tal como se guardan en la columna `op`. */
55
+ const OPS = ['attached', 'moved', 'detached'];
56
+ /** El scope que va a la fila: la raíz se guarda como `app` con uuid nulo. */
57
+ function columnsOf(scope) {
58
+ return { type: scope.type, uuid: scope.type === APP_SCOPE_TYPE ? null : scope.uuid };
59
+ }
60
+ /** La vuelta: `app` sin uuid es `APP_SCOPE`; el resto, la fila tal cual. */
61
+ function scopeOf(type, uuid) {
62
+ return type === APP_SCOPE_TYPE ? APP_SCOPE : { type, uuid: uuid === null ? null : String(uuid) };
63
+ }
64
+ /**
65
+ * Valida el cambio ANTES del INSERT (422): una cola es un diferido, y un
66
+ * `ScopeRef` mal formado que entrara aquí explotaría días después dentro del
67
+ * relay, lejos del call-site que lo escribió.
68
+ */
69
+ function assertChange(change) {
70
+ if (!change || !OPS.includes(change.op)) {
71
+ throw new AuthorizationConfigError(`sqlScopeOutbox: 'op' debe ser uno de ${OPS.join(', ')} (llegó ${String(change?.op)})`);
72
+ }
73
+ assertScope(change.child);
74
+ if (change.op !== 'detached')
75
+ assertScope(change.parent);
76
+ }
77
+ /** Namespace de los advisory locks del paquete en PostgreSQL. */
78
+ const ADVISORY_LOCK_NAMESPACE = 0x617a;
79
+ /** Tope de MySQL para el nombre de un `GET_LOCK` (más largo es un error 3158). */
80
+ const MYSQL_LOCK_NAME_MAX = 64;
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 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 const SCOPE_OUTBOX_TAKES_LEGACY_LOCK = false;
117
+ export function scopeOutboxLockNames(connection, table) {
118
+ const full = `authz_scope_outbox:${connection ?? 'default'}:${table}`;
119
+ return {
120
+ legacy: full.slice(0, MYSQL_LOCK_NAME_MAX),
121
+ current: `authz_scope_outbox:${createHash('sha1').update(full).digest('hex')}`,
122
+ };
123
+ }
124
+ /** Hash de 32 bits con signo (lo que acepta `pg_try_advisory_xact_lock`). */
125
+ function hash32(value) {
126
+ let hash = 0;
127
+ for (let i = 0; i < value.length; i++)
128
+ hash = (Math.imul(hash, 31) + value.charCodeAt(i)) | 0;
129
+ return hash;
130
+ }
131
+ /** El primer valor de un `rawQuery`, sea cual sea la forma del dialecto. */
132
+ function firstValue(result, column) {
133
+ const rows = Array.isArray(result) ? result[0] : (result?.rows ?? result);
134
+ const row = Array.isArray(rows) ? rows[0] : rows;
135
+ return row?.[column];
136
+ }
137
+ /**
138
+ * El cliente con el que se toma el lease: la conexión con nombre, la primaria
139
+ * del `db` de Lucid, o `null` si lo que se inyectó no es un cliente de Lucid
140
+ * (los dobles de test) — entonces el lease es de proceso.
141
+ */
142
+ function lockClientOf(database, connection) {
143
+ if (connection)
144
+ return db.connection(connection);
145
+ const client = database;
146
+ if (typeof client?.transaction === 'function' && client?.dialect)
147
+ return client;
148
+ if (typeof client?.connection === 'function') {
149
+ const primary = client.connection();
150
+ return typeof primary?.transaction === 'function' ? primary : null;
151
+ }
152
+ return null;
153
+ }
154
+ /**
155
+ * El sello `created_at` como `Date`, venga como venga del dialecto (medido:
156
+ * `Date` en PostgreSQL y MySQL, milisegundos como `number` en SQLite). Parsear
157
+ * un valor NO es leer el reloj (`tests/clock.spec.ts`). Ilegible ⇒ ausente.
158
+ */
159
+ function stampOf(value) {
160
+ if (value === null || value === undefined)
161
+ return undefined;
162
+ const date = value instanceof Date ? value : new Date(value);
163
+ return Number.isNaN(date.getTime()) ? undefined : date;
164
+ }
165
+ /** Una fila de la cola, tal como la ve el puerto. */
166
+ function rowToPending(row) {
167
+ const child = scopeOf(String(row.child_type), row.child_uuid);
168
+ const change = row.op === 'detached'
169
+ ? { op: 'detached', child }
170
+ : { op: row.op, child, parent: scopeOf(String(row.parent_type), row.parent_uuid) };
171
+ const actor = row.actor_type === null || row.actor_type === undefined
172
+ ? undefined
173
+ : { type: String(row.actor_type), uuid: String(row.actor_uuid) };
174
+ const createdAt = stampOf(row.created_at);
175
+ return {
176
+ id: row.id,
177
+ change,
178
+ attempts: Number(row.attempts ?? 0),
179
+ actor,
180
+ ...(row.last_error === null || row.last_error === undefined ? {} : { lastError: String(row.last_error) }),
181
+ // La EDAD de la cola (2.4.0-alpha.8): el sello de quien encoló, tal cual.
182
+ ...(createdAt === undefined ? {} : { createdAt }),
183
+ };
184
+ }
185
+ /**
186
+ * El id de la fila que acaba de insertar Lucid, sea cual sea la forma del
187
+ * dialecto (medido, 2.4.0-alpha.5): `[id]` en SQLite y MySQL, `[{ id }]` en
188
+ * PostgreSQL con `returning('id')`.
189
+ */
190
+ function insertedId(result) {
191
+ const first = Array.isArray(result) ? result[0] : result;
192
+ const id = first !== null && typeof first === 'object' ? first.id : first;
193
+ if (typeof id !== 'number' && typeof id !== 'string') {
194
+ throw new AuthorizationConfigError(`sqlScopeOutbox.enqueue: el motor no devolvió el id de la fila insertada (llegó ${JSON.stringify(first)}); ` +
195
+ 'sin recibo no hay `only`.');
196
+ }
197
+ return id;
198
+ }
199
+ /** Los ids de `only`: una lista NO vacía de números o cadenas (500 si no). */
200
+ function assertIds(ids) {
201
+ if (!Array.isArray(ids) || ids.length === 0 || ids.some((id) => typeof id !== 'number' && typeof id !== 'string')) {
202
+ throw new AuthorizationConfigError(`sqlScopeOutbox: filter.ids debe ser una lista NO vacía de ids (llegó ${JSON.stringify(ids)})`);
203
+ }
204
+ return ids;
205
+ }
206
+ /** La cota de `pending`/`dead`: entera y positiva o 500 (config rota). */
207
+ function assertLimit(limit) {
208
+ if (!(Number.isInteger(limit) && limit >= 1)) {
209
+ throw new AuthorizationConfigError(`sqlScopeOutbox: limit debe ser un entero >= 1 (llegó ${String(limit)})`);
210
+ }
211
+ }
212
+ export function sqlScopeOutbox(options = {}, database = db) {
213
+ const table = options.table ?? DEFAULT_TABLE;
214
+ if (!IDENTIFIER.test(table)) {
215
+ throw new AuthorizationConfigError(`sqlScopeOutbox: table '${table}' no es un identificador SQL simple (letras, dígitos y _).`);
216
+ }
217
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
218
+ const connection = options.connection;
219
+ const now = options.now ?? systemClock;
220
+ const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
221
+ if (!(Number.isInteger(maxAttempts) && maxAttempts >= 1)) {
222
+ throw new AuthorizationConfigError(`sqlScopeOutbox: maxAttempts debe ser un entero >= 1 (llegó ${String(options.maxAttempts)})`);
223
+ }
224
+ const requireTransaction = options.requireTransaction === undefined ? false : options.requireTransaction;
225
+ if (typeof requireTransaction !== 'boolean') {
226
+ throw new AuthorizationConfigError(`sqlScopeOutbox: requireTransaction debe ser un booleano (llegó ${String(options.requireTransaction)}); ` +
227
+ 'true = encolar sin { transaction } es 500 antes del INSERT (default false en 2.4; true en 2.5).');
228
+ }
229
+ /**
230
+ * La conexión de ESTA cola: la declarada, o la primaria del `db` de Lucid
231
+ * (un doble sin `primaryConnectionName` deja `undefined`: entonces se exige
232
+ * una transacción, pero no se juzga su conexión).
233
+ */
234
+ const ownConnection = connection ??
235
+ (typeof database.primaryConnectionName === 'string' ? database.primaryConnectionName : undefined);
236
+ /**
237
+ * El cliente del encolado: la transacción del consumidor si la hay — y
238
+ * SOLO si es una transacción abierta de la conexión de la cola (L-1 · 🟠 9,
239
+ * `assertCallerTransaction`): un `trx` de otra conexión encolaba en la base
240
+ * equivocada y `db` entero escribía fuera de toda transacción, en silencio.
241
+ */
242
+ const clientOf = (context) => {
243
+ const trx = assertCallerTransaction('sqlScopeOutbox.enqueue', context?.transaction, { connection: ownConnection });
244
+ if (trx === undefined && requireTransaction) {
245
+ // La fuga S5, nombrada (2.4.0-alpha.8): sin tu transacción la fila
246
+ // confirma sola y un rollback tuyo no se la lleva.
247
+ throw new AuthorizationConfigError('sqlScopeOutbox.enqueue: no llegó { transaction } y esta outbox tiene requireTransaction: true. Encolar fuera ' +
248
+ 'de tu unidad de trabajo es la fuga que la outbox existe para cerrar: la fila confirmaría sola, y si tu ' +
249
+ 'transacción hace rollback después el cambio se queda en la cola y el relay lo aplica al store — tu base ' +
250
+ 'diría un padre y el PDP otro. Pasa el `trx` de db.transaction() en scopes.attached/moved/detached ' +
251
+ '({ transaction: trx }). No se ha escrito nada.');
252
+ }
253
+ return trx ?? on(database);
254
+ };
255
+ const query = (operation, run) => guardSql('sqlScopeOutbox', operation, timeoutMs, run);
256
+ /** Igual que el resto del paquete: la conexión con nombre si se declaró. */
257
+ const on = (client) => (connection && client === database ? db.connection(connection) : client);
258
+ return {
259
+ async enqueue(change, context) {
260
+ assertChange(change);
261
+ if (context?.actor !== undefined)
262
+ assertSubject(context.actor);
263
+ const child = columnsOf(change.child);
264
+ const parent = change.op === 'detached' ? { type: null, uuid: null } : columnsOf(change.parent);
265
+ const client = clientOf(context);
266
+ // El RECIBO (alpha.5 · R1): el id de la fila, que es el mismo que
267
+ // devolverá `pending()`. `returning` solo lo aplica Lucid donde el
268
+ // dialecto lo soporta; en los demás el insert ya devuelve `[id]`.
269
+ const inserted = await query('enqueue', () => client.table(table).returning('id').insert({
270
+ op: change.op,
271
+ child_type: child.type,
272
+ child_uuid: child.uuid,
273
+ parent_type: parent.type,
274
+ parent_uuid: parent.uuid,
275
+ actor_type: context?.actor?.type ?? null,
276
+ actor_uuid: context?.actor?.uuid ?? null,
277
+ attempts: 0,
278
+ last_error: null,
279
+ created_at: now(),
280
+ applied_at: null,
281
+ }));
282
+ return { id: insertedId(inserted) };
283
+ },
284
+ async pending(limit, after, filter) {
285
+ assertLimit(limit);
286
+ const ids = filter === undefined ? undefined : assertIds(filter.ids);
287
+ const client = on(database);
288
+ const rows = await query('pending', () => {
289
+ // Lo APARCADO (`attempts >= maxAttempts`) sale por `dead()`, no por
290
+ // aquí: si volviera, la pasada la reintentaría eternamente.
291
+ let q = client
292
+ .from(table)
293
+ .whereNull('applied_at')
294
+ .where('attempts', '<', maxAttempts)
295
+ .orderBy('id', 'asc')
296
+ .limit(limit);
297
+ // `after` es el último id que el relay ya vio EN ESTA PASADA: lo que
298
+ // dejó sin aplicar sigue pendiente y sin esto volvería a salir el
299
+ // primero para siempre (3b-2h · 🔴 2).
300
+ if (after !== undefined)
301
+ q = q.where('id', '>', after);
302
+ // `only` (alpha.5 · R1) se filtra AQUÍ, en SQL: es la PK, 0,20 ms
303
+ // sobre un backlog de 501; en memoria habría que leer la cola entera.
304
+ if (ids !== undefined)
305
+ q = q.whereIn('id', ids);
306
+ return q;
307
+ });
308
+ return rows.map(rowToPending);
309
+ },
310
+ /**
311
+ * **La sonda de dependencia de `only`** (alpha.5 · R1): la pendiente más
312
+ * antigua ANTERIOR a `id` —sin aplicar, no aparcada— que nombre alguno de
313
+ * `scopes` como hijo o como padre. Una consulta de existencia con el
314
+ * índice `(applied_at, id)`: 0,69 ms medidos sobre 501 pendientes.
315
+ */
316
+ async earlierPending(id, scopes) {
317
+ const targets = scopes.map((scope) => {
318
+ assertScope(scope);
319
+ return columnsOf(scope);
320
+ });
321
+ if (targets.length === 0)
322
+ return null;
323
+ const client = on(database);
324
+ const rows = await query('earlierPending', () => client
325
+ .from(table)
326
+ .whereNull('applied_at')
327
+ .where('attempts', '<', maxAttempts)
328
+ .where('id', '<', id)
329
+ .where((names) => {
330
+ for (const target of targets) {
331
+ // La raíz se guarda como `app` con uuid NULO (solo puede ser padre).
332
+ names.orWhere((one) => {
333
+ one.where('child_type', target.type);
334
+ target.uuid === null ? one.whereNull('child_uuid') : one.where('child_uuid', target.uuid);
335
+ });
336
+ names.orWhere((one) => {
337
+ one.where('parent_type', target.type);
338
+ target.uuid === null ? one.whereNull('parent_uuid') : one.where('parent_uuid', target.uuid);
339
+ });
340
+ }
341
+ })
342
+ .orderBy('id', 'asc')
343
+ .limit(1));
344
+ return rows.length === 0 ? null : rowToPending(rows[0]);
345
+ },
346
+ /**
347
+ * Las entradas APARCADAS: pendientes que agotaron sus intentos. No se
348
+ * reintentan, se MIRAN (3b-2h · 🔴 2).
349
+ */
350
+ async dead(limit) {
351
+ assertLimit(limit);
352
+ const client = on(database);
353
+ const rows = await query('dead', () => client
354
+ .from(table)
355
+ .whereNull('applied_at')
356
+ .where('attempts', '>=', maxAttempts)
357
+ .orderBy('id', 'asc')
358
+ .limit(limit));
359
+ return rows.map(rowToPending);
360
+ },
361
+ /**
362
+ * **El lease del escritor ÚNICO** (3b-2h · 🟠 4). Cerrojo del SERVIDOR en
363
+ * PostgreSQL (`pg_try_advisory_xact_lock`, que se suelta con la
364
+ * transacción) y en MySQL (`GET_LOCK(name, 0)`); en SQLite —y en
365
+ * cualquier dialecto sin cerrojo de sesión— es un cerrojo de PROCESO, y
366
+ * eso está escrito: SQLite ya serializa sus escrituras y no es el motor
367
+ * de un despliegue con dos réplicas del relay.
368
+ *
369
+ * La transacción se abre SOLO para sostener el cerrojo (no escribe nada)
370
+ * y se revierte al soltarlo: es una conexión ocupada mientras dura la
371
+ * pasada, que es el precio de que dos pasadas no se pisen.
372
+ */
373
+ async acquire() {
374
+ const names = scopeOutboxLockNames(connection, table);
375
+ /** El nombre del cerrojo de PROCESO y del advisory de PG: el completo (allí no hay recorte). */
376
+ const name = `authz_scope_outbox:${connection ?? 'default'}:${table}`;
377
+ const client = lockClientOf(database, connection);
378
+ if (client === null || isSqliteDialect(client)) {
379
+ if (localLeases.has(name))
380
+ return null;
381
+ localLeases.add(name);
382
+ return { release: async () => void localLeases.delete(name) };
383
+ }
384
+ const dialect = dialectOf(client);
385
+ const trx = await query('acquire', () => client.transaction());
386
+ try {
387
+ if (dialect.startsWith('postgres')) {
388
+ const result = await query('acquire', () => trx.rawQuery('select pg_try_advisory_xact_lock(?, ?) as taken', [ADVISORY_LOCK_NAMESPACE, hash32(name)]));
389
+ if (!firstValue(result, 'taken')) {
390
+ await trx.rollback();
391
+ return null;
392
+ }
393
+ // El cerrojo es de la TRANSACCIÓN: revertirla lo suelta.
394
+ return { release: async () => void (await trx.rollback()) };
395
+ }
396
+ if (dialect.startsWith('mysql')) {
397
+ // **Los DOS locks** (alpha.5 · R6, transición): el nuevo —hash
398
+ // estable, sin colisión— y el viejo —el recortado a 64 de alpha.4—,
399
+ // para que un rolling deploy con las dos versiones siga siendo
400
+ // escritor único. Si cualquiera de los dos falla, se sueltan los que
401
+ // se tomaron y es `busy`. El viejo se retira en 2.5.
402
+ const taken = [];
403
+ const take = async (lockName) => {
404
+ const result = await query('acquire', () => trx.rawQuery('select get_lock(?, 0) as taken', [lockName]));
405
+ const ok = Number(firstValue(result, 'taken') ?? 0) === 1;
406
+ if (ok)
407
+ taken.push(lockName);
408
+ return ok;
409
+ };
410
+ const releaseAll = async () => {
411
+ try {
412
+ for (const lockName of taken.reverse())
413
+ await trx.rawQuery('select release_lock(?)', [lockName]);
414
+ }
415
+ finally {
416
+ await trx.rollback();
417
+ }
418
+ };
419
+ if (!(await take(names.current)) || (SCOPE_OUTBOX_TAKES_LEGACY_LOCK && !(await take(names.legacy)))) {
420
+ await releaseAll();
421
+ return null;
422
+ }
423
+ return { release: releaseAll };
424
+ }
425
+ // Un dialecto que no sabemos cerrar: se cierra en el proceso y se
426
+ // suelta la transacción (nunca se devuelve un lease de mentira).
427
+ await trx.rollback();
428
+ if (localLeases.has(name))
429
+ return null;
430
+ localLeases.add(name);
431
+ return { release: async () => void localLeases.delete(name) };
432
+ }
433
+ catch (error) {
434
+ await trx.rollback().catch(() => { });
435
+ throw error;
436
+ }
437
+ },
438
+ /**
439
+ * **La sonda anti-reversión de `requeue`** (alpha.5 · R2b), espejo de
440
+ * `earlierPending`: las entradas APLICADAS posteriores a `id` que nombren
441
+ * alguno de `scopes` como hijo o como padre, en orden de id. Una consulta
442
+ * acotada por `limit`; es lo que el manager mira antes de devolver una
443
+ * aparcada a la cola.
444
+ */
445
+ async laterApplied(id, scopes, limit) {
446
+ assertLimit(limit);
447
+ const targets = scopes.map((scope) => {
448
+ assertScope(scope);
449
+ return columnsOf(scope);
450
+ });
451
+ if (targets.length === 0)
452
+ return [];
453
+ const client = on(database);
454
+ const rows = await query('laterApplied', () => client
455
+ .from(table)
456
+ .whereNotNull('applied_at')
457
+ .where('id', '>', id)
458
+ .where((names) => {
459
+ for (const target of targets) {
460
+ names.orWhere((one) => {
461
+ one.where('child_type', target.type);
462
+ target.uuid === null ? one.whereNull('child_uuid') : one.where('child_uuid', target.uuid);
463
+ });
464
+ names.orWhere((one) => {
465
+ one.where('parent_type', target.type);
466
+ target.uuid === null ? one.whereNull('parent_uuid') : one.where('parent_uuid', target.uuid);
467
+ });
468
+ }
469
+ })
470
+ .orderBy('id', 'asc')
471
+ .limit(limit));
472
+ return rows.map(rowToPending);
473
+ },
474
+ /**
475
+ * **Reencola entradas APARCADAS** (alpha.5 · R2b): todo o nada, y
476
+ * `attempts = maxAttempts − 1` (decisión del dueño: vuelve a `dead()` en
477
+ * UNA pasada si la causa sigue rota, entra a la primera si se arregló;
478
+ * sin columna nueva). Cada id tiene que existir, no estar aplicado y
479
+ * tener los intentos agotados; si alguno no cumple se lanza nombrándolo
480
+ * ANTES de tocar nada — una fila aplicada no revive jamás (reencolar un
481
+ * `detached` aplicado volvería a purgar). `last_error` se conserva: la
482
+ * causa es historia, y la siguiente pasada la sobreescribe si vuelve a
483
+ * fallar. La política (qué se puede reencolar sin revertir el árbol) es
484
+ * del manager, no de aquí.
485
+ */
486
+ async requeue(ids) {
487
+ const wanted = assertIds(ids);
488
+ const client = on(database);
489
+ const rows = await query('requeue', () => client.from(table).whereIn('id', wanted).select('id', 'applied_at', 'attempts'));
490
+ const found = new Map(rows.map((row) => [String(row.id), row]));
491
+ const rejectedIds = wanted.filter((id) => {
492
+ const row = found.get(String(id));
493
+ return !row || (row.applied_at !== null && row.applied_at !== undefined) || Number(row.attempts) < maxAttempts;
494
+ });
495
+ if (rejectedIds.length > 0) {
496
+ throw new AuthorizationConfigError(`sqlScopeOutbox.requeue: ${rejectedIds.map(String).join(', ')} no existe(n) o no está(n) aparcada(s) ` +
497
+ `(ya aplicada, o todavía pendiente con intentos por gastar): no se ha tocado NINGUNA fila. ` +
498
+ 'Solo se reencola lo que sale por dead().');
499
+ }
500
+ await query('requeue', () => client
501
+ .from(table)
502
+ .whereIn('id', wanted)
503
+ .whereNull('applied_at')
504
+ .where('attempts', '>=', maxAttempts)
505
+ .update({ attempts: maxAttempts - 1 }));
506
+ // La comprobación y la escritura no van en una transacción (el cliente
507
+ // puede ser un doble): se relee, y si la cola cambió entre medias se dice.
508
+ const after = await query('requeue', () => client.from(table).whereIn('id', wanted).whereNull('applied_at').where('attempts', maxAttempts - 1).select('id'));
509
+ if (after.length !== wanted.length) {
510
+ throw new AuthorizationConfigError(`sqlScopeOutbox.requeue: se pidió reencolar ${wanted.length} fila(s) y quedaron ${after.length} en la cola: ` +
511
+ 'la outbox cambió entre la comprobación y la escritura. Mira dead() y repite.');
512
+ }
513
+ },
514
+ async markApplied(id) {
515
+ const client = on(database);
516
+ await query('markApplied', () => client.from(table).where('id', id).update({ applied_at: now(), last_error: null }));
517
+ },
518
+ async markFailed(id, error, options) {
519
+ const client = on(database);
520
+ // El intento se cuenta aunque el mensaje sea larguísimo: se recorta el
521
+ // texto, nunca el hecho de que falló. Salvo que la causa sea el BACKEND
522
+ // (`transient`, alpha.5 · R2a): entonces se escribe la causa —nada se
523
+ // vuelve silencioso— y NO se cuenta, porque `maxAttempts` converge
524
+ // sobre lo irreparable y una caída del store no dice nada de la fila.
525
+ await query('markFailed', () => {
526
+ const q = client.from(table).where('id', id).update({ last_error: String(error).slice(0, 2000) });
527
+ return options?.transient === true ? q : q.increment('attempts', 1);
528
+ });
529
+ },
530
+ };
531
+ }
532
+ //# sourceMappingURL=scope_outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope_outbox.js","sourceRoot":"","sources":["../../src/scope_outbox.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,34 @@ export declare function isAuthzError(error: unknown): boolean;
23
23
  * (2.5-B · K12): un error crudo del cliente nunca cruza la frontera.
24
24
  */
25
25
  export declare function isSqlDriverError(error: unknown): boolean;
26
+ /**
27
+ * ¿El error (o alguna de sus causas) es un choque del UNIQUE? Por la forma de
28
+ * cada cliente: PostgreSQL SQLSTATE `23505`, MySQL `errno` 1062
29
+ * (`ER_DUP_ENTRY`), SQLite `SQLITE_CONSTRAINT_UNIQUE` (o el `SQLITE_CONSTRAINT`
30
+ * genérico con «UNIQUE» en el mensaje). Se mira la cadena entera de causas:
31
+ * `guardSql` ya lo habrá envuelto en un `AuthorizationBackendError`.
32
+ *
33
+ * Lo usa el driver `database` DENTRO de una transacción del llamante (L-3):
34
+ * fuera de ella un choque se absorbe releyendo (K4), dentro no se puede —en
35
+ * PostgreSQL la transacción ya está abortada y en REPEATABLE READ la relectura
36
+ * no vería al ganador— y sale como 409 `E_AUTHZ_WRITE_CONFLICT`.
37
+ */
38
+ export declare function isUniqueViolation(error: unknown): boolean;
39
+ /**
40
+ * ¿El error (o alguna de sus causas) es un DEADLOCK que el motor resolvió
41
+ * eligiendo esta transacción como víctima? PostgreSQL SQLSTATE `40P01`
42
+ * (`deadlock_detected`; y `40001`, `serialization_failure`, la misma familia
43
+ * de «reintenta»), MySQL `errno` 1213 (`ER_LOCK_DEADLOCK`: InnoDB deshace la
44
+ * transacción ENTERA de la víctima). SQLite no tiene deadlocks entre
45
+ * escritores: el segundo recibe `SQLITE_BUSY` (503), no un deadlock.
46
+ *
47
+ * Lo usa el driver `database` de relaciones DENTRO de una transacción del
48
+ * llamante (L-4, 🟡 12 del auditor): dos transacciones que escriben dos
49
+ * relaciones en orden cruzado ⇒ el perdedor sale con **409
50
+ * `E_AUTHZ_WRITE_CONFLICT`** («haz rollback y reintenta»), jamás un error
51
+ * crudo del motor ni un 503 disfrazado de indeterminado.
52
+ */
53
+ export declare function isDeadlock(error: unknown): boolean;
26
54
  /**
27
55
  * ¿El error viene de un deadline vencido? knex lanza `KnexTimeoutError` (o, si
28
56
  * además falló la cancelación, el error de cancelación con `timeout` puesto);
@@ -87,6 +115,20 @@ export declare function resolveChain(resolver: ScopeChainResolver, scope: ScopeR
87
115
  * sigue siendo 503: no se limpia a ciegas cuando el árbol está caído.
88
116
  */
89
117
  export declare function canonicalScope(resolver: ScopeChainResolver, scope: ScopeRef, operation: string): Promise<ScopeRef>;
118
+ /**
119
+ * **Los scopes destino de un DELETE que no exige que el scope exista**
120
+ * (`revoke`/`removeDeny`; 3b-8 · A4, mismo cierre que 3b-2h · 🟠 3 dio a
121
+ * `scopes.detached`). Con la fila viva es exactamente UNO, el canónico
122
+ * (`chain[0]`). Sin cadena con la que canonizar —la fila ya borrada, o sin
123
+ * resolutor— la ortografía del llamante a secas hacía del delete un no-op
124
+ * SILENCIOSO cuando llegaba un alias del uuid (el mismo uuid sin guiones,
125
+ * que el tipo `uuid` de PG o una collation `*_ci` funden con la fila real):
126
+ * el hecho canónico seguía escrito y volvía a conceder si el scope se
127
+ * restauraba. Se reusa el fan-out de `scopeSpellings`: la ortografía del
128
+ * llamante Y la canónica de la que puede ser alias. Quitar nunca concede,
129
+ * así que borrar de más no existe aquí.
130
+ */
131
+ export declare function canonicalScopeTargets(resolver: ScopeChainResolver, scope: ScopeRef, operation: string): Promise<ScopeRef[]>;
90
132
  /** La cadena canónica, o 422 `E_AUTHZ_UNKNOWN_SCOPE` si el scope no existe (para escrituras). */
91
133
  export declare function assertKnownScope(resolver: ScopeChainResolver, scope: ScopeRef, operation: string): Promise<ScopeRef[]>;
92
134
  //# sourceMappingURL=backend_guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend_guard.d.ts","sourceRoot":"","sources":["../../../src/shared/backend_guard.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAI/D;;;;;;;;;;;GAWG;AAEH,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQxD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWzD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQlD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQrD;AAED;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAOnG;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,kBAO9B,CAAA;AAeD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,CA0C5B;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQxH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAQrB;AAED,iGAAiG;AACjG,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,EAAE,CAAC,CASrB"}