@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24

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 (182) hide show
  1. package/README.md +11 -0
  2. package/dist/Adapter.d.mts +13 -5
  3. package/dist/Adapter.mjs +9 -2
  4. package/dist/OAuthPersistence.d.mts +87 -0
  5. package/dist/OAuthPersistence.mjs +125 -0
  6. package/dist/OAuthPersistence.mjs.map +1 -0
  7. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  8. package/dist/index.d.mts +3 -1
  9. package/dist/index.mjs +3 -1
  10. package/dist/internal/authority-snapshot.mjs +53 -0
  11. package/dist/internal/authority-snapshot.mjs.map +1 -0
  12. package/dist/internal/configuration.d.mts +1 -1
  13. package/dist/internal/configuration.mjs.map +1 -1
  14. package/dist/internal/models/oauth-connected-model.d.mts +294 -0
  15. package/dist/internal/models/oauth-connected-model.mjs +22 -0
  16. package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
  17. package/dist/internal/models/oauth-model.d.mts +467 -0
  18. package/dist/internal/models/oauth-model.mjs +49 -0
  19. package/dist/internal/models/oauth-model.mjs.map +1 -0
  20. package/dist/internal/models/password-model.d.mts +4 -97
  21. package/dist/internal/models/password-model.mjs +0 -3
  22. package/dist/internal/models/password-model.mjs.map +1 -1
  23. package/dist/internal/native-sql-table.d.mts +34 -0
  24. package/dist/internal/native-sql-table.mjs +111 -0
  25. package/dist/internal/native-sql-table.mjs.map +1 -0
  26. package/dist/internal/oauth/accounts.d.mts +8 -0
  27. package/dist/internal/oauth/accounts.mjs +379 -0
  28. package/dist/internal/oauth/accounts.mjs.map +1 -0
  29. package/dist/internal/oauth/connected-access.d.mts +9 -0
  30. package/dist/internal/oauth/connected-access.mjs +229 -0
  31. package/dist/internal/oauth/connected-access.mjs.map +1 -0
  32. package/dist/internal/oauth/connected-collection.d.mts +7 -0
  33. package/dist/internal/oauth/connected-collection.mjs +214 -0
  34. package/dist/internal/oauth/connected-collection.mjs.map +1 -0
  35. package/dist/internal/oauth/connected-custody.d.mts +6 -0
  36. package/dist/internal/oauth/connected-custody.mjs +165 -0
  37. package/dist/internal/oauth/connected-custody.mjs.map +1 -0
  38. package/dist/internal/oauth/connected-flow.d.mts +7 -0
  39. package/dist/internal/oauth/connected-flow.mjs +324 -0
  40. package/dist/internal/oauth/connected-flow.mjs.map +1 -0
  41. package/dist/internal/oauth/connected-input.mjs +122 -0
  42. package/dist/internal/oauth/connected-input.mjs.map +1 -0
  43. package/dist/internal/oauth/connected-keys.d.mts +2 -0
  44. package/dist/internal/oauth/connected-keys.mjs +22 -0
  45. package/dist/internal/oauth/connected-keys.mjs.map +1 -0
  46. package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
  47. package/dist/internal/oauth/connected-maintenance.mjs +226 -0
  48. package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
  49. package/dist/internal/oauth/connected-management.d.mts +9 -0
  50. package/dist/internal/oauth/connected-management.mjs +243 -0
  51. package/dist/internal/oauth/connected-management.mjs.map +1 -0
  52. package/dist/internal/oauth/connected-reference.mjs +63 -0
  53. package/dist/internal/oauth/connected-reference.mjs.map +1 -0
  54. package/dist/internal/oauth/connected-settlement.d.mts +8 -0
  55. package/dist/internal/oauth/connected-settlement.mjs +141 -0
  56. package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
  57. package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
  58. package/dist/internal/oauth/connected-sign-in.mjs +281 -0
  59. package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
  60. package/dist/internal/oauth/connected-state.d.mts +14 -0
  61. package/dist/internal/oauth/connected-state.mjs +469 -0
  62. package/dist/internal/oauth/connected-state.mjs.map +1 -0
  63. package/dist/internal/oauth/connected-target.d.mts +21 -0
  64. package/dist/internal/oauth/connected-target.mjs +117 -0
  65. package/dist/internal/oauth/connected-target.mjs.map +1 -0
  66. package/dist/internal/oauth/flow.d.mts +5 -0
  67. package/dist/internal/oauth/flow.mjs +279 -0
  68. package/dist/internal/oauth/flow.mjs.map +1 -0
  69. package/dist/internal/oauth/input.mjs +127 -0
  70. package/dist/internal/oauth/input.mjs.map +1 -0
  71. package/dist/internal/oauth/owner.d.mts +10 -0
  72. package/dist/internal/oauth/owner.mjs +10 -0
  73. package/dist/internal/oauth/owner.mjs.map +1 -0
  74. package/dist/internal/oauth/query-compiler.d.mts +7 -0
  75. package/dist/internal/oauth/query-compiler.mjs +9 -0
  76. package/dist/internal/oauth/query-compiler.mjs.map +1 -0
  77. package/dist/internal/oauth/registration.d.mts +7 -0
  78. package/dist/internal/oauth/registration.mjs +393 -0
  79. package/dist/internal/oauth/registration.mjs.map +1 -0
  80. package/dist/internal/oauth/state.mjs +107 -0
  81. package/dist/internal/oauth/state.mjs.map +1 -0
  82. package/dist/internal/oauth/target.d.mts +31 -0
  83. package/dist/internal/oauth/target.mjs +273 -0
  84. package/dist/internal/oauth/target.mjs.map +1 -0
  85. package/dist/internal/oauth-kernel.d.mts +1423 -0
  86. package/dist/internal/oauth-kernel.mjs +187 -0
  87. package/dist/internal/oauth-kernel.mjs.map +1 -0
  88. package/dist/internal/oauth-proxy.d.mts +31 -0
  89. package/dist/internal/oauth-proxy.mjs +129 -0
  90. package/dist/internal/oauth-proxy.mjs.map +1 -0
  91. package/dist/internal/passkey/target.d.mts +1 -1
  92. package/dist/internal/passkey-kernel.d.mts +5 -5
  93. package/dist/internal/passkeys.mjs +1 -1
  94. package/dist/internal/password-credentials.d.mts +11 -0
  95. package/dist/internal/password-credentials.mjs +121 -0
  96. package/dist/internal/password-credentials.mjs.map +1 -0
  97. package/dist/internal/password-kernel.d.mts +15 -28
  98. package/dist/internal/password-kernel.mjs +35 -236
  99. package/dist/internal/password-kernel.mjs.map +1 -1
  100. package/dist/internal/persistence.d.mts +2 -0
  101. package/dist/internal/persistence.mjs +5 -5
  102. package/dist/internal/persistence.mjs.map +1 -1
  103. package/dist/internal/query-operations.d.mts +2 -0
  104. package/dist/internal/session-kernel.d.mts +5 -3
  105. package/dist/internal/session-kernel.mjs +95 -29
  106. package/dist/internal/session-kernel.mjs.map +1 -1
  107. package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
  108. package/dist/internal/sql-oauth-connected-model.mjs +7 -0
  109. package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
  110. package/dist/internal/sql-oauth-model.d.mts +60 -0
  111. package/dist/internal/sql-oauth-model.mjs +10 -0
  112. package/dist/internal/sql-oauth-model.mjs.map +1 -0
  113. package/dist/internal/sql-persistence.mjs +3 -1
  114. package/dist/internal/sql-persistence.mjs.map +1 -1
  115. package/dist/internal/sql-query.d.mts +12 -0
  116. package/dist/internal/sql-query.mjs +15 -18
  117. package/dist/internal/sql-query.mjs.map +1 -1
  118. package/dist/internal/sql-storage-validation.mjs +54 -0
  119. package/dist/internal/sql-storage-validation.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +2 -67
  121. package/dist/internal/storage-mapping.mjs.map +1 -1
  122. package/dist/internal/storage-plans.d.mts +7 -0
  123. package/dist/internal/storage-plans.mjs +189 -0
  124. package/dist/internal/storage-plans.mjs.map +1 -0
  125. package/dist/internal/storage-tables.d.mts +0 -99
  126. package/dist/internal/storage-tables.mjs +0 -43
  127. package/dist/internal/storage-tables.mjs.map +1 -1
  128. package/dist/internal/storage-validation.d.mts +6 -1
  129. package/dist/internal/storage-validation.mjs +42 -1
  130. package/dist/internal/storage-validation.mjs.map +1 -1
  131. package/dist/internal/transaction-execution-kernel.d.mts +1 -1
  132. package/dist/internal/transaction-kernel.d.mts +1 -1
  133. package/dist/internal/transaction-kernel.mjs +3 -2
  134. package/dist/internal/transaction-kernel.mjs.map +1 -1
  135. package/package.json +1 -1
  136. package/src/Adapter.ts +97 -8
  137. package/src/OAuthPersistence.ts +427 -0
  138. package/src/index.ts +3 -0
  139. package/src/internal/authority-snapshot.ts +91 -0
  140. package/src/internal/configuration.ts +1 -8
  141. package/src/internal/models/oauth-connected-model.ts +396 -0
  142. package/src/internal/models/oauth-model.ts +708 -0
  143. package/src/internal/models/password-model.ts +0 -120
  144. package/src/internal/native-sql-table.ts +178 -0
  145. package/src/internal/oauth/accounts.ts +838 -0
  146. package/src/internal/oauth/connected-access.ts +388 -0
  147. package/src/internal/oauth/connected-collection.ts +345 -0
  148. package/src/internal/oauth/connected-custody.ts +308 -0
  149. package/src/internal/oauth/connected-flow.ts +562 -0
  150. package/src/internal/oauth/connected-input.ts +147 -0
  151. package/src/internal/oauth/connected-keys.ts +31 -0
  152. package/src/internal/oauth/connected-maintenance.ts +379 -0
  153. package/src/internal/oauth/connected-management.ts +391 -0
  154. package/src/internal/oauth/connected-reference.ts +135 -0
  155. package/src/internal/oauth/connected-settlement.ts +278 -0
  156. package/src/internal/oauth/connected-sign-in.ts +472 -0
  157. package/src/internal/oauth/connected-state.ts +924 -0
  158. package/src/internal/oauth/connected-target.ts +307 -0
  159. package/src/internal/oauth/flow.ts +505 -0
  160. package/src/internal/oauth/input.ts +201 -0
  161. package/src/internal/oauth/owner.ts +22 -0
  162. package/src/internal/oauth/query-compiler.ts +15 -0
  163. package/src/internal/oauth/registration.ts +676 -0
  164. package/src/internal/oauth/state.ts +218 -0
  165. package/src/internal/oauth/target.ts +803 -0
  166. package/src/internal/oauth-kernel.ts +294 -0
  167. package/src/internal/oauth-proxy.ts +176 -0
  168. package/src/internal/password-credentials.ts +245 -0
  169. package/src/internal/password-kernel.ts +98 -712
  170. package/src/internal/persistence.ts +5 -10
  171. package/src/internal/query-operations.ts +2 -0
  172. package/src/internal/session-kernel.ts +231 -81
  173. package/src/internal/sql-oauth-connected-model.ts +145 -0
  174. package/src/internal/sql-oauth-model.ts +263 -0
  175. package/src/internal/sql-persistence.ts +2 -0
  176. package/src/internal/sql-query.ts +16 -13
  177. package/src/internal/sql-storage-validation.ts +64 -0
  178. package/src/internal/storage-mapping.ts +0 -66
  179. package/src/internal/storage-plans.ts +218 -0
  180. package/src/internal/storage-tables.ts +0 -35
  181. package/src/internal/storage-validation.ts +58 -0
  182. package/src/internal/transaction-kernel.ts +6 -2
@@ -1 +1 @@
1
- {"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyEnrollmentContext,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneAdmission, PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Crypto, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string | undefined;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: (input: {\n readonly identifier: LoginIdentifier;\n readonly registration: Registration<A[\"strategies\"][K]>;\n }) => Effect.Effect<SubjectId, PasswordUnavailable>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles =\n | \"proofRequests\"\n | \"proofSeries\"\n | \"proofGenerations\"\n | \"proofContinuations\"\n | \"proofScopes\"\n | \"proofAbuse\"\n | \"proofFailures\"\n | \"proofCommands\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never\n ? never\n : PasskeyManagementPersistence | PasskeyEnrollmentContext)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneAdmission | PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"sessionFlows\"\n | (Enabled<A, \"password\"> extends never\n ? never\n :\n | \"passwords\"\n | \"passwordAttempts\"\n | \"passwordScopes\"\n | \"passwordCharges\"\n | \"passwordCommands\")\n | (ManagedPassword<A> extends never ? never : \"passwordRegistrations\")\n | (Enabled<A, \"phone\"> extends never ? never : \"phoneState\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\" | \"emailCommands\")\n | (Enabled<A, \"passkey\"> extends never\n ? never\n :\n | \"passkeyCredentials\"\n | \"passkeyOwnership\"\n | \"passkeyHandles\"\n | \"passkeyModules\"\n | \"passkeyFlows\"\n | \"passkeyAdmissions\"\n | \"passkeyCharges\")\n | (ManagedPasskey<A> extends never ? never : \"passkeyCommands\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Create only the application subject. Runs inside the owning auth SQL transaction;\n * use that SqlClient (including Drizzle over it), and return the new subject ID.\n * Do not perform external side effects or open an independent transaction here.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | Crypto.Crypto\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n /** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */\n readonly prefix: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AAgCA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyEnrollmentContext,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneAdmission, PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Crypto, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string | undefined;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: (input: {\n readonly identifier: LoginIdentifier;\n readonly registration: Registration<A[\"strategies\"][K]>;\n }) => Effect.Effect<SubjectId, PasswordUnavailable>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles =\n | \"proofRequests\"\n | \"proofSeries\"\n | \"proofGenerations\"\n | \"proofContinuations\"\n | \"proofScopes\"\n | \"proofAbuse\"\n | \"proofFailures\"\n | \"proofCommands\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never\n ? never\n : PasskeyManagementPersistence | PasskeyEnrollmentContext)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneAdmission | PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"sessionFlows\"\n | (Enabled<A, \"password\"> extends never ? never : \"passwords\" | \"passwordCommands\")\n | (ManagedPassword<A> extends never ? never : \"passwordRegistrations\")\n | (Enabled<A, \"phone\"> extends never ? never : \"phoneState\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\" | \"emailCommands\")\n | (Enabled<A, \"passkey\"> extends never\n ? never\n :\n | \"passkeyCredentials\"\n | \"passkeyOwnership\"\n | \"passkeyHandles\"\n | \"passkeyModules\"\n | \"passkeyFlows\"\n | \"passkeyAdmissions\"\n | \"passkeyCharges\")\n | (ManagedPasskey<A> extends never ? never : \"passkeyCommands\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Create only the application subject. Runs inside the owning auth SQL transaction;\n * use that SqlClient (including Drizzle over it), and return the new subject ID.\n * Do not perform external side effects or open an independent transaction here.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | Crypto.Crypto\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n /** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */\n readonly prefix: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AAgCA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
@@ -0,0 +1,294 @@
1
+ import { SqlExpression, TableModel } from "../query-operations.mjs";
2
+ import { SubjectIdCodec } from "./common.mjs";
3
+ import { OAuthAuthorityReadTable, OAuthClock, OAuthCredentialReadTable, OAuthFlowTable, OAuthOwnershipMutation, OAuthSubjectReadTable, requiredOAuthTupleConstraints } from "./oauth-model.mjs";
4
+ import { AuthenticationRequirement } from "@yielded/auth/Sessions";
5
+ import * as M from "@yielded/auth/OAuth";
6
+ import { OAuthAccountRevision } from "@yielded/auth/OAuth";
7
+ declare namespace oauth_connected_model_d_exports {
8
+ export { OAuthConnectedAction, OAuthConnectedAdmissionTable, OAuthConnectedAuthorityMapping, OAuthConnectedClientRegistrationTable, OAuthConnectedCohortTable, OAuthConnectedCommandTable, OAuthConnectedFlowTable, OAuthConnectedGrantTable, OAuthConnectedMapping, OAuthConnectedOrderCodec, OAuthConnectedOwnershipRead, OAuthConnectedPolicyGuard, OAuthConnectedPolicyGuardTable, OAuthConnectedPolicyInput, OAuthConnectedRevocationJobTable, OAuthConnectedRevocationMapping, OAuthConnectedSqlPolicy, OAuthConnectedSubjectTable, oauthConnectedPolicyGuard, requiredOAuthConnectedConstraints, requiredOAuthConnectedRevocationConstraints };
9
+ }
10
+ type Column<T extends TableModel> = T["column"];
11
+ export type OAuthConnectedAction = M.OAuthConnectedActionChallenge["action"];
12
+ export interface OAuthConnectedSubjectTable<S extends TableModel, Expression extends SqlExpression = SqlExpression> extends OAuthSubjectReadTable<S, Expression> {
13
+ readonly decodeActionRequirement: (row: S["select"], action: OAuthConnectedAction) => AuthenticationRequirement;
14
+ }
15
+ /** All mapped order columns use exact SQL integer comparison through 2^53-1.
16
+ * These codecs bridge driver representations, not alternative ordering rules. */
17
+ export interface OAuthConnectedOrderCodec {
18
+ readonly encode: (order: number) => unknown;
19
+ readonly decode: (value: unknown) => number;
20
+ }
21
+ export interface OAuthConnectedFlowTable<F extends TableModel, N> {
22
+ readonly table: F["table"];
23
+ readonly moduleId: Column<F>;
24
+ readonly flowId: Column<F>;
25
+ readonly commandId: Column<F>;
26
+ readonly subjectId: Column<F>;
27
+ readonly clientKey: Column<F>;
28
+ readonly cohortKey: Column<F>;
29
+ readonly state: Column<F>;
30
+ readonly version: Column<F>;
31
+ readonly stateDigest: Column<F>;
32
+ readonly snapshot: Column<F>;
33
+ readonly claimId: Column<F>;
34
+ readonly claimDigest: Column<F>;
35
+ readonly claimOrder: Column<F>;
36
+ readonly claimedAt: Column<F>;
37
+ readonly claimExpiresAt: Column<F>;
38
+ readonly expiresAt: Column<F>;
39
+ readonly retentionUntil: Column<F>;
40
+ /** None before claim, Unresolved after claim, Resolved only after definite
41
+ * cancellation or atomic transfer to a grant/job. Never reset by cleanup. */
42
+ readonly work: Column<F>;
43
+ /** Exact sealed quarantine when no correctly sealed revocation job is available. */
44
+ readonly custody: Column<F>;
45
+ /** Required when retaining grants during sign-in. subjectId is NULL until
46
+ * identity resolution; custom required columns are supplied by this encoder. */
47
+ readonly encodeSignIn?: (reservation: M.OAuthSignInAccessClaim) => F["insert"];
48
+ readonly encodeInsert: (input: {
49
+ readonly flow: M.OAuthConnectedPendingFlow;
50
+ readonly subjectId: N;
51
+ }) => F["insert"];
52
+ }
53
+ export interface OAuthConnectedGrantTable<G extends TableModel, N> {
54
+ readonly table: G["table"];
55
+ readonly moduleId: Column<G>;
56
+ readonly grantId: Column<G>;
57
+ readonly subjectId: Column<G>;
58
+ readonly identityKey: Column<G>;
59
+ /** Nullable unique active slot; historical/disconnected records use null. */
60
+ readonly activeIdentityKey: Column<G>;
61
+ readonly clientKey: Column<G>;
62
+ readonly cohortKey: Column<G>;
63
+ readonly profileKey: Column<G>;
64
+ readonly grantVersion: Column<G>;
65
+ readonly tokenVersion: Column<G>;
66
+ readonly cohortGeneration: Column<G>;
67
+ readonly state: Column<G>;
68
+ readonly version: Column<G>;
69
+ readonly context: Column<G>;
70
+ readonly sealed: Column<G>;
71
+ /** Canonical OAuthConnectedSummary only: list never loads token envelopes. */
72
+ readonly summary: Column<G>;
73
+ readonly revocationJobId: Column<G>;
74
+ readonly refreshWork: Column<G>;
75
+ readonly refreshClaim: Column<G>;
76
+ readonly refreshClaimExpiresAt: Column<G>;
77
+ readonly retentionUntil: Column<G>;
78
+ readonly encodeInsert: (input: {
79
+ readonly grant: M.OAuthConnectedStoredGrant;
80
+ readonly subjectId: N;
81
+ }) => G["insert"];
82
+ }
83
+ /** Also stores a permanent provider/issuer scope anchor. clientKey must fit 52
84
+ * ASCII characters. Empty clientRegistrationId is reserved for that anchor;
85
+ * actual profile registrations are nonempty. Scope counter stays zero. */
86
+ export interface OAuthConnectedClientRegistrationTable<C extends TableModel> {
87
+ readonly table: C["table"];
88
+ readonly clientKey: Column<C>;
89
+ readonly provider: Column<C>;
90
+ readonly issuer: Column<C>;
91
+ readonly clientRegistrationId: Column<C>;
92
+ readonly counter: Column<C>;
93
+ readonly version: Column<C>;
94
+ readonly encodeInsert: (configuration: M.OAuthConnectedConfiguration) => C["insert"];
95
+ }
96
+ /** Remote authority: no local-subject column or subject-scoped key. Retain this
97
+ * anchor/cutoff when a legitimately released tuple gains a different local owner. */
98
+ export interface OAuthConnectedCohortTable<C extends TableModel> {
99
+ readonly table: C["table"];
100
+ readonly cohortKey: Column<C>;
101
+ readonly clientKey: Column<C>;
102
+ readonly identityKey: Column<C>;
103
+ readonly generation: Column<C>;
104
+ readonly cutoff: Column<C>;
105
+ readonly state: Column<C>;
106
+ readonly version: Column<C>;
107
+ readonly encodeInsert: (input: {
108
+ readonly clientKey: string;
109
+ readonly identityKey: string;
110
+ }) => C["insert"];
111
+ }
112
+ export interface OAuthConnectedAdmissionTable<A extends TableModel, N> {
113
+ readonly table: A["table"];
114
+ readonly admissionId: Column<A>;
115
+ readonly moduleId: Column<A>;
116
+ readonly grantId: Column<A>;
117
+ readonly subjectId: Column<A>;
118
+ readonly identityKey: Column<A>;
119
+ readonly clientKey: Column<A>;
120
+ readonly cohortKey: Column<A>;
121
+ readonly snapshot: Column<A>;
122
+ readonly admittedAt: Column<A>;
123
+ readonly expiresAt: Column<A>;
124
+ readonly version: Column<A>;
125
+ readonly encodeInsert: (input: {
126
+ readonly admissionId: string;
127
+ readonly grant: M.OAuthConnectedStoredGrant;
128
+ readonly authorization: M.OAuthConnectedUseAuthorization;
129
+ readonly subjectId: N;
130
+ }) => A["insert"];
131
+ }
132
+ export interface OAuthConnectedCommandTable<C extends TableModel, N> {
133
+ readonly table: C["table"];
134
+ readonly moduleId: Column<C>;
135
+ readonly commandId: Column<C>;
136
+ readonly subjectId: Column<C>;
137
+ readonly grantId: Column<C>;
138
+ readonly intent: Column<C>;
139
+ readonly decision: Column<C>;
140
+ readonly retentionUntil: Column<C>;
141
+ readonly version: Column<C>;
142
+ readonly encodeInsert: (input: {
143
+ readonly commandId: string;
144
+ readonly grant: M.OAuthConnectedDisconnectGrant;
145
+ readonly subjectId: N;
146
+ }) => C["insert"];
147
+ }
148
+ export interface OAuthConnectedRevocationJobTable<J extends TableModel, N> {
149
+ readonly table: J["table"];
150
+ readonly jobId: Column<J>;
151
+ readonly moduleId: Column<J>;
152
+ readonly subjectId: Column<J>;
153
+ readonly identityKey: Column<J>;
154
+ readonly clientKey: Column<J>;
155
+ readonly cohortKey: Column<J>;
156
+ readonly grantId: Column<J>;
157
+ readonly snapshot: Column<J>;
158
+ readonly state: Column<J>;
159
+ readonly claimId: Column<J>;
160
+ readonly claimedAt: Column<J>;
161
+ readonly claimExpiresAt: Column<J>;
162
+ readonly retentionUntil: Column<J>;
163
+ readonly version: Column<J>;
164
+ readonly encodeInsert: (input: {
165
+ readonly job: M.OAuthConnectedRevocationJob;
166
+ readonly subjectId: N;
167
+ }) => J["insert"];
168
+ }
169
+ export type OAuthConnectedPolicyInput<N> = {
170
+ readonly subjectId: N;
171
+ readonly revision: OAuthAccountRevision;
172
+ } & ({
173
+ readonly kind: "action";
174
+ readonly operation: "issue" | "claim" | "settle" | "disconnect";
175
+ readonly authorization: M.OAuthConnectedActionAuthorization;
176
+ readonly configuration: M.OAuthConnectedConfiguration;
177
+ readonly grant?: M.OAuthConnectedTokenContext;
178
+ } | {
179
+ readonly kind: "sign-in";
180
+ readonly credential: M.OAuthCredentialSnapshot;
181
+ readonly configuration: M.OAuthConnectedConfiguration;
182
+ readonly grant?: M.OAuthConnectedTokenContext;
183
+ } | {
184
+ readonly kind: "metadata" | "use";
185
+ readonly authorization: M.OAuthConnectedUseAuthorization;
186
+ readonly grant?: M.OAuthConnectedTokenContext;
187
+ });
188
+ export interface OAuthConnectedPolicyGuardTable<T extends TableModel, N, Expression extends SqlExpression = SqlExpression> {
189
+ readonly table: T["table"];
190
+ readonly orderBy: Column<T>;
191
+ readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;
192
+ }
193
+ export interface OAuthConnectedPolicyGuard<N, Expression extends SqlExpression = SqlExpression, Descriptor extends object = object> {
194
+ readonly table: Descriptor;
195
+ readonly orderBy: string;
196
+ readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;
197
+ }
198
+ export declare const oauthConnectedPolicyGuard: <T extends TableModel, N, Expression extends SqlExpression = SqlExpression>(input: OAuthConnectedPolicyGuardTable<T, N, Expression>) => OAuthConnectedPolicyGuard<N, Expression, T["table"]>;
199
+ export interface OAuthConnectedSqlPolicy<N, Expression extends SqlExpression = SqlExpression, Descriptor extends object = object> {
200
+ /** Up to 32 scopes, each selecting 1..64 stable rows in global lock order.
201
+ * Omit only when subject/shared authority locks already serialize every mutable
202
+ * policy dependency or policy is immutable. All writers follow that order. */
203
+ readonly guards?: ReadonlyArray<OAuthConnectedPolicyGuard<N, Expression, Descriptor>>;
204
+ /** Recheck exact policy revision, purpose, optional grant/profile restrictions
205
+ * and current profile/permissions; joins and absence assumptions are authority.
206
+ * The engine checks this before and after every owner/application write. */
207
+ readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;
208
+ }
209
+ export declare const requiredOAuthConnectedConstraints: {
210
+ readonly flow: "unique(connectedFlow.moduleId,connectedFlow.flowId)";
211
+ readonly flowCommand: "unique(connectedFlow.moduleId,connectedFlow.commandId)";
212
+ readonly stateDigest: "unique(connectedFlow.stateDigest)";
213
+ readonly client: "unique(connectedClient.clientKey)";
214
+ readonly cohort: "unique(connectedCohort.cohortKey)";
215
+ readonly grant: "unique(connectedGrant.moduleId,connectedGrant.grantId)";
216
+ readonly grantIdentity: "unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)";
217
+ readonly admission: "unique(connectedAdmission.admissionId)";
218
+ readonly command: "unique(connectedCommand.moduleId,connectedCommand.commandId)";
219
+ };
220
+ export declare const requiredOAuthConnectedRevocationConstraints: {
221
+ readonly job: "unique(connectedRevocation.jobId)";
222
+ };
223
+ /** Existing ownership only; workers never allocate or acquire a tuple. */
224
+ export type OAuthConnectedOwnershipRead<T extends TableModel, O extends TableModel, N, Expression extends SqlExpression = SqlExpression> = {
225
+ readonly mode: "integrated";
226
+ readonly tuple: Omit<OAuthOwnershipMutation<T, O, N, Expression>["tuple"], "encodeInsert">;
227
+ } | {
228
+ readonly mode: "separate";
229
+ readonly tuple: Omit<OAuthOwnershipMutation<T, O, N, Expression>["tuple"], "encodeInsert">;
230
+ readonly external: Omit<Extract<OAuthOwnershipMutation<T, O, N, Expression>, {
231
+ readonly mode: "separate";
232
+ }>["external"], "encodeInsert">;
233
+ };
234
+ export interface OAuthConnectedAuthorityMapping<T extends TableModel, O extends TableModel, F extends TableModel, G extends TableModel, C extends TableModel, H extends TableModel, N, Expression extends SqlExpression = SqlExpression> {
235
+ readonly ownership: OAuthConnectedOwnershipRead<T, O, N, Expression>;
236
+ readonly subjectId: SubjectIdCodec<N>;
237
+ readonly flow: Omit<OAuthConnectedFlowTable<F, N>, "encodeInsert">;
238
+ readonly grant: Omit<OAuthConnectedGrantTable<G, N>, "encodeInsert">;
239
+ readonly client: Omit<OAuthConnectedClientRegistrationTable<C>, "encodeInsert">;
240
+ readonly cohort: Omit<OAuthConnectedCohortTable<H>, "encodeInsert">;
241
+ readonly clock: OAuthClock<Expression>;
242
+ readonly order: OAuthConnectedOrderCodec;
243
+ readonly tupleConstraints: typeof requiredOAuthTupleConstraints;
244
+ /** Safe metadata retention, never an expiry/quiescence proof for unresolved work. */
245
+ readonly retentionMillis: number;
246
+ }
247
+ export interface OAuthConnectedMapping<S extends TableModel, AC extends TableModel, T extends TableModel, O extends TableModel, F extends TableModel, G extends TableModel, C extends TableModel, H extends TableModel, A extends TableModel, D extends TableModel, N, J extends TableModel = never, Expression extends SqlExpression = SqlExpression, Descriptor extends object = object, SignIn extends TableModel = TableModel> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N, Expression> {
248
+ readonly ownership: OAuthOwnershipMutation<T, O, N, Expression>;
249
+ readonly flow: OAuthConnectedFlowTable<F, N>;
250
+ readonly grant: OAuthConnectedGrantTable<G, N>;
251
+ readonly client: OAuthConnectedClientRegistrationTable<C>;
252
+ readonly cohort: OAuthConnectedCohortTable<H>;
253
+ readonly subject: OAuthConnectedSubjectTable<S, Expression>;
254
+ readonly authority: OAuthAuthorityReadTable<AC, Expression>;
255
+ readonly admission: OAuthConnectedAdmissionTable<A, N>;
256
+ /** Same shared sign-in tables used by OAuthSignInPersistence. Enable only
257
+ * alongside a nullable connected-flow subjectId and flow.encodeSignIn. */
258
+ readonly signIn?: {
259
+ readonly credential: OAuthCredentialReadTable<SignIn, Expression>;
260
+ readonly flow: OAuthFlowTable<SignIn>;
261
+ };
262
+ readonly command: OAuthConnectedCommandTable<D, N>;
263
+ readonly policy: OAuthConnectedSqlPolicy<N, Expression, Descriptor>;
264
+ readonly constraints: typeof requiredOAuthConnectedConstraints;
265
+ readonly revocation: {
266
+ readonly mode: "unsupported";
267
+ } | {
268
+ readonly mode: "cohort";
269
+ readonly job: OAuthConnectedRevocationJobTable<J, N>;
270
+ readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;
271
+ };
272
+ /** Without an authoritative same-owner predicate, conservatively retain Owned.
273
+ * Include login/other installed references; never filter away former-subject
274
+ * work. This pure SQL constructor supports both bound values and correlated
275
+ * column expressions with identical semantics; it performs no I/O.
276
+ * Compare through the reference table's typed columns (for example, eq) so
277
+ * native values use their encoders. Back the same-owner lookup with an index:
278
+ * discovery applies this predicate before its candidate limit.
279
+ * Connected references are checked separately. */
280
+ readonly externalReference?: (input: {
281
+ readonly identityKey: string;
282
+ readonly subjectId: N;
283
+ } | {
284
+ readonly identityKey: Expression;
285
+ readonly subjectId: Expression;
286
+ }) => Expression;
287
+ }
288
+ export interface OAuthConnectedRevocationMapping<T extends TableModel, O extends TableModel, F extends TableModel, G extends TableModel, C extends TableModel, H extends TableModel, J extends TableModel, N, Expression extends SqlExpression = SqlExpression> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N, Expression> {
289
+ readonly job: Omit<OAuthConnectedRevocationJobTable<J, N>, "encodeInsert">;
290
+ readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;
291
+ }
292
+ //#endregion
293
+ export { oauth_connected_model_d_exports };
294
+ //# sourceMappingURL=oauth-connected-model.d.mts.map
@@ -0,0 +1,22 @@
1
+ //#region src/internal/models/oauth-connected-model.ts
2
+ const oauthConnectedPolicyGuard = (input) => Object.freeze({
3
+ table: input.table,
4
+ orderBy: input.orderBy,
5
+ condition: input.condition
6
+ });
7
+ const requiredOAuthConnectedConstraints = {
8
+ flow: "unique(connectedFlow.moduleId,connectedFlow.flowId)",
9
+ flowCommand: "unique(connectedFlow.moduleId,connectedFlow.commandId)",
10
+ stateDigest: "unique(connectedFlow.stateDigest)",
11
+ client: "unique(connectedClient.clientKey)",
12
+ cohort: "unique(connectedCohort.cohortKey)",
13
+ grant: "unique(connectedGrant.moduleId,connectedGrant.grantId)",
14
+ grantIdentity: "unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)",
15
+ admission: "unique(connectedAdmission.admissionId)",
16
+ command: "unique(connectedCommand.moduleId,connectedCommand.commandId)"
17
+ };
18
+ const requiredOAuthConnectedRevocationConstraints = { job: "unique(connectedRevocation.jobId)" };
19
+ //#endregion
20
+ export { oauthConnectedPolicyGuard, requiredOAuthConnectedConstraints, requiredOAuthConnectedRevocationConstraints };
21
+
22
+ //# sourceMappingURL=oauth-connected-model.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-connected-model.mjs","names":[],"sources":["../../../src/internal/models/oauth-connected-model.ts"],"sourcesContent":["import type { OAuthAccountRevision } from \"@yielded/auth/OAuth\";\nimport type * as M from \"@yielded/auth/OAuth\";\nimport type { AuthenticationRequirement } from \"@yielded/auth/Sessions\";\n\nimport type { TableModel as Table, SqlExpression } from \"../query-operations\";\nimport type { SubjectIdCodec } from \"./common\";\nimport type {\n OAuthAuthorityReadTable,\n OAuthClock,\n OAuthCredentialReadTable,\n OAuthFlowTable,\n OAuthOwnershipMutation,\n OAuthSubjectReadTable,\n requiredOAuthTupleConstraints,\n} from \"./oauth-model\";\n\ntype Column<T extends Table> = T[\"column\"];\nexport type OAuthConnectedAction = M.OAuthConnectedActionChallenge[\"action\"];\n\nexport interface OAuthConnectedSubjectTable<\n S extends Table,\n Expression extends SqlExpression = SqlExpression,\n> extends OAuthSubjectReadTable<S, Expression> {\n readonly decodeActionRequirement: (\n row: S[\"select\"],\n action: OAuthConnectedAction,\n ) => AuthenticationRequirement;\n}\n\n/** All mapped order columns use exact SQL integer comparison through 2^53-1.\n * These codecs bridge driver representations, not alternative ordering rules. */\nexport interface OAuthConnectedOrderCodec {\n readonly encode: (order: number) => unknown;\n readonly decode: (value: unknown) => number;\n}\n\nexport interface OAuthConnectedFlowTable<F extends Table, N> {\n readonly table: F[\"table\"];\n readonly moduleId: Column<F>;\n readonly flowId: Column<F>;\n readonly commandId: Column<F>;\n readonly subjectId: Column<F>;\n readonly clientKey: Column<F>;\n readonly cohortKey: Column<F>;\n readonly state: Column<F>;\n readonly version: Column<F>;\n readonly stateDigest: Column<F>;\n readonly snapshot: Column<F>;\n readonly claimId: Column<F>;\n readonly claimDigest: Column<F>;\n readonly claimOrder: Column<F>;\n readonly claimedAt: Column<F>;\n readonly claimExpiresAt: Column<F>;\n readonly expiresAt: Column<F>;\n readonly retentionUntil: Column<F>;\n /** None before claim, Unresolved after claim, Resolved only after definite\n * cancellation or atomic transfer to a grant/job. Never reset by cleanup. */\n readonly work: Column<F>;\n /** Exact sealed quarantine when no correctly sealed revocation job is available. */\n readonly custody: Column<F>;\n /** Required when retaining grants during sign-in. subjectId is NULL until\n * identity resolution; custom required columns are supplied by this encoder. */\n readonly encodeSignIn?: (reservation: M.OAuthSignInAccessClaim) => F[\"insert\"];\n readonly encodeInsert: (input: {\n readonly flow: M.OAuthConnectedPendingFlow;\n readonly subjectId: N;\n }) => F[\"insert\"];\n}\n\nexport interface OAuthConnectedGrantTable<G extends Table, N> {\n readonly table: G[\"table\"];\n readonly moduleId: Column<G>;\n readonly grantId: Column<G>;\n readonly subjectId: Column<G>;\n readonly identityKey: Column<G>;\n /** Nullable unique active slot; historical/disconnected records use null. */\n readonly activeIdentityKey: Column<G>;\n readonly clientKey: Column<G>;\n readonly cohortKey: Column<G>;\n readonly profileKey: Column<G>;\n readonly grantVersion: Column<G>;\n readonly tokenVersion: Column<G>;\n readonly cohortGeneration: Column<G>;\n readonly state: Column<G>;\n readonly version: Column<G>;\n readonly context: Column<G>;\n readonly sealed: Column<G>;\n /** Canonical OAuthConnectedSummary only: list never loads token envelopes. */\n readonly summary: Column<G>;\n readonly revocationJobId: Column<G>;\n readonly refreshWork: Column<G>;\n readonly refreshClaim: Column<G>;\n readonly refreshClaimExpiresAt: Column<G>;\n readonly retentionUntil: Column<G>;\n readonly encodeInsert: (input: {\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly subjectId: N;\n }) => G[\"insert\"];\n}\n\n/** Also stores a permanent provider/issuer scope anchor. clientKey must fit 52\n * ASCII characters. Empty clientRegistrationId is reserved for that anchor;\n * actual profile registrations are nonempty. Scope counter stays zero. */\nexport interface OAuthConnectedClientRegistrationTable<C extends Table> {\n readonly table: C[\"table\"];\n readonly clientKey: Column<C>;\n readonly provider: Column<C>;\n readonly issuer: Column<C>;\n readonly clientRegistrationId: Column<C>;\n readonly counter: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (configuration: M.OAuthConnectedConfiguration) => C[\"insert\"];\n}\n\n/** Remote authority: no local-subject column or subject-scoped key. Retain this\n * anchor/cutoff when a legitimately released tuple gains a different local owner. */\nexport interface OAuthConnectedCohortTable<C extends Table> {\n readonly table: C[\"table\"];\n readonly cohortKey: Column<C>;\n readonly clientKey: Column<C>;\n readonly identityKey: Column<C>;\n readonly generation: Column<C>;\n readonly cutoff: Column<C>;\n readonly state: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly clientKey: string;\n readonly identityKey: string;\n }) => C[\"insert\"];\n}\n\nexport interface OAuthConnectedAdmissionTable<A extends Table, N> {\n readonly table: A[\"table\"];\n readonly admissionId: Column<A>;\n readonly moduleId: Column<A>;\n readonly grantId: Column<A>;\n readonly subjectId: Column<A>;\n readonly identityKey: Column<A>;\n readonly clientKey: Column<A>;\n readonly cohortKey: Column<A>;\n readonly snapshot: Column<A>;\n readonly admittedAt: Column<A>;\n readonly expiresAt: Column<A>;\n readonly version: Column<A>;\n readonly encodeInsert: (input: {\n readonly admissionId: string;\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly subjectId: N;\n }) => A[\"insert\"];\n}\n\nexport interface OAuthConnectedCommandTable<C extends Table, N> {\n readonly table: C[\"table\"];\n readonly moduleId: Column<C>;\n readonly commandId: Column<C>;\n readonly subjectId: Column<C>;\n readonly grantId: Column<C>;\n readonly intent: Column<C>;\n readonly decision: Column<C>;\n readonly retentionUntil: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly commandId: string;\n readonly grant: M.OAuthConnectedDisconnectGrant;\n readonly subjectId: N;\n }) => C[\"insert\"];\n}\n\nexport interface OAuthConnectedRevocationJobTable<J extends Table, N> {\n readonly table: J[\"table\"];\n readonly jobId: Column<J>;\n readonly moduleId: Column<J>;\n readonly subjectId: Column<J>;\n readonly identityKey: Column<J>;\n readonly clientKey: Column<J>;\n readonly cohortKey: Column<J>;\n readonly grantId: Column<J>;\n readonly snapshot: Column<J>;\n readonly state: Column<J>;\n readonly claimId: Column<J>;\n readonly claimedAt: Column<J>;\n readonly claimExpiresAt: Column<J>;\n readonly retentionUntil: Column<J>;\n readonly version: Column<J>;\n readonly encodeInsert: (input: {\n readonly job: M.OAuthConnectedRevocationJob;\n readonly subjectId: N;\n }) => J[\"insert\"];\n}\n\nexport type OAuthConnectedPolicyInput<N> = {\n readonly subjectId: N;\n readonly revision: OAuthAccountRevision;\n} & (\n | {\n readonly kind: \"action\";\n readonly operation: \"issue\" | \"claim\" | \"settle\" | \"disconnect\";\n readonly authorization: M.OAuthConnectedActionAuthorization;\n readonly configuration: M.OAuthConnectedConfiguration;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n | {\n readonly kind: \"sign-in\";\n readonly credential: M.OAuthCredentialSnapshot;\n readonly configuration: M.OAuthConnectedConfiguration;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n | {\n readonly kind: \"metadata\" | \"use\";\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n);\n\nexport interface OAuthConnectedPolicyGuardTable<\n T extends Table,\n N,\n Expression extends SqlExpression = SqlExpression,\n> {\n readonly table: T[\"table\"];\n readonly orderBy: Column<T>;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;\n}\n\nexport interface OAuthConnectedPolicyGuard<\n N,\n Expression extends SqlExpression = SqlExpression,\n Descriptor extends object = object,\n> {\n readonly table: Descriptor;\n readonly orderBy: string;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;\n}\n\nexport const oauthConnectedPolicyGuard = <\n T extends Table,\n N,\n Expression extends SqlExpression = SqlExpression,\n>(\n input: OAuthConnectedPolicyGuardTable<T, N, Expression>,\n): OAuthConnectedPolicyGuard<N, Expression, T[\"table\"]> =>\n Object.freeze({\n table: input.table,\n orderBy: input.orderBy,\n condition: input.condition,\n });\n\nexport interface OAuthConnectedSqlPolicy<\n N,\n Expression extends SqlExpression = SqlExpression,\n Descriptor extends object = object,\n> {\n /** Up to 32 scopes, each selecting 1..64 stable rows in global lock order.\n * Omit only when subject/shared authority locks already serialize every mutable\n * policy dependency or policy is immutable. All writers follow that order. */\n readonly guards?: ReadonlyArray<OAuthConnectedPolicyGuard<N, Expression, Descriptor>>;\n /** Recheck exact policy revision, purpose, optional grant/profile restrictions\n * and current profile/permissions; joins and absence assumptions are authority.\n * The engine checks this before and after every owner/application write. */\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => Expression;\n}\n\nexport const requiredOAuthConnectedConstraints = {\n flow: \"unique(connectedFlow.moduleId,connectedFlow.flowId)\",\n flowCommand: \"unique(connectedFlow.moduleId,connectedFlow.commandId)\",\n stateDigest: \"unique(connectedFlow.stateDigest)\",\n client: \"unique(connectedClient.clientKey)\",\n cohort: \"unique(connectedCohort.cohortKey)\",\n grant: \"unique(connectedGrant.moduleId,connectedGrant.grantId)\",\n grantIdentity:\n \"unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)\",\n admission: \"unique(connectedAdmission.admissionId)\",\n command: \"unique(connectedCommand.moduleId,connectedCommand.commandId)\",\n} as const;\n\nexport const requiredOAuthConnectedRevocationConstraints = {\n job: \"unique(connectedRevocation.jobId)\",\n} as const;\n\n/** Existing ownership only; workers never allocate or acquire a tuple. */\nexport type OAuthConnectedOwnershipRead<\n T extends Table,\n O extends Table,\n N,\n Expression extends SqlExpression = SqlExpression,\n> =\n | {\n readonly mode: \"integrated\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N, Expression>[\"tuple\"], \"encodeInsert\">;\n }\n | {\n readonly mode: \"separate\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N, Expression>[\"tuple\"], \"encodeInsert\">;\n readonly external: Omit<\n Extract<\n OAuthOwnershipMutation<T, O, N, Expression>,\n { readonly mode: \"separate\" }\n >[\"external\"],\n \"encodeInsert\"\n >;\n };\n\nexport interface OAuthConnectedAuthorityMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n N,\n Expression extends SqlExpression = SqlExpression,\n> {\n readonly ownership: OAuthConnectedOwnershipRead<T, O, N, Expression>;\n readonly subjectId: SubjectIdCodec<N>;\n readonly flow: Omit<OAuthConnectedFlowTable<F, N>, \"encodeInsert\">;\n readonly grant: Omit<OAuthConnectedGrantTable<G, N>, \"encodeInsert\">;\n readonly client: Omit<OAuthConnectedClientRegistrationTable<C>, \"encodeInsert\">;\n readonly cohort: Omit<OAuthConnectedCohortTable<H>, \"encodeInsert\">;\n readonly clock: OAuthClock<Expression>;\n readonly order: OAuthConnectedOrderCodec;\n readonly tupleConstraints: typeof requiredOAuthTupleConstraints;\n /** Safe metadata retention, never an expiry/quiescence proof for unresolved work. */\n readonly retentionMillis: number;\n}\n\nexport interface OAuthConnectedMapping<\n S extends Table,\n AC extends Table,\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n A extends Table,\n D extends Table,\n N,\n J extends Table = never,\n Expression extends SqlExpression = SqlExpression,\n Descriptor extends object = object,\n SignIn extends Table = Table,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N, Expression> {\n readonly ownership: OAuthOwnershipMutation<T, O, N, Expression>;\n readonly flow: OAuthConnectedFlowTable<F, N>;\n readonly grant: OAuthConnectedGrantTable<G, N>;\n readonly client: OAuthConnectedClientRegistrationTable<C>;\n readonly cohort: OAuthConnectedCohortTable<H>;\n readonly subject: OAuthConnectedSubjectTable<S, Expression>;\n readonly authority: OAuthAuthorityReadTable<AC, Expression>;\n readonly admission: OAuthConnectedAdmissionTable<A, N>;\n /** Same shared sign-in tables used by OAuthSignInPersistence. Enable only\n * alongside a nullable connected-flow subjectId and flow.encodeSignIn. */\n readonly signIn?: {\n readonly credential: OAuthCredentialReadTable<SignIn, Expression>;\n readonly flow: OAuthFlowTable<SignIn>;\n };\n readonly command: OAuthConnectedCommandTable<D, N>;\n readonly policy: OAuthConnectedSqlPolicy<N, Expression, Descriptor>;\n readonly constraints: typeof requiredOAuthConnectedConstraints;\n readonly revocation:\n | { readonly mode: \"unsupported\" }\n | {\n readonly mode: \"cohort\";\n readonly job: OAuthConnectedRevocationJobTable<J, N>;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n };\n /** Without an authoritative same-owner predicate, conservatively retain Owned.\n * Include login/other installed references; never filter away former-subject\n * work. This pure SQL constructor supports both bound values and correlated\n * column expressions with identical semantics; it performs no I/O.\n * Compare through the reference table's typed columns (for example, eq) so\n * native values use their encoders. Back the same-owner lookup with an index:\n * discovery applies this predicate before its candidate limit.\n * Connected references are checked separately. */\n readonly externalReference?: (\n input:\n | { readonly identityKey: string; readonly subjectId: N }\n | { readonly identityKey: Expression; readonly subjectId: Expression },\n ) => Expression;\n}\n\nexport interface OAuthConnectedRevocationMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n J extends Table,\n N,\n Expression extends SqlExpression = SqlExpression,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N, Expression> {\n readonly job: Omit<OAuthConnectedRevocationJobTable<J, N>, \"encodeInsert\">;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n}\n"],"mappings":";AA2OA,MAAa,6BAKX,UAEA,OAAO,OAAO;CACZ,OAAO,MAAM;CACb,SAAS,MAAM;CACf,WAAW,MAAM;AACnB,CAAC;AAiBH,MAAa,oCAAoC;CAC/C,MAAM;CACN,aAAa;CACb,aAAa;CACb,QAAQ;CACR,QAAQ;CACR,OAAO;CACP,eACE;CACF,WAAW;CACX,SAAS;AACX;AAEA,MAAa,8CAA8C,EACzD,KAAK,oCACP"}