@yielded/auth-persistence 0.1.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +27 -0
  2. package/dist/Adapter.d.mts +30 -0
  3. package/dist/Adapter.mjs +25 -0
  4. package/dist/index.d.mts +7 -0
  5. package/dist/index.mjs +7 -0
  6. package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
  7. package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
  8. package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
  9. package/dist/internal/configuration.d.mts +174 -0
  10. package/dist/internal/configuration.mjs +7 -0
  11. package/dist/internal/configuration.mjs.map +1 -0
  12. package/dist/internal/email-kernel.d.mts +275 -0
  13. package/dist/internal/email-kernel.mjs +487 -0
  14. package/dist/internal/email-kernel.mjs.map +1 -0
  15. package/dist/internal/mapping-error.d.mts +10 -0
  16. package/dist/internal/mapping-error.mjs +30 -0
  17. package/dist/internal/mapping-error.mjs.map +1 -0
  18. package/dist/internal/models/common.d.mts +12 -0
  19. package/dist/internal/models/email-model.d.mts +353 -0
  20. package/dist/internal/models/email-model.mjs +21 -0
  21. package/dist/internal/models/email-model.mjs.map +1 -0
  22. package/dist/internal/models/passkey-model.d.mts +266 -0
  23. package/dist/internal/models/passkey-model.mjs +34 -0
  24. package/dist/internal/models/passkey-model.mjs.map +1 -0
  25. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
  26. package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
  27. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
  28. package/dist/internal/models/passkey-write-model.d.mts +189 -0
  29. package/dist/internal/models/passkey-write-model.mjs +13 -0
  30. package/dist/internal/models/passkey-write-model.mjs.map +1 -0
  31. package/dist/internal/models/password-model.d.mts +328 -0
  32. package/dist/internal/models/password-model.mjs +23 -0
  33. package/dist/internal/models/password-model.mjs.map +1 -0
  34. package/dist/internal/models/proof-model.d.mts +285 -0
  35. package/dist/internal/models/proof-model.mjs +17 -0
  36. package/dist/internal/models/proof-model.mjs.map +1 -0
  37. package/dist/internal/models/session-model.d.mts +199 -0
  38. package/dist/internal/models/session-model.mjs +20 -0
  39. package/dist/internal/models/session-model.mjs.map +1 -0
  40. package/dist/internal/models/step-up-model.d.mts +66 -0
  41. package/dist/internal/models/step-up-model.mjs +9 -0
  42. package/dist/internal/models/step-up-model.mjs.map +1 -0
  43. package/dist/internal/oauth-app.d.mts +11 -0
  44. package/dist/internal/oauth-app.mjs +57 -0
  45. package/dist/internal/oauth-app.mjs.map +1 -0
  46. package/dist/internal/oauth-server.d.mts +10 -0
  47. package/dist/internal/oauth-server.mjs +66 -0
  48. package/dist/internal/oauth-server.mjs.map +1 -0
  49. package/dist/internal/passkey/admission.d.mts +6 -0
  50. package/dist/internal/passkey/admission.mjs +216 -0
  51. package/dist/internal/passkey/admission.mjs.map +1 -0
  52. package/dist/internal/passkey/credentials.d.mts +14 -0
  53. package/dist/internal/passkey/credentials.mjs +249 -0
  54. package/dist/internal/passkey/credentials.mjs.map +1 -0
  55. package/dist/internal/passkey/enrollment.mjs +138 -0
  56. package/dist/internal/passkey/enrollment.mjs.map +1 -0
  57. package/dist/internal/passkey/flow.d.mts +21 -0
  58. package/dist/internal/passkey/flow.mjs +452 -0
  59. package/dist/internal/passkey/flow.mjs.map +1 -0
  60. package/dist/internal/passkey/management.mjs +231 -0
  61. package/dist/internal/passkey/management.mjs.map +1 -0
  62. package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
  63. package/dist/internal/passkey/registration-ceremony.mjs +129 -0
  64. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
  65. package/dist/internal/passkey/registration-custody.d.mts +2 -0
  66. package/dist/internal/passkey/registration-custody.mjs +52 -0
  67. package/dist/internal/passkey/registration-custody.mjs.map +1 -0
  68. package/dist/internal/passkey/registration-write.mjs +211 -0
  69. package/dist/internal/passkey/registration-write.mjs.map +1 -0
  70. package/dist/internal/passkey/state.d.mts +10 -0
  71. package/dist/internal/passkey/state.mjs +234 -0
  72. package/dist/internal/passkey/state.mjs.map +1 -0
  73. package/dist/internal/passkey/target.d.mts +19 -0
  74. package/dist/internal/passkey/target.mjs +190 -0
  75. package/dist/internal/passkey/target.mjs.map +1 -0
  76. package/dist/internal/passkey/write-state.d.mts +17 -0
  77. package/dist/internal/passkey/write-state.mjs +345 -0
  78. package/dist/internal/passkey/write-state.mjs.map +1 -0
  79. package/dist/internal/passkey/write-target.mjs +192 -0
  80. package/dist/internal/passkey/write-target.mjs.map +1 -0
  81. package/dist/internal/passkey-kernel.d.mts +5592 -0
  82. package/dist/internal/passkey-kernel.mjs +49 -0
  83. package/dist/internal/passkey-kernel.mjs.map +1 -0
  84. package/dist/internal/passkeys.d.mts +7 -0
  85. package/dist/internal/passkeys.mjs +368 -0
  86. package/dist/internal/passkeys.mjs.map +1 -0
  87. package/dist/internal/password-kernel.d.mts +290 -0
  88. package/dist/internal/password-kernel.mjs +704 -0
  89. package/dist/internal/password-kernel.mjs.map +1 -0
  90. package/dist/internal/persistence.d.mts +25 -0
  91. package/dist/internal/persistence.mjs +291 -0
  92. package/dist/internal/persistence.mjs.map +1 -0
  93. package/dist/internal/phone-kernel.d.mts +162 -0
  94. package/dist/internal/phone-kernel.mjs +434 -0
  95. package/dist/internal/phone-kernel.mjs.map +1 -0
  96. package/dist/internal/proof-kernel.d.mts +93 -0
  97. package/dist/internal/proof-kernel.mjs +619 -0
  98. package/dist/internal/proof-kernel.mjs.map +1 -0
  99. package/dist/internal/query-operations.d.mts +61 -0
  100. package/dist/internal/registration-contract.d.mts +16 -0
  101. package/dist/internal/registration.mjs +66 -0
  102. package/dist/internal/registration.mjs.map +1 -0
  103. package/dist/internal/session-kernel.d.mts +48 -0
  104. package/dist/internal/session-kernel.mjs +737 -0
  105. package/dist/internal/session-kernel.mjs.map +1 -0
  106. package/dist/internal/sql-persistence.d.mts +10 -0
  107. package/dist/internal/sql-persistence.mjs +37 -0
  108. package/dist/internal/sql-persistence.mjs.map +1 -0
  109. package/dist/internal/sql-query.mjs +207 -0
  110. package/dist/internal/sql-query.mjs.map +1 -0
  111. package/dist/internal/sql-table.d.mts +36 -0
  112. package/dist/internal/sql-table.mjs +81 -0
  113. package/dist/internal/sql-table.mjs.map +1 -0
  114. package/dist/internal/standalone.d.mts +9 -0
  115. package/dist/internal/standalone.mjs +13 -0
  116. package/dist/internal/standalone.mjs.map +1 -0
  117. package/dist/internal/step-up-state.d.mts +49 -0
  118. package/dist/internal/step-up-state.mjs +32 -0
  119. package/dist/internal/step-up-state.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +686 -0
  121. package/dist/internal/storage-mapping.mjs.map +1 -0
  122. package/dist/internal/storage-tables.d.mts +913 -0
  123. package/dist/internal/storage-tables.mjs +387 -0
  124. package/dist/internal/storage-tables.mjs.map +1 -0
  125. package/dist/internal/storage-validation.mjs +58 -0
  126. package/dist/internal/storage-validation.mjs.map +1 -0
  127. package/dist/internal/transaction-execution-kernel.d.mts +38 -0
  128. package/dist/internal/transaction-execution-kernel.mjs +116 -0
  129. package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
  130. package/dist/internal/transaction-kernel.d.mts +77 -0
  131. package/dist/internal/transaction-kernel.mjs +260 -0
  132. package/dist/internal/transaction-kernel.mjs.map +1 -0
  133. package/package.json +1 -0
  134. package/src/Adapter.ts +291 -0
  135. package/src/index.ts +6 -0
  136. package/src/internal/PasswordPreparedPostconditions.ts +30 -0
  137. package/src/internal/configuration.ts +282 -0
  138. package/src/internal/email-kernel.ts +1535 -0
  139. package/src/internal/mapping-error.ts +36 -0
  140. package/src/internal/models/common.ts +11 -0
  141. package/src/internal/models/email-model.ts +559 -0
  142. package/src/internal/models/passkey-model.ts +390 -0
  143. package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
  144. package/src/internal/models/passkey-write-model.ts +297 -0
  145. package/src/internal/models/password-model.ts +513 -0
  146. package/src/internal/models/proof-model.ts +424 -0
  147. package/src/internal/models/session-model.ts +375 -0
  148. package/src/internal/models/step-up-model.ts +135 -0
  149. package/src/internal/oauth-app.ts +82 -0
  150. package/src/internal/oauth-server.ts +89 -0
  151. package/src/internal/passkey/admission.ts +361 -0
  152. package/src/internal/passkey/credentials.ts +511 -0
  153. package/src/internal/passkey/enrollment.ts +326 -0
  154. package/src/internal/passkey/flow.ts +1062 -0
  155. package/src/internal/passkey/management.ts +421 -0
  156. package/src/internal/passkey/registration-ceremony.ts +283 -0
  157. package/src/internal/passkey/registration-custody.ts +91 -0
  158. package/src/internal/passkey/registration-write.ts +426 -0
  159. package/src/internal/passkey/state.ts +353 -0
  160. package/src/internal/passkey/target.ts +586 -0
  161. package/src/internal/passkey/write-state.ts +681 -0
  162. package/src/internal/passkey/write-target.ts +452 -0
  163. package/src/internal/passkey-kernel.ts +104 -0
  164. package/src/internal/passkeys.ts +606 -0
  165. package/src/internal/password-kernel.ts +2124 -0
  166. package/src/internal/persistence.ts +588 -0
  167. package/src/internal/phone-kernel.ts +846 -0
  168. package/src/internal/proof-kernel.ts +1954 -0
  169. package/src/internal/query-operations.ts +81 -0
  170. package/src/internal/registration-contract.ts +22 -0
  171. package/src/internal/registration.ts +161 -0
  172. package/src/internal/session-kernel.ts +2217 -0
  173. package/src/internal/sql-persistence.ts +45 -0
  174. package/src/internal/sql-query.ts +298 -0
  175. package/src/internal/sql-table.ts +104 -0
  176. package/src/internal/standalone.ts +30 -0
  177. package/src/internal/step-up-state.ts +139 -0
  178. package/src/internal/storage-mapping.ts +708 -0
  179. package/src/internal/storage-tables.ts +408 -0
  180. package/src/internal/storage-validation.ts +117 -0
  181. package/src/internal/transaction-execution-kernel.ts +313 -0
  182. package/src/internal/transaction-kernel.ts +593 -0
@@ -0,0 +1,586 @@
1
+ /* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
2
+
3
+ import { type PreparedCommit, LifecycleHooks } from "@yielded/auth/Hooks";
4
+ import {
5
+ PasskeyConfigurationError,
6
+ type PasskeyUnavailable,
7
+ type PasskeyCredentials,
8
+ type PasskeyEnrollmentContext,
9
+ type PasskeyPersistence,
10
+ type PreparePasskeyCommit,
11
+ PasskeyAccess,
12
+ PasskeyAssertionVerified,
13
+ PasskeyCeremony,
14
+ PasskeyClaim,
15
+ PasskeyClaimDecision,
16
+ PasskeyCleanupResult,
17
+ PasskeyCredential,
18
+ PasskeyEnrollmentSnapshot,
19
+ PasskeyEvidence,
20
+ PasskeyInstant,
21
+ PasskeyIssueDecision,
22
+ PasskeyModuleId,
23
+ PasskeyProfile,
24
+ PasskeyProtocolCredentialId,
25
+ PasskeySettlement,
26
+ PasskeyMethodPolicy,
27
+ snapshotPasskeySync,
28
+ } from "@yielded/auth/Passkey";
29
+ import { SubjectId } from "@yielded/auth/Schema";
30
+ import { Effect, Schema } from "effect";
31
+ import type { Statement } from "effect/unstable/sql/Statement";
32
+
33
+ import type { PersistenceMappingError } from "../mapping-error";
34
+ import type {
35
+ PasskeyCredentialServices,
36
+ PasskeyEnrollmentContextServices,
37
+ PasskeyMappingSource,
38
+ PasskeyPersistenceServices,
39
+ } from "../models/passkey-model";
40
+ import type { PasskeyRegistrationCeremonyServices } from "../models/passkey-registration-ceremony-model";
41
+ import type {
42
+ TransactionCoordinatorError,
43
+ TransactionExecution,
44
+ TransactionTargetConfiguration,
45
+ makeTransactionExecutionKernel,
46
+ } from "../transaction-execution-kernel";
47
+ import type { makePasskeyCredentialsKernel } from "./credentials";
48
+ import type { makePasskeyFlowKernel } from "./flow";
49
+ import type { makePasskeyRegistrationCeremonyKernel } from "./registration-ceremony";
50
+ import { CurrentPasskeyTransaction } from "./state";
51
+ import type { makePasskeyStateKernel } from "./state";
52
+
53
+ export type PasskeyTargetConfiguration = TransactionTargetConfiguration<PasskeyUnavailable>;
54
+
55
+ export type PasskeyCoordinatorError<E> =
56
+ | TransactionCoordinatorError<E, PasskeyUnavailable>
57
+ | PasskeyConfigurationError
58
+ | PersistenceMappingError;
59
+
60
+ export type PasskeyExecution = TransactionExecution<
61
+ PasskeyUnavailable,
62
+ CurrentPasskeyTransaction,
63
+ never
64
+ >;
65
+
66
+ export const makePasskeyTargetKernel = (
67
+ credentials: Pick<
68
+ ReturnType<typeof makePasskeyCredentialsKernel>,
69
+ "captureEnrollmentContext" | "lookupCredential"
70
+ >,
71
+ flow: Pick<
72
+ ReturnType<typeof makePasskeyFlowKernel>,
73
+ | "assertionPurposes"
74
+ | "claimAssertion"
75
+ | "cleanupCeremonies"
76
+ | "contextAssertion"
77
+ | "issueAssertion"
78
+ | "settleAssertion"
79
+ >,
80
+ registrationCeremony: Pick<
81
+ ReturnType<typeof makePasskeyRegistrationCeremonyKernel>,
82
+ "claimRegistration" | "contextRegistration" | "settleRegistration"
83
+ >,
84
+ state: Pick<
85
+ ReturnType<typeof makePasskeyStateKernel>,
86
+ "captureMapping" | "invariant" | "nonce" | "unavailable" | "validateMapping"
87
+ >,
88
+ execution: Pick<
89
+ ReturnType<typeof makeTransactionExecutionKernel>,
90
+ | "coordinateTransactionOwner"
91
+ | "makeTransactionExecution"
92
+ | "sqlClientTransactionStandaloneGuard"
93
+ >,
94
+ ) => {
95
+ const { captureEnrollmentContext, lookupCredential } = credentials;
96
+
97
+ const {
98
+ assertionPurposes,
99
+ claimAssertion,
100
+ cleanupCeremonies,
101
+ contextAssertion,
102
+ issueAssertion,
103
+ settleAssertion,
104
+ } = flow;
105
+
106
+ const { claimRegistration, contextRegistration, settleRegistration } = registrationCeremony;
107
+ const { captureMapping, nonce, unavailable, validateMapping } = state;
108
+ const invariant: (value: unknown) => asserts value = state.invariant;
109
+
110
+ const {
111
+ coordinateTransactionOwner,
112
+ makeTransactionExecution,
113
+ sqlClientTransactionStandaloneGuard,
114
+ } = execution;
115
+
116
+ const sqlClientPasskeyStandaloneGuard = (
117
+ database: Parameters<typeof sqlClientTransactionStandaloneGuard>[1],
118
+ ) => sqlClientTransactionStandaloneGuard(unavailable, database);
119
+
120
+ const emptyHooks: LifecycleHooks["Service"] = {
121
+ before: () => Effect.void,
122
+ after: () => Effect.succeed([]),
123
+ };
124
+
125
+ const makePasskeyExecution = (
126
+ database: Parameters<typeof makeTransactionExecution>[1],
127
+ hooks: LifecycleHooks["Service"],
128
+ configuration: PasskeyTargetConfiguration,
129
+ ): PasskeyExecution => {
130
+ const execution = makeTransactionExecution(
131
+ CurrentPasskeyTransaction,
132
+ database,
133
+ configuration,
134
+ unavailable,
135
+ nonce,
136
+ );
137
+
138
+ return {
139
+ ...execution,
140
+ run: (operation, mutation) =>
141
+ execution.run(operation, mutation).pipe(Effect.provideService(LifecycleHooks, hooks)),
142
+ };
143
+ };
144
+
145
+ const issueInput = Schema.Struct({ ceremony: PasskeyCeremony, policy: PasskeyMethodPolicy });
146
+
147
+ const claimInput = Schema.Struct({
148
+ access: PasskeyAccess,
149
+ policy: PasskeyMethodPolicy,
150
+ ceremony: PasskeyCeremony,
151
+ claimId: PasskeyClaim.fields.claimId,
152
+ credential: Schema.optionalKey(PasskeyCredential),
153
+ });
154
+
155
+ const settleInput = Schema.Struct({
156
+ claim: PasskeyClaim,
157
+ nowMillis: PasskeyInstant,
158
+ outcome: Schema.Union([
159
+ Schema.TaggedStruct("Rejected", {}),
160
+ Schema.TaggedStruct("Ambiguous", {}),
161
+ Schema.TaggedStruct("Assertion", {
162
+ credential: PasskeyCredential,
163
+ assertion: PasskeyAssertionVerified,
164
+ evidence: PasskeyEvidence,
165
+ }),
166
+ ]),
167
+ });
168
+
169
+ const cleanupInput = Schema.Struct({
170
+ moduleId: PasskeyModuleId,
171
+ nowMillis: PasskeyInstant,
172
+ limit: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1000 })),
173
+ });
174
+
175
+ const lookupInput = Schema.Struct({
176
+ rpId: PasskeyProfile.fields.rpId,
177
+ protocolCredentialId: PasskeyProtocolCredentialId,
178
+ });
179
+
180
+ const contextInput = Schema.Struct({
181
+ moduleId: PasskeyModuleId,
182
+ rpId: PasskeyProfile.fields.rpId,
183
+ subjectId: SubjectId,
184
+ });
185
+
186
+ const inputs = {
187
+ issue: issueInput,
188
+ context: PasskeyAccess,
189
+ claim: claimInput,
190
+ settle: settleInput,
191
+ cleanup: cleanupInput,
192
+ };
193
+
194
+ const capturedService = <S>(
195
+ service: S,
196
+ schemas: Record<string, Schema.Codec<any, any, never, never>>,
197
+ execute: PasskeyExecution,
198
+ ): S =>
199
+ Object.freeze(
200
+ Object.fromEntries(
201
+ Object.entries(
202
+ service as Record<string, (...args: any[]) => Effect.Effect<any, PasskeyUnavailable>>,
203
+ ).map(([name, method]) => [
204
+ name,
205
+ (original: any, prepare?: any) =>
206
+ Effect.suspend(() => {
207
+ invariant(execute.active());
208
+ const input = snapshotPasskeySync(schemas[name]!, original);
209
+
210
+ return method(input, prepare);
211
+ }).pipe(
212
+ Effect.onExit((exit) =>
213
+ Effect.sync(() => {
214
+ if (exit._tag === "Failure") execute.poison();
215
+ }),
216
+ ),
217
+ Effect.catchDefect(() => Effect.fail(unavailable())),
218
+ ),
219
+ ]),
220
+ ),
221
+ ) as S;
222
+
223
+ const prepareValue = <Value, A>(
224
+ value: Value,
225
+ prepare: PreparePasskeyCommit<Value, A>,
226
+ ): Effect.Effect<PreparedCommit<A>, never, CurrentPasskeyTransaction> =>
227
+ Effect.flatMap(CurrentPasskeyTransaction, (owner) =>
228
+ Effect.sync(() => {
229
+ owner.guards.push(owner.journal.prepare(undefined));
230
+ const receipt = prepare(value, owner.journal);
231
+
232
+ invariant(
233
+ receipt !== null &&
234
+ typeof receipt === "object" &&
235
+ receipt._tag === "PreparedCommit" &&
236
+ typeof receipt.read === "object" &&
237
+ Effect.isEffect(receipt.read),
238
+ );
239
+ owner.guards.push(receipt);
240
+
241
+ return receipt;
242
+ }),
243
+ );
244
+
245
+ const requirePurpose = (purpose: string, registration: boolean, enrollment = false) =>
246
+ invariant(
247
+ enrollment
248
+ ? purpose === "enrollment"
249
+ : registration
250
+ ? purpose === "registration"
251
+ : assertionPurposes.some((supported) => supported === purpose),
252
+ );
253
+
254
+ /** Context is advisory. Retain observations and final predicates without taking
255
+ * later-ranked locks before a subsequent mutation acquires its full lock set. */
256
+ const observationalContext = <A, E, R>(operation: Effect.Effect<A, E, R>) =>
257
+ Effect.flatMap(CurrentPasskeyTransaction, (owner) =>
258
+ operation.pipe(
259
+ Effect.provideService(CurrentPasskeyTransaction, {
260
+ ...owner,
261
+ read: (table, where, options) => owner.read(table, where, { ...options, lock: false }),
262
+ }),
263
+ ),
264
+ );
265
+
266
+ const makePasskeyPersistence = (
267
+ mapping: any,
268
+ execute: PasskeyExecution,
269
+ configuration: PasskeyTargetConfiguration,
270
+ registration = false,
271
+ enrollment = false,
272
+ ): PasskeyPersistence["Service"] =>
273
+ capturedService<PasskeyPersistence["Service"]>(
274
+ {
275
+ issue: (input, prepare) =>
276
+ Effect.suspend(() => {
277
+ invariant(!registration && !enrollment);
278
+ requirePurpose(input.ceremony.purpose, false);
279
+
280
+ return execute.run(
281
+ Effect.gen(function* () {
282
+ const value = yield* issueAssertion(mapping, input.ceremony, input.policy);
283
+
284
+ return yield* prepareValue(
285
+ snapshotPasskeySync(PasskeyIssueDecision, value),
286
+ prepare,
287
+ );
288
+ }),
289
+ );
290
+ }),
291
+ context: (access) =>
292
+ Effect.suspend(() => {
293
+ requirePurpose(access.purpose, registration, enrollment);
294
+
295
+ return execute.run(
296
+ observationalContext(
297
+ registration
298
+ ? contextRegistration(mapping, access)
299
+ : contextAssertion(mapping, access),
300
+ ),
301
+ false,
302
+ );
303
+ }),
304
+ claim: (input, prepare) =>
305
+ Effect.suspend(() => {
306
+ requirePurpose(input.access.purpose, registration, enrollment);
307
+ requirePurpose(input.ceremony.purpose, registration, enrollment);
308
+ invariant((!registration && !enrollment) || input.credential === undefined);
309
+
310
+ return execute.run(
311
+ Effect.gen(function* () {
312
+ const value = yield* registration
313
+ ? claimRegistration(mapping, input)
314
+ : claimAssertion(mapping, input);
315
+
316
+ return yield* prepareValue(
317
+ snapshotPasskeySync(PasskeyClaimDecision, value),
318
+ prepare,
319
+ );
320
+ }),
321
+ );
322
+ }),
323
+ settle: (input, prepare) =>
324
+ Effect.suspend(() => {
325
+ requirePurpose(input.claim.ceremony.purpose, registration, enrollment);
326
+ invariant((!registration && !enrollment) || input.outcome._tag !== "Assertion");
327
+
328
+ return execute.run(
329
+ Effect.gen(function* () {
330
+ const value = yield* registration
331
+ ? settleRegistration(
332
+ mapping,
333
+ input.claim,
334
+ input.outcome._tag as "Rejected" | "Ambiguous",
335
+ )
336
+ : settleAssertion(mapping, input.claim, input.outcome, configuration.dialect);
337
+
338
+ return yield* prepareValue(snapshotPasskeySync(PasskeySettlement, value), prepare);
339
+ }),
340
+ );
341
+ }),
342
+ cleanup: (input, prepare) =>
343
+ execute.run(
344
+ Effect.gen(function* () {
345
+ const value = yield* cleanupCeremonies(
346
+ mapping,
347
+ input,
348
+ registration ? ["registration"] : enrollment ? ["enrollment"] : assertionPurposes,
349
+ );
350
+
351
+ return yield* prepareValue(snapshotPasskeySync(PasskeyCleanupResult, value), prepare);
352
+ }),
353
+ ),
354
+ },
355
+ inputs,
356
+ execute,
357
+ );
358
+
359
+ const credentialService = (
360
+ mapping: any,
361
+ execute: PasskeyExecution,
362
+ ): PasskeyCredentials["Service"] =>
363
+ capturedService<PasskeyCredentials["Service"]>(
364
+ {
365
+ lookup: (input) =>
366
+ execute.run(lookupCredential(mapping, input.rpId, input.protocolCredentialId), false),
367
+ },
368
+ { lookup: lookupInput },
369
+ execute,
370
+ );
371
+
372
+ const enrollmentService = (
373
+ mapping: any,
374
+ execute: PasskeyExecution,
375
+ ): PasskeyEnrollmentContext["Service"] =>
376
+ capturedService<PasskeyEnrollmentContext["Service"]>(
377
+ {
378
+ capture: (input) =>
379
+ execute.run(
380
+ Effect.map(captureEnrollmentContext(mapping, input), (value) =>
381
+ value === undefined
382
+ ? undefined
383
+ : snapshotPasskeySync(PasskeyEnrollmentSnapshot, value),
384
+ ),
385
+ false,
386
+ ),
387
+ },
388
+ { capture: contextInput },
389
+ execute,
390
+ );
391
+
392
+ const registrationServices = (
393
+ mapping: any,
394
+ execute: PasskeyExecution,
395
+ configuration: PasskeyTargetConfiguration,
396
+ ): PasskeyRegistrationCeremonyServices => ({
397
+ capabilities: Object.freeze({
398
+ purposes: Object.freeze(["registration"] as const),
399
+ issue: "registration-authority",
400
+ assertionSettlement: false,
401
+ }),
402
+ passkeyPersistence: makePasskeyPersistence(mapping, execute, configuration, true),
403
+ });
404
+
405
+ const capturedMapping = <M, RSetup>(
406
+ source: PasskeyMappingSource<M, RSetup>,
407
+ kind: "read" | "context" | "assertion" | "registration",
408
+ configuration: PasskeyTargetConfiguration,
409
+ ): Effect.Effect<M, PasskeyConfigurationError | PersistenceMappingError, RSetup> =>
410
+ Effect.flatMap(Effect.isEffect(source) ? source : Effect.succeed(source), (mapping) =>
411
+ Effect.try({
412
+ try: () => {
413
+ const captured = captureMapping(mapping);
414
+
415
+ validateMapping(captured, kind);
416
+ if (configuration.mode === "batch") invariant((captured as any).d1?.primary === true);
417
+
418
+ return captured;
419
+ },
420
+ catch: () => PasskeyConfigurationError.make({}),
421
+ }),
422
+ );
423
+
424
+ const makeTargetPasskeyCredentials = <M, RSetup>(
425
+ database: any,
426
+ source: PasskeyMappingSource<M, RSetup>,
427
+ configuration: PasskeyTargetConfiguration,
428
+ ): Effect.Effect<
429
+ PasskeyCredentialServices,
430
+ PasskeyConfigurationError | PersistenceMappingError,
431
+ RSetup
432
+ > =>
433
+ Effect.map(capturedMapping(source, "read", configuration), (mapping) => ({
434
+ passkeyCredentials: credentialService(
435
+ mapping,
436
+ makePasskeyExecution(database, emptyHooks, configuration),
437
+ ),
438
+ }));
439
+
440
+ const makeTargetPasskeyEnrollmentContext = <M, RSetup>(
441
+ database: any,
442
+ source: PasskeyMappingSource<M, RSetup>,
443
+ configuration: PasskeyTargetConfiguration,
444
+ ): Effect.Effect<
445
+ PasskeyEnrollmentContextServices,
446
+ PasskeyConfigurationError | PersistenceMappingError,
447
+ RSetup
448
+ > =>
449
+ Effect.map(capturedMapping(source, "context", configuration), (mapping) => ({
450
+ passkeyEnrollmentContext: enrollmentService(
451
+ mapping,
452
+ makePasskeyExecution(database, emptyHooks, configuration),
453
+ ),
454
+ }));
455
+
456
+ const makeTargetPasskeyPersistence = <M, RSetup>(
457
+ database: any,
458
+ source: PasskeyMappingSource<M, RSetup>,
459
+ configuration: PasskeyTargetConfiguration,
460
+ ): Effect.Effect<
461
+ PasskeyPersistenceServices,
462
+ PasskeyConfigurationError | PersistenceMappingError,
463
+ RSetup | LifecycleHooks
464
+ > =>
465
+ Effect.gen(function* () {
466
+ const mapping = yield* capturedMapping(source, "assertion", configuration);
467
+ const hooks = yield* LifecycleHooks;
468
+
469
+ return {
470
+ passkeyPersistence: makePasskeyPersistence(
471
+ mapping,
472
+ makePasskeyExecution(database, hooks, configuration),
473
+ configuration,
474
+ ),
475
+ };
476
+ });
477
+
478
+ const makeTargetPasskeyRegistration = <M, RSetup>(
479
+ database: any,
480
+ source: PasskeyMappingSource<M, RSetup>,
481
+ configuration: PasskeyTargetConfiguration,
482
+ ): Effect.Effect<
483
+ PasskeyRegistrationCeremonyServices,
484
+ PasskeyConfigurationError | PersistenceMappingError,
485
+ RSetup | LifecycleHooks
486
+ > =>
487
+ Effect.gen(function* () {
488
+ const mapping = yield* capturedMapping(source, "registration", configuration);
489
+ const hooks = yield* LifecycleHooks;
490
+
491
+ return registrationServices(
492
+ mapping,
493
+ makePasskeyExecution(database, hooks, configuration),
494
+ configuration,
495
+ );
496
+ });
497
+
498
+ const coordinateTargetPasskey = <M, RSetup, Transaction, A, E, R>(
499
+ database: any,
500
+ source: PasskeyMappingSource<M, RSetup>,
501
+ configuration: PasskeyTargetConfiguration,
502
+ owner: (
503
+ transaction: Transaction,
504
+ services: PasskeyPersistenceServices,
505
+ append: (statement: Statement<unknown>) => void,
506
+ ) => Effect.Effect<A, E, R>,
507
+ ): Effect.Effect<A, PasskeyCoordinatorError<E>, R | RSetup | LifecycleHooks> =>
508
+ Effect.flatMap(capturedMapping(source, "assertion", configuration), (mapping) =>
509
+ Effect.flatMap(LifecycleHooks, (hooks) =>
510
+ coordinateTransactionOwner(
511
+ database,
512
+ CurrentPasskeyTransaction,
513
+ configuration,
514
+ Effect.void,
515
+ unavailable,
516
+ nonce,
517
+ (execution) => ({
518
+ passkeyPersistence: makePasskeyPersistence(
519
+ mapping,
520
+ {
521
+ ...execution,
522
+ run: (operation, mutation) =>
523
+ execution
524
+ .run(operation, mutation)
525
+ .pipe(Effect.provideService(LifecycleHooks, hooks)),
526
+ },
527
+ configuration,
528
+ ),
529
+ }),
530
+ owner,
531
+ ).pipe(Effect.provideService(LifecycleHooks, hooks)),
532
+ ),
533
+ );
534
+
535
+ const coordinateTargetPasskeyRegistration = <M, RSetup, Transaction, A, E, R>(
536
+ database: any,
537
+ source: PasskeyMappingSource<M, RSetup>,
538
+ configuration: PasskeyTargetConfiguration,
539
+ owner: (
540
+ transaction: Transaction,
541
+ services: PasskeyRegistrationCeremonyServices,
542
+ append: (statement: Statement<unknown>) => void,
543
+ ) => Effect.Effect<A, E, R>,
544
+ ): Effect.Effect<A, PasskeyCoordinatorError<E>, R | RSetup | LifecycleHooks> =>
545
+ Effect.flatMap(capturedMapping(source, "registration", configuration), (mapping) =>
546
+ Effect.flatMap(LifecycleHooks, (hooks) =>
547
+ coordinateTransactionOwner(
548
+ database,
549
+ CurrentPasskeyTransaction,
550
+ configuration,
551
+ Effect.void,
552
+ unavailable,
553
+ nonce,
554
+ (execution) =>
555
+ registrationServices(
556
+ mapping,
557
+ {
558
+ ...execution,
559
+ run: (operation, mutation) =>
560
+ execution
561
+ .run(operation, mutation)
562
+ .pipe(Effect.provideService(LifecycleHooks, hooks)),
563
+ },
564
+ configuration,
565
+ ),
566
+ owner,
567
+ ).pipe(Effect.provideService(LifecycleHooks, hooks)),
568
+ ),
569
+ );
570
+
571
+ return {
572
+ sqlClientPasskeyStandaloneGuard,
573
+ makePasskeyExecution,
574
+ capturedService,
575
+ prepareValue,
576
+ observationalContext,
577
+ makePasskeyPersistence,
578
+ capturedMapping,
579
+ makeTargetPasskeyCredentials,
580
+ makeTargetPasskeyEnrollmentContext,
581
+ makeTargetPasskeyPersistence,
582
+ makeTargetPasskeyRegistration,
583
+ coordinateTargetPasskey,
584
+ coordinateTargetPasskeyRegistration,
585
+ };
586
+ };