@yielded/auth-persistence-drizzle 0.1.0-beta.18 → 0.1.0-beta.19
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.
- package/README.md +8 -1
- package/dist/D1.d.mts +8 -8
- package/dist/Libsql.d.mts +44 -44
- package/dist/Mysql2.d.mts +44 -44
- package/dist/Pglite.d.mts +44 -44
- package/dist/Postgres.d.mts +44 -44
- package/dist/SqliteBun.d.mts +44 -44
- package/dist/SqliteDo.d.mts +112 -134
- package/dist/SqliteDo.mjs +30 -45
- package/dist/SqliteDo.mjs.map +1 -1
- package/dist/SqliteNode.d.mts +38 -38
- package/dist/SqliteWasm.d.mts +38 -38
- package/dist/drizzle/crypto.mjs +11 -0
- package/dist/drizzle/crypto.mjs.map +1 -0
- package/dist/drizzle/d1-identity.mjs +2 -2
- package/dist/drizzle/d1-identity.mjs.map +1 -1
- package/dist/drizzle/d1-oauth.d.mts +11 -11
- package/dist/drizzle/d1-oauth.mjs.map +1 -1
- package/dist/drizzle/d1-passkey.d.mts +6 -6
- package/dist/drizzle/d1-passkey.mjs.map +1 -1
- package/dist/drizzle/d1-passwords.d.mts +1 -1
- package/dist/drizzle/d1-phone.d.mts +3 -3
- package/dist/drizzle/d1-phone.mjs.map +1 -1
- package/dist/drizzle/d1-proofs.d.mts +1 -1
- package/dist/drizzle/d1-sessions.d.mts +1 -1
- package/dist/drizzle/d1-totp.d.mts +3 -3
- package/dist/drizzle/d1-totp.mjs.map +1 -1
- package/dist/drizzle/mysql-identity.mjs +2 -2
- package/dist/drizzle/mysql-identity.mjs.map +1 -1
- package/dist/drizzle/oauth-accounts.mjs +9 -8
- package/dist/drizzle/oauth-accounts.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-collection.mjs +1 -1
- package/dist/drizzle/oauth-connected-collection.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-custody.mjs +5 -4
- package/dist/drizzle/oauth-connected-custody.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-flow.mjs +8 -7
- package/dist/drizzle/oauth-connected-flow.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-management.mjs +3 -2
- package/dist/drizzle/oauth-connected-management.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-reference.mjs +8 -4
- package/dist/drizzle/oauth-connected-reference.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-settlement.mjs +4 -4
- package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-sign-in.mjs +11 -10
- package/dist/drizzle/oauth-connected-sign-in.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-state.d.mts +13 -13
- package/dist/drizzle/oauth-connected-state.mjs +23 -20
- package/dist/drizzle/oauth-connected-state.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-target.d.mts +7 -7
- package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
- package/dist/drizzle/oauth-drivers.mjs.map +1 -1
- package/dist/drizzle/oauth-flow.mjs +1 -1
- package/dist/drizzle/oauth-flow.mjs.map +1 -1
- package/dist/drizzle/oauth-model.d.mts +4 -4
- package/dist/drizzle/oauth-model.mjs.map +1 -1
- package/dist/drizzle/oauth-registration.mjs +2 -2
- package/dist/drizzle/oauth-registration.mjs.map +1 -1
- package/dist/drizzle/oauth-state.mjs +11 -12
- package/dist/drizzle/oauth-state.mjs.map +1 -1
- package/dist/drizzle/oauth-target.d.mts +4 -4
- package/dist/drizzle/oauth-target.mjs.map +1 -1
- package/dist/drizzle/passkey-drivers.mjs.map +1 -1
- package/dist/drizzle/passkey-write-drivers.mjs.map +1 -1
- package/dist/drizzle/passkey-write-target.d.mts +1 -1
- package/dist/drizzle/password-prepared-state.mjs +1 -1
- package/dist/drizzle/pg-identity.mjs +2 -2
- package/dist/drizzle/pg-identity.mjs.map +1 -1
- package/dist/drizzle/phone-model.d.mts +2 -2
- package/dist/drizzle/phone-model.mjs +3 -3
- package/dist/drizzle/phone-model.mjs.map +1 -1
- package/dist/drizzle/phone-target.d.mts +1 -1
- package/dist/drizzle/phone-target.mjs +2 -2
- package/dist/drizzle/phone-target.mjs.map +1 -1
- package/dist/drizzle/sqlite-do-database.d.mts +29 -0
- package/dist/drizzle/sqlite-do-database.mjs +45 -0
- package/dist/drizzle/sqlite-do-database.mjs.map +1 -0
- package/dist/drizzle/sqlite-emails.mjs.map +1 -1
- package/dist/drizzle/sqlite-identity.mjs +2 -2
- package/dist/drizzle/sqlite-identity.mjs.map +1 -1
- package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
- package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
- package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
- package/dist/drizzle/storage-validation.mjs +17 -9
- package/dist/drizzle/storage-validation.mjs.map +1 -1
- package/dist/drizzle/totp-state.mjs +8 -8
- package/dist/drizzle/totp-state.mjs.map +1 -1
- package/dist/drizzle/totp-target.d.mts +1 -1
- package/dist/drizzle/totp-target.mjs +2 -2
- package/dist/drizzle/totp-target.mjs.map +1 -1
- package/package.json +1 -1
- package/src/SqliteDo.ts +63 -70
- package/src/drizzle/crypto.ts +14 -0
- package/src/drizzle/d1-identity.ts +2 -2
- package/src/drizzle/d1-oauth.ts +14 -5
- package/src/drizzle/d1-passkey.ts +5 -1
- package/src/drizzle/d1-phone.ts +2 -1
- package/src/drizzle/d1-totp.ts +6 -2
- package/src/drizzle/mysql-identity.ts +2 -2
- package/src/drizzle/oauth-accounts.ts +14 -13
- package/src/drizzle/oauth-connected-collection.ts +1 -1
- package/src/drizzle/oauth-connected-custody.ts +8 -6
- package/src/drizzle/oauth-connected-drivers.ts +9 -3
- package/src/drizzle/oauth-connected-flow.ts +6 -6
- package/src/drizzle/oauth-connected-management.ts +1 -1
- package/src/drizzle/oauth-connected-reference.ts +14 -9
- package/src/drizzle/oauth-connected-settlement.ts +4 -4
- package/src/drizzle/oauth-connected-sign-in.ts +11 -10
- package/src/drizzle/oauth-connected-state.ts +40 -25
- package/src/drizzle/oauth-connected-target.ts +9 -5
- package/src/drizzle/oauth-drivers.ts +13 -5
- package/src/drizzle/oauth-flow.ts +1 -1
- package/src/drizzle/oauth-model.ts +4 -4
- package/src/drizzle/oauth-registration.ts +2 -2
- package/src/drizzle/oauth-state.ts +17 -15
- package/src/drizzle/oauth-target.ts +11 -3
- package/src/drizzle/passkey-drivers.ts +7 -3
- package/src/drizzle/passkey-write-drivers.ts +11 -2
- package/src/drizzle/pg-identity.ts +2 -2
- package/src/drizzle/phone-model.ts +3 -3
- package/src/drizzle/phone-target.ts +11 -4
- package/src/drizzle/sqlite-do-database.ts +87 -0
- package/src/drizzle/sqlite-emails.ts +1 -1
- package/src/drizzle/sqlite-identity.ts +2 -2
- package/src/drizzle/sqlite-passwords.ts +1 -1
- package/src/drizzle/sqlite-proofs.ts +2 -2
- package/src/drizzle/sqlite-sessions.ts +2 -2
- package/src/drizzle/storage-validation.ts +15 -6
- package/src/drizzle/totp-state.ts +8 -8
- package/src/drizzle/totp-target.ts +11 -5
package/src/SqliteDo.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { SqliteClient } from "@effect/sql-sqlite-do/SqliteClient";
|
|
2
2
|
import { NativeDatabase, PersistenceConfigurationError } from "@yielded/auth-persistence/Adapter";
|
|
3
3
|
import type { AnyRelations } from "drizzle-orm";
|
|
4
|
-
import {
|
|
4
|
+
import { makeWithDefaults } from "drizzle-orm/effect-sqlite-do";
|
|
5
|
+
|
|
6
|
+
import { type DatabaseValue, makeDatabase } from "./drizzle/sqlite-do-database";
|
|
7
|
+
export { type DatabaseValue, type Transaction, makeDatabase } from "./drizzle/sqlite-do-database";
|
|
5
8
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
6
9
|
import { Context, Effect, Layer } from "effect";
|
|
7
10
|
|
|
8
11
|
import { nativeDatabase } from "./drizzle/native-database";
|
|
9
12
|
|
|
10
|
-
/** The application-owned Drizzle
|
|
11
|
-
export class Database extends Context.Service<Database,
|
|
13
|
+
/** The application-owned Drizzle queries with Effect SQL transaction ownership. */
|
|
14
|
+
export class Database extends Context.Service<Database, DatabaseValue<AnyRelations>>()(
|
|
12
15
|
"effect-auth/persistence-drizzle/SqliteDo/Database",
|
|
13
16
|
) {}
|
|
14
17
|
|
|
15
|
-
/**
|
|
18
|
+
/** Construct Drizzle with asynchronous transactions owned by Effect SQL. */
|
|
16
19
|
export const databaseLayer = Layer.effect(
|
|
17
20
|
Database,
|
|
18
21
|
Effect.gen(function* () {
|
|
@@ -21,19 +24,22 @@ export const databaseLayer = Layer.effect(
|
|
|
21
24
|
|
|
22
25
|
if (storage === undefined)
|
|
23
26
|
return yield* PersistenceConfigurationError.make({
|
|
24
|
-
reason:
|
|
25
|
-
"SqliteDo requires a SQL client configured with storage for synchronous transactions",
|
|
27
|
+
reason: "SqliteDo requires a SQL client configured with Durable Object storage",
|
|
26
28
|
});
|
|
27
29
|
|
|
28
|
-
return yield* makeWithDefaults({ storage });
|
|
30
|
+
return yield* makeDatabase(yield* makeWithDefaults({ storage }));
|
|
29
31
|
}),
|
|
30
32
|
);
|
|
31
33
|
|
|
34
|
+
import { sqlClientEmailStandaloneGuard } from "./drizzle/email-target";
|
|
32
35
|
import type {
|
|
33
36
|
ExternalIdentityTables,
|
|
34
37
|
IdentityTables,
|
|
35
38
|
SubjectProvisioningTables,
|
|
36
39
|
} from "./drizzle/model";
|
|
40
|
+
import { sqlClientPasswordStandaloneGuard } from "./drizzle/password-target";
|
|
41
|
+
import { sqlClientProofStandaloneGuard } from "./drizzle/proof-target";
|
|
42
|
+
import { sqlClientSessionStandaloneGuard } from "./drizzle/session-target";
|
|
37
43
|
import { makeSqliteEmailTarget, sqliteEmailConfiguration } from "./drizzle/sqlite-emails";
|
|
38
44
|
import {
|
|
39
45
|
makeSqliteExternalIdentityServices,
|
|
@@ -44,31 +50,36 @@ import { makeSqlitePasswordTarget, sqlitePasswordConfiguration } from "./drizzle
|
|
|
44
50
|
import { makeSqliteProofTarget, sqliteProofConfiguration } from "./drizzle/sqlite-proofs";
|
|
45
51
|
import { makeSqliteSessionTarget, sqliteSessionConfiguration } from "./drizzle/sqlite-sessions";
|
|
46
52
|
|
|
47
|
-
const sessionTarget = makeSqliteSessionTarget<Database,
|
|
53
|
+
const sessionTarget = makeSqliteSessionTarget<Database, DatabaseValue<AnyRelations>>(
|
|
48
54
|
Database,
|
|
49
|
-
sqliteSessionConfiguration("
|
|
55
|
+
(service) => sqliteSessionConfiguration("interactive", sqlClientSessionStandaloneGuard(service)),
|
|
50
56
|
);
|
|
51
57
|
|
|
52
|
-
const proofTarget = makeSqliteProofTarget<Database,
|
|
58
|
+
const proofTarget = makeSqliteProofTarget<Database, DatabaseValue<AnyRelations>>(
|
|
53
59
|
Database,
|
|
54
|
-
sqliteProofConfiguration("
|
|
60
|
+
(service) => sqliteProofConfiguration("interactive", sqlClientProofStandaloneGuard(service)),
|
|
55
61
|
);
|
|
56
62
|
|
|
57
|
-
const passwordTarget = makeSqlitePasswordTarget<
|
|
63
|
+
const passwordTarget = makeSqlitePasswordTarget<Database, DatabaseValue<AnyRelations>>(
|
|
58
64
|
Database,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
(service) =>
|
|
66
|
+
sqlitePasswordConfiguration(
|
|
67
|
+
"interactive",
|
|
68
|
+
sqlClientPasswordStandaloneGuard(service),
|
|
69
|
+
sqlClientProofStandaloneGuard(service),
|
|
70
|
+
),
|
|
71
|
+
);
|
|
62
72
|
|
|
63
|
-
const emailTarget = makeSqliteEmailTarget<Database,
|
|
73
|
+
const emailTarget = makeSqliteEmailTarget<Database, DatabaseValue<AnyRelations>>(
|
|
64
74
|
Database,
|
|
65
|
-
|
|
75
|
+
(service) =>
|
|
76
|
+
sqliteEmailConfiguration(
|
|
77
|
+
"interactive",
|
|
78
|
+
sqlClientEmailStandaloneGuard(service),
|
|
79
|
+
sqlClientProofStandaloneGuard(service),
|
|
80
|
+
),
|
|
66
81
|
);
|
|
67
82
|
|
|
68
|
-
/**
|
|
69
|
-
* Email mutations must own their outer transactionSync. Arbitrary raw Drizzle
|
|
70
|
-
* nesting is not detectable; owner bodies and mapping allocators stay runSync-compatible.
|
|
71
|
-
*/
|
|
72
83
|
export const {
|
|
73
84
|
coordinateEmailAddress,
|
|
74
85
|
coordinateEmailRegistration,
|
|
@@ -77,11 +88,6 @@ export const {
|
|
|
77
88
|
makeEmailSignInServices,
|
|
78
89
|
} = emailTarget;
|
|
79
90
|
|
|
80
|
-
/**
|
|
81
|
-
* Password mutations and registration must own their outer transactionSync.
|
|
82
|
-
* Arbitrary raw Drizzle nesting is not detectable; owner bodies must remain
|
|
83
|
-
* runSync-compatible. Detectable Effect commit scopes are rejected pre-write.
|
|
84
|
-
*/
|
|
85
91
|
export const {
|
|
86
92
|
coordinatePasswordPersistence,
|
|
87
93
|
coordinatePasswordRegistration,
|
|
@@ -89,21 +95,8 @@ export const {
|
|
|
89
95
|
makePasswordRegistrationServices,
|
|
90
96
|
} = passwordTarget;
|
|
91
97
|
|
|
92
|
-
/**
|
|
93
|
-
* Proof mutations and coordinateProofPersistence must own their outermost
|
|
94
|
-
* transactionSync call. The installed driver cannot detect an arbitrary raw
|
|
95
|
-
* Drizzle outer transaction; calling either boundary from one is unsupported.
|
|
96
|
-
* The owner body must remain runSync-compatible.
|
|
97
|
-
*/
|
|
98
98
|
export const { coordinateProofPersistence, makeProofPersistenceServices } = proofTarget;
|
|
99
99
|
|
|
100
|
-
/**
|
|
101
|
-
* Session mutation methods and coordinate* functions must own their outermost
|
|
102
|
-
* transactionSync call. The installed Drizzle driver exposes no context marker
|
|
103
|
-
* for an arbitrary raw outer database.transaction call, so invoking either
|
|
104
|
-
* boundary from one is unsupported and cannot be detected. Detectable Effect
|
|
105
|
-
* commit scopes are rejected before writes.
|
|
106
|
-
*/
|
|
107
100
|
export const {
|
|
108
101
|
coordinateAuthenticationAuthority,
|
|
109
102
|
coordinatePendingAuthentication,
|
|
@@ -117,7 +110,7 @@ export const {
|
|
|
117
110
|
makeStatefulSessionServices,
|
|
118
111
|
} = sessionTarget;
|
|
119
112
|
|
|
120
|
-
export const commitMode = "
|
|
113
|
+
export const commitMode = "interactive" as const;
|
|
121
114
|
|
|
122
115
|
export const makeIdentityServices = <
|
|
123
116
|
Subject extends AnySQLiteTable,
|
|
@@ -128,7 +121,7 @@ export const makeIdentityServices = <
|
|
|
128
121
|
>(
|
|
129
122
|
mapping: IdentityTables<Subject, Identifier, External, Request, NativeId>,
|
|
130
123
|
) =>
|
|
131
|
-
makeSqliteIdentityServices(mapping, "
|
|
124
|
+
makeSqliteIdentityServices(mapping, "interactive").pipe(
|
|
132
125
|
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(Database)),
|
|
133
126
|
);
|
|
134
127
|
|
|
@@ -140,7 +133,7 @@ export const makeSubjectProvisioningServices = <
|
|
|
140
133
|
>(
|
|
141
134
|
mapping: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>,
|
|
142
135
|
) =>
|
|
143
|
-
makeSqliteSubjectProvisioningServices(mapping, "
|
|
136
|
+
makeSqliteSubjectProvisioningServices(mapping, "interactive").pipe(
|
|
144
137
|
Effect.provideServiceEffect(NativeDatabase, nativeDatabase(Database)),
|
|
145
138
|
);
|
|
146
139
|
|
|
@@ -159,9 +152,14 @@ import { makeSqlitePasswordPreparedTarget } from "./drizzle/sqlite-password-prep
|
|
|
159
152
|
|
|
160
153
|
const passwordPreparedTarget = makeSqlitePasswordPreparedTarget<
|
|
161
154
|
Database,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
155
|
+
DatabaseValue<AnyRelations>
|
|
156
|
+
>(Database, (service) =>
|
|
157
|
+
sqlitePasswordConfiguration(
|
|
158
|
+
"interactive",
|
|
159
|
+
sqlClientPasswordStandaloneGuard(service),
|
|
160
|
+
sqlClientProofStandaloneGuard(service),
|
|
161
|
+
),
|
|
162
|
+
);
|
|
165
163
|
|
|
166
164
|
export const { makePasswordPreparedPersistenceServices, coordinatePasswordPreparedPersistence } =
|
|
167
165
|
passwordPreparedTarget;
|
|
@@ -169,17 +167,17 @@ export const { makePasswordPreparedPersistenceServices, coordinatePasswordPrepar
|
|
|
169
167
|
export { passwordPreparedPersistenceLayer } from "./drizzle/password-prepared-target";
|
|
170
168
|
|
|
171
169
|
import { makeOAuthTarget } from "./drizzle/oauth-drivers";
|
|
170
|
+
import { sqlClientOAuthStandaloneGuard } from "./drizzle/oauth-target";
|
|
172
171
|
|
|
173
172
|
const oauthTarget = makeOAuthTarget<
|
|
174
173
|
Database,
|
|
175
|
-
|
|
176
|
-
AnySQLiteTable<{ dialect: "sqlite" }
|
|
177
|
-
true
|
|
174
|
+
DatabaseValue<AnyRelations>,
|
|
175
|
+
AnySQLiteTable<{ dialect: "sqlite" }>
|
|
178
176
|
>(Database, {
|
|
179
|
-
mode: "
|
|
177
|
+
mode: "interactive",
|
|
180
178
|
dialect: "sqlite",
|
|
181
179
|
locking: false,
|
|
182
|
-
standaloneGuard:
|
|
180
|
+
standaloneGuard: sqlClientOAuthStandaloneGuard,
|
|
183
181
|
});
|
|
184
182
|
|
|
185
183
|
export const {
|
|
@@ -198,18 +196,17 @@ export const {
|
|
|
198
196
|
} = oauthTarget;
|
|
199
197
|
|
|
200
198
|
import { makePasskeyTarget } from "./drizzle/passkey-drivers";
|
|
199
|
+
import { sqlClientPasskeyStandaloneGuard } from "./drizzle/passkey-target";
|
|
201
200
|
|
|
202
201
|
const passkeyTarget = makePasskeyTarget<
|
|
203
202
|
Database,
|
|
204
|
-
|
|
205
|
-
AnySQLiteTable<{ dialect: "sqlite" }
|
|
206
|
-
unknown,
|
|
207
|
-
true
|
|
203
|
+
DatabaseValue<AnyRelations>,
|
|
204
|
+
AnySQLiteTable<{ dialect: "sqlite" }>
|
|
208
205
|
>(Database, {
|
|
209
|
-
mode: "
|
|
206
|
+
mode: "interactive",
|
|
210
207
|
dialect: "sqlite",
|
|
211
208
|
locking: false,
|
|
212
|
-
standaloneGuard:
|
|
209
|
+
standaloneGuard: sqlClientPasskeyStandaloneGuard,
|
|
213
210
|
});
|
|
214
211
|
|
|
215
212
|
export const {
|
|
@@ -225,36 +222,32 @@ export const {
|
|
|
225
222
|
coordinatePasskeyRegistration,
|
|
226
223
|
} = passkeyTarget;
|
|
227
224
|
|
|
228
|
-
import { makeTotpTarget } from "./drizzle/totp-target";
|
|
225
|
+
import { makeTotpTarget, sqlClientTotpStandaloneGuard } from "./drizzle/totp-target";
|
|
229
226
|
|
|
230
227
|
const totpTarget = makeTotpTarget<
|
|
231
228
|
Database,
|
|
232
|
-
|
|
233
|
-
AnySQLiteTable<{ dialect: "sqlite" }
|
|
234
|
-
unknown,
|
|
235
|
-
true
|
|
229
|
+
DatabaseValue<AnyRelations>,
|
|
230
|
+
AnySQLiteTable<{ dialect: "sqlite" }>
|
|
236
231
|
>(Database, {
|
|
237
|
-
mode: "
|
|
232
|
+
mode: "interactive",
|
|
238
233
|
dialect: "sqlite",
|
|
239
234
|
locking: false,
|
|
240
|
-
standaloneGuard:
|
|
235
|
+
standaloneGuard: sqlClientTotpStandaloneGuard,
|
|
241
236
|
});
|
|
242
237
|
|
|
243
238
|
export const { makeTotpPersistenceServices, coordinateTotpPersistence } = totpTarget;
|
|
244
239
|
|
|
245
|
-
import { makePhoneTarget } from "./drizzle/phone-target";
|
|
240
|
+
import { makePhoneTarget, sqlClientPhoneStandaloneGuard } from "./drizzle/phone-target";
|
|
246
241
|
|
|
247
242
|
const phoneTarget = makePhoneTarget<
|
|
248
243
|
Database,
|
|
249
|
-
|
|
250
|
-
AnySQLiteTable<{ dialect: "sqlite" }
|
|
251
|
-
unknown,
|
|
252
|
-
true
|
|
244
|
+
DatabaseValue<AnyRelations>,
|
|
245
|
+
AnySQLiteTable<{ dialect: "sqlite" }>
|
|
253
246
|
>(Database, {
|
|
254
|
-
mode: "
|
|
247
|
+
mode: "interactive",
|
|
255
248
|
dialect: "sqlite",
|
|
256
249
|
locking: false,
|
|
257
|
-
standaloneGuard:
|
|
250
|
+
standaloneGuard: sqlClientPhoneStandaloneGuard,
|
|
258
251
|
});
|
|
259
252
|
|
|
260
253
|
export const { makePhonePersistenceServices, coordinatePhonePersistence } = phoneTarget;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TokenDigest } from "@yielded/auth/Schema";
|
|
2
|
+
import { Crypto, Effect } from "effect";
|
|
3
|
+
import { Base64Url } from "effect/encoding";
|
|
4
|
+
|
|
5
|
+
const encoder = new TextEncoder();
|
|
6
|
+
|
|
7
|
+
export const randomId = Effect.flatMap(Crypto.Crypto, (crypto) =>
|
|
8
|
+
crypto.randomBytes(32).pipe(Effect.map(Base64Url.encode)),
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
export const digest = (value: string) =>
|
|
12
|
+
Effect.flatMap(Crypto.Crypto, (crypto) => crypto.digest("SHA-256", encoder.encode(value))).pipe(
|
|
13
|
+
Effect.map((bytes) => TokenDigest.make(Base64Url.encode(bytes))),
|
|
14
|
+
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { D1Client } from "@effect/sql-d1/D1Client";
|
|
2
|
-
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
2
|
+
import { withStorageValidation, NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
3
3
|
import {
|
|
4
4
|
ExternalIdentityMutation,
|
|
5
5
|
type ExternalIdentity,
|
|
@@ -382,4 +382,4 @@ export const makeD1IdentityServices = Effect.fnUntraced(function* <
|
|
|
382
382
|
...(yield* makeD1SubjectProvisioningServices(mapping)),
|
|
383
383
|
...(yield* makeD1ExternalIdentityServices(mapping)),
|
|
384
384
|
};
|
|
385
|
-
});
|
|
385
|
+
}, withStorageValidation);
|
package/src/drizzle/d1-oauth.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { AnyRelations } from "drizzle-orm";
|
|
11
11
|
import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
|
|
12
12
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
13
|
-
import { Effect, Context } from "effect";
|
|
13
|
+
import { type Crypto, Effect, Context } from "effect";
|
|
14
14
|
|
|
15
15
|
import { Database as DatabaseService } from "./d1-database";
|
|
16
16
|
import { makeD1Owner } from "./d1-planning";
|
|
@@ -129,7 +129,10 @@ export function coordinateD1OAuthSignIn<
|
|
|
129
129
|
): Effect.Effect<
|
|
130
130
|
A,
|
|
131
131
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
132
|
-
Exclude<R, OAuthSignInPersistence | D1BatchStatements>
|
|
132
|
+
| Exclude<R, OAuthSignInPersistence | D1BatchStatements>
|
|
133
|
+
| Crypto.Crypto
|
|
134
|
+
| LifecycleHooks
|
|
135
|
+
| DatabaseRequirements
|
|
133
136
|
> {
|
|
134
137
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
135
138
|
coordinateTargetOAuthSignIn<
|
|
@@ -189,7 +192,10 @@ export function coordinateD1OAuthRegistrationIntents<
|
|
|
189
192
|
): Effect.Effect<
|
|
190
193
|
A,
|
|
191
194
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
192
|
-
Exclude<R, OAuthRegistrationIntents | D1BatchStatements>
|
|
195
|
+
| Exclude<R, OAuthRegistrationIntents | D1BatchStatements>
|
|
196
|
+
| Crypto.Crypto
|
|
197
|
+
| LifecycleHooks
|
|
198
|
+
| DatabaseRequirements
|
|
193
199
|
> {
|
|
194
200
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
195
201
|
coordinateTargetOAuthRegistrationIntents<
|
|
@@ -244,7 +250,10 @@ export function coordinateD1OAuthAccounts<
|
|
|
244
250
|
): Effect.Effect<
|
|
245
251
|
A,
|
|
246
252
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
247
|
-
Exclude<R, OAuthAccountsPersistence | D1BatchStatements>
|
|
253
|
+
| Exclude<R, OAuthAccountsPersistence | D1BatchStatements>
|
|
254
|
+
| Crypto.Crypto
|
|
255
|
+
| LifecycleHooks
|
|
256
|
+
| DatabaseRequirements
|
|
248
257
|
> {
|
|
249
258
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
250
259
|
coordinateTargetOAuthAccounts<
|
|
@@ -312,7 +321,7 @@ export function coordinateD1OAuthRegistration<
|
|
|
312
321
|
): Effect.Effect<
|
|
313
322
|
A,
|
|
314
323
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
315
|
-
Exclude<R, TargetId | D1BatchStatements> | LifecycleHooks | DatabaseRequirements
|
|
324
|
+
Exclude<R, TargetId | D1BatchStatements> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
|
|
316
325
|
> {
|
|
317
326
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
318
327
|
coordinateTargetOAuthRegistration<
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import type { AnyRelations } from "drizzle-orm";
|
|
10
10
|
import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
|
|
11
11
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
12
|
-
import { Effect, Context } from "effect";
|
|
12
|
+
import { type Crypto, Effect, Context } from "effect";
|
|
13
13
|
|
|
14
14
|
import { Database as DatabaseService } from "./d1-database";
|
|
15
15
|
import { makeD1Owner } from "./d1-planning";
|
|
@@ -94,6 +94,7 @@ export function coordinatePasskeyPersistence<
|
|
|
94
94
|
A,
|
|
95
95
|
PasskeyCoordinatorError<E> | DatabaseError,
|
|
96
96
|
| Exclude<R, PasskeyPersistence | D1BatchStatements>
|
|
97
|
+
| Crypto.Crypto
|
|
97
98
|
| LifecycleHooks
|
|
98
99
|
| DatabaseRequirements
|
|
99
100
|
| RSetup
|
|
@@ -156,6 +157,7 @@ export function coordinatePasskeyRegistrationCeremony<
|
|
|
156
157
|
A,
|
|
157
158
|
PasskeyCoordinatorError<E> | DatabaseError,
|
|
158
159
|
| Exclude<R, PasskeyPersistence | D1BatchStatements>
|
|
160
|
+
| Crypto.Crypto
|
|
159
161
|
| LifecycleHooks
|
|
160
162
|
| DatabaseRequirements
|
|
161
163
|
| RSetup
|
|
@@ -239,6 +241,7 @@ export function coordinatePasskeyManagement<
|
|
|
239
241
|
A,
|
|
240
242
|
PasskeyCoordinatorError<E> | DatabaseError,
|
|
241
243
|
| Exclude<R, PasskeyPersistence | PasskeyManagementPersistence | D1BatchStatements>
|
|
244
|
+
| Crypto.Crypto
|
|
242
245
|
| LifecycleHooks
|
|
243
246
|
| DatabaseRequirements
|
|
244
247
|
| RSetup
|
|
@@ -328,6 +331,7 @@ export function coordinatePasskeyRegistration<
|
|
|
328
331
|
A,
|
|
329
332
|
PasskeyCoordinatorError<E> | DatabaseError,
|
|
330
333
|
| Exclude<R, PasskeyPersistence | AuthorityId | D1BatchStatements>
|
|
334
|
+
| Crypto.Crypto
|
|
331
335
|
| LifecycleHooks
|
|
332
336
|
| DatabaseRequirements
|
|
333
337
|
| RSetup
|
package/src/drizzle/d1-phone.ts
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import type { AnyRelations } from "drizzle-orm";
|
|
11
11
|
import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
|
|
12
12
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
13
|
-
import { Context, Effect } from "effect";
|
|
13
|
+
import { type Crypto, Context, Effect } from "effect";
|
|
14
14
|
|
|
15
15
|
import { Database as DatabaseService } from "./d1-database";
|
|
16
16
|
import { makeD1Owner } from "./d1-planning";
|
|
@@ -59,6 +59,7 @@ export const coordinatePhonePersistence = <
|
|
|
59
59
|
A,
|
|
60
60
|
PhoneCoordinatorError<E> | DatabaseError,
|
|
61
61
|
| Exclude<R, PhonePersistence | PhoneAdmission | PhoneSignInTargets | D1BatchStatements>
|
|
62
|
+
| Crypto.Crypto
|
|
62
63
|
| LifecycleHooks
|
|
63
64
|
| RSetup
|
|
64
65
|
| DatabaseRequirements
|
package/src/drizzle/d1-totp.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { TotpUnavailable, TotpPersistence } from "@yielded/auth/Totp";
|
|
|
5
5
|
import type { AnyRelations } from "drizzle-orm";
|
|
6
6
|
import type { EffectSQLiteD1Database } from "drizzle-orm/effect-d1";
|
|
7
7
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
8
|
-
import { Context, Effect } from "effect";
|
|
8
|
+
import { type Crypto, Context, Effect } from "effect";
|
|
9
9
|
|
|
10
10
|
import { Database as DatabaseService } from "./d1-database";
|
|
11
11
|
import { makeD1Owner } from "./d1-planning";
|
|
@@ -50,7 +50,11 @@ export const coordinateTotpPersistence = <
|
|
|
50
50
|
): Effect.Effect<
|
|
51
51
|
A,
|
|
52
52
|
TotpCoordinatorError<E> | DatabaseError,
|
|
53
|
-
Exclude<R, TotpPersistence | D1BatchStatements>
|
|
53
|
+
| Exclude<R, TotpPersistence | D1BatchStatements>
|
|
54
|
+
| Crypto.Crypto
|
|
55
|
+
| LifecycleHooks
|
|
56
|
+
| RSetup
|
|
57
|
+
| DatabaseRequirements
|
|
54
58
|
> =>
|
|
55
59
|
Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
56
60
|
coordinateTargetTotp(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
1
|
+
import { withStorageValidation, NativeDatabase } from "@yielded/auth-persistence/Adapter";
|
|
2
2
|
import {
|
|
3
3
|
ExternalIdentityMutation,
|
|
4
4
|
type ExternalIdentity,
|
|
@@ -331,4 +331,4 @@ export const makeMysqlIdentityServices = Effect.fnUntraced(function* <
|
|
|
331
331
|
...(yield* makeMysqlSubjectProvisioningServices(mapping)),
|
|
332
332
|
...(yield* makeMysqlExternalIdentityServices(mapping)),
|
|
333
333
|
};
|
|
334
|
-
});
|
|
334
|
+
}, withStorageValidation);
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
readFlow,
|
|
31
31
|
terminalFlow,
|
|
32
32
|
} from "./oauth-flow";
|
|
33
|
-
import type { OAuthEligibilityFact } from "./oauth-model";
|
|
33
|
+
import type { OAuthReferenceGuardDescriptor, OAuthEligibilityFact } from "./oauth-model";
|
|
34
34
|
import { both, col, copiedRow, equal, CurrentOAuthTransaction } from "./oauth-owner";
|
|
35
35
|
import { acquireTuple, readTuple } from "./oauth-registration";
|
|
36
36
|
import {
|
|
@@ -71,7 +71,7 @@ const authorization = (
|
|
|
71
71
|
mapping.subject.decodeActionRequirement(copiedRow(current.row), action),
|
|
72
72
|
);
|
|
73
73
|
|
|
74
|
-
const accepted = validAction(
|
|
74
|
+
const accepted = yield* validAction(
|
|
75
75
|
auth,
|
|
76
76
|
{
|
|
77
77
|
moduleId: auth.challenge.moduleId,
|
|
@@ -493,7 +493,7 @@ const targetCredential = (mapping: any, moduleId: string, credentialId: string,
|
|
|
493
493
|
subject: ownership[t.externalSubject],
|
|
494
494
|
};
|
|
495
495
|
|
|
496
|
-
invariant(oauthIdentityKey(identity) === row[cr.identityKey]);
|
|
496
|
+
invariant((yield* oauthIdentityKey(identity)) === row[cr.identityKey]);
|
|
497
497
|
|
|
498
498
|
const shared = current.revision.credentials.find(
|
|
499
499
|
(item: any) => item.credentialId === credentialId,
|
|
@@ -648,23 +648,24 @@ export const unlink = (mapping: any, input: any) =>
|
|
|
648
648
|
const current = yield* accountCurrent(mapping, expected.revision.subjectId);
|
|
649
649
|
|
|
650
650
|
if (current === undefined) return { _tag: "Rejected" } as const;
|
|
651
|
-
|
|
651
|
+
|
|
652
|
+
const guards: ReadonlyArray<OAuthReferenceGuardDescriptor<unknown>> =
|
|
653
|
+
mapping.connectedReferenceGuards ?? [];
|
|
652
654
|
|
|
653
655
|
invariant(guards.length <= 32);
|
|
654
656
|
let referencesGuarded = mapping.connectedReference !== undefined && guards.length > 0;
|
|
655
657
|
|
|
656
658
|
if (mapping.connectedReference !== undefined)
|
|
657
659
|
for (const guard of guards) {
|
|
660
|
+
const condition = guard.condition({
|
|
661
|
+
identity: snapshotOAuthSync(OAuthCredentialSnapshot.fields.identity, expected.identity),
|
|
662
|
+
identityKey: yield* oauthIdentityKey(expected.identity),
|
|
663
|
+
subjectId: copiedRow({ value: current.nativeId }).value,
|
|
664
|
+
});
|
|
665
|
+
|
|
658
666
|
const read = yield* owner.read(
|
|
659
667
|
guard.table,
|
|
660
|
-
|
|
661
|
-
identity: snapshotOAuthSync(
|
|
662
|
-
OAuthCredentialSnapshot.fields.identity,
|
|
663
|
-
expected.identity,
|
|
664
|
-
),
|
|
665
|
-
identityKey: oauthIdentityKey(expected.identity),
|
|
666
|
-
subjectId: copiedRow({ value: current.nativeId }).value,
|
|
667
|
-
}),
|
|
668
|
+
Effect.isEffect(condition) ? yield* condition : condition,
|
|
668
669
|
{ orderBy: col(guard.table, guard.orderBy) },
|
|
669
670
|
);
|
|
670
671
|
|
|
@@ -730,7 +731,7 @@ export const unlink = (mapping: any, input: any) =>
|
|
|
730
731
|
cr = mapping.credential,
|
|
731
732
|
a = mapping.authority;
|
|
732
733
|
|
|
733
|
-
const key = oauthIdentityKey(expected.identity),
|
|
734
|
+
const key = yield* oauthIdentityKey(expected.identity),
|
|
734
735
|
t = mapping.ownership.tuple;
|
|
735
736
|
|
|
736
737
|
const connected = mapping.connectedReference?.({
|
|
@@ -285,7 +285,7 @@ export const collect = Effect.fn("oauthConnected.collect")(function* (
|
|
|
285
285
|
subject: candidate[t.externalSubject],
|
|
286
286
|
};
|
|
287
287
|
|
|
288
|
-
invariant(oauthIdentityKey(identity) === candidate[t.identityKey]);
|
|
288
|
+
invariant((yield* oauthIdentityKey(identity)) === candidate[t.identityKey]);
|
|
289
289
|
|
|
290
290
|
const tuple = yield* F.inspectTuple(mapping, identity),
|
|
291
291
|
row = tuple.row;
|
|
@@ -113,9 +113,9 @@ export const storeJob = Effect.fn("oauthConnected.storeJob")(function* (
|
|
|
113
113
|
);
|
|
114
114
|
|
|
115
115
|
const j = mapping.revocation.job,
|
|
116
|
-
identity = oauthIdentityKey(token.identity),
|
|
117
|
-
client = S.clientKey(token.configuration),
|
|
118
|
-
cohort = S.cohortKey(client, identity);
|
|
116
|
+
identity = yield* oauthIdentityKey(token.identity),
|
|
117
|
+
client = yield* S.clientKey(token.configuration),
|
|
118
|
+
cohort = yield* S.cohortKey(client, identity);
|
|
119
119
|
|
|
120
120
|
const h = mapping.cohort;
|
|
121
121
|
|
|
@@ -254,14 +254,16 @@ export const noFormerOwner = (
|
|
|
254
254
|
client: string,
|
|
255
255
|
native: unknown,
|
|
256
256
|
) =>
|
|
257
|
-
Effect.
|
|
257
|
+
Effect.gen(function* () {
|
|
258
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
259
|
+
|
|
258
260
|
const g = mapping.grant,
|
|
259
261
|
f = mapping.flow,
|
|
260
262
|
j = S.jobTable(mapping);
|
|
261
263
|
|
|
262
264
|
const condition = both(
|
|
263
265
|
sql`not exists(select 1 from ${g.table} where ${both(eq(col(g.table, g.identityKey), identity), sql`not (${owner.exact(g.table, { [g.subjectId]: native })})`)})`,
|
|
264
|
-
sql`not exists(select 1 from ${f.table} where ${both(eq(col(f.table, f.cohortKey), S.cohortKey(client, identity)), eq(col(f.table, f.work), "Unresolved"), sql`not (${owner.exact(f.table, { [f.subjectId]: native })})`)})`,
|
|
266
|
+
sql`not exists(select 1 from ${f.table} where ${both(eq(col(f.table, f.cohortKey), yield* S.cohortKey(client, identity)), eq(col(f.table, f.work), "Unresolved"), sql`not (${owner.exact(f.table, { [f.subjectId]: native })})`)})`,
|
|
265
267
|
...(j === undefined
|
|
266
268
|
? []
|
|
267
269
|
: [
|
|
@@ -269,7 +271,7 @@ export const noFormerOwner = (
|
|
|
269
271
|
]),
|
|
270
272
|
);
|
|
271
273
|
|
|
272
|
-
return Effect.map(owner.check(condition), (accepted) => {
|
|
274
|
+
return yield* Effect.map(owner.check(condition), (accepted) => {
|
|
273
275
|
if (accepted) owner.postconditions.push(condition);
|
|
274
276
|
|
|
275
277
|
return accepted;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
OAuthUnavailable,
|
|
7
7
|
} from "@yielded/auth/OAuth";
|
|
8
8
|
import type { Table } from "drizzle-orm";
|
|
9
|
-
import { Effect, Context } from "effect";
|
|
9
|
+
import { type Crypto, Effect, Context } from "effect";
|
|
10
10
|
|
|
11
11
|
import { makeD1Owner } from "./d1-planning";
|
|
12
12
|
import { D1BatchStatements } from "./D1BatchStatements";
|
|
@@ -69,6 +69,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
69
69
|
A,
|
|
70
70
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
71
71
|
| (Synchronous extends true ? never : Exclude<R, OAuthConnectedPersistence>)
|
|
72
|
+
| Crypto.Crypto
|
|
72
73
|
| LifecycleHooks
|
|
73
74
|
| DatabaseRequirements
|
|
74
75
|
>;
|
|
@@ -108,6 +109,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
108
109
|
A,
|
|
109
110
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
110
111
|
| (Synchronous extends true ? never : Exclude<R, OAuthConnectedPersistence | TxId>)
|
|
112
|
+
| Crypto.Crypto
|
|
111
113
|
| LifecycleHooks
|
|
112
114
|
| DatabaseRequirements
|
|
113
115
|
>;
|
|
@@ -142,7 +144,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
142
144
|
): Effect.Effect<
|
|
143
145
|
A,
|
|
144
146
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
145
|
-
Exclude<R, OAuthConnectedPersistence> | LifecycleHooks | DatabaseRequirements
|
|
147
|
+
Exclude<R, OAuthConnectedPersistence> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
|
|
146
148
|
> {
|
|
147
149
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
148
150
|
coordinateTargetOAuthConnected<TransactionOf<D>, A, E, Exclude<R, OAuthConnectedPersistence>>(
|
|
@@ -193,6 +195,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
193
195
|
A,
|
|
194
196
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
195
197
|
| (Synchronous extends true ? never : Exclude<R, OAuthConnectedRevocations>)
|
|
198
|
+
| Crypto.Crypto
|
|
196
199
|
| LifecycleHooks
|
|
197
200
|
| DatabaseRequirements
|
|
198
201
|
>;
|
|
@@ -228,6 +231,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
228
231
|
A,
|
|
229
232
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
230
233
|
| (Synchronous extends true ? never : Exclude<R, OAuthConnectedRevocations | TxId>)
|
|
234
|
+
| Crypto.Crypto
|
|
231
235
|
| LifecycleHooks
|
|
232
236
|
| DatabaseRequirements
|
|
233
237
|
>;
|
|
@@ -258,7 +262,7 @@ export const makeOAuthConnectedTarget = <
|
|
|
258
262
|
): Effect.Effect<
|
|
259
263
|
A,
|
|
260
264
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
261
|
-
Exclude<R, OAuthConnectedRevocations> | LifecycleHooks | DatabaseRequirements
|
|
265
|
+
Exclude<R, OAuthConnectedRevocations> | Crypto.Crypto | LifecycleHooks | DatabaseRequirements
|
|
262
266
|
> {
|
|
263
267
|
return Effect.flatMap(nativeDatabase(acquire), (database) =>
|
|
264
268
|
coordinateTargetOAuthConnectedRevocations<
|
|
@@ -361,6 +365,7 @@ export const makeD1OAuthConnectedTarget = <DatabaseId, Database, Family extends
|
|
|
361
365
|
A,
|
|
362
366
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
363
367
|
| Exclude<R, OAuthConnectedPersistence | D1BatchStatements>
|
|
368
|
+
| Crypto.Crypto
|
|
364
369
|
| LifecycleHooks
|
|
365
370
|
| DatabaseRequirements
|
|
366
371
|
> {
|
|
@@ -435,6 +440,7 @@ export const makeD1OAuthConnectedTarget = <DatabaseId, Database, Family extends
|
|
|
435
440
|
A,
|
|
436
441
|
OAuthCoordinatorError<E> | DatabaseError,
|
|
437
442
|
| Exclude<R, OAuthConnectedRevocations | D1BatchStatements>
|
|
443
|
+
| Crypto.Crypto
|
|
438
444
|
| LifecycleHooks
|
|
439
445
|
| DatabaseRequirements
|
|
440
446
|
> {
|