@yielded/auth-persistence 0.1.0-beta.23 → 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.
- package/README.md +11 -0
- package/dist/Adapter.d.mts +12 -6
- package/dist/Adapter.mjs +8 -3
- package/dist/OAuthPersistence.d.mts +87 -0
- package/dist/OAuthPersistence.mjs +125 -0
- package/dist/OAuthPersistence.mjs.map +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/dist/internal/configuration.d.mts +1 -1
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/models/oauth-connected-model.d.mts +294 -0
- package/dist/internal/models/oauth-connected-model.mjs +22 -0
- package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
- package/dist/internal/models/oauth-model.d.mts +467 -0
- package/dist/internal/models/oauth-model.mjs +49 -0
- package/dist/internal/models/oauth-model.mjs.map +1 -0
- package/dist/internal/models/password-model.d.mts +4 -47
- package/dist/internal/models/password-model.mjs +0 -1
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +4 -0
- package/dist/internal/native-sql-table.mjs +7 -0
- package/dist/internal/native-sql-table.mjs.map +1 -1
- package/dist/internal/oauth/accounts.d.mts +8 -0
- package/dist/internal/oauth/accounts.mjs +379 -0
- package/dist/internal/oauth/accounts.mjs.map +1 -0
- package/dist/internal/oauth/connected-access.d.mts +9 -0
- package/dist/internal/oauth/connected-access.mjs +229 -0
- package/dist/internal/oauth/connected-access.mjs.map +1 -0
- package/dist/internal/oauth/connected-collection.d.mts +7 -0
- package/dist/internal/oauth/connected-collection.mjs +214 -0
- package/dist/internal/oauth/connected-collection.mjs.map +1 -0
- package/dist/internal/oauth/connected-custody.d.mts +6 -0
- package/dist/internal/oauth/connected-custody.mjs +165 -0
- package/dist/internal/oauth/connected-custody.mjs.map +1 -0
- package/dist/internal/oauth/connected-flow.d.mts +7 -0
- package/dist/internal/oauth/connected-flow.mjs +324 -0
- package/dist/internal/oauth/connected-flow.mjs.map +1 -0
- package/dist/internal/oauth/connected-input.mjs +122 -0
- package/dist/internal/oauth/connected-input.mjs.map +1 -0
- package/dist/internal/oauth/connected-keys.d.mts +2 -0
- package/dist/internal/oauth/connected-keys.mjs +22 -0
- package/dist/internal/oauth/connected-keys.mjs.map +1 -0
- package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
- package/dist/internal/oauth/connected-maintenance.mjs +226 -0
- package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
- package/dist/internal/oauth/connected-management.d.mts +9 -0
- package/dist/internal/oauth/connected-management.mjs +243 -0
- package/dist/internal/oauth/connected-management.mjs.map +1 -0
- package/dist/internal/oauth/connected-reference.mjs +63 -0
- package/dist/internal/oauth/connected-reference.mjs.map +1 -0
- package/dist/internal/oauth/connected-settlement.d.mts +8 -0
- package/dist/internal/oauth/connected-settlement.mjs +141 -0
- package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
- package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
- package/dist/internal/oauth/connected-sign-in.mjs +281 -0
- package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
- package/dist/internal/oauth/connected-state.d.mts +14 -0
- package/dist/internal/oauth/connected-state.mjs +469 -0
- package/dist/internal/oauth/connected-state.mjs.map +1 -0
- package/dist/internal/oauth/connected-target.d.mts +21 -0
- package/dist/internal/oauth/connected-target.mjs +117 -0
- package/dist/internal/oauth/connected-target.mjs.map +1 -0
- package/dist/internal/oauth/flow.d.mts +5 -0
- package/dist/internal/oauth/flow.mjs +279 -0
- package/dist/internal/oauth/flow.mjs.map +1 -0
- package/dist/internal/oauth/input.mjs +127 -0
- package/dist/internal/oauth/input.mjs.map +1 -0
- package/dist/internal/oauth/owner.d.mts +10 -0
- package/dist/internal/oauth/owner.mjs +10 -0
- package/dist/internal/oauth/owner.mjs.map +1 -0
- package/dist/internal/oauth/query-compiler.d.mts +7 -0
- package/dist/internal/oauth/query-compiler.mjs +9 -0
- package/dist/internal/oauth/query-compiler.mjs.map +1 -0
- package/dist/internal/oauth/registration.d.mts +7 -0
- package/dist/internal/oauth/registration.mjs +393 -0
- package/dist/internal/oauth/registration.mjs.map +1 -0
- package/dist/internal/oauth/state.mjs +107 -0
- package/dist/internal/oauth/state.mjs.map +1 -0
- package/dist/internal/oauth/target.d.mts +31 -0
- package/dist/internal/oauth/target.mjs +273 -0
- package/dist/internal/oauth/target.mjs.map +1 -0
- package/dist/internal/oauth-kernel.d.mts +1423 -0
- package/dist/internal/oauth-kernel.mjs +187 -0
- package/dist/internal/oauth-kernel.mjs.map +1 -0
- package/dist/internal/oauth-proxy.d.mts +31 -0
- package/dist/internal/oauth-proxy.mjs +129 -0
- package/dist/internal/oauth-proxy.mjs.map +1 -0
- package/dist/internal/passkey-kernel.d.mts +5 -5
- package/dist/internal/{password-attempts.d.mts → password-credentials.d.mts} +3 -4
- package/dist/internal/password-credentials.mjs +121 -0
- package/dist/internal/password-credentials.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +8 -24
- package/dist/internal/password-kernel.mjs +4 -42
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +1 -1
- package/dist/internal/persistence.mjs +2 -2
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +2 -0
- package/dist/internal/session-kernel.d.mts +2 -2
- package/dist/internal/session-kernel.mjs +74 -18
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
- package/dist/internal/sql-oauth-connected-model.mjs +7 -0
- package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
- package/dist/internal/sql-oauth-model.d.mts +60 -0
- package/dist/internal/sql-oauth-model.mjs +10 -0
- package/dist/internal/sql-oauth-model.mjs.map +1 -0
- package/dist/internal/sql-query.d.mts +12 -0
- package/dist/internal/sql-query.mjs +15 -18
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/sql-storage-validation.mjs +54 -0
- package/dist/internal/sql-storage-validation.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +1 -37
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-plans.d.mts +7 -0
- package/dist/internal/storage-plans.mjs +189 -0
- package/dist/internal/storage-plans.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +0 -56
- package/dist/internal/storage-tables.mjs +0 -17
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +6 -1
- package/dist/internal/storage-validation.mjs +42 -1
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-kernel.mjs +3 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +89 -5
- package/src/OAuthPersistence.ts +427 -0
- package/src/index.ts +3 -0
- package/src/internal/configuration.ts +1 -3
- package/src/internal/models/oauth-connected-model.ts +396 -0
- package/src/internal/models/oauth-model.ts +708 -0
- package/src/internal/models/password-model.ts +0 -56
- package/src/internal/native-sql-table.ts +14 -0
- package/src/internal/oauth/accounts.ts +838 -0
- package/src/internal/oauth/connected-access.ts +388 -0
- package/src/internal/oauth/connected-collection.ts +345 -0
- package/src/internal/oauth/connected-custody.ts +308 -0
- package/src/internal/oauth/connected-flow.ts +562 -0
- package/src/internal/oauth/connected-input.ts +147 -0
- package/src/internal/oauth/connected-keys.ts +31 -0
- package/src/internal/oauth/connected-maintenance.ts +379 -0
- package/src/internal/oauth/connected-management.ts +391 -0
- package/src/internal/oauth/connected-reference.ts +135 -0
- package/src/internal/oauth/connected-settlement.ts +278 -0
- package/src/internal/oauth/connected-sign-in.ts +472 -0
- package/src/internal/oauth/connected-state.ts +924 -0
- package/src/internal/oauth/connected-target.ts +307 -0
- package/src/internal/oauth/flow.ts +505 -0
- package/src/internal/oauth/input.ts +201 -0
- package/src/internal/oauth/owner.ts +22 -0
- package/src/internal/oauth/query-compiler.ts +15 -0
- package/src/internal/oauth/registration.ts +676 -0
- package/src/internal/oauth/state.ts +218 -0
- package/src/internal/oauth/target.ts +803 -0
- package/src/internal/oauth-kernel.ts +294 -0
- package/src/internal/oauth-proxy.ts +176 -0
- package/src/internal/password-credentials.ts +245 -0
- package/src/internal/password-kernel.ts +5 -98
- package/src/internal/persistence.ts +1 -1
- package/src/internal/query-operations.ts +2 -0
- package/src/internal/session-kernel.ts +143 -55
- package/src/internal/sql-oauth-connected-model.ts +145 -0
- package/src/internal/sql-oauth-model.ts +263 -0
- package/src/internal/sql-query.ts +16 -13
- package/src/internal/sql-storage-validation.ts +64 -0
- package/src/internal/storage-mapping.ts +0 -37
- package/src/internal/storage-plans.ts +218 -0
- package/src/internal/storage-tables.ts +0 -20
- package/src/internal/storage-validation.ts +58 -0
- package/src/internal/transaction-kernel.ts +6 -2
- package/dist/internal/password-attempts.mjs +0 -197
- package/dist/internal/password-attempts.mjs.map +0 -1
- package/src/internal/password-attempts.ts +0 -466
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import * as Shared from "./models/oauth-model";
|
|
2
|
+
import type { TableModel } from "./query-operations";
|
|
3
|
+
import type { Fragment as SQL, Table } from "./sql-table";
|
|
4
|
+
|
|
5
|
+
export interface SqlTableModel<T extends Table = Table> extends TableModel {
|
|
6
|
+
readonly table: T;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
type OAuthFlowState,
|
|
11
|
+
type OAuthTupleState,
|
|
12
|
+
type OAuthRegistrationState,
|
|
13
|
+
type OAuthAction,
|
|
14
|
+
type OAuthEligibilityFact,
|
|
15
|
+
type OAuthD1Mapping,
|
|
16
|
+
requiredOAuthSignInConstraints,
|
|
17
|
+
requiredOAuthTupleConstraints,
|
|
18
|
+
requiredOAuthRegistrationConstraints,
|
|
19
|
+
requiredOAuthAccountsConstraints,
|
|
20
|
+
type OAuthRegistrationAuthority,
|
|
21
|
+
} from "./models/oauth-model";
|
|
22
|
+
|
|
23
|
+
/** The engine writes every critical column explicitly after applying the pure
|
|
24
|
+
* consumer encoder. Native instant values must roundtrip without precision loss. */
|
|
25
|
+
export type OAuthClock = Shared.OAuthClock<SQL>;
|
|
26
|
+
|
|
27
|
+
export type OAuthSubjectReadTable<S extends Table> = Shared.OAuthSubjectReadTable<
|
|
28
|
+
SqlTableModel<S>,
|
|
29
|
+
SQL
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
export type OAuthSubjectTable<S extends Table> = Shared.OAuthSubjectTable<SqlTableModel<S>, SQL>;
|
|
33
|
+
|
|
34
|
+
export type OAuthOwnershipReadTable<O extends Table, N> = Shared.OAuthOwnershipReadTable<
|
|
35
|
+
SqlTableModel<O>,
|
|
36
|
+
N,
|
|
37
|
+
SQL
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
export type OAuthOwnershipTable<O extends Table, N> = Shared.OAuthOwnershipTable<
|
|
41
|
+
SqlTableModel<O>,
|
|
42
|
+
N,
|
|
43
|
+
SQL
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export type OAuthCredentialReadTable<C extends Table> = Shared.OAuthCredentialReadTable<
|
|
47
|
+
SqlTableModel<C>,
|
|
48
|
+
SQL
|
|
49
|
+
>;
|
|
50
|
+
|
|
51
|
+
export type OAuthCredentialTable<C extends Table, N> = Shared.OAuthCredentialTable<
|
|
52
|
+
SqlTableModel<C>,
|
|
53
|
+
N,
|
|
54
|
+
SQL
|
|
55
|
+
>;
|
|
56
|
+
|
|
57
|
+
export type OAuthAuthorityReadTable<C extends Table> = Shared.OAuthAuthorityReadTable<
|
|
58
|
+
SqlTableModel<C>,
|
|
59
|
+
SQL
|
|
60
|
+
>;
|
|
61
|
+
|
|
62
|
+
export type OAuthAuthorityTable<C extends Table, N> = Shared.OAuthAuthorityTable<
|
|
63
|
+
SqlTableModel<C>,
|
|
64
|
+
N,
|
|
65
|
+
SQL
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
/** Both purposes may use the same physical table. Shared tables share module/flow
|
|
69
|
+
* and module/command uniqueness. The snapshot has the exact purpose-specific core
|
|
70
|
+
* codec; terminal writes erase it, while identity, claim and retention columns remain. */
|
|
71
|
+
export type OAuthFlowTable<F extends Table> = Shared.OAuthFlowTable<SqlTableModel<F>>;
|
|
72
|
+
|
|
73
|
+
export type OAuthTupleAuthorityTable<T extends Table, N> = Shared.OAuthTupleAuthorityTable<
|
|
74
|
+
SqlTableModel<T>,
|
|
75
|
+
N
|
|
76
|
+
>;
|
|
77
|
+
|
|
78
|
+
export type OAuthRegistrationIntentTable<I extends Table> = Shared.OAuthRegistrationIntentTable<
|
|
79
|
+
SqlTableModel<I>
|
|
80
|
+
>;
|
|
81
|
+
|
|
82
|
+
export type OAuthRegistrationCommandTable<
|
|
83
|
+
R extends Table,
|
|
84
|
+
Registration,
|
|
85
|
+
> = Shared.OAuthRegistrationCommandTable<SqlTableModel<R>, Registration>;
|
|
86
|
+
|
|
87
|
+
export type OAuthUnlinkCommandTable<U extends Table> = Shared.OAuthUnlinkCommandTable<
|
|
88
|
+
SqlTableModel<U>
|
|
89
|
+
>;
|
|
90
|
+
|
|
91
|
+
/** Each descriptor reads only its own subject-scoped method rows. The native row
|
|
92
|
+
* projector and SQL condition must express the same installed method eligibility.
|
|
93
|
+
* All source/identifier/policy mutations advance subject securityRevision. D1 also
|
|
94
|
+
* guards exact selected IDs/versions and absence/count assumptions, never just a
|
|
95
|
+
* stale boolean. An over-limit graph fails closed rather than truncating. */
|
|
96
|
+
export type OAuthEligibilityTable<T extends Table, N> = Shared.OAuthEligibilityTable<
|
|
97
|
+
SqlTableModel<T>,
|
|
98
|
+
N,
|
|
99
|
+
SQL
|
|
100
|
+
>;
|
|
101
|
+
|
|
102
|
+
/** Authenticated command replay uses current subject status and this additional
|
|
103
|
+
* nonconsuming SQL policy. It never consumes a fresh action factor. */
|
|
104
|
+
export type OAuthMetadataPolicy<N> = Shared.OAuthMetadataPolicy<N, SQL>;
|
|
105
|
+
|
|
106
|
+
export type OAuthCleanupTable<T extends Table, N> = Shared.OAuthCleanupTable<
|
|
107
|
+
SqlTableModel<T>,
|
|
108
|
+
N,
|
|
109
|
+
SQL
|
|
110
|
+
>;
|
|
111
|
+
|
|
112
|
+
export type OAuthRegistrationEligibility<N> = Shared.OAuthRegistrationEligibility<N, SQL>;
|
|
113
|
+
|
|
114
|
+
export type OAuthSignInMapping<
|
|
115
|
+
S extends Table,
|
|
116
|
+
O extends Table,
|
|
117
|
+
C extends Table,
|
|
118
|
+
AC extends Table,
|
|
119
|
+
F extends Table,
|
|
120
|
+
N,
|
|
121
|
+
> = Shared.OAuthSignInMapping<
|
|
122
|
+
SqlTableModel<S>,
|
|
123
|
+
SqlTableModel<O>,
|
|
124
|
+
SqlTableModel<C>,
|
|
125
|
+
SqlTableModel<AC>,
|
|
126
|
+
SqlTableModel<F>,
|
|
127
|
+
N,
|
|
128
|
+
SQL
|
|
129
|
+
>;
|
|
130
|
+
|
|
131
|
+
export type OAuthOwnershipMutation<
|
|
132
|
+
T extends Table,
|
|
133
|
+
O extends Table,
|
|
134
|
+
N,
|
|
135
|
+
> = Shared.OAuthOwnershipMutation<SqlTableModel<T>, SqlTableModel<O>, N, SQL>;
|
|
136
|
+
|
|
137
|
+
export type OAuthRegistrationIntentMapping<
|
|
138
|
+
S extends Table,
|
|
139
|
+
O extends Table,
|
|
140
|
+
C extends Table,
|
|
141
|
+
AC extends Table,
|
|
142
|
+
F extends Table,
|
|
143
|
+
T extends Table,
|
|
144
|
+
I extends Table,
|
|
145
|
+
N,
|
|
146
|
+
> = Shared.OAuthRegistrationIntentMapping<
|
|
147
|
+
SqlTableModel<S>,
|
|
148
|
+
SqlTableModel<O>,
|
|
149
|
+
SqlTableModel<C>,
|
|
150
|
+
SqlTableModel<AC>,
|
|
151
|
+
SqlTableModel<F>,
|
|
152
|
+
SqlTableModel<T>,
|
|
153
|
+
SqlTableModel<I>,
|
|
154
|
+
N,
|
|
155
|
+
SQL
|
|
156
|
+
>;
|
|
157
|
+
|
|
158
|
+
export type OAuthRegistrationBase<
|
|
159
|
+
Registration,
|
|
160
|
+
T extends Table,
|
|
161
|
+
O extends Table,
|
|
162
|
+
I extends Table,
|
|
163
|
+
R extends Table,
|
|
164
|
+
N,
|
|
165
|
+
> = Shared.OAuthRegistrationBase<
|
|
166
|
+
Registration,
|
|
167
|
+
SqlTableModel<T>,
|
|
168
|
+
SqlTableModel<O>,
|
|
169
|
+
SqlTableModel<I>,
|
|
170
|
+
SqlTableModel<R>,
|
|
171
|
+
N,
|
|
172
|
+
SQL,
|
|
173
|
+
Table
|
|
174
|
+
>;
|
|
175
|
+
|
|
176
|
+
export type OAuthRegistrationGuardTable<
|
|
177
|
+
T extends Table,
|
|
178
|
+
Registration,
|
|
179
|
+
N,
|
|
180
|
+
> = Shared.OAuthRegistrationGuardTable<SqlTableModel<T>, Registration, N, SQL>;
|
|
181
|
+
|
|
182
|
+
export type OAuthRegistrationGuardDescriptor<Registration, N> =
|
|
183
|
+
Shared.OAuthRegistrationGuardDescriptor<Registration, N, SQL, Table>;
|
|
184
|
+
|
|
185
|
+
export const oauthRegistrationGuardTable = <T extends Table, Registration, N>(
|
|
186
|
+
input: OAuthRegistrationGuardTable<T, Registration, N>,
|
|
187
|
+
): OAuthRegistrationGuardDescriptor<Registration, N> =>
|
|
188
|
+
Shared.oauthRegistrationGuardTable<SqlTableModel<T>, Registration, N, SQL>(input);
|
|
189
|
+
|
|
190
|
+
export type OAuthRegistrationMapping<
|
|
191
|
+
Registration,
|
|
192
|
+
S extends Table,
|
|
193
|
+
O extends Table,
|
|
194
|
+
C extends Table,
|
|
195
|
+
AC extends Table,
|
|
196
|
+
T extends Table,
|
|
197
|
+
I extends Table,
|
|
198
|
+
R extends Table,
|
|
199
|
+
N,
|
|
200
|
+
> = Shared.OAuthRegistrationMapping<
|
|
201
|
+
Registration,
|
|
202
|
+
SqlTableModel<S>,
|
|
203
|
+
SqlTableModel<O>,
|
|
204
|
+
SqlTableModel<C>,
|
|
205
|
+
SqlTableModel<AC>,
|
|
206
|
+
SqlTableModel<T>,
|
|
207
|
+
SqlTableModel<I>,
|
|
208
|
+
SqlTableModel<R>,
|
|
209
|
+
N,
|
|
210
|
+
SQL,
|
|
211
|
+
Table
|
|
212
|
+
>;
|
|
213
|
+
|
|
214
|
+
/** Heterogeneous descriptors are made by typed factories and projected privately. */
|
|
215
|
+
export type OAuthEligibilityDescriptor<N> = Shared.OAuthEligibilityDescriptor<N, SQL, Table>;
|
|
216
|
+
|
|
217
|
+
export const oauthEligibilityTable = <T extends Table, N>(
|
|
218
|
+
input: OAuthEligibilityTable<T, N>,
|
|
219
|
+
): OAuthEligibilityDescriptor<N> => Shared.oauthEligibilityTable<SqlTableModel<T>, N, SQL>(input);
|
|
220
|
+
|
|
221
|
+
export type OAuthCleanupDescriptor<N> = Shared.OAuthCleanupDescriptor<N, SQL, Table>;
|
|
222
|
+
|
|
223
|
+
export const oauthCleanupTable = <T extends Table, N>(
|
|
224
|
+
input: OAuthCleanupTable<T, N>,
|
|
225
|
+
): OAuthCleanupDescriptor<N> => Shared.oauthCleanupTable<SqlTableModel<T>, N, SQL>(input);
|
|
226
|
+
|
|
227
|
+
/** A stable application/reference lock acquired before an ownership target.
|
|
228
|
+
* Missing rows retain ownership. Conditions use detached native IDs; every
|
|
229
|
+
* writer of the guarded reference follows the same order. */
|
|
230
|
+
export type OAuthReferenceGuardTable<T extends Table, N> = Shared.OAuthReferenceGuardTable<
|
|
231
|
+
SqlTableModel<T>,
|
|
232
|
+
N,
|
|
233
|
+
SQL
|
|
234
|
+
>;
|
|
235
|
+
|
|
236
|
+
export type OAuthReferenceGuardDescriptor<N> = Shared.OAuthReferenceGuardDescriptor<N, SQL, Table>;
|
|
237
|
+
|
|
238
|
+
export const oauthReferenceGuardTable = <T extends Table, N>(
|
|
239
|
+
input: OAuthReferenceGuardTable<T, N>,
|
|
240
|
+
): OAuthReferenceGuardDescriptor<N> =>
|
|
241
|
+
Shared.oauthReferenceGuardTable<SqlTableModel<T>, N, SQL>(input);
|
|
242
|
+
|
|
243
|
+
export type OAuthAccountsMapping<
|
|
244
|
+
S extends Table,
|
|
245
|
+
O extends Table,
|
|
246
|
+
C extends Table,
|
|
247
|
+
AC extends Table,
|
|
248
|
+
F extends Table,
|
|
249
|
+
T extends Table,
|
|
250
|
+
U extends Table,
|
|
251
|
+
N,
|
|
252
|
+
> = Shared.OAuthAccountsMapping<
|
|
253
|
+
SqlTableModel<S>,
|
|
254
|
+
SqlTableModel<O>,
|
|
255
|
+
SqlTableModel<C>,
|
|
256
|
+
SqlTableModel<AC>,
|
|
257
|
+
SqlTableModel<F>,
|
|
258
|
+
SqlTableModel<T>,
|
|
259
|
+
SqlTableModel<U>,
|
|
260
|
+
N,
|
|
261
|
+
SQL,
|
|
262
|
+
Table
|
|
263
|
+
>;
|
|
@@ -46,7 +46,7 @@ const combine = (separator: string, values: ReadonlyArray<Fragment | undefined>)
|
|
|
46
46
|
);
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
const and = (...values: ReadonlyArray<Fragment | undefined>) => combine(" AND ", values);
|
|
49
|
+
export const and = (...values: ReadonlyArray<Fragment | undefined>) => combine(" AND ", values);
|
|
50
50
|
const or = (...values: ReadonlyArray<Fragment | undefined>) => combine(" OR ", values);
|
|
51
51
|
|
|
52
52
|
const operations = {
|
|
@@ -54,9 +54,11 @@ const operations = {
|
|
|
54
54
|
or,
|
|
55
55
|
asc: (value: unknown) => template`${value} ASC`,
|
|
56
56
|
eq: (left: unknown, right: unknown) => template`${left} = ${right}`,
|
|
57
|
+
ne: (left: unknown, right: unknown) => template`${left} <> ${right}`,
|
|
57
58
|
gt: (left: unknown, right: unknown) => template`${left} > ${right}`,
|
|
58
59
|
gte: (left: unknown, right: unknown) => template`${left} >= ${right}`,
|
|
59
60
|
lte: (left: unknown, right: unknown) => template`${left} <= ${right}`,
|
|
61
|
+
isNotNull: (value: unknown) => template`${value} IS NOT NULL`,
|
|
60
62
|
isNull: (value: unknown) => template`${value} IS NULL`,
|
|
61
63
|
notExists: (value: unknown) => template`NOT EXISTS (${value})`,
|
|
62
64
|
inArray: (column: unknown, values: ReadonlyArray<unknown>) =>
|
|
@@ -119,8 +121,8 @@ interface QueryState {
|
|
|
119
121
|
const selectionFor = (state: QueryState): Selection =>
|
|
120
122
|
state.selection ?? (state.table instanceof Table ? state.table.columns : {});
|
|
121
123
|
|
|
124
|
+
// Logical keys may exceed SQL identifier limits; only short internal aliases reach the driver.
|
|
122
125
|
const selectionFields = (selection: Selection) => {
|
|
123
|
-
const used = new Set(Object.keys(selection));
|
|
124
126
|
let index = 0;
|
|
125
127
|
|
|
126
128
|
return Object.entries(selection).flatMap<{
|
|
@@ -130,16 +132,13 @@ const selectionFields = (selection: Selection) => {
|
|
|
130
132
|
readonly alias: string;
|
|
131
133
|
}>(([key, value]) =>
|
|
132
134
|
value instanceof Table
|
|
133
|
-
? Object.entries(value.columns).map(([name, column]) => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return { key, name, column, alias };
|
|
141
|
-
})
|
|
142
|
-
: [{ key, name: undefined, column: value, alias: key }],
|
|
135
|
+
? Object.entries(value.columns).map(([name, column]) => ({
|
|
136
|
+
key,
|
|
137
|
+
name,
|
|
138
|
+
column,
|
|
139
|
+
alias: `auth_column_${index++}`,
|
|
140
|
+
}))
|
|
141
|
+
: [{ key, name: undefined, column: value, alias: `auth_column_${index++}` }],
|
|
143
142
|
);
|
|
144
143
|
};
|
|
145
144
|
|
|
@@ -214,7 +213,9 @@ const compile = (state: QueryState, compiler: Compiler): string => {
|
|
|
214
213
|
|
|
215
214
|
/** Captures one Effect SQL client. Its transaction service remains the physical owner. */
|
|
216
215
|
export const makeSqlDatabase = Effect.fnUntraced(function* (dialect: Dialect) {
|
|
217
|
-
|
|
216
|
+
// Mappings and projection aliases own their names. Retain the client's
|
|
217
|
+
// connection and transaction identity while bypassing naming transforms.
|
|
218
|
+
const client = (yield* SqlClient).withoutTransforms();
|
|
218
219
|
|
|
219
220
|
const query = (state: QueryState): Query => {
|
|
220
221
|
const execute = Effect.suspend(() => {
|
|
@@ -299,3 +300,5 @@ export interface SqlDatabase {
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
export const sql = template;
|
|
303
|
+
|
|
304
|
+
export const { eq } = operations;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
|
|
4
|
+
import { PersistenceMappingError } from "./mapping-error";
|
|
5
|
+
import { Table } from "./sql-table";
|
|
6
|
+
import { storageKeyPlans } from "./storage-plans";
|
|
7
|
+
import { validateStorageBatch, type StorageValidation } from "./storage-validation";
|
|
8
|
+
import { NativeDatabase } from "./transaction-kernel";
|
|
9
|
+
|
|
10
|
+
/** Check declared mappings against physical keys before acquiring OAuth services. */
|
|
11
|
+
export const validateSqlStorage = Effect.fnUntraced(function* (mapping: unknown) {
|
|
12
|
+
const plans = yield* Effect.try({
|
|
13
|
+
try: () =>
|
|
14
|
+
storageKeyPlans(mapping, (table) => {
|
|
15
|
+
if (!(table instanceof Table)) throw new Error("Expected a SQL table");
|
|
16
|
+
|
|
17
|
+
return table.columns;
|
|
18
|
+
}),
|
|
19
|
+
catch: () =>
|
|
20
|
+
PersistenceMappingError.make({ operation: "mapping", cause: "Invalid storage key mapping" }),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const { $client: client } = yield* NativeDatabase;
|
|
24
|
+
|
|
25
|
+
const dialect = client.onDialectOrElse({
|
|
26
|
+
pg: () => "pg" as const,
|
|
27
|
+
sqlite: () => "sqlite" as const,
|
|
28
|
+
orElse: () => undefined,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (dialect === undefined)
|
|
32
|
+
return yield* PersistenceMappingError.make({
|
|
33
|
+
operation: "mapping",
|
|
34
|
+
cause: "OAuth persistence requires PostgreSQL or SQLite",
|
|
35
|
+
});
|
|
36
|
+
const validations: StorageValidation[] = [];
|
|
37
|
+
|
|
38
|
+
for (const plan of plans) {
|
|
39
|
+
if (!(plan.table instanceof Table))
|
|
40
|
+
return yield* PersistenceMappingError.make({
|
|
41
|
+
operation: "mapping",
|
|
42
|
+
cause: "Expected a SQL table",
|
|
43
|
+
});
|
|
44
|
+
validations.push({
|
|
45
|
+
table: {
|
|
46
|
+
name: plan.table.name,
|
|
47
|
+
...(plan.table.schema === undefined ? {} : { schema: plan.table.schema }),
|
|
48
|
+
columns: Object.fromEntries(
|
|
49
|
+
Object.entries(plan.table.columns).map(([key, column]) => [key, column.options]),
|
|
50
|
+
),
|
|
51
|
+
},
|
|
52
|
+
required: [plan.keys],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
yield* validateStorageBatch(dialect, validations).pipe(
|
|
56
|
+
Effect.provideService(SqlClient.SqlClient, client),
|
|
57
|
+
Effect.mapError(() =>
|
|
58
|
+
PersistenceMappingError.make({
|
|
59
|
+
operation: "mapping",
|
|
60
|
+
cause: "Required SQL storage key is unavailable",
|
|
61
|
+
}),
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EmailCredentialSnapshot } from "@yielded/auth/Email";
|
|
2
2
|
import { LoginIdentifier } from "@yielded/auth/Identity";
|
|
3
3
|
import {
|
|
4
|
-
PasswordAttemptId,
|
|
5
4
|
PasswordCredentialSnapshot,
|
|
6
5
|
EncodedPasswordHash,
|
|
7
6
|
type PasswordReplacement,
|
|
@@ -375,7 +374,6 @@ export const makeMappings = Effect.fnUntraced(function* (input: MappingInput) {
|
|
|
375
374
|
isCommandConflict: () => false,
|
|
376
375
|
encodeInstant: instant,
|
|
377
376
|
decodeInstant: readInstant,
|
|
378
|
-
allocateAttemptId: allocate.pipe(Effect.map(PasswordAttemptId.make)),
|
|
379
377
|
allocateCredentialId: allocate,
|
|
380
378
|
allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),
|
|
381
379
|
commandRetentionMillis: 86_400_000,
|
|
@@ -444,41 +442,6 @@ export const makeMappings = Effect.fnUntraced(function* (input: MappingInput) {
|
|
|
444
442
|
});
|
|
445
443
|
}),
|
|
446
444
|
},
|
|
447
|
-
attempt: {
|
|
448
|
-
...mapped("passwordAttempts"),
|
|
449
|
-
moduleId: "moduleId",
|
|
450
|
-
action: "action",
|
|
451
|
-
attemptId: "attemptId",
|
|
452
|
-
identifierNamespace: "identifierNamespace",
|
|
453
|
-
identifierValue: "identifierValue",
|
|
454
|
-
subjectId: "subjectId",
|
|
455
|
-
credentialId: "credentialId",
|
|
456
|
-
securityRevision: "securityRevision",
|
|
457
|
-
credentialRevision: "credentialRevision",
|
|
458
|
-
verifierVersion: "verifierVersion",
|
|
459
|
-
identifierBindingRevision: "identifierBindingRevision",
|
|
460
|
-
admittedAt: "admittedAt",
|
|
461
|
-
deadline: "deadline",
|
|
462
|
-
retentionUntil: "retentionUntil",
|
|
463
|
-
state: "state",
|
|
464
|
-
encodeInsert: (row, { nativeSubjectId, state }) => ({
|
|
465
|
-
moduleId: row.moduleId,
|
|
466
|
-
action: row.action,
|
|
467
|
-
attemptId: row.attemptId,
|
|
468
|
-
identifierNamespace: row.identifier.namespace,
|
|
469
|
-
identifierValue: row.identifier.value,
|
|
470
|
-
subjectId: nativeSubjectId ?? null,
|
|
471
|
-
credentialId: row.credentialId ?? null,
|
|
472
|
-
securityRevision: row.securityRevision ?? null,
|
|
473
|
-
credentialRevision: row.credentialRevision ?? null,
|
|
474
|
-
verifierVersion: row.verifierVersion ?? null,
|
|
475
|
-
identifierBindingRevision: row.identifierBindingRevision ?? null,
|
|
476
|
-
admittedAt: instant(row.admittedAtMillis),
|
|
477
|
-
deadline: instant(row.deadlineMillis),
|
|
478
|
-
retentionUntil: instant(row.retentionUntilMillis),
|
|
479
|
-
state,
|
|
480
|
-
}),
|
|
481
|
-
},
|
|
482
445
|
command: {
|
|
483
446
|
...mapped("passwordCommands"),
|
|
484
447
|
moduleId: "moduleId",
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { Predicate, Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
// These are mapping contracts, not a parser for arbitrary constraint declarations.
|
|
4
|
+
// Role aliases are explicit because their documentation names differ from mapping fields.
|
|
5
|
+
const uniquePlans: Readonly<Record<string, readonly [string, ReadonlyArray<string>]>> = {
|
|
6
|
+
"unique(abuseEvent.moduleId,abuseEvent.action,abuseEvent.scopeKind,abuseEvent.scopeKey,abuseEvent.commandId)":
|
|
7
|
+
["abuseEvent", ["moduleId", "action", "scopeKind", "scopeKey", "commandId"]],
|
|
8
|
+
"unique(admission.moduleId)": ["admission", ["moduleId"]],
|
|
9
|
+
"unique(attempt.moduleId,attempt.attemptId)": ["attempt", ["moduleId", "attemptId"]],
|
|
10
|
+
"unique(authority.subjectId,authority.credentialId)": [
|
|
11
|
+
"authority",
|
|
12
|
+
["subjectId", "credentialId"],
|
|
13
|
+
],
|
|
14
|
+
"unique(authorityCredential.subjectId,authorityCredential.credentialId)": [
|
|
15
|
+
"authorityCredential",
|
|
16
|
+
["subjectId", "credentialId"],
|
|
17
|
+
],
|
|
18
|
+
"unique(command.moduleId,command.commandId)": ["command", ["moduleId", "commandId"]],
|
|
19
|
+
"unique(connectedAdmission.admissionId)": ["admission", ["admissionId"]],
|
|
20
|
+
"unique(connectedClient.clientKey)": ["client", ["clientKey"]],
|
|
21
|
+
"unique(connectedCohort.cohortKey)": ["cohort", ["cohortKey"]],
|
|
22
|
+
"unique(connectedCommand.moduleId,connectedCommand.commandId)": [
|
|
23
|
+
"command",
|
|
24
|
+
["moduleId", "commandId"],
|
|
25
|
+
],
|
|
26
|
+
"unique(connectedFlow.moduleId,connectedFlow.commandId)": ["flow", ["moduleId", "commandId"]],
|
|
27
|
+
"unique(connectedFlow.moduleId,connectedFlow.flowId)": ["flow", ["moduleId", "flowId"]],
|
|
28
|
+
"unique(connectedFlow.stateDigest)": ["flow", ["stateDigest"]],
|
|
29
|
+
"unique(connectedGrant.moduleId,connectedGrant.grantId)": ["grant", ["moduleId", "grantId"]],
|
|
30
|
+
"unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)":
|
|
31
|
+
["grant", ["moduleId", "subjectId", "profileKey", "activeIdentityKey"]],
|
|
32
|
+
"unique(connectedRevocation.jobId)": ["job", ["jobId"]],
|
|
33
|
+
"unique(continuation.moduleId,continuation.continuationId)": [
|
|
34
|
+
"continuation",
|
|
35
|
+
["moduleId", "continuationId"],
|
|
36
|
+
],
|
|
37
|
+
"unique(continuation.moduleId,continuation.digest)": ["continuation", ["moduleId", "digest"]],
|
|
38
|
+
"unique(credential.credentialId)": ["credential", ["credentialId"]],
|
|
39
|
+
"unique(credential.identityKey)": ["credential", ["identityKey"]],
|
|
40
|
+
"unique(credential.moduleId,credential.credentialId)": [
|
|
41
|
+
"credential",
|
|
42
|
+
["moduleId", "credentialId"],
|
|
43
|
+
],
|
|
44
|
+
"unique(credential.moduleId,credential.subjectId)": ["credential", ["moduleId", "subjectId"]],
|
|
45
|
+
"unique(emailCommand.moduleId,emailCommand.commandId)": ["command", ["moduleId", "commandId"]],
|
|
46
|
+
"unique(emailCredential.moduleId,emailCredential.credentialId)": [
|
|
47
|
+
"credential",
|
|
48
|
+
["moduleId", "credentialId"],
|
|
49
|
+
],
|
|
50
|
+
"unique(emailCredential.moduleId,emailCredential.identifierNamespace,emailCredential.identifierValue)":
|
|
51
|
+
["credential", ["moduleId", "identifierNamespace", "identifierValue"]],
|
|
52
|
+
"unique(emailRegistration.moduleId,emailRegistration.commandId)": [
|
|
53
|
+
"registration",
|
|
54
|
+
["moduleId", "commandId"],
|
|
55
|
+
],
|
|
56
|
+
"unique(emailRegistration.pendingReference)": ["registration", ["pendingReference"]],
|
|
57
|
+
"unique(failureEvent.moduleId,failureEvent.seriesKey,failureEvent.commandId)": [
|
|
58
|
+
"failureEvent",
|
|
59
|
+
["moduleId", "seriesKey", "commandId"],
|
|
60
|
+
],
|
|
61
|
+
"unique(flow.flowId)": ["flow", ["flowId"]],
|
|
62
|
+
"unique(flow.moduleId,flow.commandId)": ["flow", ["moduleId", "commandId"]],
|
|
63
|
+
"unique(flow.moduleId,flow.flowId)": ["flow", ["moduleId", "flowId"]],
|
|
64
|
+
"unique(flow.stateDigest)": ["flow", ["stateDigest"]],
|
|
65
|
+
"unique(generation.moduleId,generation.deliveryId)": ["generation", ["moduleId", "deliveryId"]],
|
|
66
|
+
"unique(generation.moduleId,generation.proofId)": ["generation", ["moduleId", "proofId"]],
|
|
67
|
+
"unique(identifier.namespace,identifier.value)": ["identifier", ["namespace", "value"]],
|
|
68
|
+
"unique(intent.digest)": ["intent", ["digest"]],
|
|
69
|
+
"unique(intent.flowId)": ["intent", ["flowId"]],
|
|
70
|
+
"unique(intent.moduleId,intent.commandId)": ["intent", ["moduleId", "commandId"]],
|
|
71
|
+
"unique(intent.moduleId,intent.flowId)": ["intent", ["moduleId", "flowId"]],
|
|
72
|
+
"unique(intent.moduleId,intent.intentId)": ["intent", ["moduleId", "intentId"]],
|
|
73
|
+
"unique(intent.moduleId,intent.reference)": ["intent", ["moduleId", "reference"]],
|
|
74
|
+
"unique(ownership.identityKey)": ["ownership", ["identityKey"]],
|
|
75
|
+
"unique(pending.digest)": ["pending", ["digest"]],
|
|
76
|
+
"unique(pending.flowId)": ["pending", ["flowId"]],
|
|
77
|
+
"unique(rateScope.moduleId,rateScope.purpose,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)":
|
|
78
|
+
["rateScope", ["moduleId", "purpose", "action", "scopeKind", "scopeKey"]],
|
|
79
|
+
"unique(registration.moduleId,registration.requestId)": [
|
|
80
|
+
"registration",
|
|
81
|
+
["moduleId", "requestId"],
|
|
82
|
+
],
|
|
83
|
+
"unique(registration.recoveryReference)": ["registration", ["recoveryReference"]],
|
|
84
|
+
"unique(registrationCommand.moduleId,registrationCommand.commandId)": [
|
|
85
|
+
"command",
|
|
86
|
+
["moduleId", "commandId"],
|
|
87
|
+
],
|
|
88
|
+
"unique(request.moduleId,request.requestId)": ["request", ["moduleId", "requestId"]],
|
|
89
|
+
"unique(series.moduleId,series.purpose,series.scopeKey)": [
|
|
90
|
+
"series",
|
|
91
|
+
["moduleId", "purpose", "scopeKey"],
|
|
92
|
+
],
|
|
93
|
+
"unique(session.digest)": ["session", ["digest"]],
|
|
94
|
+
"unique(tombstone.subjectId,tombstone.sessionId)": ["tombstone", ["subjectId", "sessionId"]],
|
|
95
|
+
"unique(tuple.identityKey)": ["ownership.tuple", ["identityKey"]],
|
|
96
|
+
"unique(unlinkCommand.moduleId,unlinkCommand.commandId)": ["command", ["moduleId", "commandId"]],
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const arrayRoles: Readonly<Record<string, string>> = {
|
|
100
|
+
credentialId: "credential",
|
|
101
|
+
factor: "authority",
|
|
102
|
+
credentialOwnership: "credentialOwnership",
|
|
103
|
+
handleOwnership: "handleOwnership",
|
|
104
|
+
boundSubjectHandle: "handleOwnership",
|
|
105
|
+
flow: "flow",
|
|
106
|
+
command: "flow",
|
|
107
|
+
admission: "admission",
|
|
108
|
+
charge: "charge",
|
|
109
|
+
intentFlow: "intent",
|
|
110
|
+
intentCommand: "intent",
|
|
111
|
+
handle: "handle",
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const nestedMappings = [
|
|
115
|
+
"read",
|
|
116
|
+
"signIn",
|
|
117
|
+
"password",
|
|
118
|
+
"proof",
|
|
119
|
+
"proofs",
|
|
120
|
+
"revocation",
|
|
121
|
+
"pending",
|
|
122
|
+
"session",
|
|
123
|
+
"source",
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const constraintGroups = [
|
|
127
|
+
"constraints",
|
|
128
|
+
"tupleConstraints",
|
|
129
|
+
"registrationConstraints",
|
|
130
|
+
"managementConstraints",
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const MappingRecord = Schema.Record(Schema.String, Schema.Unknown);
|
|
134
|
+
const record = Schema.decodeUnknownSync(MappingRecord);
|
|
135
|
+
|
|
136
|
+
export const storageKeyPlans = (
|
|
137
|
+
input: unknown,
|
|
138
|
+
columnsFor: (table: object) => Readonly<Record<string, unknown>>,
|
|
139
|
+
) => {
|
|
140
|
+
const mapping = record(input);
|
|
141
|
+
const result: Array<{ readonly table: object; readonly keys: ReadonlyArray<string> }> = [];
|
|
142
|
+
|
|
143
|
+
for (const group of constraintGroups) {
|
|
144
|
+
if (mapping[group] === undefined) continue;
|
|
145
|
+
for (const [name, declaration] of Object.entries(record(mapping[group]))) {
|
|
146
|
+
if (declaration === "notNull(provisioningRequest.subjectId)") continue;
|
|
147
|
+
let role: string;
|
|
148
|
+
let keys: ReadonlyArray<string>;
|
|
149
|
+
|
|
150
|
+
if (typeof declaration === "string") {
|
|
151
|
+
const plan = uniquePlans[declaration];
|
|
152
|
+
|
|
153
|
+
if (plan !== undefined) [role, keys] = plan;
|
|
154
|
+
else if (declaration === "unique(id)") {
|
|
155
|
+
role = "subject";
|
|
156
|
+
keys = ["id"];
|
|
157
|
+
} else if (declaration === "unique(scope)") {
|
|
158
|
+
role = name;
|
|
159
|
+
keys = ["scope"];
|
|
160
|
+
} else if (declaration === "unique(credentialId)") {
|
|
161
|
+
role = "credential";
|
|
162
|
+
keys = ["id"];
|
|
163
|
+
} else if (declaration === "unique(namespace,value)") {
|
|
164
|
+
role = "identifier";
|
|
165
|
+
keys = ["namespace", "value"];
|
|
166
|
+
} else if (declaration === "unique(provider,issuer,subject)") {
|
|
167
|
+
role = "externalIdentity";
|
|
168
|
+
keys = ["provider", "issuer", "subject"];
|
|
169
|
+
} else if (declaration === "unique(requestId)") {
|
|
170
|
+
role = "provisioningRequest";
|
|
171
|
+
keys = ["requestId"];
|
|
172
|
+
} else throw new Error("Unknown storage constraint");
|
|
173
|
+
} else if (Array.isArray(declaration) && declaration.every(Predicate.isString)) {
|
|
174
|
+
const mappedRole =
|
|
175
|
+
group === "managementConstraints" && name === "command" ? "command" : arrayRoles[name];
|
|
176
|
+
|
|
177
|
+
if (mappedRole === undefined) throw new Error("Unknown storage constraint");
|
|
178
|
+
role = mappedRole;
|
|
179
|
+
keys = declaration;
|
|
180
|
+
} else throw new Error("Invalid storage constraint");
|
|
181
|
+
|
|
182
|
+
let target: unknown = mapping;
|
|
183
|
+
|
|
184
|
+
for (const segment of role.split(".")) target = record(target)[segment];
|
|
185
|
+
// Session authority places its pending tables in one nested mapping.
|
|
186
|
+
if (target === undefined && Schema.is(MappingRecord)(mapping.pending))
|
|
187
|
+
target = mapping.pending[role];
|
|
188
|
+
let mapped = record(target);
|
|
189
|
+
|
|
190
|
+
if (role === "pending" && mapped.table === undefined) mapped = record(mapped.pending);
|
|
191
|
+
// OAuth ownership can be integrated into its tuple authority or separate.
|
|
192
|
+
if (role === "ownership" && mapped.table === undefined)
|
|
193
|
+
mapped = record(mapped.external ?? mapped.tuple);
|
|
194
|
+
if (!Predicate.isObject(mapped.table)) throw new Error("Missing storage table");
|
|
195
|
+
const columns = columnsFor(mapped.table);
|
|
196
|
+
|
|
197
|
+
result.push({
|
|
198
|
+
table: mapped.table,
|
|
199
|
+
keys: keys.map((key) => {
|
|
200
|
+
const value = mapped[key];
|
|
201
|
+
|
|
202
|
+
if (typeof value !== "string" || columns[value] === undefined)
|
|
203
|
+
throw new Error("Missing mapped key");
|
|
204
|
+
|
|
205
|
+
return value;
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const nested of nestedMappings) {
|
|
211
|
+
const value = mapping[nested];
|
|
212
|
+
|
|
213
|
+
if (Schema.is(MappingRecord)(value) && value.table === undefined)
|
|
214
|
+
result.push(...storageKeyPlans(value, columnsFor));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return result;
|
|
218
|
+
};
|