@jantstack/adonis-authz 1.1.0 → 2.0.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 (132) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +417 -58
  3. package/build/commands/authz_catalog_diff.d.ts +28 -0
  4. package/build/commands/authz_catalog_diff.d.ts.map +1 -0
  5. package/build/commands/authz_catalog_diff.js +67 -0
  6. package/build/commands/authz_catalog_diff.js.map +1 -0
  7. package/build/commands/authz_catalog_sync.d.ts +20 -0
  8. package/build/commands/authz_catalog_sync.d.ts.map +1 -0
  9. package/build/commands/authz_catalog_sync.js +58 -0
  10. package/build/commands/authz_catalog_sync.js.map +1 -0
  11. package/build/commands/main.d.ts +2 -0
  12. package/build/commands/main.d.ts.map +1 -1
  13. package/build/commands/main.js +2 -0
  14. package/build/commands/main.js.map +1 -1
  15. package/build/commands/openfga_import.d.ts +8 -2
  16. package/build/commands/openfga_import.d.ts.map +1 -1
  17. package/build/commands/openfga_import.js +29 -6
  18. package/build/commands/openfga_import.js.map +1 -1
  19. package/build/commands/openfga_provision.d.ts.map +1 -1
  20. package/build/commands/openfga_provision.js +2 -1
  21. package/build/commands/openfga_provision.js.map +1 -1
  22. package/build/index.d.ts +45 -6
  23. package/build/index.d.ts.map +1 -1
  24. package/build/index.js +37 -4
  25. package/build/index.js.map +1 -1
  26. package/build/src/catalog.d.ts +249 -2
  27. package/build/src/catalog.d.ts.map +1 -1
  28. package/build/src/catalog.js +709 -59
  29. package/build/src/catalog.js.map +1 -1
  30. package/build/src/catalog_cache.d.ts +300 -0
  31. package/build/src/catalog_cache.d.ts.map +1 -0
  32. package/build/src/catalog_cache.js +656 -0
  33. package/build/src/catalog_cache.js.map +1 -0
  34. package/build/src/clock.d.ts +24 -0
  35. package/build/src/clock.d.ts.map +1 -0
  36. package/build/src/clock.js +7 -0
  37. package/build/src/clock.js.map +1 -0
  38. package/build/src/define_config.d.ts +102 -6
  39. package/build/src/define_config.d.ts.map +1 -1
  40. package/build/src/define_config.js.map +1 -1
  41. package/build/src/drivers/backend_guard.d.ts +92 -0
  42. package/build/src/drivers/backend_guard.d.ts.map +1 -0
  43. package/build/src/drivers/backend_guard.js +221 -0
  44. package/build/src/drivers/backend_guard.js.map +1 -0
  45. package/build/src/drivers/database_driver.d.ts +242 -15
  46. package/build/src/drivers/database_driver.d.ts.map +1 -1
  47. package/build/src/drivers/database_driver.js +692 -127
  48. package/build/src/drivers/database_driver.js.map +1 -1
  49. package/build/src/drivers/openfga_driver.d.ts +341 -27
  50. package/build/src/drivers/openfga_driver.d.ts.map +1 -1
  51. package/build/src/drivers/openfga_driver.js +1020 -271
  52. package/build/src/drivers/openfga_driver.js.map +1 -1
  53. package/build/src/drivers/sql_expiry.d.ts +53 -0
  54. package/build/src/drivers/sql_expiry.d.ts.map +1 -0
  55. package/build/src/drivers/sql_expiry.js +66 -0
  56. package/build/src/drivers/sql_expiry.js.map +1 -0
  57. package/build/src/errors.d.ts +333 -0
  58. package/build/src/errors.d.ts.map +1 -1
  59. package/build/src/errors.js +352 -0
  60. package/build/src/errors.js.map +1 -1
  61. package/build/src/expiry.d.ts +27 -0
  62. package/build/src/expiry.d.ts.map +1 -0
  63. package/build/src/expiry.js +50 -0
  64. package/build/src/expiry.js.map +1 -0
  65. package/build/src/hierarchical_resolver.d.ts +56 -0
  66. package/build/src/hierarchical_resolver.d.ts.map +1 -0
  67. package/build/src/hierarchical_resolver.js +87 -0
  68. package/build/src/hierarchical_resolver.js.map +1 -0
  69. package/build/src/identity.d.ts +155 -0
  70. package/build/src/identity.d.ts.map +1 -0
  71. package/build/src/identity.js +359 -0
  72. package/build/src/identity.js.map +1 -0
  73. package/build/src/manager.d.ts +225 -7
  74. package/build/src/manager.d.ts.map +1 -1
  75. package/build/src/manager.js +1670 -23
  76. package/build/src/manager.js.map +1 -1
  77. package/build/src/memoize_ancestors.d.ts +23 -0
  78. package/build/src/memoize_ancestors.d.ts.map +1 -0
  79. package/build/src/memoize_ancestors.js +42 -0
  80. package/build/src/memoize_ancestors.js.map +1 -0
  81. package/build/src/middleware/app_access_middleware.d.ts +8 -6
  82. package/build/src/middleware/app_access_middleware.d.ts.map +1 -1
  83. package/build/src/middleware/app_access_middleware.js +10 -26
  84. package/build/src/middleware/app_access_middleware.js.map +1 -1
  85. package/build/src/models/authz_assignment.d.ts +5 -5
  86. package/build/src/models/authz_assignment.d.ts.map +1 -1
  87. package/build/src/models/authz_deny.d.ts +5 -5
  88. package/build/src/models/authz_deny.d.ts.map +1 -1
  89. package/build/src/models/authz_permission.d.ts +11 -5
  90. package/build/src/models/authz_permission.d.ts.map +1 -1
  91. package/build/src/models/authz_permission.js +4 -0
  92. package/build/src/models/authz_permission.js.map +1 -1
  93. package/build/src/models/authz_role.d.ts +13 -6
  94. package/build/src/models/authz_role.d.ts.map +1 -1
  95. package/build/src/models/authz_role.js +6 -1
  96. package/build/src/models/authz_role.js.map +1 -1
  97. package/build/src/models/authz_role_permission.d.ts +5 -5
  98. package/build/src/models/authz_role_permission.d.ts.map +1 -1
  99. package/build/src/openfga.d.ts +13 -0
  100. package/build/src/openfga.d.ts.map +1 -0
  101. package/build/src/openfga.js +12 -0
  102. package/build/src/openfga.js.map +1 -0
  103. package/build/src/sql_descendants.d.ts +51 -0
  104. package/build/src/sql_descendants.d.ts.map +1 -0
  105. package/build/src/sql_descendants.js +129 -0
  106. package/build/src/sql_descendants.js.map +1 -0
  107. package/build/src/testing/contract.d.ts +138 -7
  108. package/build/src/testing/contract.d.ts.map +1 -1
  109. package/build/src/testing/contract.js +2946 -24
  110. package/build/src/testing/contract.js.map +1 -1
  111. package/build/src/testing/main.d.ts +4 -2
  112. package/build/src/testing/main.d.ts.map +1 -1
  113. package/build/src/testing/main.js +2 -1
  114. package/build/src/testing/main.js.map +1 -1
  115. package/build/src/testing/scope_tree.d.ts +65 -0
  116. package/build/src/testing/scope_tree.d.ts.map +1 -0
  117. package/build/src/testing/scope_tree.js +145 -0
  118. package/build/src/testing/scope_tree.js.map +1 -0
  119. package/build/src/traits/authz_scopes.d.ts +30 -6
  120. package/build/src/traits/authz_scopes.d.ts.map +1 -1
  121. package/build/src/traits/authz_scopes.js +30 -18
  122. package/build/src/traits/authz_scopes.js.map +1 -1
  123. package/build/src/traits/has_uuid.d.ts +5 -5
  124. package/build/src/traits/has_uuid.d.ts.map +1 -1
  125. package/build/src/types.d.ts +530 -28
  126. package/build/src/types.d.ts.map +1 -1
  127. package/build/src/types.js +10 -4
  128. package/build/src/types.js.map +1 -1
  129. package/build/stubs/config/app_acl.stub +4 -2
  130. package/build/stubs/config/authorization.stub +66 -12
  131. package/build/stubs/migration.stub +57 -13
  132. package/package.json +11 -6
@@ -1 +1 @@
1
- {"version":3,"file":"authz_role.js","sourceRoot":"","sources":["../../../src/models/authz_role.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAChD,OAAO,eAAe,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,MAAM,CAAC,KAAK,GAAG,aAAa,CAAA;IAC5B,MAAM,CAAC,UAAU,GAAG,MAAM,CAAA;;AAGzB;IADP,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;uCACR;AAGZ;IADP,MAAM,EAAE;;uCACW;AAGZ;IADP,MAAM,EAAE;;uCACW;AAGZ;IADP,MAAM,EAAE;;8CACyB;AAG1B;IADP,MAAM,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;;4CACZ;AAIjB;IADP,MAAM,EAAE;;uCACW;AAUZ;IARP,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE;QACjC,UAAU,EAAE,wBAAwB;QACpC,QAAQ,EAAE,MAAM;QAChB,eAAe,EAAE,WAAW;QAC5B,UAAU,EAAE,MAAM;QAClB,sBAAsB,EAAE,iBAAiB;QACzC,eAAe,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE;KAC/D,CAAC;;8CACqD;AAG/C;IADP,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;8BAC7C,QAAQ;4CAAA;AAGnB;IADP,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;8BAC/D,QAAQ;4CAAA"}
1
+ {"version":3,"file":"authz_role.js","sourceRoot":"","sources":["../../../src/models/authz_role.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAEnE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAChD,OAAO,eAAe,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC;IACzD,MAAM,CAAC,KAAK,GAAG,aAAa,CAAA;IAC5B,MAAM,CAAC,UAAU,GAAG,MAAM,CAAA;;AAGzB;IADP,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;;uCACR;AAGZ;IADP,MAAM,EAAE;;uCACW;AAGZ;IADP,MAAM,EAAE;;uCACW;AAGZ;IADP,MAAM,EAAE;;8CACyB;AAG1B;IADP,MAAM,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;;4CACZ;AAIjB;IADP,MAAM,EAAE;;uCACW;AAQZ;IADP,MAAM,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC;;gDACb;AAUrB;IARP,UAAU,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE;QACjC,UAAU,EAAE,wBAAwB;QACpC,QAAQ,EAAE,MAAM;QAChB,eAAe,EAAE,WAAW;QAC5B,UAAU,EAAE,MAAM;QAClB,sBAAsB,EAAE,iBAAiB;QACzC,eAAe,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE;KAC/D,CAAC;;8CACqD;AAG/C;IADP,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;8BAC7C,QAAQ;4CAAA;AAGnB;IADP,MAAM,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;8BAC/D,QAAQ;4CAAA"}
@@ -62,15 +62,18 @@ declare const AuthzRolePermission_base: {
62
62
  selfAssignPrimaryKey: boolean;
63
63
  generateUuid(model: any): void;
64
64
  nextUuid(): string;
65
+ all: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T>[]>;
65
66
  find: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<null | InstanceType<T>>;
67
+ table: string;
68
+ connection?: string | undefined;
69
+ first: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<null | InstanceType<T>>;
70
+ truncate: (cascade?: boolean) => Promise<void>;
66
71
  readonly booted: boolean;
67
72
  $columnsDefinitions: Map<string, import("@adonisjs/lucid/types/model").ModelColumnOptions>;
68
73
  $relationsDefinitions: Map<string, import("@adonisjs/lucid/types/relations").RelationshipsContract>;
69
74
  $computedDefinitions: Map<string, import("@adonisjs/lucid/types/model").ComputedOptions>;
70
75
  primaryKey: string;
71
- connection?: string | undefined;
72
76
  namingStrategy: import("@adonisjs/lucid/types/model").NamingStrategyContract;
73
- table: string;
74
77
  $adapter: import("@adonisjs/lucid/types/model").AdapterContract;
75
78
  useAdapter: (adapter: import("@adonisjs/lucid/types/model").AdapterContract) => void;
76
79
  $hooks: import("@poppinss/hooks").default<any>;
@@ -128,7 +131,6 @@ declare const AuthzRolePermission_base: {
128
131
  <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, clause: Record<string, unknown>, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T>[]>;
129
132
  <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, key: string, value: any, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions): Promise<InstanceType<T>[]>;
130
133
  };
131
- first: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<null | InstanceType<T>>;
132
134
  firstOrFail: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T>>;
133
135
  findMany: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, value: any[], options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T>[]>;
134
136
  firstOrNew: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, searchPayload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>>, savePayload?: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>>, options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T>>;
@@ -137,10 +139,8 @@ declare const AuthzRolePermission_base: {
137
139
  fetchOrNewUpMany: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T>[]>;
138
140
  fetchOrCreateMany: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T>[]>;
139
141
  updateOrCreateMany: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, predicate: keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>> | (keyof import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>)[], payload: Partial<import("@adonisjs/lucid/types/model").ModelAttributes<InstanceType<T>>>[], options?: import("@adonisjs/lucid/types/model").ModelAssignOptions) => Promise<InstanceType<T>[]>;
140
- all: <T extends import("@adonisjs/lucid/types/model").LucidModel>(this: T, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => Promise<InstanceType<T>[]>;
141
142
  query: <Model extends import("@adonisjs/lucid/types/model").LucidModel, Result = InstanceType<Model>>(this: Model, options?: import("@adonisjs/lucid/types/model").ModelAdapterOptions) => import("@adonisjs/lucid/types/model").ModelQueryBuilderContract<Model, Result>;
142
143
  transaction: import("@adonisjs/lucid/types/database").TransactionFn;
143
- truncate: (cascade?: boolean) => Promise<void>;
144
144
  } & import("@adonisjs/lucid/types/model").LucidModel;
145
145
  /** Pivot rol ↔ permiso del motor de autorización propio. */
146
146
  export default class AuthzRolePermission extends AuthzRolePermission_base {
@@ -1 +1 @@
1
- {"version":3,"file":"authz_role_permission.d.ts","sourceRoot":"","sources":["../../../src/models/authz_role_permission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oZAsBoxtB,gDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAjBr2tB,4DAA4D;AAC5D,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,wBAA2B;IAC1E,OAAc,KAAK,SAA2B;IAC9C,OAAc,UAAU,SAAS;IAGzB,IAAI,EAAE,MAAM,CAAA;IAGZ,QAAQ,EAAE,MAAM,CAAA;IAGhB,cAAc,EAAE,MAAM,CAAA;IAGtB,SAAS,EAAE,QAAQ,CAAA;CAC5B"}
1
+ {"version":3,"file":"authz_role_permission.d.ts","sourceRoot":"","sources":["../../../src/models/authz_role_permission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oZAsBoxtB,gDAAiD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAjBr2tB,4DAA4D;AAC5D,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,wBAA2B;IAC1E,OAAc,KAAK,SAA2B;IAC9C,OAAc,UAAU,SAAS;IAGzB,IAAI,EAAE,MAAM,CAAA;IAGZ,QAAQ,EAAE,MAAM,CAAA;IAGhB,cAAc,EAAE,MAAM,CAAA;IAGtB,SAAS,EAAE,QAAQ,CAAA;CAC5B"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Entrada del subpath `@jantstack/adonis-authz/openfga` — TODO lo que tira de
3
+ * `@openfga/sdk` (peer opcional) vive detrás de esta puerta (D9):
4
+ *
5
+ * const { OpenFgaAuthorizationDriver } = await import('@jantstack/adonis-authz/openfga')
6
+ *
7
+ * La entrada principal (`@jantstack/adonis-authz`) no importa este módulo ni
8
+ * el driver: un consumidor solo-database arranca sin el SDK instalado, y
9
+ * `scripts/check_purity.mjs` (regla 3) lo vigila en cada build.
10
+ */
11
+ export { OpenFgaAuthorizationDriver, openFgaAuthorizationModel, provisionOpenFgaStore, importAuthzFactsToOpenFga, assertHolderTypes, parseBindingId, correlateBatchResults, isDuplicateWrite, DEFAULT_TIMEOUT_MS, } from './drivers/openfga_driver.js';
12
+ export type { HolderTypeMap, OpenFgaDriverOptions, ImportFactsOptions, ImportFactsResult, } from './drivers/openfga_driver.js';
13
+ //# sourceMappingURL=openfga.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfga.d.ts","sourceRoot":"","sources":["../../src/openfga.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,6BAA6B,CAAA;AACpC,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,6BAA6B,CAAA"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Entrada del subpath `@jantstack/adonis-authz/openfga` — TODO lo que tira de
3
+ * `@openfga/sdk` (peer opcional) vive detrás de esta puerta (D9):
4
+ *
5
+ * const { OpenFgaAuthorizationDriver } = await import('@jantstack/adonis-authz/openfga')
6
+ *
7
+ * La entrada principal (`@jantstack/adonis-authz`) no importa este módulo ni
8
+ * el driver: un consumidor solo-database arranca sin el SDK instalado, y
9
+ * `scripts/check_purity.mjs` (regla 3) lo vigila en cada build.
10
+ */
11
+ export { OpenFgaAuthorizationDriver, openFgaAuthorizationModel, provisionOpenFgaStore, importAuthzFactsToOpenFga, assertHolderTypes, parseBindingId, correlateBatchResults, isDuplicateWrite, DEFAULT_TIMEOUT_MS, } from './drivers/openfga_driver.js';
12
+ //# sourceMappingURL=openfga.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfga.js","sourceRoot":"","sources":["../../src/openfga.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,qBAAqB,EACrB,yBAAyB,EACzB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,6BAA6B,CAAA"}
@@ -0,0 +1,51 @@
1
+ import type { ScopeDescendantsResolver } from './types.js';
2
+ /**
3
+ * `descendantsOf` opt-in sobre una tabla del consumidor con columna padre
4
+ * (2.1, B2). Genera UNA CTE recursiva (`WITH RECURSIVE`), la misma para
5
+ * PostgreSQL, SQLite y MySQL 8 (2.5 · J3: solo cambia el carácter con el que
6
+ * se citan los identificadores); cualquier otro dialecto lanza 500
7
+ * `E_AUTHZ_UNSUPPORTED_DIALECT` en la primera llamada hasta que la suite lo
8
+ * observe. El paquete sigue sin conocer el árbol del consumidor: aquí solo
9
+ * se le dice qué tabla y qué columnas lo guardan.
10
+ *
11
+ * scopes: {
12
+ * resolveChain,
13
+ * descendantsOf: sqlDescendantsOf({
14
+ * table: 'organization_nodes', uuidColumn: 'uuid', parentColumn: 'parent_uuid', typeColumn: 'kind',
15
+ * }),
16
+ * }
17
+ *
18
+ * Cotas: la consulta lee como mucho `maxNodes + 1` filas (`LIMIT`) y no baja
19
+ * más de `maxNodes + 1` niveles, así que un ciclo en la tabla termina Y se
20
+ * nota: un ciclo alcanzable produce al menos una fila por nivel, es decir
21
+ * más de `maxNodes` ⇒ 422 `E_AUTHZ_TOO_MANY_SCOPES` con «posible ciclo» en
22
+ * el mensaje (con `depth < maxNodes` un ciclo de dos nodos devolvía
23
+ * `maxNodes - 1` filas duplicadas en silencio). No hay segunda barrera por
24
+ * uuid repetido: con esta profundidad un ciclo NUNCA cabe en `maxNodes`
25
+ * filas, así que era código muerto (G1, CR6). Nunca una lista parcial. Un
26
+ * scope que no está en la tabla ⇒ `null`. `scopeType` pasa por la gramática
27
+ * de identidad al construir (auditor 11).
28
+ */
29
+ export interface SqlDescendantsOptions {
30
+ table: string;
31
+ uuidColumn: string;
32
+ /** Columna con el uuid del padre; `NULL` = el nodo cuelga de `app`. */
33
+ parentColumn: string;
34
+ /** Columna con el `ScopeRef.type` de cada fila. Excluyente con `scopeType`. */
35
+ typeColumn?: string;
36
+ /** Tipo fijo de todos los nodos de la tabla (sin columna de tipo). Excluyente con `typeColumn`. */
37
+ scopeType?: string;
38
+ /** Cota propia del helper: se aplica `min(maxNodes del llamante, esta)`. */
39
+ maxNodes?: number;
40
+ /** Nombre de la conexión Lucid (default: la primaria). */
41
+ connection?: string;
42
+ /** Deadline de cada consulta en ms (default 5000): vencido ⇒ 503. */
43
+ timeoutMs?: number;
44
+ }
45
+ /** Lo que necesita del `db` de Lucid; inyectable para probar el dialecto sin servidor. */
46
+ interface DatabaseLike {
47
+ connection(name?: string): any;
48
+ }
49
+ export declare function sqlDescendantsOf(options: SqlDescendantsOptions, database?: DatabaseLike): ScopeDescendantsResolver;
50
+ export {};
51
+ //# sourceMappingURL=sql_descendants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql_descendants.d.ts","sourceRoot":"","sources":["../../src/sql_descendants.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAY,MAAM,YAAY,CAAA;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mGAAmG;IACnG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAuBD,0FAA0F;AAC1F,UAAU,YAAY;IACpB,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,CAAA;CAC/B;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,GAAE,YAAiB,GAC1B,wBAAwB,CAiH1B"}
@@ -0,0 +1,129 @@
1
+ import db from '@adonisjs/lucid/services/db';
2
+ import { AuthorizationConfigError, TooManyScopesError, UnsupportedDialectError } from './errors.js';
3
+ import { assertScope, assertScopeType } from './identity.js';
4
+ import { guardSql } from './drivers/backend_guard.js';
5
+ import { APP_SCOPE_TYPE } from './types.js';
6
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
7
+ /** Dialecto → carácter con el que cita identificadores (MySQL: backtick; el resto: comilla doble ANSI). */
8
+ const QUOTE_BY_DIALECT = {
9
+ 'postgres': '"',
10
+ 'sqlite3': '"',
11
+ 'better-sqlite3': '"',
12
+ 'mysql': '`',
13
+ 'mysql2': '`',
14
+ };
15
+ const DEFAULT_TIMEOUT_MS = 5_000;
16
+ function identifier(kind, value) {
17
+ if (typeof value !== 'string' || !IDENTIFIER.test(value)) {
18
+ throw new AuthorizationConfigError(`sqlDescendantsOf: ${kind} '${String(value)}' no es un identificador SQL simple (letras, dígitos y _); ` +
19
+ `nada que no lo sea se interpola en la consulta.`);
20
+ }
21
+ return value;
22
+ }
23
+ export function sqlDescendantsOf(options, database = db) {
24
+ const table = identifier('table', options.table);
25
+ const uuidCol = identifier('uuidColumn', options.uuidColumn);
26
+ const parentCol = identifier('parentColumn', options.parentColumn);
27
+ if ((options.typeColumn === undefined) === (options.scopeType === undefined)) {
28
+ throw new AuthorizationConfigError('sqlDescendantsOf: declara exactamente uno de typeColumn (el tipo viene de la fila) o scopeType (tipo fijo).');
29
+ }
30
+ const typeCol = options.typeColumn === undefined ? null : identifier('typeColumn', options.typeColumn);
31
+ const fixedType = options.scopeType ?? null;
32
+ if (fixedType !== null) {
33
+ // Es identidad de scope (minúsculas, ≤ 20, sin separadores) y nunca la
34
+ // raíz: `app` no tiene fila ni uuid.
35
+ try {
36
+ assertScopeType(fixedType);
37
+ }
38
+ catch (error) {
39
+ throw new AuthorizationConfigError(`sqlDescendantsOf: scopeType inválido: ${error.message}`);
40
+ }
41
+ if (fixedType === APP_SCOPE_TYPE) {
42
+ throw new AuthorizationConfigError("sqlDescendantsOf: scopeType no puede ser 'app' (la raíz no tiene fila)");
43
+ }
44
+ }
45
+ if (options.maxNodes !== undefined && !(Number.isInteger(options.maxNodes) && options.maxNodes >= 1)) {
46
+ throw new AuthorizationConfigError(`sqlDescendantsOf: maxNodes debe ser un entero >= 1 (llegó ${String(options.maxNodes)})`);
47
+ }
48
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
49
+ const connectionName = options.connection;
50
+ /** El SQL por carácter de cita (se construye una vez por dialecto). */
51
+ const statements = new Map();
52
+ function statementsFor(quote) {
53
+ let built = statements.get(quote);
54
+ if (built)
55
+ return built;
56
+ const q = (name) => `${quote}${name}${quote}`;
57
+ const typeSelect = typeCol ? `t.${q(typeCol)}` : 'NULL';
58
+ // MySQL corta una CTE recursiva a `cte_max_recursion_depth` iteraciones
59
+ // (1000 por defecto) con el error 3636: con una cota mayor, un ciclo
60
+ // salía como 503 «backend caído» en vez del 422 «posible ciclo» del
61
+ // contrato (2.5 · J3, hallazgo). El hint `SET_VAR` sube el tope SOLO para
62
+ // esta sentencia, a la cota + 1 que la propia consulta ya impone con
63
+ // `depth < ?`; el placeholder `%DEPTH%` se rellena con ese entero (nunca
64
+ // con entrada del consumidor). Los demás dialectos no tienen tope.
65
+ const hint = quote === '`' ? '/*+ SET_VAR(cte_max_recursion_depth = %DEPTH%) */ ' : '';
66
+ built = {
67
+ exists: `SELECT 1 AS ${q('ok')} FROM ${q(table)} t WHERE %WHERE% LIMIT 1`,
68
+ walk: `WITH RECURSIVE ${q('authz_descendants')}(${q('uuid')}, ${q('type')}, ${q('depth')}) AS (` +
69
+ ` SELECT t.${q(uuidCol)}, ${typeSelect}, 1 FROM ${q(table)} t WHERE ` +
70
+ `%ANCHOR%` +
71
+ ` UNION ALL` +
72
+ ` SELECT t.${q(uuidCol)}, ${typeSelect}, d.${q('depth')} + 1 FROM ${q(table)} t` +
73
+ ` JOIN ${q('authz_descendants')} d ON t.${q(parentCol)} = d.${q('uuid')} WHERE d.${q('depth')} < ?` +
74
+ `) SELECT ${hint}${q('uuid')}, ${q('type')} FROM ${q('authz_descendants')} LIMIT ?`,
75
+ };
76
+ statements.set(quote, built);
77
+ return built;
78
+ }
79
+ return async (scope, { maxNodes }) => {
80
+ assertScope(scope);
81
+ const cap = Math.min(maxNodes, options.maxNodes ?? Infinity);
82
+ if (!(Number.isInteger(cap) && cap >= 1)) {
83
+ throw new AuthorizationConfigError(`sqlDescendantsOf: maxNodes debe ser un entero >= 1 (llegó ${String(maxNodes)})`);
84
+ }
85
+ const connection = database.connection(connectionName);
86
+ const dialect = connection?.dialect?.name ?? connection?.client?.driverName ?? 'desconocido';
87
+ const quote = QUOTE_BY_DIALECT[dialect];
88
+ if (!quote) {
89
+ throw new UnsupportedDialectError(`sqlDescendantsOf: dialecto '${dialect}' sin observación en la suite (hoy: PostgreSQL, MySQL 8 y SQLite). ` +
90
+ `Implementa scopes.descendantsOf a mano para este motor.`);
91
+ }
92
+ const q = (name) => `${quote}${name}${quote}`;
93
+ const sql = statementsFor(quote);
94
+ const query = (operation, text, bindings) => guardSql('sqlDescendantsOf', operation, timeoutMs, () => connection.rawQuery(text, bindings));
95
+ // Forma del resultado crudo por cliente: pg `{ rows }`, sqlite `rows[]`,
96
+ // mysql2 `[rows[], fields[]]` (2.5 · J3).
97
+ const rowsOf = (result) => {
98
+ if (Array.isArray(result)) {
99
+ return result.length === 2 && Array.isArray(result[0]) && Array.isArray(result[1]) ? result[0] : result;
100
+ }
101
+ return result?.rows ?? [];
102
+ };
103
+ let anchor;
104
+ let bindings;
105
+ if (scope.type === APP_SCOPE_TYPE) {
106
+ anchor = `t.${q(parentCol)} IS NULL`;
107
+ bindings = [];
108
+ }
109
+ else {
110
+ if (fixedType !== null && scope.type !== fixedType)
111
+ return null;
112
+ const where = `t.${q(uuidCol)} = ?` + (typeCol ? ` AND t.${q(typeCol)} = ?` : '');
113
+ const args = typeCol ? [scope.uuid, scope.type] : [scope.uuid];
114
+ const exists = rowsOf(await query('descendantsOf.exists', sql.exists.replace('%WHERE%', where), args));
115
+ if (exists.length === 0)
116
+ return null;
117
+ anchor = `t.${q(parentCol)} = ?`;
118
+ bindings = [scope.uuid];
119
+ }
120
+ const walk = sql.walk.replace('%ANCHOR%', anchor).replace('%DEPTH%', String(cap + 2));
121
+ const rows = rowsOf(await query('descendantsOf', walk, [...bindings, cap + 1, cap + 1]));
122
+ if (rows.length > cap) {
123
+ throw new TooManyScopesError(`sqlDescendantsOf: ${scope.type}:${scope.uuid ?? ''} tiene más de ${cap} descendientes — o la tabla tiene un ` +
124
+ `posible ciclo (un ciclo alcanzable produce una fila por nivel hasta la cota); no se devuelve una lista parcial.`);
125
+ }
126
+ return rows.map((row) => ({ type: typeCol ? row.type : fixedType, uuid: row.uuid }));
127
+ };
128
+ }
129
+ //# sourceMappingURL=sql_descendants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql_descendants.js","sourceRoot":"","sources":["../../src/sql_descendants.ts"],"names":[],"mappings":"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"}
@@ -1,27 +1,158 @@
1
1
  import '@japa/assert';
2
+ import type { Assert } from '@japa/assert';
2
3
  import type { AuthorizationDriver, CatalogSpec } from '../types.js';
4
+ import type { ContractScopeTree } from './scope_tree.js';
3
5
  /**
4
6
  * Suite de contrato del sistema de autorización — EL JUEZ.
5
7
  *
6
8
  * Cualquier driver (database, openfga, custom del consumidor) debe pasar
7
9
  * estos casos sin modificarlos: son la definición ejecutable de la semántica
8
- * documentada en `#services/authorization/types`. Para enchufar un driver:
10
+ * documentada en `../types.js`. Para enchufar un driver:
9
11
  *
10
12
  * runAuthorizationDriverContract({
11
13
  * name: 'mi-driver',
12
- * makeDriver: () => new MiDriver(),
13
- * seedCatalog: (catalog) => ..., // materializa roles/permisos
14
- * cleanup: () => ..., // borra hechos + catálogo entre tests
14
+ * level: '2.0', // omitido = 'core': solo los casos de 1.x
15
+ * capabilities: { hierarchyFacts: false, transactions: false, truncationSignal: false,
16
+ * singleCheckAuthorize: false, injectableClock: false },
17
+ * seedCatalog: (catalog) => ..., // materializa roles/permisos
18
+ * makeDriver: (tree) => new MiDriver({ resolveChain: ... }), // recibe el árbol
19
+ * cleanup: () => ..., // borra hechos + catálogo entre tests
15
20
  * })
16
21
  *
22
+ * Llámala en el TOP LEVEL del spec (no dentro de un `test` ni de un hook):
23
+ * Japa registra los grupos al importar el archivo y descarta los tardíos.
24
+ *
17
25
  * `seedCatalog`/`cleanup` son del harness (no del contrato) porque cada
18
26
  * backend materializa el catálogo a su manera (tablas propias, FGA model...).
27
+ *
28
+ * El ÁRBOL de scopes también es del harness (`makeTree`, por defecto
29
+ * `memoryScopeTree()`): la suite lo construye caso a caso con `tree.attach` y
30
+ * el driver lo ve a través de lo que el harness le inyecte (un
31
+ * `resolveChain` que lo camina, o hechos en el backend). Así el
32
+ * invariante 1 se prueba con cadenas reales y no con el resolutor plano.
33
+ */
34
+ /**
35
+ * Lo que un driver DECLARA poder hacer. Cada capacidad tiene en la suite un
36
+ * par de casos `{ whenTrue, whenFalse }` — nunca un `skip`. Hoy hay par para
37
+ * `truncationSignal: false` (con `exhaustiveLists`) y `hierarchyFacts: false`;
38
+ * los demás llegan con su fase, y mientras tanto declararlos `true` hace que
39
+ * la suite lance al registrarse: una promesa sin juez no pasa.
19
40
  */
41
+ export interface DriverCapabilities {
42
+ /** El driver materializa el árbol como hechos propios (Fase 3b). */
43
+ hierarchyFacts: boolean;
44
+ /** Acepta `{ trx }` externa en las escrituras (Fase 2.5). */
45
+ transactions: boolean;
46
+ /**
47
+ * Los `list*` lanzan si el backend trunca. Ningún driver del paquete trunca
48
+ * (L0.7 se cerró enumerando con `Read` paginado), así que no hay caso para
49
+ * `true`: es el par que un driver de terceros con backend con tope tendría
50
+ * que traer.
51
+ */
52
+ truncationSignal: boolean;
53
+ /** `authorize` = una sola llamada al backend (modo facts, Fase 3b). */
54
+ singleCheckAuthorize: boolean;
55
+ /**
56
+ * El driver acepta un reloj inyectado (2.5 · J1): `withClock(now)` en el
57
+ * puerto devuelve una vista que evalúa la caducidad con ESE `now` (SQL,
58
+ * `current_time` de FGA, filtros en cliente y los tres estados del
59
+ * re-grant). Con `true` el juez fija el instante y observa la caducidad
60
+ * exacta (T−1 ms concede, T no) sin dormir; con `false` solo puede
61
+ * observarla en tiempo real (el caso de los tres estados espera 1,5 s).
62
+ */
63
+ injectableClock: boolean;
64
+ /**
65
+ * Los `list*` devuelven TODO sin tope. Es la verdad de los dos drivers del
66
+ * paquete: `database` (SQL sin límite) y `openfga` (enumeración por `Read`
67
+ * paginado, L0.7). Con `false` el harness debe dar `limits.listMaxResults`
68
+ * y el juez prueba solo la frontera exacta: es la declaración honesta de
69
+ * un driver de terceros cuyo backend trunca y que no lo señala.
70
+ */
71
+ exhaustiveLists: boolean;
72
+ /**
73
+ * El driver implementa el método opcional `listDenies` (2.1). Solo tiene
74
+ * casos en `level: '2.1'` (2E · I5): con `true` se juzgan `listDenies`,
75
+ * `effectivePermissions`, `authorizedScopes` y la versión compartida del
76
+ * catálogo; con `false` se juzga que esas primitivas lo digan (500
77
+ * `E_AUTHZ_UNSUPPORTED` nombrándolo) en vez de simular «sin denies». En un
78
+ * harness `core`/`'2.0'` no hay caso que lo observe: declara `false`.
79
+ */
80
+ listDenies: boolean;
81
+ /**
82
+ * `purgeRole(roleUuid)` está implementado de verdad (3B · B4): revoca
83
+ * todas las asignaciones del rol en todos los scopes, borra sus vínculos
84
+ * y el rol, atómicamente, y recrear el slug no revive nada. Solo tiene
85
+ * casos en `level: '2.2'`: con `true` se juzga la purga; con `false` se
86
+ * juzga que el driver lo DIGA (500 `E_AUTHZ_UNSUPPORTED`) y que no deje
87
+ * nada a medias — el driver `openfga` del paquete hasta 3b (`facts` +
88
+ * `reconcile`): sus bindings no son enumerables por rol sin leer el
89
+ * store entero. En un harness por debajo de `'2.2'` declara `false`.
90
+ */
91
+ purgeRole: boolean;
92
+ /**
93
+ * Las escrituras del CATÁLOGO se serializan de verdad en el motor (3E ·
94
+ * R2, tester): dos `defineScopedRole` del mismo `(slug, nivel)` en
95
+ * paralelo terminan con EXACTAMENTE uno confirmado y el otro con 422
96
+ * `E_AUTHZ_CATALOG_CONFLICT` — nunca un fallo de backend. Es lo que da el
97
+ * cerrojo sobre la fila de `authz_catalog_version` (3D · M2) en PostgreSQL
98
+ * y MySQL, y es la promesa que el README hace.
99
+ *
100
+ * Con `false` (SQLite, que serializa bloqueando la BASE ENTERA) el juez
101
+ * solo puede exigir lo innegociable: nunca dos ganadores y el perdedor no
102
+ * escribe — su transacción puede morir con `SQLITE_BUSY`, que es un 503
103
+ * legítimo. Declararlo `true` en un motor así convierte un flake en un
104
+ * fallo; declararlo `false` en PG/MySQL deja pasar un mutante que
105
+ * convierte la colisión en 503. Solo tiene casos en `level: '2.2'`.
106
+ */
107
+ serializedCatalogWrites: boolean;
108
+ }
109
+ export type ContractLevel = 'core' | '2.0' | '2.1' | '2.2';
20
110
  export interface DriverContractHarness {
21
111
  name: string;
22
- makeDriver: () => AuthorizationDriver | Promise<AuthorizationDriver>;
23
- seedCatalog: (catalog: CatalogSpec) => Promise<void>;
24
- cleanup: () => Promise<void>;
112
+ /** `core` (default): los casos de 1.x. `'2.0'`: además los de la Fase 0/1. `'2.1'`: además las primitivas de la Fase 2. `'2.2'`: además los roles locales a un scope (Fase 3). */
113
+ level?: ContractLevel;
114
+ capabilities: DriverCapabilities;
115
+ /** Tope de resultados del backend de test. Obligatorio con `exhaustiveLists: false`. */
116
+ limits?: {
117
+ listMaxResults?: number;
118
+ };
119
+ /**
120
+ * Materializa el catálogo del juez. Devuelve lo que quiera (3E: el
121
+ * `syncAuthzCatalog` del paquete devuelve su reporte): el juez no lo mira.
122
+ */
123
+ seedCatalog(catalog: CatalogSpec): Promise<unknown>;
124
+ /** Árbol que usará la suite. Default: `memoryScopeTree()`. */
125
+ makeTree?(): Promise<ContractScopeTree>;
126
+ makeDriver(tree: ContractScopeTree): AuthorizationDriver | Promise<AuthorizationDriver>;
127
+ /**
128
+ * OTRA instancia del driver sobre el MISMO backend de hechos y con su
129
+ * propio memo del catálogo — lo que sería el mismo driver en otro proceso
130
+ * (2D · F1). Default: si el driver expone `catalog` (`CatalogCache`, los
131
+ * dos del paquete), una vista por prototipo con un memo nuevo; si no, el
132
+ * mismo driver (un driver que lea el catálogo en vivo pasa igual).
133
+ */
134
+ makeTwin?(driver: AuthorizationDriver, tree: ContractScopeTree): AuthorizationDriver | Promise<AuthorizationDriver>;
135
+ cleanup(): Promise<void>;
136
+ }
137
+ /**
138
+ * Lo mínimo que el juez necesita del runner. En producción es el `test` de
139
+ * Japa; en `tests/contract_harness.spec.ts` es una API falsa que solo anota
140
+ * títulos, porque Japa no permite registrar grupos desde un test en marcha y
141
+ * la regla de capacidades hay que poder probarla.
142
+ */
143
+ export interface ContractTestApi {
144
+ group(title: string, define: (group: {
145
+ each: {
146
+ setup(fn: () => Promise<void>): unknown;
147
+ };
148
+ teardown(fn: () => Promise<void>): unknown;
149
+ }) => void): unknown;
150
+ test(title: string, fn: (ctx: {
151
+ assert: Assert;
152
+ }) => Promise<void>): {
153
+ timeout(ms: number): unknown;
154
+ } | undefined | void;
25
155
  }
26
156
  export declare function runAuthorizationDriverContract(harness: DriverContractHarness): void;
157
+ export declare function registerAuthorizationDriverContract(harness: DriverContractHarness, api: ContractTestApi): void;
27
158
  //# sourceMappingURL=contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/testing/contract.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAA;AAGrB,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EAGZ,MAAM,aAAa,CAAA;AAGpB;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACpE,WAAW,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAoBD,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qBAAqB,QAkP5E"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/testing/contract.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAG1C,OAAO,KAAK,EACV,mBAAmB,EAEnB,WAAW,EAKZ,MAAM,aAAa,CAAA;AAQpB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,cAAc,EAAE,OAAO,CAAA;IACvB,6DAA6D;IAC7D,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;OAKG;IACH,gBAAgB,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,eAAe,EAAE,OAAO,CAAA;IACxB;;;;;;OAMG;IACH,eAAe,EAAE,OAAO,CAAA;IACxB;;;;;;;OAOG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;;;;OASG;IACH,SAAS,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;;;;OAcG;IACH,uBAAuB,EAAE,OAAO,CAAA;CACjC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAA;AAE1D,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,kLAAkL;IAClL,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,YAAY,EAAE,kBAAkB,CAAA;IAChC,wFAAwF;IACxF,MAAM,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACpC;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACnD,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACvC,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACvF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,MAAM,EAAE,mBAAmB,EAAE,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAA;IACnH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAiPD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CACH,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,KAAK,EAAE;QACd,IAAI,EAAE;YAAE,KAAK,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;SAAE,CAAA;QACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;KAC3C,KAAK,IAAI,GACT,OAAO,CAAA;IACV,IAAI,CACF,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C;QAAE,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI,CAAA;CACvD;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qBAAqB,QAK5E;AAED,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,qBAAqB,EAC9B,GAAG,EAAE,eAAe,QAuyGrB"}