@jantstack/adonis-authz 2.4.0-alpha.8 → 2.5.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 (119) hide show
  1. package/README.md +300 -15
  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 +10 -3
  12. package/build/commands/authz_reconcile.d.ts.map +1 -1
  13. package/build/commands/authz_reconcile.js +168 -18
  14. package/build/commands/authz_reconcile.js.map +1 -1
  15. package/build/commands/authz_relations_reconcile.d.ts +66 -19
  16. package/build/commands/authz_relations_reconcile.d.ts.map +1 -1
  17. package/build/commands/authz_relations_reconcile.js +231 -44
  18. package/build/commands/authz_relations_reconcile.js.map +1 -1
  19. package/build/commands/authz_scopes_relay.d.ts.map +1 -1
  20. package/build/commands/authz_scopes_relay.js +82 -6
  21. package/build/commands/authz_scopes_relay.js.map +1 -1
  22. package/build/commands/authz_scopes_requeue.d.ts.map +1 -1
  23. package/build/commands/authz_scopes_requeue.js +0 -2
  24. package/build/commands/authz_scopes_requeue.js.map +1 -1
  25. package/build/commands/authz_unfreeze.d.ts +12 -4
  26. package/build/commands/authz_unfreeze.d.ts.map +1 -1
  27. package/build/commands/authz_unfreeze.js +74 -9
  28. package/build/commands/authz_unfreeze.js.map +1 -1
  29. package/build/index.d.ts +6 -4
  30. package/build/index.d.ts.map +1 -1
  31. package/build/index.js +5 -3
  32. package/build/index.js.map +1 -1
  33. package/build/providers/authz_provider.d.ts +1 -1
  34. package/build/providers/authz_provider.d.ts.map +1 -1
  35. package/build/providers/authz_provider.js +2 -1
  36. package/build/providers/authz_provider.js.map +1 -1
  37. package/build/src/define_config.d.ts +9 -0
  38. package/build/src/define_config.d.ts.map +1 -1
  39. package/build/src/define_config.js +11 -0
  40. package/build/src/define_config.js.map +1 -1
  41. package/build/src/drivers/database_driver.d.ts.map +1 -1
  42. package/build/src/drivers/database_driver.js +1 -0
  43. package/build/src/drivers/database_driver.js.map +1 -1
  44. package/build/src/drivers/database_relations_driver.d.ts +18 -6
  45. package/build/src/drivers/database_relations_driver.d.ts.map +1 -1
  46. package/build/src/drivers/database_relations_driver.js +91 -44
  47. package/build/src/drivers/database_relations_driver.js.map +1 -1
  48. package/build/src/drivers/openfga_driver.d.ts +62 -4
  49. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  50. package/build/src/drivers/openfga_driver.js +182 -28
  51. package/build/src/drivers/openfga_driver.js.map +1 -1
  52. package/build/src/drivers/openfga_relations_driver.d.ts +16 -4
  53. package/build/src/drivers/openfga_relations_driver.d.ts.map +1 -1
  54. package/build/src/drivers/openfga_relations_driver.js +71 -34
  55. package/build/src/drivers/openfga_relations_driver.js.map +1 -1
  56. package/build/src/errors.d.ts +50 -0
  57. package/build/src/errors.d.ts.map +1 -1
  58. package/build/src/errors.js +50 -0
  59. package/build/src/errors.js.map +1 -1
  60. package/build/src/freeze.d.ts +63 -4
  61. package/build/src/freeze.d.ts.map +1 -1
  62. package/build/src/freeze.js +107 -18
  63. package/build/src/freeze.js.map +1 -1
  64. package/build/src/identity.d.ts +120 -9
  65. package/build/src/identity.d.ts.map +1 -1
  66. package/build/src/identity.js +164 -12
  67. package/build/src/identity.js.map +1 -1
  68. package/build/src/manager.d.ts +64 -2
  69. package/build/src/manager.d.ts.map +1 -1
  70. package/build/src/manager.js +271 -35
  71. package/build/src/manager.js.map +1 -1
  72. package/build/src/openfga.d.ts +1 -1
  73. package/build/src/openfga.d.ts.map +1 -1
  74. package/build/src/openfga.js +1 -1
  75. package/build/src/openfga.js.map +1 -1
  76. package/build/src/relations/check_many.d.ts +17 -0
  77. package/build/src/relations/check_many.d.ts.map +1 -0
  78. package/build/src/relations/check_many.js +57 -0
  79. package/build/src/relations/check_many.js.map +1 -0
  80. package/build/src/relations/manager.d.ts +20 -0
  81. package/build/src/relations/manager.d.ts.map +1 -1
  82. package/build/src/relations/manager.js +84 -13
  83. package/build/src/relations/manager.js.map +1 -1
  84. package/build/src/relations/reconcile.d.ts +67 -0
  85. package/build/src/relations/reconcile.d.ts.map +1 -1
  86. package/build/src/relations/reconcile.js +123 -30
  87. package/build/src/relations/reconcile.js.map +1 -1
  88. package/build/src/relations/tuple_key.d.ts +25 -0
  89. package/build/src/relations/tuple_key.d.ts.map +1 -0
  90. package/build/src/relations/tuple_key.js +60 -0
  91. package/build/src/relations/tuple_key.js.map +1 -0
  92. package/build/src/shared/backend_guard.d.ts +30 -11
  93. package/build/src/shared/backend_guard.d.ts.map +1 -1
  94. package/build/src/shared/backend_guard.js +45 -15
  95. package/build/src/shared/backend_guard.js.map +1 -1
  96. package/build/src/shared/sql_expiry.d.ts +2 -0
  97. package/build/src/shared/sql_expiry.d.ts.map +1 -1
  98. package/build/src/shared/sql_expiry.js +4 -0
  99. package/build/src/shared/sql_expiry.js.map +1 -1
  100. package/build/src/testing/main.d.ts +3 -1
  101. package/build/src/testing/main.d.ts.map +1 -1
  102. package/build/src/testing/main.js +2 -1
  103. package/build/src/testing/main.js.map +1 -1
  104. package/build/src/testing/migration_contract.d.ts +9 -0
  105. package/build/src/testing/migration_contract.d.ts.map +1 -1
  106. package/build/src/testing/migration_contract.js +5 -2
  107. package/build/src/testing/migration_contract.js.map +1 -1
  108. package/build/src/testing/relations_contract.d.ts +14 -0
  109. package/build/src/testing/relations_contract.d.ts.map +1 -1
  110. package/build/src/testing/relations_contract.js +297 -21
  111. package/build/src/testing/relations_contract.js.map +1 -1
  112. package/build/src/testing/test_clock.d.ts +15 -0
  113. package/build/src/testing/test_clock.d.ts.map +1 -0
  114. package/build/src/testing/test_clock.js +65 -0
  115. package/build/src/testing/test_clock.js.map +1 -0
  116. package/build/src/types.d.ts +148 -9
  117. package/build/src/types.d.ts.map +1 -1
  118. package/build/src/types.js.map +1 -1
  119. package/package.json +5 -1
@@ -1,6 +1,6 @@
1
- import type { AuthorizationConfig } from './define_config.js';
1
+ import { type AuthorizationConfig } from './define_config.js';
2
2
  import type { FreezeKind, FreezeToken } from './freeze.js';
3
- import type { AuthorizationDriver, AuthorizationDriverCapabilities, AuthorizedScopes, CatalogRole, CatalogRoleRef, DenyOptions, DenyRef, ExcludedSubtree, GrantOptions, GrantOutcome, RoleQuery, ScopedRoleChanges, ScopedRoleSpec, ReconcileOptions, ReconcileReport, ScopedWriteOptions, ScopeEnqueueReceipt, ScopeRequeueReport, ScopeRelayReport, ScopeRef, ScopeTreeWriteOptions, ScopeType, SubjectRef } from './types.js';
3
+ import type { AuthorizationDriver, AuthorizationDriverCapabilities, AuthorizedScopes, CatalogRole, CatalogRoleRef, DenyOptions, DenyRef, ExcludedSubtree, GrantOptions, GrantOutcome, RoleQuery, ScopedRoleChanges, ScopedRoleSpec, ReconcileOptions, ReconcileReport, ScopedWriteOptions, ScopeEnqueueReceipt, ScopeRequeueReport, ScopeRelayReport, ScopeRef, ScopeTreeWriteOptions, ScopeType, SubjectRef, RelationsDriverFactory } from './types.js';
4
4
  /**
5
5
  * Lo que devuelve `AuthorizationManager.forRequest()`: la misma API que el
6
6
  * manager (lecturas, escrituras, `scopes.*`), con los ancestros memoizados
@@ -106,7 +106,27 @@ export interface FreezeStatus {
106
106
  */
107
107
  export interface FrozenWindow {
108
108
  fence: number;
109
+ /**
110
+ * El lease de la ventana. En la ventana del OPERADOR adoptada
111
+ * (2.4.0-alpha.10) es lo que le QUEDABA al adoptarla (`untilMs − ahora`);
112
+ * `null` solo si la ventana no caduca.
113
+ */
109
114
  leaseMs: number | null;
115
+ /**
116
+ * Solo en la ventana del OPERADOR adoptada (2.4.0-alpha.10, 🟡 3 del sexto
117
+ * informe de COGNITIV): el instante (ms de pared) en el que caduca, de la
118
+ * fila, o `null` si no caduca. Quien adopta la ventana no la renueva: si
119
+ * caduca a mitad, `lapsed` lo dice; esto dice que PUEDE caducar.
120
+ */
121
+ untilMs?: number | null;
122
+ /**
123
+ * EN QUÉ ventana corrió (2.4.0-alpha.10, S2): la del OPERADOR adoptada, una
124
+ * PROPIA tomada para la pasada, o la que ya sostenía este manager
125
+ * (`enclosing`). La salida de los comandos lo dice siempre: «corrí dentro del
126
+ * cutover» y «tomé mi ventana porque no había ninguna» eran letra a letra
127
+ * iguales.
128
+ */
129
+ window?: 'operator' | 'own' | 'enclosing';
110
130
  /** ¿Se perdió la ventana en algún momento? (renovación fallida, fila ajena o vencida). */
111
131
  lapsed: () => Promise<boolean>;
112
132
  }
@@ -234,8 +254,50 @@ export declare class AuthorizationManager {
234
254
  * `freezeStatus()`: eso es la fila, no la memoria.
235
255
  */
236
256
  get frozen(): boolean;
257
+ /**
258
+ * La clave (en `relations.drivers`) del driver de RELACIONES activo:
259
+ * `relations.default ?? default` (2.4.0-alpha.10). La lee el guard de la
260
+ * dirección rancia de `runRelationsReconcile`; desde el cierre del auditor
261
+ * (R4) el llamante ya no puede contradecirla con un `activeKey` propio.
262
+ */
263
+ relationsDriverKey(): string;
264
+ /** Las claves registradas en `relations.drivers` del config de ESTE manager (vacío si no hay relaciones). */
265
+ relationsDriverKeys(): string[];
266
+ /**
267
+ * La factory registrada bajo `key` en `relations.drivers` de ESTE manager, o
268
+ * `undefined` (cierre del auditor de alpha.10 · R4). `runRelationsReconcile`
269
+ * resuelve el origen y el destino por aquí —del MISMO config que decide el
270
+ * activo— y juzga la identidad por la factory resuelta, no por la etiqueta:
271
+ * dos claves con la misma factory son el mismo driver (y dos factories DISTINTAS
272
+ * sobre el mismo backend NO: límite declarado, cierre-2 · C1).
273
+ *
274
+ * **API de PLATAFORMA, junto a `driver()`** (cierre-2 · C6): con esta factory
275
+ * cualquier código que tenga el manager —vistas de `forRequest()` incluidas—
276
+ * construye un driver de relaciones NO activo y escribe en él sin `assertWrite`,
277
+ * `requireActor`, freeze ni `onRelationWrite`. No añade capacidad (el config ya
278
+ * da las factories), pero es otra cosa que buscar en una revisión.
279
+ */
280
+ relationsDriverFactory(key: string): RelationsDriverFactory | undefined;
237
281
  /** El freeze VIVO de la fila compartida, o `null`. Lo lee cualquiera; solo el token lo levanta. */
238
282
  freezeStatus(): Promise<FreezeStatus | null>;
283
+ /**
284
+ * **La ÚLTIMA ventana de la fila, también la CADUCADA** (2.4.0-alpha.10, 🟠 2
285
+ * del sexto informe de COGNITIV). `freezeStatus()` responde solo lo VIVO —y
286
+ * así lo siguen usando la barrera y los comandos—, de modo que una ventana
287
+ * cuyo lease venció era indistinguible de «nunca hubo»: `authz:unfreeze`
288
+ * decía éxito y el último paso del cutover no delataba que las escrituras
289
+ * habían vuelto a entrar a mitad. Se distingue por la fila: `releaseFreeze`
290
+ * pone el motivo a `null` al levantar, así que motivo puesto + `untilMs` en
291
+ * el pasado es una ventana que CADUCÓ sin que nadie la cerrase.
292
+ *
293
+ * `null` = nunca hubo o ya se levantó; `expired: false` = viva (lo mismo que
294
+ * `freezeStatus()`); `expired: true` = caducada. `checkedAtMs` es el instante
295
+ * del reloj del config con el que se decidió.
296
+ */
297
+ lastFreezeStatus(): Promise<(FreezeStatus & {
298
+ expired: boolean;
299
+ checkedAtMs: number;
300
+ }) | null>;
239
301
  /**
240
302
  * `freeze()` + `finally unfreeze(token)`. El `finally` es la parte que
241
303
  * importa: una migración que revienta a la mitad no puede dejar la
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/manager.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,16 @@
1
1
  var _a;
2
2
  import { Exception } from '@adonisjs/core/exceptions';
3
3
  import { v7 as uuidv7 } from 'uuid';
4
- import { assertCatalogUuid, assertIdentity, assertScope, assertScopeType, assertSubject, assertValidSlug, chainKeysFrom, normalizeRoleQuery, scopeFromKey, scopeKey, scopeSpellings, } from './identity.js';
4
+ import { activeRelationsDriverKey } from './define_config.js';
5
+ import { assertCatalogUuid, assertIdentity, assertScope, assertScopeType, assertSubject, assertValidSlug, chainKeysFrom, normalizeRoleQuery, scopeFromKey, scopeKey, } from './identity.js';
5
6
  import { expiryChanged } from './expiry.js';
6
7
  import { randomBytes } from 'node:crypto';
7
- import { DEFAULT_FREEZE_LEASE_MS, acquireFreeze, freezeIsLive, freezeKindOf, readFreezeRow, assertNotFrozenRow, releaseFreeze, renewFreeze, } from './freeze.js';
8
- import { assertKnownScope, isAuthzError, resolveChain, rootOnlyResolver } from './shared/backend_guard.js';
8
+ import { DEFAULT_FREEZE_LEASE_MS, acquireFreezeOutcome, UNKNOWN_HOLDER, freezeIsLive, freezeKindOf, readFreezeRow, assertNotFrozenRow, releaseFreeze, renewFreeze, } from './freeze.js';
9
+ import { assertKnownScope, canonicalScopeTargets, isAuthzError, resolveChain, rootOnlyResolver } from './shared/backend_guard.js';
9
10
  import { CatalogCache, GLOBAL_OWNER_KEY, invalidateAuthzCatalog, isRoleVisibleWith, readLocalRoles, withAuthzCatalogWrite } from './catalog/catalog_cache.js';
10
11
  import { assertAssignableAt } from './catalog/catalog.js';
11
12
  import { systemClock } from './clock.js';
12
- import { ActorRequiredError, AuthorizationBackendError, AuthorizationBackendTimeoutError, AuthorizationFrozenError, AuthorizationConfigError, AuthorizationInternalError, CatalogConflictError, InvalidIdentityError, MassPurgeRefusedError, PruneInterruptedError, NoDescendantsResolverError, NotWithinError, PermissionNotDelegableError, RankExceededError, RoleImmutableError, RoleLevelAboveOwnerError, ScopeCycleError, ScopeResolverError, TooManyScopesError, UnknownPermissionError, UnknownRoleError, FreezeHeldError, UnsupportedOperationError, ViewExpiredError, WithinRequiredError, WithinRootForbiddenError, ScopeDriftUnguardedError, ScopeDepthLimitError, ScopeRequeueRefusedError, } from './errors.js';
13
+ import { ActorRequiredError, AuthorizationBackendError, AuthorizationBackendTimeoutError, AuthorizationFrozenError, AuthorizationConfigError, AuthorizationInternalError, CatalogConflictError, InvalidIdentityError, MassPurgeRefusedError, PruneInterruptedError, NoDescendantsResolverError, NotWithinError, PermissionNotDelegableError, RankExceededError, RoleImmutableError, RoleLevelAboveOwnerError, ScopeCycleError, ScopeResolverError, TooManyScopesError, UnknownPermissionError, UnknownRoleError, FreezeHeldError, UnsupportedOperationError, ViewExpiredError, WithinRequiredError, WithinRootForbiddenError, ScopeDriftUnguardedError, ScopeDepthLimitError, ScopeRequeueRefusedError, StaleSourceRefusedError, OperatorWindowExpiredError, } from './errors.js';
13
14
  import { APP_SCOPE_TYPE, normalizeAuthorizationCapabilities } from './types.js';
14
15
  import { memoizeAncestors } from './memoize_ancestors.js';
15
16
  import { AUTHZ_TABLES_ORIGIN } from './reconcile.js';
@@ -20,12 +21,26 @@ const ROLE_DESCRIPTION_MAX = 500;
20
21
  function describeAnswer(answer) {
21
22
  if (!Array.isArray(answer))
22
23
  return `${answer === null ? 'null' : typeof answer} (no es un array)`;
23
- const offending = answer.find((b) => typeof b !== 'boolean');
24
- if (offending !== undefined || answer.some((b) => typeof b !== 'boolean')) {
25
- return `un array de ${answer.length} con un elemento que no es boolean (${typeof offending})`;
24
+ for (let i = 0; i < answer.length; i++) {
25
+ if (!(i in answer))
26
+ return `un array de ${answer.length} con un HUECO en la posición ${i}`;
27
+ if (typeof answer[i] !== 'boolean')
28
+ return `un array de ${answer.length} con un elemento que no es boolean en la posición ${i} (${typeof answer[i]})`;
26
29
  }
27
30
  return `un array de ${answer.length}`;
28
31
  }
32
+ /**
33
+ * ¿Cada POSICIÓN del array es un boolean? Por índice, nunca con `some`/`every`
34
+ * (que saltan los huecos de un array disperso: 🟡 A3 del auditor del lote A;
35
+ * gemelo de `checkMany` en `src/relations/manager.ts`).
36
+ */
37
+ function everyPositionBoolean(answers) {
38
+ for (let i = 0; i < answers.length; i++) {
39
+ if (typeof answers[i] !== 'boolean')
40
+ return false;
41
+ }
42
+ return true;
43
+ }
29
44
  /**
30
45
  * Configs que ya recibieron el aviso de seguridad opt-in (B7/B1): una vez
31
46
  * por config, no por instancia — `forRequest()` construye managers hijos con
@@ -406,12 +421,30 @@ export class AuthorizationManager {
406
421
  const finalReason = reason ?? 'una operación de plataforma';
407
422
  const holder = `${kind}:${process.pid}:${randomBytes(4).toString('hex')}`;
408
423
  const nowMs = root.#wallMs();
409
- const token = await acquireFreeze({ reason: finalReason, holder, untilMs: leaseMs === null ? null : nowMs + leaseMs, nowMs }, { driver: this.#config.default });
410
- if (token === null) {
411
- const row = await readFreezeRow({ driver: this.#config.default });
424
+ // alpha.10 · S2 (+ cierre del auditor · R2): una ventana de OPERADOR
425
+ // caducada no se pisa en silencio. Una fila con el lease vencido se toma
426
+ // encima (así la flota vuelve sola tras un SIGKILL de una pasada), pero la
427
+ // del operador es EVIDENCIA: pisarla y soltarla después la deja a NULL y
428
+ // borra el único rastro de que las escrituras volvieron a entrar a mitad
429
+ // del cutover. La exclusión vive DENTRO del UPDATE de `acquireFreezeOutcome`
430
+ // (atómica): aquí solo se lee el veredicto y la fila para redactarlo — un
431
+ // «compruebo y luego tomo» con dos lecturas del reloj era un TOCTOU.
432
+ const outcome = await acquireFreezeOutcome({ reason: finalReason, holder, untilMs: leaseMs === null ? null : nowMs + leaseMs, nowMs }, { driver: this.#config.default });
433
+ if (outcome.status === 'expired-operator') {
434
+ const row = outcome.row;
435
+ const when = row.untilMs === null ? '?' : new Date(row.untilMs).toISOString();
436
+ throw new OperatorWindowExpiredError(`freeze (${reason ?? kind}): la ventana del OPERADOR (${row.holder ?? '?'}, ${row.reason}, fence ` +
437
+ `${row.fence}) CADUCÓ el ${when} y nadie la cerró: desde entonces las escrituras volvieron a entrar en ` +
438
+ `toda la flota. No se toma una ventana propia encima —borraría ese rastro al soltarla y la pasada se ` +
439
+ `certificaría como si hubiera corrido dentro del cutover—. Reconócela con authz:unfreeze (dice cuánto ` +
440
+ `tiempo estuvo abierta y limpia la fila) y reabre la ventana con authz:freeze antes de repetir.`);
441
+ }
442
+ if (outcome.status === 'held') {
443
+ const row = outcome.row;
412
444
  throw new FreezeHeldError(`freeze: ya hay un freeze VIVO de otro dueño (${row.holder ?? '?'}, fence ${row.fence}, motivo: ${row.reason ?? '?'}). ` +
413
445
  `Espera a que termine, o levántalo con authz:unfreeze si su proceso murió sin lease.`);
414
446
  }
447
+ const token = outcome.token;
415
448
  const held = { token, reason: finalReason, kind, leaseMs, timer: null, lapsed: false };
416
449
  if (leaseMs !== null) {
417
450
  const interval = Math.max(250, Math.floor(leaseMs / 3));
@@ -472,6 +505,39 @@ export class AuthorizationManager {
472
505
  get frozen() {
473
506
  return this.#root().#heldFreeze !== null;
474
507
  }
508
+ /**
509
+ * La clave (en `relations.drivers`) del driver de RELACIONES activo:
510
+ * `relations.default ?? default` (2.4.0-alpha.10). La lee el guard de la
511
+ * dirección rancia de `runRelationsReconcile`; desde el cierre del auditor
512
+ * (R4) el llamante ya no puede contradecirla con un `activeKey` propio.
513
+ */
514
+ relationsDriverKey() {
515
+ return activeRelationsDriverKey(this.#root().#config);
516
+ }
517
+ /** Las claves registradas en `relations.drivers` del config de ESTE manager (vacío si no hay relaciones). */
518
+ relationsDriverKeys() {
519
+ return Object.keys(this.#root().#config.relations?.drivers ?? {});
520
+ }
521
+ /**
522
+ * La factory registrada bajo `key` en `relations.drivers` de ESTE manager, o
523
+ * `undefined` (cierre del auditor de alpha.10 · R4). `runRelationsReconcile`
524
+ * resuelve el origen y el destino por aquí —del MISMO config que decide el
525
+ * activo— y juzga la identidad por la factory resuelta, no por la etiqueta:
526
+ * dos claves con la misma factory son el mismo driver (y dos factories DISTINTAS
527
+ * sobre el mismo backend NO: límite declarado, cierre-2 · C1).
528
+ *
529
+ * **API de PLATAFORMA, junto a `driver()`** (cierre-2 · C6): con esta factory
530
+ * cualquier código que tenga el manager —vistas de `forRequest()` incluidas—
531
+ * construye un driver de relaciones NO activo y escribe en él sin `assertWrite`,
532
+ * `requireActor`, freeze ni `onRelationWrite`. No añade capacidad (el config ya
533
+ * da las factories), pero es otra cosa que buscar en una revisión.
534
+ */
535
+ relationsDriverFactory(key) {
536
+ const drivers = this.#root().#config.relations?.drivers;
537
+ if (drivers === undefined || !Object.prototype.hasOwnProperty.call(drivers, key))
538
+ return undefined;
539
+ return drivers[key];
540
+ }
475
541
  /** El freeze VIVO de la fila compartida, o `null`. Lo lee cualquiera; solo el token lo levanta. */
476
542
  async freezeStatus() {
477
543
  const row = await readFreezeRow({ driver: this.#config.default });
@@ -479,12 +545,42 @@ export class AuthorizationManager {
479
545
  return null;
480
546
  return {
481
547
  reason: row.reason,
482
- holder: row.holder ?? '?',
548
+ holder: row.holder ?? UNKNOWN_HOLDER,
483
549
  kind: freezeKindOf(row.holder),
484
550
  untilMs: row.untilMs,
485
551
  fence: row.fence,
486
552
  };
487
553
  }
554
+ /**
555
+ * **La ÚLTIMA ventana de la fila, también la CADUCADA** (2.4.0-alpha.10, 🟠 2
556
+ * del sexto informe de COGNITIV). `freezeStatus()` responde solo lo VIVO —y
557
+ * así lo siguen usando la barrera y los comandos—, de modo que una ventana
558
+ * cuyo lease venció era indistinguible de «nunca hubo»: `authz:unfreeze`
559
+ * decía éxito y el último paso del cutover no delataba que las escrituras
560
+ * habían vuelto a entrar a mitad. Se distingue por la fila: `releaseFreeze`
561
+ * pone el motivo a `null` al levantar, así que motivo puesto + `untilMs` en
562
+ * el pasado es una ventana que CADUCÓ sin que nadie la cerrase.
563
+ *
564
+ * `null` = nunca hubo o ya se levantó; `expired: false` = viva (lo mismo que
565
+ * `freezeStatus()`); `expired: true` = caducada. `checkedAtMs` es el instante
566
+ * del reloj del config con el que se decidió.
567
+ */
568
+ async lastFreezeStatus() {
569
+ const row = await readFreezeRow({ driver: this.#config.default });
570
+ if (row.reason === null)
571
+ return null;
572
+ const nowMs = this.#root().#wallMs();
573
+ return {
574
+ reason: row.reason,
575
+ holder: row.holder ?? UNKNOWN_HOLDER,
576
+ kind: freezeKindOf(row.holder),
577
+ untilMs: row.untilMs,
578
+ fence: row.fence,
579
+ expired: !freezeIsLive(row, nowMs),
580
+ // El instante (reloj del config) con el que se decidió `expired`.
581
+ checkedAtMs: nowMs,
582
+ };
583
+ }
488
584
  /**
489
585
  * `freeze()` + `finally unfreeze(token)`. El `finally` es la parte que
490
586
  * importa: una migración que revienta a la mitad no puede dejar la
@@ -502,7 +598,13 @@ export class AuthorizationManager {
502
598
  operatorAsContext: options.operatorAsContext === true,
503
599
  });
504
600
  try {
505
- return await fn({ fence: context.fence, leaseMs: context.leaseMs, lapsed: context.lapsed });
601
+ return await fn({
602
+ fence: context.fence,
603
+ leaseMs: context.leaseMs,
604
+ lapsed: context.lapsed,
605
+ ...(context.untilMs === undefined ? {} : { untilMs: context.untilMs }),
606
+ ...(context.window === undefined ? {} : { window: context.window }),
607
+ });
506
608
  }
507
609
  finally {
508
610
  await context.release();
@@ -528,6 +630,7 @@ export class AuthorizationManager {
528
630
  return {
529
631
  fence: outer.token.fence,
530
632
  leaseMs: outer.leaseMs,
633
+ window: 'enclosing',
531
634
  release: async () => { },
532
635
  lapsed: () => root.#tokenLapsed(outer.token, outer.lapsed),
533
636
  };
@@ -536,9 +639,14 @@ export class AuthorizationManager {
536
639
  const status = await this.freezeStatus();
537
640
  if (status !== null && status.kind === 'operator') {
538
641
  const token = { fence: status.fence, holder: status.holder };
642
+ // alpha.10 · 3: la caducidad REAL de la ventana adoptada. Hasta alpha.9
643
+ // `leaseMs: null` fijo — el reporte decía «sin caducidad» de una ventana
644
+ // abierta con `--lease-ms` (la certificación sí leía la fila).
539
645
  return {
540
646
  fence: status.fence,
541
- leaseMs: null,
647
+ leaseMs: status.untilMs === null ? null : Math.max(0, status.untilMs - root.#wallMs()),
648
+ untilMs: status.untilMs,
649
+ window: 'operator',
542
650
  release: async () => { },
543
651
  lapsed: () => root.#tokenLapsed(token, false),
544
652
  };
@@ -549,6 +657,7 @@ export class AuthorizationManager {
549
657
  return {
550
658
  fence: token.fence,
551
659
  leaseMs: held.leaseMs,
660
+ window: 'own',
552
661
  release: async () => {
553
662
  try {
554
663
  await this.unfreeze(token);
@@ -731,14 +840,19 @@ export class AuthorizationManager {
731
840
  //
732
841
  // Y cuando NO hay cadena —la fila ya no existe, que es el orden
733
842
  // soportado de `detached` (3F · S1)— no hay con qué canonizar: se
734
- // purgan TODAS las ortografías de las que el uuid del llamante puede
735
- // ser alias (`scopeSpellings`, 3b-2h · 🟠 3). Con la fila viva esto es
736
- // exactamente una, la de la tabla; sin ella, la del llamante y la
737
- // canónica que un motor pudo fundir con la suya. Antes se usaba la del
738
- // llamante a secas: `purgeScope` demostraba cero sobre un objeto que no
739
- // existe, devolvía OK, y el scope real seguía concediendo para siempre.
740
- const chain = await resolveChain(this.#freshResolver(), child, 'scopes.detached');
741
- const targets = chain ? [chain[0]] : scopeSpellings(child);
843
+ // purgan las ortografías de las que el uuid del llamante puede ser
844
+ // alias (`scopeSpellings`, 3b-2h · 🟠 3: lo que PG funde,
845
+ // `pgFusedSpellings`) MENOS las que el resolutor reconozca como un
846
+ // scope VIVO distinto del pedido (cierre-3 del lote A · 🟡 C2-2: con
847
+ // ids de texto, la canónica de un alias puede ser la unit viva de otro
848
+ // tenant, y `purgeScope` en `database` le borraría los denies). Con la
849
+ // fila viva esto es exactamente una, la de la tabla. El punto de paso
850
+ // es ÚNICO y compartido con `revoke`/`removeDeny` de los dos drivers
851
+ // (`canonicalScopeTargets`); un resolutor que falla al contrastar es
852
+ // 503, nunca «purgar por si acaso». Antes se usaba la del llamante a
853
+ // secas: `purgeScope` demostraba cero sobre un objeto que no existe,
854
+ // devolvía OK, y el scope real seguía concediendo para siempre.
855
+ const targets = await canonicalScopeTargets(this.#freshResolver(), child, 'scopes.detached');
742
856
  const outbox = this.#outbox();
743
857
  if (outbox) {
744
858
  // La identidad se resuelve AQUÍ, con la fila del consumidor todavía
@@ -857,12 +971,13 @@ export class AuthorizationManager {
857
971
  throw new AuthorizationConfigError(`authz:scopes:relay: deadDetail solo admite 'full' (llegó ${JSON.stringify(options.deadDetail)}); sin él, bajo only, dead[] va sin error.`);
858
972
  }
859
973
  const inputs = await _a.#relayInputs(outbox, options.only, batchSize, options.deadDetail);
860
- const { only, filter, dead, deadCount, deadEntries, missingOf } = inputs;
974
+ const { only, filter, dead, deadCount, deadEntries, unseenOf } = inputs;
861
975
  if (dryRun) {
862
976
  const batch = await outbox.pending(limit, undefined, filter);
863
977
  _a.#assertOnlyHonoured(batch, only);
864
978
  const extra = batch.length >= limit ? true : false;
865
979
  const seen = new Set([...batch.map((b) => String(b.id)), ...deadEntries.map((d) => String(d.id))]);
980
+ const unseen = await unseenOf(seen);
866
981
  // La edad es GLOBAL: sin filtro la cabeza es `batch[0]`; con `only` hay que mirar la cola entera.
867
982
  const queue = filter === undefined ? this.#queueOf(batch[0]) : (await this.#queueStatus(outbox)).queue;
868
983
  return {
@@ -878,7 +993,7 @@ export class AuthorizationManager {
878
993
  dryRun: true,
879
994
  wouldApply: batch.map((item) => ({ id: item.id, change: item.change, attempts: item.attempts })),
880
995
  entries: deadEntries,
881
- missing: missingOf(seen),
996
+ ...unseen,
882
997
  };
883
998
  }
884
999
  // El lease del escritor ÚNICO. Una outbox que no sabe darlo se comporta
@@ -919,8 +1034,14 @@ export class AuthorizationManager {
919
1034
  // llegar a `batchSize`). Una sola lectura de más, y solo si no cabía.
920
1035
  const census = dead.length < batchSize ? dead : await _a.#deadLetters(outbox, PARKED_CENSUS_MAX);
921
1036
  const deadCount = census.length;
922
- /** Lo aparcado que se pidió (con `only`) o todo lo aparcado, para `entries`. */
923
- const deadEntries = read
1037
+ /**
1038
+ * Lo aparcado que se pidió (con `only`) o todo lo aparcado, para `entries`.
1039
+ * Cierre-2 · C2 (gemelo): con `only` sale del CENSO completo si la lectura se
1040
+ * cortó en `batchSize` — si no, un id pedido aparcado más allá del primer lote
1041
+ * era `missing` («no estaba ni pendiente ni aparcado») estando aparcado.
1042
+ */
1043
+ const deadSource = only !== undefined && read.length >= batchSize ? census : read;
1044
+ const deadEntries = deadSource
924
1045
  .filter((item) => only === undefined || only.has(String(item.id)))
925
1046
  .map((item) => ({
926
1047
  id: item.id,
@@ -929,8 +1050,29 @@ export class AuthorizationManager {
929
1050
  attempts: item.attempts,
930
1051
  ...(item.error === undefined ? {} : { reason: item.error }),
931
1052
  }));
932
- const missingOf = (seen) => only === undefined ? [] : [...only.values()].filter((id) => !seen.has(String(id))).map((id) => _a.#rawId(onlyIds, id));
933
- return { only, filter, dead, census, deadCount, deadEntries, missingOf };
1053
+ /**
1054
+ * **Qué fue de los ids pedidos que la pasada NO vio** (cierre-2 de alpha.10 ·
1055
+ * C2). Hasta el cierre-2 todos eran `missing`; pero un id fuera del lote porque
1056
+ * la pasada alcanzó `limit` sigue PENDIENTE. Se pregunta a la cola —filtrada por
1057
+ * esos ids, sin límite propio, UNA lectura y solo si hay alguno sin ver—: lo que
1058
+ * sigue pendiente es `notEvaluated`; el resto, `missing` (no está en la cola: lo
1059
+ * aparcado ya está en `seen`).
1060
+ */
1061
+ const unseenOf = async (seen) => {
1062
+ if (only === undefined)
1063
+ return { missing: [], notEvaluated: [] };
1064
+ const unseen = [...only.values()].filter((id) => !seen.has(id)).map((id) => _a.#rawId(onlyIds, id));
1065
+ if (unseen.length === 0)
1066
+ return { missing: [], notEvaluated: [] };
1067
+ const stillPending = await outbox.pending(unseen.length, undefined, { ids: unseen });
1068
+ _a.#assertOnlyHonoured(stillPending, new Set(unseen.map(String)));
1069
+ const pendingIds = new Set(stillPending.map((item) => String(item.id)));
1070
+ return {
1071
+ missing: unseen.filter((id) => !pendingIds.has(String(id))),
1072
+ notEvaluated: unseen.filter((id) => pendingIds.has(String(id))),
1073
+ };
1074
+ };
1075
+ return { only, filter, dead, census, deadCount, deadEntries, unseenOf };
934
1076
  }
935
1077
  /** El reporte de una pasada que NO entró: otra tiene el lease. Nada aplicado, nada tocado. */
936
1078
  async #busyReport(outbox, inputs) {
@@ -949,6 +1091,7 @@ export class AuthorizationManager {
949
1091
  wouldApply: [],
950
1092
  entries: inputs.deadEntries,
951
1093
  missing: [],
1094
+ notEvaluated: [],
952
1095
  };
953
1096
  }
954
1097
  /**
@@ -1020,7 +1163,7 @@ export class AuthorizationManager {
1020
1163
  * (auditor 🟠 5: tomar el lease dos veces era `busy` en verde).
1021
1164
  */
1022
1165
  async #drainScopeChanges(outbox, input) {
1023
- const { limit, batchSize, only, filter, dead, census, deadCount, deadEntries, missingOf } = input;
1166
+ const { limit, batchSize, only, filter, dead, census, deadCount, deadEntries, unseenOf } = input;
1024
1167
  const driver = await this.driver();
1025
1168
  const applied = [];
1026
1169
  const deferred = [];
@@ -1188,7 +1331,7 @@ export class AuthorizationManager {
1188
1331
  dryRun: false,
1189
1332
  wouldApply: [],
1190
1333
  entries,
1191
- missing: missingOf(new Set(entries.map((e) => String(e.id)))),
1334
+ ...(await unseenOf(new Set(entries.map((e) => String(e.id))))),
1192
1335
  };
1193
1336
  }
1194
1337
  /**
@@ -1586,6 +1729,49 @@ export class AuthorizationManager {
1586
1729
  throw new UnsupportedOperationError('reconcile', `authz:reconcile --to=${name}`, name, `El driver '${name}' no sabe reconstruirse desde 'authz_*' + el árbol del consumidor. ` +
1587
1730
  `El driver 'database' es ese caso a propósito: sus tablas son el ORIGEN.`);
1588
1731
  }
1732
+ // **La dirección RANCIA** (2.4.0-alpha.10; 🔴 4 del sexto informe de
1733
+ // COGNITIV y cuarta iteración del LINAJE «origen rancio del reconcile»:
1734
+ // 3b-5 cerró la pasada por defecto, el primer guard de alpha.10 cerró
1735
+ // `--from`, y quedaba abierto el origen elegido POR DESCARTE). Regla del
1736
+ // dueño: el tercer defecto de un linaje se arregla estructuralmente, así
1737
+ // que el guard NO mira la bandera, mira el ORIGEN RESUELTO — con la MISMA
1738
+ // decisión que `#factsOrigin` (`#isMaintenancePass`), no con una copia:
1739
+ // si `to` es el driver ACTIVO, el único origen legítimo es `to` mismo (la
1740
+ // pasada de MANTENIMIENTO); cualquier otro —`--from`, el driver elegido por
1741
+ // descarte o las tablas `authz_*`— dejó de ver los hechos en el cutover y la
1742
+ // pasada re-escribiría lo revocado después (con o sin --prune; el seguro de
1743
+ // borrado masivo no salta: el origen no está vacío). Se decide por `to`,
1744
+ // nunca por `from`: el cutover inicial y la vuelta atrás ANTES de cambiar
1745
+ // `default` tienen `to` ≠ activo y pasan. Antes de resolver el origen y de
1746
+ // congelar. `--dry-run` no lanza (es el verificador): lo marca y
1747
+ // `reconcileLines` lo hace deriva. `from === to` sigue siendo el 500
1748
+ // E_AUTHZ_CONFIG de 3b-3b.
1749
+ const stale = this.#sameDriver(name, this.#config.default) &&
1750
+ (options.from === undefined || !this.#sameDriver(options.from, name)) &&
1751
+ !this.#isMaintenancePass(name, target, options.from);
1752
+ const allowStale = options.allowStaleSource === true;
1753
+ if (stale && !allowStale && options.dryRun !== true) {
1754
+ const how = options.from !== undefined ? `--from=${options.from}` : 'sin --from';
1755
+ // Cierre del auditor (⚪ 8): con UN solo driver registrado no hay descarte que
1756
+ // hacer; la letra no puede hablar de un origen que no existe.
1757
+ const others = Object.keys(this.#config.drivers ?? {}).filter((key) => !this.#sameDriver(key, name));
1758
+ const originLabel = options.from !== undefined
1759
+ ? `el origen '${options.from}' no lo es`
1760
+ : others.length === 0
1761
+ ? `no hay ningún otro driver registrado ni pasada de mantenimiento para él (no guarda sus hechos en su ` +
1762
+ `backend: no declara hierarchyFacts), así que no hay nada de dónde reconciliar hacia '${name}'`
1763
+ : `sin --from el origen se elegiría POR DESCARTE (las tablas authz_* u otro driver registrado: ` +
1764
+ `${others.join(', ')}), y no es '${name}'`;
1765
+ throw new StaleSourceRefusedError(`authz:reconcile --to=${name} ${how}: '${name}' es el driver ACTIVO (config.default` +
1766
+ `${name === this.#config.default ? '' : `: la clave resuelve la MISMA factory que '${this.#config.default}'`}) y ${originLabel}. ` +
1767
+ `Esa dirección re-escribiría en el driver activo hechos que el origen ya no refleja: resucitaría las ` +
1768
+ `revocaciones hechas desde el cutover (y con --prune borraría lo concedido después). No se ha leído, ` +
1769
+ `congelado ni escrito nada. Hacia el driver activo el único origen es él mismo: la pasada de mantenimiento ` +
1770
+ `(authz:reconcile --to=${name} [--dry-run], sin --from) si su driver guarda sus hechos; y si lo que quieres ` +
1771
+ `es volver atrás, migra en la otra dirección ANTES de cambiar config.default. Si de verdad el origen sigue ` +
1772
+ `siendo la fuente de verdad (el motor aún no ha escrito desde '${name}'), la decisión humana es ` +
1773
+ `--allow-stale-source (allowStaleSource: true).`);
1774
+ }
1589
1775
  // **De dónde salen los HECHOS** (3b-5): la decisión que faltaba, y la que
1590
1776
  // el destino no puede tomar por su cuenta. Ver `#factsOrigin`.
1591
1777
  const origin = await this.#factsOrigin(name, target, options.from);
@@ -1596,10 +1782,19 @@ export class AuthorizationManager {
1596
1782
  // `authz_*` no construye ningún driver de más. Y el origen se resuelve
1597
1783
  // UNA vez.
1598
1784
  facts: origin.enumerate,
1599
- factsOrigin: { name: origin.name, authzTables: origin.authzTables },
1785
+ factsOrigin: { name: origin.name, authzTables: origin.authzTables, ...(origin.self ? { self: true } : {}) },
1600
1786
  // El techo de profundidad del consumidor, si lo declaró (alpha.5 · R0):
1601
1787
  // el mismo mando que el gate de `scopes.*`.
1602
1788
  ...(this.#config.scopes?.maxDepth === undefined ? {} : { maxDepth: this.#config.scopes.maxDepth }),
1789
+ // **Los tipos del OTRO subsistema del mismo backend** (alpha.9 · B · S4),
1790
+ // si el consumidor declaró relaciones. Es SOLO para el reporte: parte lo
1791
+ // ajeno en «declarado» y «no lo reclama nadie». La decisión de BORRAR no
1792
+ // lo mira —la toma la lista blanca congelada del destino—, porque el
1793
+ // proceso que corre un cutover puede no tener esta config y aun así
1794
+ // tiene que respetar las tuplas de quien sí la tiene.
1795
+ ...(this.#config.relations?.config === undefined
1796
+ ? {}
1797
+ : { relationTypes: this.#config.relations.config.objectTypes.map((type) => type.type) }),
1603
1798
  };
1604
1799
  const pass = async () => {
1605
1800
  const report = await target.reconcile(source, options);
@@ -1609,6 +1804,8 @@ export class AuthorizationManager {
1609
1804
  // Quién fue el origen se DICE, siempre: es la diferencia entre una
1610
1805
  // migración y una pasada de mantenimiento contra el driver activo.
1611
1806
  report.factsFrom = origin.resolved();
1807
+ if (stale)
1808
+ report.staleSource = { to: name, from: origin.resolved(), allowed: allowStale };
1612
1809
  return report;
1613
1810
  };
1614
1811
  // La pasada que escribe congela; el verificador NO (ver el docblock).
@@ -1632,6 +1829,8 @@ export class AuthorizationManager {
1632
1829
  lapsed: await window.lapsed(),
1633
1830
  leaseMs: window.leaseMs,
1634
1831
  fence: window.fence,
1832
+ ...(window.untilMs === undefined ? {} : { untilMs: window.untilMs }),
1833
+ ...(window.window === undefined ? {} : { window: window.window }),
1635
1834
  };
1636
1835
  return report;
1637
1836
  }
@@ -1659,7 +1858,9 @@ export class AuthorizationManager {
1659
1858
  * MANTENIMIENTO: los hechos se leen del propio destino por el puerto
1660
1859
  * (`enumerateFacts`), se rehace lo DERIVADO (marcador, catálogo, árbol)
1661
1860
  * y se aplica el barrido de visibilidad del invariante 18 con el árbol
1662
- * y el catálogo de HOY. No se inventa ni se borra un solo hecho. Un
1861
+ * y el catálogo de HOY. No se inventa ningún hecho; con `--prune` se
1862
+ * borran los que ya no respaldan nada (scopes que ya no resuelven,
1863
+ * caducados, roles/permisos retirados, y los que sobran). Un
1663
1864
  * destino activo con `hierarchyFacts` que no sepa enumerar sus hechos
1664
1865
  * es 500 `E_AUTHZ_UNSUPPORTED`: leerle `authz_*` sería justo el defecto.
1665
1866
  * 2. **`--from=<nombre>` manda**, y por eso se resuelve YA: de la
@@ -1673,7 +1874,7 @@ export class AuthorizationManager {
1673
1874
  * de 3b-3b (`#factsEnumerator`).
1674
1875
  */
1675
1876
  async #factsOrigin(to, target, from) {
1676
- if (from === undefined && to === this.#config.default && target.capabilities?.hierarchyFacts === true) {
1877
+ if (this.#isMaintenancePass(to, target, from)) {
1677
1878
  if (typeof target.enumerateFacts !== 'function') {
1678
1879
  throw new UnsupportedOperationError('enumerateFacts', `authz:reconcile --to=${to}`, to, `El motor SIRVE desde '${to}' y ese driver declara que el árbol y los hechos viven en su backend ` +
1679
1880
  `(hierarchyFacts), así que 'authz_assignments'/'authz_denies' NO son la fuente de verdad de sus ` +
@@ -1683,6 +1884,7 @@ export class AuthorizationManager {
1683
1884
  return {
1684
1885
  name: to,
1685
1886
  authzTables: false,
1887
+ self: true,
1686
1888
  enumerate: (page) => target.enumerateFacts(page),
1687
1889
  resolved: () => to,
1688
1890
  };
@@ -1708,10 +1910,44 @@ export class AuthorizationManager {
1708
1910
  });
1709
1911
  return { name: AUTHZ_TABLES_ORIGIN, authzTables: true, enumerate, resolved: () => resolvedName };
1710
1912
  }
1913
+ /**
1914
+ * **¿Es la pasada de MANTENIMIENTO?** (3b-5; punto ÚNICO desde alpha.10): el
1915
+ * destino es el driver ACTIVO, sus hechos viven en su backend
1916
+ * (`hierarchyFacts`) y nadie nombró otro origen. Es la ÚNICA forma en la que
1917
+ * el origen resuelto de una pasada hacia el driver activo es el propio
1918
+ * destino, y la usan los dos que la necesitan: `#factsOrigin` (de dónde se
1919
+ * leen los hechos) y el guard de la dirección rancia de `reconcile` (si ese
1920
+ * origen es legítimo). Dos copias de esta condición eran dos respuestas.
1921
+ */
1922
+ #isMaintenancePass(to, target, from) {
1923
+ return from === undefined && this.#sameDriver(to, this.#config.default) && target.capabilities?.hierarchyFacts === true;
1924
+ }
1925
+ /**
1926
+ * **¿Dos claves de `drivers` son el MISMO driver?** (cierre del auditor de
1927
+ * alpha.10 · R4, gemelo de roles): misma clave o la MISMA factory registrada
1928
+ * bajo dos claves. El guard de la dirección rancia, el mantenimiento, el
1929
+ * `from === to` y el descarte juzgan la identidad RESUELTA, no la etiqueta:
1930
+ * con `drivers: { openfga: f, fga: f }` y `default: 'openfga'`, `--to=fga
1931
+ * --from=database` es la misma dirección rancia que `--to=openfga`.
1932
+ *
1933
+ * **Límite DECLARADO (cierre-2 · C1)**: la identidad es la REFERENCIA de la
1934
+ * factory, no el backend. Dos factories distintas que construyen el mismo
1935
+ * store (`openfga: () => new OpenFga…(…)`, `fga: () => new OpenFga…(…)`) NO son
1936
+ * el mismo driver para este predicado y el alias pasa el guard. Identidad de
1937
+ * backend declarada por el driver ⇒ roadmap 2.5.
1938
+ */
1939
+ #sameDriver(a, b) {
1940
+ if (a === b)
1941
+ return true;
1942
+ const drivers = this.#config.drivers ?? {};
1943
+ const own = (key) => (Object.prototype.hasOwnProperty.call(drivers, key) ? drivers[key] : undefined);
1944
+ const fa = own(a);
1945
+ return fa !== undefined && fa === own(b);
1946
+ }
1711
1947
  /** El driver que `--from` nombra, con los dos errores de 3b-3b intactos. */
1712
1948
  async #originDriver(from, to) {
1713
1949
  const registered = Object.keys(this.#config.drivers ?? {});
1714
- if (from === to) {
1950
+ if (this.#sameDriver(from, to)) {
1715
1951
  throw new AuthorizationConfigError(`authz:reconcile --from=${from} --to=${to}: el origen y el destino son el mismo driver. ` +
1716
1952
  `Si lo que quieres es VERIFICAR y reparar lo derivado del driver activo, no lo digas con --from: ` +
1717
1953
  `la pasada ya lee sus hechos de él cuando es el driver por defecto.`);
@@ -1747,7 +1983,7 @@ export class AuthorizationManager {
1747
1983
  const registered = Object.keys(this.#config.drivers ?? {});
1748
1984
  const candidates = [];
1749
1985
  for (const candidate of registered) {
1750
- if (candidate === to)
1986
+ if (this.#sameDriver(candidate, to))
1751
1987
  continue;
1752
1988
  const driver = await this.#config.drivers[candidate]();
1753
1989
  if (typeof driver.enumerateFacts === 'function')
@@ -2179,7 +2415,7 @@ export class AuthorizationManager {
2179
2415
  const answer = await driver.authorizeMany(subject, permission, scopes);
2180
2416
  // Un `boolean[]` desalineado se leería por posición (F5, CR3): es un
2181
2417
  // bug del driver, no una decisión. 500 nombrando al culpable.
2182
- if (!Array.isArray(answer) || answer.length !== scopes.length || answer.some((b) => typeof b !== 'boolean')) {
2418
+ if (!Array.isArray(answer) || answer.length !== scopes.length || !everyPositionBoolean(answer)) {
2183
2419
  throw new AuthorizationInternalError(`authorizeMany: el driver '${this.#config.default}' devolvió ${describeAnswer(answer)} para ${scopes.length} scopes; ` +
2184
2420
  `el puerto exige un boolean[] con exactamente una posición por scope.`);
2185
2421
  }