@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/Adapter.d.mts +13 -5
- package/dist/Adapter.mjs +9 -2
- 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/authority-snapshot.mjs +53 -0
- package/dist/internal/authority-snapshot.mjs.map +1 -0
- 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 -97
- package/dist/internal/models/password-model.mjs +0 -3
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +34 -0
- package/dist/internal/native-sql-table.mjs +111 -0
- package/dist/internal/native-sql-table.mjs.map +1 -0
- 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/target.d.mts +1 -1
- package/dist/internal/passkey-kernel.d.mts +5 -5
- package/dist/internal/passkeys.mjs +1 -1
- package/dist/internal/password-credentials.d.mts +11 -0
- package/dist/internal/password-credentials.mjs +121 -0
- package/dist/internal/password-credentials.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +15 -28
- package/dist/internal/password-kernel.mjs +35 -236
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +2 -0
- package/dist/internal/persistence.mjs +5 -5
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +2 -0
- package/dist/internal/session-kernel.d.mts +5 -3
- package/dist/internal/session-kernel.mjs +95 -29
- 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-persistence.mjs +3 -1
- package/dist/internal/sql-persistence.mjs.map +1 -1
- 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 +2 -67
- 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 -99
- package/dist/internal/storage-tables.mjs +0 -43
- 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-execution-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.d.mts +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 +97 -8
- package/src/OAuthPersistence.ts +427 -0
- package/src/index.ts +3 -0
- package/src/internal/authority-snapshot.ts +91 -0
- package/src/internal/configuration.ts +1 -8
- 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 -120
- package/src/internal/native-sql-table.ts +178 -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 +98 -712
- package/src/internal/persistence.ts +5 -10
- package/src/internal/query-operations.ts +2 -0
- package/src/internal/session-kernel.ts +231 -81
- package/src/internal/sql-oauth-connected-model.ts +145 -0
- package/src/internal/sql-oauth-model.ts +263 -0
- package/src/internal/sql-persistence.ts +2 -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 -66
- package/src/internal/storage-plans.ts +218 -0
- package/src/internal/storage-tables.ts +0 -35
- package/src/internal/storage-validation.ts +58 -0
- package/src/internal/transaction-kernel.ts +6 -2
|
@@ -3,18 +3,10 @@ import { AnyTableModel, SqlExpression, TableModel } from "../query-operations.mj
|
|
|
3
3
|
import { SubjectIdCodec } from "./common.mjs";
|
|
4
4
|
import { Effect, Redacted } from "effect";
|
|
5
5
|
import { AuthenticationRequirement, SecurityRevision } from "@yielded/auth/Sessions";
|
|
6
|
-
import { EncodedPasswordHash, PasswordAction,
|
|
6
|
+
import { EncodedPasswordHash, PasswordAction, PasswordCredentialSnapshot, PasswordRegistrationDecision, PasswordReplacement } from "@yielded/auth/Password";
|
|
7
7
|
import { LoginIdentifier, RecoveryReference } from "@yielded/auth/Identity";
|
|
8
8
|
//#region src/internal/models/password-model.d.ts
|
|
9
9
|
type ColumnKey<T extends TableModel> = T["column"];
|
|
10
|
-
export type PasswordAttemptAction = "sign-in" | "change";
|
|
11
|
-
export type PasswordAttemptState = "pending" | "verified" | "rejected";
|
|
12
|
-
export type PasswordRateScopeKind = "action" | "identifier" | "subject";
|
|
13
|
-
export interface PasswordScopeKeys {
|
|
14
|
-
readonly action: string;
|
|
15
|
-
readonly identifier: string;
|
|
16
|
-
readonly subject?: string;
|
|
17
|
-
}
|
|
18
10
|
export interface PasswordSubjectTable<Subject extends TableModel, _NativeSubjectId> {
|
|
19
11
|
readonly table: Subject["table"];
|
|
20
12
|
readonly id: ColumnKey<Subject>;
|
|
@@ -90,75 +82,6 @@ export interface PasswordCredentialTable<Subject extends TableModel, Identifier
|
|
|
90
82
|
readonly credential: Credential["select"];
|
|
91
83
|
}) => Effect.Effect<PasswordCredentialSnapshot, PersistenceMappingError>;
|
|
92
84
|
}
|
|
93
|
-
export interface PasswordAttemptRecord {
|
|
94
|
-
readonly moduleId: string;
|
|
95
|
-
readonly action: PasswordAttemptAction;
|
|
96
|
-
readonly attemptId: PasswordAttemptId;
|
|
97
|
-
readonly identifier: LoginIdentifier;
|
|
98
|
-
readonly subjectId?: string;
|
|
99
|
-
readonly credentialId?: string;
|
|
100
|
-
readonly securityRevision?: SecurityRevision;
|
|
101
|
-
readonly credentialRevision?: SecurityRevision;
|
|
102
|
-
readonly verifierVersion?: SecurityRevision;
|
|
103
|
-
readonly identifierBindingRevision?: SecurityRevision;
|
|
104
|
-
readonly admittedAtMillis: number;
|
|
105
|
-
readonly deadlineMillis: number;
|
|
106
|
-
readonly retentionUntilMillis: number;
|
|
107
|
-
}
|
|
108
|
-
export interface PasswordAttemptTable<Attempt extends TableModel, NativeSubjectId> {
|
|
109
|
-
readonly table: Attempt["table"];
|
|
110
|
-
readonly moduleId: ColumnKey<Attempt>;
|
|
111
|
-
readonly action: ColumnKey<Attempt>;
|
|
112
|
-
readonly attemptId: ColumnKey<Attempt>;
|
|
113
|
-
readonly identifierNamespace: ColumnKey<Attempt>;
|
|
114
|
-
readonly identifierValue: ColumnKey<Attempt>;
|
|
115
|
-
readonly subjectId: ColumnKey<Attempt>;
|
|
116
|
-
readonly credentialId: ColumnKey<Attempt>;
|
|
117
|
-
readonly securityRevision: ColumnKey<Attempt>;
|
|
118
|
-
readonly credentialRevision: ColumnKey<Attempt>;
|
|
119
|
-
readonly verifierVersion: ColumnKey<Attempt>;
|
|
120
|
-
readonly identifierBindingRevision: ColumnKey<Attempt>;
|
|
121
|
-
readonly admittedAt: ColumnKey<Attempt>;
|
|
122
|
-
readonly deadline: ColumnKey<Attempt>;
|
|
123
|
-
readonly retentionUntil: ColumnKey<Attempt>;
|
|
124
|
-
readonly state: ColumnKey<Attempt>;
|
|
125
|
-
readonly encodeInsert: (record: PasswordAttemptRecord, input: {
|
|
126
|
-
readonly nativeSubjectId?: NativeSubjectId;
|
|
127
|
-
readonly state: "pending";
|
|
128
|
-
}) => Attempt["insert"];
|
|
129
|
-
}
|
|
130
|
-
export interface PasswordRateScopeTable<RateScope extends TableModel> {
|
|
131
|
-
readonly table: RateScope["table"];
|
|
132
|
-
readonly moduleId: ColumnKey<RateScope>;
|
|
133
|
-
readonly action: ColumnKey<RateScope>;
|
|
134
|
-
readonly scopeKind: ColumnKey<RateScope>;
|
|
135
|
-
readonly scopeKey: ColumnKey<RateScope>;
|
|
136
|
-
readonly encodeInsert: (input: {
|
|
137
|
-
readonly moduleId: string;
|
|
138
|
-
readonly action: PasswordAttemptAction;
|
|
139
|
-
readonly scopeKind: PasswordRateScopeKind;
|
|
140
|
-
readonly scopeKey: string;
|
|
141
|
-
}) => RateScope["insert"];
|
|
142
|
-
}
|
|
143
|
-
export interface PasswordChargeTable<Charge extends TableModel> {
|
|
144
|
-
readonly table: Charge["table"];
|
|
145
|
-
readonly moduleId: ColumnKey<Charge>;
|
|
146
|
-
readonly action: ColumnKey<Charge>;
|
|
147
|
-
readonly scopeKind: ColumnKey<Charge>;
|
|
148
|
-
readonly scopeKey: ColumnKey<Charge>;
|
|
149
|
-
readonly attemptId: ColumnKey<Charge>;
|
|
150
|
-
readonly occurredAt: ColumnKey<Charge>;
|
|
151
|
-
readonly retentionUntil: ColumnKey<Charge>;
|
|
152
|
-
readonly encodeInsert: (input: {
|
|
153
|
-
readonly moduleId: string;
|
|
154
|
-
readonly action: PasswordAttemptAction;
|
|
155
|
-
readonly scopeKind: PasswordRateScopeKind;
|
|
156
|
-
readonly scopeKey: string;
|
|
157
|
-
readonly attemptId: PasswordAttemptId;
|
|
158
|
-
readonly occurredAtMillis: number;
|
|
159
|
-
readonly retentionUntilMillis: number;
|
|
160
|
-
}) => Charge["insert"];
|
|
161
|
-
}
|
|
162
85
|
export interface PasswordCommandTable<Command extends TableModel> {
|
|
163
86
|
readonly table: Command["table"];
|
|
164
87
|
readonly moduleId: ColumnKey<Command>;
|
|
@@ -181,17 +104,12 @@ export interface RequiredPasswordConstraints {
|
|
|
181
104
|
readonly authorityCredential: "unique(authorityCredential.subjectId,authorityCredential.credentialId)";
|
|
182
105
|
readonly credentialSubject: "unique(credential.moduleId,credential.subjectId)";
|
|
183
106
|
readonly credentialId: "unique(credential.moduleId,credential.credentialId)";
|
|
184
|
-
readonly attempt: "unique(attempt.moduleId,attempt.attemptId)";
|
|
185
|
-
readonly rateScope: "unique(rateScope.moduleId,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)";
|
|
186
|
-
readonly charge: "unique(charge.moduleId,charge.action,charge.scopeKind,charge.scopeKey,charge.attemptId)";
|
|
187
107
|
readonly command: "unique(command.moduleId,command.commandId)";
|
|
188
108
|
}
|
|
189
109
|
export declare const requiredPasswordConstraints: RequiredPasswordConstraints;
|
|
190
110
|
export interface PasswordConstraintClassifier {
|
|
191
111
|
/** Match only the mapped command uniqueness constraint. */
|
|
192
112
|
readonly isCommandConflict: (cause: unknown) => boolean;
|
|
193
|
-
/** Match only the mapped rate-scope uniqueness constraint. */
|
|
194
|
-
readonly isRateScopeConflict: (cause: unknown) => boolean;
|
|
195
113
|
}
|
|
196
114
|
export interface PasswordD1Clock<Expression extends SqlExpression = SqlExpression> {
|
|
197
115
|
readonly engineNow: Expression;
|
|
@@ -199,28 +117,17 @@ export interface PasswordD1Clock<Expression extends SqlExpression = SqlExpressio
|
|
|
199
117
|
readonly engineInstantMinus: (millis: number) => Expression;
|
|
200
118
|
readonly engineInstantPlus: (millis: number) => Expression;
|
|
201
119
|
}
|
|
202
|
-
export interface PasswordPersistenceMapping<Subject extends TableModel, Identifier extends TableModel, Credential extends TableModel, AuthorityCredential extends TableModel,
|
|
120
|
+
export interface PasswordPersistenceMapping<Subject extends TableModel, Identifier extends TableModel, Credential extends TableModel, AuthorityCredential extends TableModel, Command extends TableModel, NativeSubjectId, Expression extends SqlExpression = SqlExpression> extends PasswordConstraintClassifier {
|
|
203
121
|
readonly subject: PasswordSubjectTable<Subject, NativeSubjectId>;
|
|
204
122
|
readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression>;
|
|
205
123
|
readonly credential: PasswordCredentialTable<Subject, Identifier, Credential, NativeSubjectId>;
|
|
206
124
|
/** Every factor revision named by action evidence, including non-password factors. */
|
|
207
125
|
readonly authorityCredential: PasswordAuthorityCredentialTable<AuthorityCredential, NativeSubjectId>;
|
|
208
|
-
readonly attempt: PasswordAttemptTable<Attempt, NativeSubjectId>;
|
|
209
|
-
readonly rateScope: PasswordRateScopeTable<RateScope>;
|
|
210
|
-
readonly charge: PasswordChargeTable<Charge>;
|
|
211
126
|
readonly command: PasswordCommandTable<Command>;
|
|
212
127
|
readonly subjectId: SubjectIdCodec<NativeSubjectId>;
|
|
213
128
|
readonly constraints: RequiredPasswordConstraints;
|
|
214
|
-
readonly scopeKeys: (input: {
|
|
215
|
-
readonly moduleId: string;
|
|
216
|
-
readonly action: PasswordAttemptAction;
|
|
217
|
-
readonly identifier: LoginIdentifier;
|
|
218
|
-
readonly subjectId?: string;
|
|
219
|
-
}) => PasswordScopeKeys;
|
|
220
129
|
readonly encodeInstant: (epochMillis: number) => unknown;
|
|
221
130
|
readonly decodeInstant: (native: unknown) => Effect.Effect<number, PersistenceMappingError>;
|
|
222
|
-
readonly allocateAttemptId?: Effect.Effect<PasswordAttemptId, PersistenceMappingError>;
|
|
223
|
-
readonly allocateAttemptIdSync?: () => PasswordAttemptId;
|
|
224
131
|
readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;
|
|
225
132
|
readonly allocateCredentialIdSync?: () => string;
|
|
226
133
|
readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;
|
|
@@ -230,7 +137,7 @@ export interface PasswordPersistenceMapping<Subject extends TableModel, Identifi
|
|
|
230
137
|
readonly sessionInvalidation: "same-authority-immediate" | "original-absolute-expiry";
|
|
231
138
|
readonly d1?: PasswordD1Clock<Expression>;
|
|
232
139
|
}
|
|
233
|
-
export type D1PasswordPersistenceMapping<Subject extends TableModel, Identifier extends TableModel, Credential extends TableModel, AuthorityCredential extends TableModel,
|
|
140
|
+
export type D1PasswordPersistenceMapping<Subject extends TableModel, Identifier extends TableModel, Credential extends TableModel, AuthorityCredential extends TableModel, Command extends TableModel, NativeSubjectId, Expression extends SqlExpression = SqlExpression> = PasswordPersistenceMapping<Subject, Identifier, Credential, AuthorityCredential, Command, NativeSubjectId, Expression> & {
|
|
234
141
|
readonly d1: PasswordD1Clock<Expression>;
|
|
235
142
|
readonly subject: PasswordSubjectTable<Subject, NativeSubjectId> & {
|
|
236
143
|
readonly d1ActiveStatusValue: unknown;
|
|
@@ -322,7 +229,7 @@ export type PasswordRegistrationMapping<Registration, Subject extends TableModel
|
|
|
322
229
|
readonly allocateRecoveryReference?: Effect.Effect<typeof RecoveryReference.Type, PersistenceMappingError>;
|
|
323
230
|
readonly allocateRecoveryReferenceSync?: () => typeof RecoveryReference.Type;
|
|
324
231
|
});
|
|
325
|
-
export type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> = PasswordPersistenceMapping<AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel,
|
|
232
|
+
export type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> = PasswordPersistenceMapping<AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel, unknown, Expression>;
|
|
326
233
|
export type AnyPasswordRegistrationMapping<Registration = unknown, Expression extends SqlExpression = SqlExpression> = PasswordRegistrationMapping<Registration, AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel, AnyTableModel, unknown, Expression>;
|
|
327
234
|
//#endregion
|
|
328
235
|
//# sourceMappingURL=password-model.d.mts.map
|
|
@@ -5,9 +5,6 @@ const requiredPasswordConstraints = {
|
|
|
5
5
|
authorityCredential: "unique(authorityCredential.subjectId,authorityCredential.credentialId)",
|
|
6
6
|
credentialSubject: "unique(credential.moduleId,credential.subjectId)",
|
|
7
7
|
credentialId: "unique(credential.moduleId,credential.credentialId)",
|
|
8
|
-
attempt: "unique(attempt.moduleId,attempt.attemptId)",
|
|
9
|
-
rateScope: "unique(rateScope.moduleId,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)",
|
|
10
|
-
charge: "unique(charge.moduleId,charge.action,charge.scopeKind,charge.scopeKey,charge.attemptId)",
|
|
11
8
|
command: "unique(command.moduleId,command.commandId)"
|
|
12
9
|
};
|
|
13
10
|
const requiredPasswordRegistrationConstraints = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-model.mjs","names":[],"sources":["../../../src/internal/models/password-model.ts"],"sourcesContent":["import { type RecoveryReference, type LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasswordAction,\n PasswordAttemptId,\n PasswordCredentialSnapshot,\n PasswordReplacement,\n PasswordRegistrationDecision,\n EncodedPasswordHash,\n} from \"@yielded/auth/Password\";\nimport type { AuthenticationRequirement, SecurityRevision } from \"@yielded/auth/Sessions\";\nimport type { Effect, Redacted } from \"effect\";\n\nimport type { AnyTableModel, TableModel as Table, SqlExpression } from \"../query-operations\";\nimport type { PersistenceMappingError, SubjectIdCodec } from \"./common\";\n\ntype ColumnKey<T extends Table> = T[\"column\"];\n\nexport type PasswordAttemptAction = \"sign-in\" | \"change\";\n\nexport type PasswordAttemptState = \"pending\" | \"verified\" | \"rejected\";\n\nexport type PasswordRateScopeKind = \"action\" | \"identifier\" | \"subject\";\n\nexport interface PasswordScopeKeys {\n readonly action: string;\n readonly identifier: string;\n readonly subject?: string;\n}\n\nexport interface PasswordSubjectTable<Subject extends Table, _NativeSubjectId> {\n readonly table: Subject[\"table\"];\n readonly id: ColumnKey<Subject>;\n readonly status: ColumnKey<Subject>;\n readonly securityRevision: ColumnKey<Subject>;\n readonly isActiveStatus: (value: unknown) => boolean;\n readonly decodeRequirement: (\n row: Subject[\"select\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Current policy for this exact credential mutation, decoded under the subject lock. */\n readonly decodeActionRequirement: (\n row: Subject[\"select\"],\n action: PasswordAction,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n readonly nextSecurityRevision?: (\n current: SecurityRevision,\n ) => Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly nextSecurityRevisionSync?: (current: SecurityRevision) => SecurityRevision;\n /** Required only by D1 conditional statements. */\n readonly d1ActiveStatusValue?: unknown;\n}\n\nexport interface PasswordIdentifierTable<\n Identifier extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> {\n readonly table: Identifier[\"table\"];\n readonly namespace: ColumnKey<Identifier>;\n readonly value: ColumnKey<Identifier>;\n readonly subjectId: ColumnKey<Identifier>;\n readonly verifiedAt: ColumnKey<Identifier>;\n readonly bindingRevision: ColumnKey<Identifier>;\n /** Consumer-owned current login/recovery eligibility, including row status. */\n readonly isCurrent: (row: Identifier[\"select\"]) => boolean;\n /** Required by D1 so current ownership/eligibility is rechecked in the batch. */\n readonly d1CurrentCondition?: (input: {\n readonly identifier: LoginIdentifier;\n readonly nativeSubjectId: NativeSubjectId;\n }) => Expression;\n readonly encodeInitialInsert: (\n identifier: LoginIdentifier,\n subjectId: NativeSubjectId,\n bindingRevision: SecurityRevision,\n ) => Identifier[\"insert\"];\n}\n\nexport interface PasswordAuthorityCredentialTable<Credential extends Table, NativeSubjectId> {\n readonly table: Credential[\"table\"];\n readonly subjectId: ColumnKey<Credential>;\n readonly credentialId: ColumnKey<Credential>;\n readonly revision: ColumnKey<Credential>;\n readonly status?: ColumnKey<Credential>;\n readonly isActiveStatus?: (value: unknown) => boolean;\n readonly d1ActiveStatusValue?: unknown;\n readonly encodeInsert: (input: {\n readonly subjectId: NativeSubjectId;\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n }) => Credential[\"insert\"];\n readonly encodeRevision: (revision: SecurityRevision) => Partial<Credential[\"insert\"]>;\n}\n\nexport interface PasswordCredentialTable<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n NativeSubjectId,\n> {\n readonly table: Credential[\"table\"];\n readonly moduleId: ColumnKey<Credential>;\n readonly subjectId: ColumnKey<Credential>;\n readonly credentialId: ColumnKey<Credential>;\n readonly credentialRevision: ColumnKey<Credential>;\n readonly verifierVersion: ColumnKey<Credential>;\n readonly verifier: ColumnKey<Credential>;\n readonly normalization: ColumnKey<Credential>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly subjectId: NativeSubjectId;\n readonly credentialId: string;\n readonly credentialRevision: SecurityRevision;\n readonly verifierVersion: SecurityRevision;\n readonly replacement: PasswordReplacement;\n }) => Credential[\"insert\"];\n readonly encodeVerifier: (\n verifier: Redacted.Redacted<EncodedPasswordHash>,\n verifierVersion: SecurityRevision,\n ) => Partial<Credential[\"insert\"]>;\n readonly encodeReplacement: (input: {\n readonly replacement: PasswordReplacement;\n readonly credentialRevision: SecurityRevision;\n readonly verifierVersion: SecurityRevision;\n }) => Partial<Credential[\"insert\"]>;\n readonly decode: (input: {\n readonly moduleId: string;\n readonly subject: Subject[\"select\"];\n readonly identifier: Identifier[\"select\"];\n readonly credential: Credential[\"select\"];\n }) => Effect.Effect<PasswordCredentialSnapshot, PersistenceMappingError>;\n}\n\nexport interface PasswordAttemptRecord {\n readonly moduleId: string;\n readonly action: PasswordAttemptAction;\n readonly attemptId: PasswordAttemptId;\n readonly identifier: LoginIdentifier;\n readonly subjectId?: string;\n readonly credentialId?: string;\n readonly securityRevision?: SecurityRevision;\n readonly credentialRevision?: SecurityRevision;\n readonly verifierVersion?: SecurityRevision;\n readonly identifierBindingRevision?: SecurityRevision;\n readonly admittedAtMillis: number;\n readonly deadlineMillis: number;\n readonly retentionUntilMillis: number;\n}\n\nexport interface PasswordAttemptTable<Attempt extends Table, NativeSubjectId> {\n readonly table: Attempt[\"table\"];\n readonly moduleId: ColumnKey<Attempt>;\n readonly action: ColumnKey<Attempt>;\n readonly attemptId: ColumnKey<Attempt>;\n readonly identifierNamespace: ColumnKey<Attempt>;\n readonly identifierValue: ColumnKey<Attempt>;\n readonly subjectId: ColumnKey<Attempt>;\n readonly credentialId: ColumnKey<Attempt>;\n readonly securityRevision: ColumnKey<Attempt>;\n readonly credentialRevision: ColumnKey<Attempt>;\n readonly verifierVersion: ColumnKey<Attempt>;\n readonly identifierBindingRevision: ColumnKey<Attempt>;\n readonly admittedAt: ColumnKey<Attempt>;\n readonly deadline: ColumnKey<Attempt>;\n readonly retentionUntil: ColumnKey<Attempt>;\n readonly state: ColumnKey<Attempt>;\n readonly encodeInsert: (\n record: PasswordAttemptRecord,\n input: {\n readonly nativeSubjectId?: NativeSubjectId;\n readonly state: \"pending\";\n },\n ) => Attempt[\"insert\"];\n}\n\nexport interface PasswordRateScopeTable<RateScope extends Table> {\n readonly table: RateScope[\"table\"];\n readonly moduleId: ColumnKey<RateScope>;\n readonly action: ColumnKey<RateScope>;\n readonly scopeKind: ColumnKey<RateScope>;\n readonly scopeKey: ColumnKey<RateScope>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly action: PasswordAttemptAction;\n readonly scopeKind: PasswordRateScopeKind;\n readonly scopeKey: string;\n }) => RateScope[\"insert\"];\n}\n\nexport interface PasswordChargeTable<Charge extends Table> {\n readonly table: Charge[\"table\"];\n readonly moduleId: ColumnKey<Charge>;\n readonly action: ColumnKey<Charge>;\n readonly scopeKind: ColumnKey<Charge>;\n readonly scopeKey: ColumnKey<Charge>;\n readonly attemptId: ColumnKey<Charge>;\n readonly occurredAt: ColumnKey<Charge>;\n readonly retentionUntil: ColumnKey<Charge>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly action: PasswordAttemptAction;\n readonly scopeKind: PasswordRateScopeKind;\n readonly scopeKey: string;\n readonly attemptId: PasswordAttemptId;\n readonly occurredAtMillis: number;\n readonly retentionUntilMillis: number;\n }) => Charge[\"insert\"];\n}\n\nexport interface PasswordCommandTable<Command extends Table> {\n readonly table: Command[\"table\"];\n readonly moduleId: ColumnKey<Command>;\n readonly commandId: ColumnKey<Command>;\n readonly action: ColumnKey<Command>;\n readonly bindingDigest: ColumnKey<Command>;\n readonly decision: ColumnKey<Command>;\n readonly retentionUntil: ColumnKey<Command>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly commandId: string;\n readonly action: string;\n readonly bindingDigest: string;\n readonly decision: \"changed\";\n readonly retentionUntilMillis: number;\n }) => Command[\"insert\"];\n}\n\nexport interface RequiredPasswordConstraints {\n readonly identifier: \"unique(identifier.namespace,identifier.value)\";\n readonly authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\";\n readonly credentialSubject: \"unique(credential.moduleId,credential.subjectId)\";\n readonly credentialId: \"unique(credential.moduleId,credential.credentialId)\";\n readonly attempt: \"unique(attempt.moduleId,attempt.attemptId)\";\n readonly rateScope: \"unique(rateScope.moduleId,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)\";\n readonly charge: \"unique(charge.moduleId,charge.action,charge.scopeKind,charge.scopeKey,charge.attemptId)\";\n readonly command: \"unique(command.moduleId,command.commandId)\";\n}\n\nexport const requiredPasswordConstraints: RequiredPasswordConstraints = {\n identifier: \"unique(identifier.namespace,identifier.value)\",\n authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\",\n credentialSubject: \"unique(credential.moduleId,credential.subjectId)\",\n credentialId: \"unique(credential.moduleId,credential.credentialId)\",\n attempt: \"unique(attempt.moduleId,attempt.attemptId)\",\n rateScope: \"unique(rateScope.moduleId,rateScope.action,rateScope.scopeKind,rateScope.scopeKey)\",\n charge: \"unique(charge.moduleId,charge.action,charge.scopeKind,charge.scopeKey,charge.attemptId)\",\n command: \"unique(command.moduleId,command.commandId)\",\n};\n\nexport interface PasswordConstraintClassifier {\n /** Match only the mapped command uniqueness constraint. */\n readonly isCommandConflict: (cause: unknown) => boolean;\n /** Match only the mapped rate-scope uniqueness constraint. */\n readonly isRateScopeConflict: (cause: unknown) => boolean;\n}\n\nexport interface PasswordD1Clock<Expression extends SqlExpression = SqlExpression> {\n readonly engineNow: Expression;\n readonly engineNowMillis: Expression;\n readonly engineInstantMinus: (millis: number) => Expression;\n readonly engineInstantPlus: (millis: number) => Expression;\n}\n\nexport interface PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Attempt extends Table,\n RateScope extends Table,\n Charge extends Table,\n Command extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> extends PasswordConstraintClassifier {\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId>;\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression>;\n readonly credential: PasswordCredentialTable<Subject, Identifier, Credential, NativeSubjectId>;\n /** Every factor revision named by action evidence, including non-password factors. */\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n >;\n readonly attempt: PasswordAttemptTable<Attempt, NativeSubjectId>;\n readonly rateScope: PasswordRateScopeTable<RateScope>;\n readonly charge: PasswordChargeTable<Charge>;\n readonly command: PasswordCommandTable<Command>;\n readonly subjectId: SubjectIdCodec<NativeSubjectId>;\n readonly constraints: RequiredPasswordConstraints;\n readonly scopeKeys: (input: {\n readonly moduleId: string;\n readonly action: PasswordAttemptAction;\n readonly identifier: LoginIdentifier;\n readonly subjectId?: string;\n }) => PasswordScopeKeys;\n readonly encodeInstant: (epochMillis: number) => unknown;\n readonly decodeInstant: (native: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly allocateAttemptId?: Effect.Effect<PasswordAttemptId, PersistenceMappingError>;\n readonly allocateAttemptIdSync?: () => PasswordAttemptId;\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n readonly commandRetentionMillis: number;\n /** All semantic replacements still bump subject revision. This flag only declares bearer invalidation latency. */\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n readonly d1?: PasswordD1Clock<Expression>;\n}\n\nexport type D1PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Attempt extends Table,\n RateScope extends Table,\n Charge extends Table,\n Command extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordPersistenceMapping<\n Subject,\n Identifier,\n Credential,\n AuthorityCredential,\n Attempt,\n RateScope,\n Charge,\n Command,\n NativeSubjectId,\n Expression\n> & {\n readonly d1: PasswordD1Clock<Expression>;\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId> & {\n readonly d1ActiveStatusValue: unknown;\n };\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression> & {\n readonly d1CurrentCondition: (input: {\n readonly identifier: LoginIdentifier;\n readonly nativeSubjectId: NativeSubjectId;\n }) => Expression;\n };\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n > & {\n readonly d1ActiveStatusValue: unknown;\n };\n};\n\nexport type PasswordRegistrationState = \"pending\" | \"created\";\n\nexport interface PasswordRegistrationIntent<Registration> {\n readonly moduleId: string;\n readonly requestId: string;\n readonly identifier: LoginIdentifier;\n readonly registration: Registration;\n readonly replacement: PasswordReplacement;\n}\n\nexport interface PasswordRegistrationTable<Registration, Request extends Table, NativeSubjectId> {\n readonly table: Request[\"table\"];\n readonly moduleId: ColumnKey<Request>;\n readonly requestId: ColumnKey<Request>;\n readonly state: ColumnKey<Request>;\n readonly subjectId: ColumnKey<Request>;\n readonly recoveryReference: ColumnKey<Request>;\n readonly encodeInsert: (\n input: PasswordRegistrationIntent<Registration>,\n state: {\n readonly state: PasswordRegistrationState;\n readonly nativeSubjectId?: NativeSubjectId;\n readonly recoveryReference?: typeof RecoveryReference.Type;\n },\n ) => Request[\"insert\"];\n /** Replay projection must never expose a stored subject or decode protected intent. */\n readonly decodeReplay: (row: Request[\"select\"]) => Effect.Effect<\n Exclude<\n PasswordRegistrationDecision,\n {\n readonly _tag: \"Created\";\n }\n >,\n PersistenceMappingError\n >;\n}\n\nexport interface PasswordRegistrationProvisioning<\n Registration,\n Subject extends Table,\n NativeSubjectId,\n> {\n readonly encodeSubjectInsert: (\n input: PasswordRegistrationIntent<Registration>,\n values: {\n readonly nativeSubjectId: NativeSubjectId | undefined;\n readonly securityRevision: SecurityRevision;\n },\n ) => Subject[\"insert\"];\n readonly allocateSubjectId?: Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n readonly allocateSubjectIdSync?: () => NativeSubjectId;\n /** Interactive targets use this for database-generated/autoincrement keys. */\n readonly decodeGeneratedId?: (\n rows: ReadonlyArray<unknown>,\n ) => Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n}\n\nexport interface RequiredPasswordRegistrationConstraints {\n readonly request: \"unique(registration.moduleId,registration.requestId)\";\n readonly recoveryReference: \"unique(registration.recoveryReference)\";\n readonly identifier: \"unique(identifier.namespace,identifier.value)\";\n readonly credentialSubject: \"unique(credential.moduleId,credential.subjectId)\";\n readonly authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\";\n}\n\nexport const requiredPasswordRegistrationConstraints: RequiredPasswordRegistrationConstraints = {\n request: \"unique(registration.moduleId,registration.requestId)\",\n recoveryReference: \"unique(registration.recoveryReference)\",\n identifier: \"unique(identifier.namespace,identifier.value)\",\n credentialSubject: \"unique(credential.moduleId,credential.subjectId)\",\n authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\",\n};\n\nexport interface PasswordRegistrationConstraintClassifier {\n readonly isRequestConflict: (cause: unknown) => boolean;\n readonly isIdentifierConflict: (cause: unknown) => boolean;\n readonly isCredentialConflict: (cause: unknown) => boolean;\n}\n\nexport type PasswordRegistrationMapping<\n Registration,\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Request extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordRegistrationConstraintClassifier & {\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId>;\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression>;\n readonly credential: PasswordCredentialTable<Subject, Identifier, Credential, NativeSubjectId>;\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n >;\n readonly registration: PasswordRegistrationTable<Registration, Request, NativeSubjectId>;\n readonly subjectId: SubjectIdCodec<NativeSubjectId>;\n readonly constraints: RequiredPasswordRegistrationConstraints;\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n} & (\n | {\n readonly mode: \"atomic\";\n readonly provisioning: PasswordRegistrationProvisioning<\n Registration,\n Subject,\n NativeSubjectId\n > &\n (\n | {\n readonly idMode: \"allocated\";\n readonly allocateSubjectId: Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n }\n | {\n readonly idMode: \"synchronous\";\n readonly allocateSubjectIdSync: () => NativeSubjectId;\n }\n | {\n readonly idMode: \"generated\";\n readonly decodeGeneratedId: (\n rows: ReadonlyArray<unknown>,\n ) => Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n }\n );\n }\n | {\n readonly mode: \"pending\";\n readonly allocateRecoveryReference?: Effect.Effect<\n typeof RecoveryReference.Type,\n PersistenceMappingError\n >;\n readonly allocateRecoveryReferenceSync?: () => typeof RecoveryReference.Type;\n }\n );\n\nexport type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> =\n PasswordPersistenceMapping<\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown,\n Expression\n >;\n\nexport type AnyPasswordRegistrationMapping<\n Registration = unknown,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordRegistrationMapping<\n Registration,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown,\n Expression\n>;\n"],"mappings":";;AA4OA,MAAa,8BAA2D;CACtE,YAAY;CACZ,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;CACd,SAAS;CACT,WAAW;CACX,QAAQ;CACR,SAAS;AACX;AAwKA,MAAa,0CAAmF;CAC9F,SAAS;CACT,mBAAmB;CACnB,YAAY;CACZ,mBAAmB;CACnB,qBAAqB;AACvB"}
|
|
1
|
+
{"version":3,"file":"password-model.mjs","names":[],"sources":["../../../src/internal/models/password-model.ts"],"sourcesContent":["import { type RecoveryReference, type LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasswordAction,\n PasswordCredentialSnapshot,\n PasswordReplacement,\n PasswordRegistrationDecision,\n EncodedPasswordHash,\n} from \"@yielded/auth/Password\";\nimport type { AuthenticationRequirement, SecurityRevision } from \"@yielded/auth/Sessions\";\nimport type { Effect, Redacted } from \"effect\";\n\nimport type { AnyTableModel, TableModel as Table, SqlExpression } from \"../query-operations\";\nimport type { PersistenceMappingError, SubjectIdCodec } from \"./common\";\n\ntype ColumnKey<T extends Table> = T[\"column\"];\n\nexport interface PasswordSubjectTable<Subject extends Table, _NativeSubjectId> {\n readonly table: Subject[\"table\"];\n readonly id: ColumnKey<Subject>;\n readonly status: ColumnKey<Subject>;\n readonly securityRevision: ColumnKey<Subject>;\n readonly isActiveStatus: (value: unknown) => boolean;\n readonly decodeRequirement: (\n row: Subject[\"select\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Current policy for this exact credential mutation, decoded under the subject lock. */\n readonly decodeActionRequirement: (\n row: Subject[\"select\"],\n action: PasswordAction,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n readonly nextSecurityRevision?: (\n current: SecurityRevision,\n ) => Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly nextSecurityRevisionSync?: (current: SecurityRevision) => SecurityRevision;\n /** Required only by D1 conditional statements. */\n readonly d1ActiveStatusValue?: unknown;\n}\n\nexport interface PasswordIdentifierTable<\n Identifier extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> {\n readonly table: Identifier[\"table\"];\n readonly namespace: ColumnKey<Identifier>;\n readonly value: ColumnKey<Identifier>;\n readonly subjectId: ColumnKey<Identifier>;\n readonly verifiedAt: ColumnKey<Identifier>;\n readonly bindingRevision: ColumnKey<Identifier>;\n /** Consumer-owned current login/recovery eligibility, including row status. */\n readonly isCurrent: (row: Identifier[\"select\"]) => boolean;\n /** Required by D1 so current ownership/eligibility is rechecked in the batch. */\n readonly d1CurrentCondition?: (input: {\n readonly identifier: LoginIdentifier;\n readonly nativeSubjectId: NativeSubjectId;\n }) => Expression;\n readonly encodeInitialInsert: (\n identifier: LoginIdentifier,\n subjectId: NativeSubjectId,\n bindingRevision: SecurityRevision,\n ) => Identifier[\"insert\"];\n}\n\nexport interface PasswordAuthorityCredentialTable<Credential extends Table, NativeSubjectId> {\n readonly table: Credential[\"table\"];\n readonly subjectId: ColumnKey<Credential>;\n readonly credentialId: ColumnKey<Credential>;\n readonly revision: ColumnKey<Credential>;\n readonly status?: ColumnKey<Credential>;\n readonly isActiveStatus?: (value: unknown) => boolean;\n readonly d1ActiveStatusValue?: unknown;\n readonly encodeInsert: (input: {\n readonly subjectId: NativeSubjectId;\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n }) => Credential[\"insert\"];\n readonly encodeRevision: (revision: SecurityRevision) => Partial<Credential[\"insert\"]>;\n}\n\nexport interface PasswordCredentialTable<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n NativeSubjectId,\n> {\n readonly table: Credential[\"table\"];\n readonly moduleId: ColumnKey<Credential>;\n readonly subjectId: ColumnKey<Credential>;\n readonly credentialId: ColumnKey<Credential>;\n readonly credentialRevision: ColumnKey<Credential>;\n readonly verifierVersion: ColumnKey<Credential>;\n readonly verifier: ColumnKey<Credential>;\n readonly normalization: ColumnKey<Credential>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly subjectId: NativeSubjectId;\n readonly credentialId: string;\n readonly credentialRevision: SecurityRevision;\n readonly verifierVersion: SecurityRevision;\n readonly replacement: PasswordReplacement;\n }) => Credential[\"insert\"];\n readonly encodeVerifier: (\n verifier: Redacted.Redacted<EncodedPasswordHash>,\n verifierVersion: SecurityRevision,\n ) => Partial<Credential[\"insert\"]>;\n readonly encodeReplacement: (input: {\n readonly replacement: PasswordReplacement;\n readonly credentialRevision: SecurityRevision;\n readonly verifierVersion: SecurityRevision;\n }) => Partial<Credential[\"insert\"]>;\n readonly decode: (input: {\n readonly moduleId: string;\n readonly subject: Subject[\"select\"];\n readonly identifier: Identifier[\"select\"];\n readonly credential: Credential[\"select\"];\n }) => Effect.Effect<PasswordCredentialSnapshot, PersistenceMappingError>;\n}\n\nexport interface PasswordCommandTable<Command extends Table> {\n readonly table: Command[\"table\"];\n readonly moduleId: ColumnKey<Command>;\n readonly commandId: ColumnKey<Command>;\n readonly action: ColumnKey<Command>;\n readonly bindingDigest: ColumnKey<Command>;\n readonly decision: ColumnKey<Command>;\n readonly retentionUntil: ColumnKey<Command>;\n readonly encodeInsert: (input: {\n readonly moduleId: string;\n readonly commandId: string;\n readonly action: string;\n readonly bindingDigest: string;\n readonly decision: \"changed\";\n readonly retentionUntilMillis: number;\n }) => Command[\"insert\"];\n}\n\nexport interface RequiredPasswordConstraints {\n readonly identifier: \"unique(identifier.namespace,identifier.value)\";\n readonly authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\";\n readonly credentialSubject: \"unique(credential.moduleId,credential.subjectId)\";\n readonly credentialId: \"unique(credential.moduleId,credential.credentialId)\";\n readonly command: \"unique(command.moduleId,command.commandId)\";\n}\n\nexport const requiredPasswordConstraints: RequiredPasswordConstraints = {\n identifier: \"unique(identifier.namespace,identifier.value)\",\n authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\",\n credentialSubject: \"unique(credential.moduleId,credential.subjectId)\",\n credentialId: \"unique(credential.moduleId,credential.credentialId)\",\n command: \"unique(command.moduleId,command.commandId)\",\n};\n\nexport interface PasswordConstraintClassifier {\n /** Match only the mapped command uniqueness constraint. */\n readonly isCommandConflict: (cause: unknown) => boolean;\n}\n\nexport interface PasswordD1Clock<Expression extends SqlExpression = SqlExpression> {\n readonly engineNow: Expression;\n readonly engineNowMillis: Expression;\n readonly engineInstantMinus: (millis: number) => Expression;\n readonly engineInstantPlus: (millis: number) => Expression;\n}\n\nexport interface PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Command extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> extends PasswordConstraintClassifier {\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId>;\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression>;\n readonly credential: PasswordCredentialTable<Subject, Identifier, Credential, NativeSubjectId>;\n /** Every factor revision named by action evidence, including non-password factors. */\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n >;\n readonly command: PasswordCommandTable<Command>;\n readonly subjectId: SubjectIdCodec<NativeSubjectId>;\n readonly constraints: RequiredPasswordConstraints;\n readonly encodeInstant: (epochMillis: number) => unknown;\n readonly decodeInstant: (native: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n readonly commandRetentionMillis: number;\n /** All semantic replacements still bump subject revision. This flag only declares bearer invalidation latency. */\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n readonly d1?: PasswordD1Clock<Expression>;\n}\n\nexport type D1PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Command extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordPersistenceMapping<\n Subject,\n Identifier,\n Credential,\n AuthorityCredential,\n Command,\n NativeSubjectId,\n Expression\n> & {\n readonly d1: PasswordD1Clock<Expression>;\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId> & {\n readonly d1ActiveStatusValue: unknown;\n };\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression> & {\n readonly d1CurrentCondition: (input: {\n readonly identifier: LoginIdentifier;\n readonly nativeSubjectId: NativeSubjectId;\n }) => Expression;\n };\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n > & {\n readonly d1ActiveStatusValue: unknown;\n };\n};\n\nexport type PasswordRegistrationState = \"pending\" | \"created\";\n\nexport interface PasswordRegistrationIntent<Registration> {\n readonly moduleId: string;\n readonly requestId: string;\n readonly identifier: LoginIdentifier;\n readonly registration: Registration;\n readonly replacement: PasswordReplacement;\n}\n\nexport interface PasswordRegistrationTable<Registration, Request extends Table, NativeSubjectId> {\n readonly table: Request[\"table\"];\n readonly moduleId: ColumnKey<Request>;\n readonly requestId: ColumnKey<Request>;\n readonly state: ColumnKey<Request>;\n readonly subjectId: ColumnKey<Request>;\n readonly recoveryReference: ColumnKey<Request>;\n readonly encodeInsert: (\n input: PasswordRegistrationIntent<Registration>,\n state: {\n readonly state: PasswordRegistrationState;\n readonly nativeSubjectId?: NativeSubjectId;\n readonly recoveryReference?: typeof RecoveryReference.Type;\n },\n ) => Request[\"insert\"];\n /** Replay projection must never expose a stored subject or decode protected intent. */\n readonly decodeReplay: (row: Request[\"select\"]) => Effect.Effect<\n Exclude<\n PasswordRegistrationDecision,\n {\n readonly _tag: \"Created\";\n }\n >,\n PersistenceMappingError\n >;\n}\n\nexport interface PasswordRegistrationProvisioning<\n Registration,\n Subject extends Table,\n NativeSubjectId,\n> {\n readonly encodeSubjectInsert: (\n input: PasswordRegistrationIntent<Registration>,\n values: {\n readonly nativeSubjectId: NativeSubjectId | undefined;\n readonly securityRevision: SecurityRevision;\n },\n ) => Subject[\"insert\"];\n readonly allocateSubjectId?: Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n readonly allocateSubjectIdSync?: () => NativeSubjectId;\n /** Interactive targets use this for database-generated/autoincrement keys. */\n readonly decodeGeneratedId?: (\n rows: ReadonlyArray<unknown>,\n ) => Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n}\n\nexport interface RequiredPasswordRegistrationConstraints {\n readonly request: \"unique(registration.moduleId,registration.requestId)\";\n readonly recoveryReference: \"unique(registration.recoveryReference)\";\n readonly identifier: \"unique(identifier.namespace,identifier.value)\";\n readonly credentialSubject: \"unique(credential.moduleId,credential.subjectId)\";\n readonly authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\";\n}\n\nexport const requiredPasswordRegistrationConstraints: RequiredPasswordRegistrationConstraints = {\n request: \"unique(registration.moduleId,registration.requestId)\",\n recoveryReference: \"unique(registration.recoveryReference)\",\n identifier: \"unique(identifier.namespace,identifier.value)\",\n credentialSubject: \"unique(credential.moduleId,credential.subjectId)\",\n authorityCredential: \"unique(authorityCredential.subjectId,authorityCredential.credentialId)\",\n};\n\nexport interface PasswordRegistrationConstraintClassifier {\n readonly isRequestConflict: (cause: unknown) => boolean;\n readonly isIdentifierConflict: (cause: unknown) => boolean;\n readonly isCredentialConflict: (cause: unknown) => boolean;\n}\n\nexport type PasswordRegistrationMapping<\n Registration,\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n Request extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordRegistrationConstraintClassifier & {\n readonly subject: PasswordSubjectTable<Subject, NativeSubjectId>;\n readonly identifier: PasswordIdentifierTable<Identifier, NativeSubjectId, Expression>;\n readonly credential: PasswordCredentialTable<Subject, Identifier, Credential, NativeSubjectId>;\n readonly authorityCredential: PasswordAuthorityCredentialTable<\n AuthorityCredential,\n NativeSubjectId\n >;\n readonly registration: PasswordRegistrationTable<Registration, Request, NativeSubjectId>;\n readonly subjectId: SubjectIdCodec<NativeSubjectId>;\n readonly constraints: RequiredPasswordRegistrationConstraints;\n readonly allocateCredentialId?: Effect.Effect<string, PersistenceMappingError>;\n readonly allocateCredentialIdSync?: () => string;\n readonly allocateRevision?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateRevisionSync?: () => SecurityRevision;\n} & (\n | {\n readonly mode: \"atomic\";\n readonly provisioning: PasswordRegistrationProvisioning<\n Registration,\n Subject,\n NativeSubjectId\n > &\n (\n | {\n readonly idMode: \"allocated\";\n readonly allocateSubjectId: Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n }\n | {\n readonly idMode: \"synchronous\";\n readonly allocateSubjectIdSync: () => NativeSubjectId;\n }\n | {\n readonly idMode: \"generated\";\n readonly decodeGeneratedId: (\n rows: ReadonlyArray<unknown>,\n ) => Effect.Effect<NativeSubjectId, PersistenceMappingError>;\n }\n );\n }\n | {\n readonly mode: \"pending\";\n readonly allocateRecoveryReference?: Effect.Effect<\n typeof RecoveryReference.Type,\n PersistenceMappingError\n >;\n readonly allocateRecoveryReferenceSync?: () => typeof RecoveryReference.Type;\n }\n );\n\nexport type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> =\n PasswordPersistenceMapping<\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown,\n Expression\n >;\n\nexport type AnyPasswordRegistrationMapping<\n Registration = unknown,\n Expression extends SqlExpression = SqlExpression,\n> = PasswordRegistrationMapping<\n Registration,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n AnyTableModel,\n unknown,\n Expression\n>;\n"],"mappings":";;AAgJA,MAAa,8BAA2D;CACtE,YAAY;CACZ,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;CACd,SAAS;AACX;AAkJA,MAAa,0CAAmF;CAC9F,SAAS;CACT,mBAAmB;CACnB,YAAY;CACZ,mBAAmB;CACnB,qBAAqB;AACvB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import "./mapping-error.mjs";
|
|
2
|
+
import { PhysicalTextColumn } from "./storage-validation.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import * as Statement$1 from "effect/sql/Statement";
|
|
5
|
+
import { SqlClient } from "effect/sql/SqlClient";
|
|
6
|
+
//#region src/internal/native-sql-table.d.ts
|
|
7
|
+
/** Physical SQL representation only. Semantic row codecs and policy remain in
|
|
8
|
+
* the owning mapping. Execute assembled statements without name/result transforms. */
|
|
9
|
+
export interface SqlTable {
|
|
10
|
+
readonly name: Statement$1.Fragment;
|
|
11
|
+
/** Alias reads only; writes continue to target the original physical table. */
|
|
12
|
+
readonly as: (alias: string) => SqlTable;
|
|
13
|
+
readonly column: (key: string) => Statement$1.Fragment;
|
|
14
|
+
/** Optional codec-free text candidate. Physical type/collation compatibility
|
|
15
|
+
* must still be checked against the database before comparing columns. */
|
|
16
|
+
readonly unencodedTextColumn?: (key: string) => PhysicalTextColumn | undefined;
|
|
17
|
+
/** Project every column under an ordinal alias, using a distinct prefix per table. */
|
|
18
|
+
readonly fields: (prefix: string) => Statement$1.Fragment;
|
|
19
|
+
readonly decode: (row: Readonly<Record<string, unknown>>, prefix: string) => Record<string, unknown>;
|
|
20
|
+
readonly value: (key: string, value: unknown) => Statement$1.Fragment;
|
|
21
|
+
/** Complete INSERT prefix; append conflict/returning clauses in the owning operation. */
|
|
22
|
+
readonly insert: (values: Readonly<Record<string, unknown>>) => Statement$1.Fragment;
|
|
23
|
+
/** Complete UPDATE ... SET prefix; the owning operation supplies its predicate. */
|
|
24
|
+
readonly update: (values: Readonly<Record<string, unknown>>) => Statement$1.Fragment;
|
|
25
|
+
}
|
|
26
|
+
export type NativeSqlTables = (table: object) => SqlTable;
|
|
27
|
+
/** Prefixes are short ASCII identifiers so ordinal aliases cannot be truncated
|
|
28
|
+
* by PostgreSQL. Callers distinguish tables by prefix, never by physical names. */
|
|
29
|
+
export declare const nativeSqlAlias: (prefix: string, index: number) => string;
|
|
30
|
+
/** Cache physical metadata only; insert/update materialize values and hooks at
|
|
31
|
+
* each call. Construction acquires no client and owns no transaction. */
|
|
32
|
+
export declare const makeNativeSqlTables: (client: SqlClient, convert?: (client: SqlClient, table: object) => SqlTable) => NativeSqlTables;
|
|
33
|
+
//#endregion
|
|
34
|
+
//# sourceMappingURL=native-sql-table.d.mts.map
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
2
|
+
import { Table, identifier } from "./sql-table.mjs";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import * as Statement$1 from "effect/sql/Statement";
|
|
5
|
+
//#region src/internal/native-sql-table.ts
|
|
6
|
+
/** Capture synchronous mapping callbacks without changing SQL execution errors. */
|
|
7
|
+
const sqlMapping = (thunk) => Effect.try({
|
|
8
|
+
try: thunk,
|
|
9
|
+
catch: (cause) => PersistenceMappingError.make({
|
|
10
|
+
operation: "mapping",
|
|
11
|
+
cause
|
|
12
|
+
})
|
|
13
|
+
});
|
|
14
|
+
/** Prefixes are short ASCII identifiers so ordinal aliases cannot be truncated
|
|
15
|
+
* by PostgreSQL. Callers distinguish tables by prefix, never by physical names. */
|
|
16
|
+
const nativeSqlAlias = (prefix, index) => {
|
|
17
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]{0,39}$/.test(prefix)) throw PersistenceMappingError.make({
|
|
18
|
+
operation: "mapping",
|
|
19
|
+
cause: "Invalid SQL alias prefix"
|
|
20
|
+
});
|
|
21
|
+
return `${prefix}${index}`;
|
|
22
|
+
};
|
|
23
|
+
const directTable = (client, physical) => {
|
|
24
|
+
if (!(physical instanceof Table)) throw PersistenceMappingError.make({
|
|
25
|
+
operation: "mapping",
|
|
26
|
+
cause: "Expected a SQL table"
|
|
27
|
+
});
|
|
28
|
+
const quote = (name) => {
|
|
29
|
+
if (name.includes("\0")) throw PersistenceMappingError.make({
|
|
30
|
+
operation: "mapping",
|
|
31
|
+
cause: "NUL in SQL identifier"
|
|
32
|
+
});
|
|
33
|
+
return client.literal(identifier(name));
|
|
34
|
+
};
|
|
35
|
+
const name = physical.schema === void 0 ? quote(physical.name) : client`${quote(physical.schema)}.${quote(physical.name)}`;
|
|
36
|
+
const columns = Object.entries(physical.columns);
|
|
37
|
+
const getColumn = (key) => {
|
|
38
|
+
const column = Object.hasOwn(physical.columns, key) ? physical.columns[key] : void 0;
|
|
39
|
+
if (column === void 0) throw PersistenceMappingError.make({
|
|
40
|
+
operation: "column",
|
|
41
|
+
cause: "Missing SQL column"
|
|
42
|
+
});
|
|
43
|
+
return column;
|
|
44
|
+
};
|
|
45
|
+
const value = (key, input) => {
|
|
46
|
+
const mapped = getColumn(key);
|
|
47
|
+
if (Statement$1.isFragment(input)) return input;
|
|
48
|
+
const encoded = input === null ? null : mapped.mapToDriverValue(input);
|
|
49
|
+
return Statement$1.fragment([Statement$1.parameter(typeof encoded === "boolean" ? client.onDialectOrElse({
|
|
50
|
+
sqlite: () => Number(encoded),
|
|
51
|
+
orElse: () => encoded
|
|
52
|
+
}) : encoded)]);
|
|
53
|
+
};
|
|
54
|
+
const entries = (values) => Object.entries(values).filter(([key, input]) => Object.hasOwn(physical.columns, key) && input !== void 0);
|
|
55
|
+
const bind = (alias) => {
|
|
56
|
+
const reference = alias === void 0 ? name : quote(alias);
|
|
57
|
+
const column = (key) => client`${reference}.${quote(getColumn(key).options.name)}`;
|
|
58
|
+
return {
|
|
59
|
+
name: alias === void 0 ? name : client`${name} AS ${reference}`,
|
|
60
|
+
as: bind,
|
|
61
|
+
column,
|
|
62
|
+
unencodedTextColumn: (key) => getColumn(key).options.type === "text" ? {
|
|
63
|
+
table: {
|
|
64
|
+
name: physical.name,
|
|
65
|
+
...physical.schema === void 0 ? {} : { schema: physical.schema }
|
|
66
|
+
},
|
|
67
|
+
name: getColumn(key).options.name
|
|
68
|
+
} : void 0,
|
|
69
|
+
value,
|
|
70
|
+
fields: (prefix) => client.join(", ", false)(columns.map(([key], index) => client`${column(key)} AS ${quote(nativeSqlAlias(prefix, index))}`)),
|
|
71
|
+
decode: (row, prefix) => Object.fromEntries(columns.map(([key, field], index) => {
|
|
72
|
+
const alias = nativeSqlAlias(prefix, index);
|
|
73
|
+
if (!Object.hasOwn(row, alias)) throw PersistenceMappingError.make({
|
|
74
|
+
operation: "decode",
|
|
75
|
+
cause: "Missing SQL projection field"
|
|
76
|
+
});
|
|
77
|
+
return [key, field.decode(row[alias])];
|
|
78
|
+
})),
|
|
79
|
+
insert: (values) => {
|
|
80
|
+
const fields = entries(values);
|
|
81
|
+
if (fields.length === 0) return client`INSERT INTO ${name} DEFAULT VALUES`;
|
|
82
|
+
return client`INSERT INTO ${name} (${client.join(", ", false)(fields.map(([key]) => quote(getColumn(key).options.name)))}) VALUES (${client.join(", ", false)(fields.map(([key, input]) => value(key, input)))})`;
|
|
83
|
+
},
|
|
84
|
+
update: (values) => {
|
|
85
|
+
const fields = entries(values);
|
|
86
|
+
if (fields.length === 0) throw PersistenceMappingError.make({
|
|
87
|
+
operation: "mapping",
|
|
88
|
+
cause: "Empty SQL update"
|
|
89
|
+
});
|
|
90
|
+
return client`UPDATE ${name} SET ${client.join(", ", false)(fields.map(([key, input]) => client`${quote(getColumn(key).options.name)} = ${value(key, input)}`))}`;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
return bind();
|
|
95
|
+
};
|
|
96
|
+
/** Cache physical metadata only; insert/update materialize values and hooks at
|
|
97
|
+
* each call. Construction acquires no client and owns no transaction. */
|
|
98
|
+
const makeNativeSqlTables = (client, convert = directTable) => {
|
|
99
|
+
const tables = /* @__PURE__ */ new WeakMap();
|
|
100
|
+
return (table) => {
|
|
101
|
+
const existing = tables.get(table);
|
|
102
|
+
if (existing !== void 0) return existing;
|
|
103
|
+
const mapped = convert(client, table);
|
|
104
|
+
tables.set(table, mapped);
|
|
105
|
+
return mapped;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
//#endregion
|
|
109
|
+
export { makeNativeSqlTables, nativeSqlAlias, sqlMapping };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=native-sql-table.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-sql-table.mjs","names":["Statement"],"sources":["../../src/internal/native-sql-table.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport type { SqlClient } from \"effect/sql/SqlClient\";\nimport * as Statement from \"effect/sql/Statement\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { Table, identifier } from \"./sql-table\";\nimport type { PhysicalTextColumn } from \"./storage-validation\";\n\n/** Capture synchronous mapping callbacks without changing SQL execution errors. */\nexport const sqlMapping = <A>(thunk: () => A): Effect.Effect<A, PersistenceMappingError> =>\n Effect.try({\n try: thunk,\n catch: (cause) => PersistenceMappingError.make({ operation: \"mapping\", cause }),\n });\n\n/** Physical SQL representation only. Semantic row codecs and policy remain in\n * the owning mapping. Execute assembled statements without name/result transforms. */\nexport interface SqlTable {\n readonly name: Statement.Fragment;\n /** Alias reads only; writes continue to target the original physical table. */\n readonly as: (alias: string) => SqlTable;\n readonly column: (key: string) => Statement.Fragment;\n /** Optional codec-free text candidate. Physical type/collation compatibility\n * must still be checked against the database before comparing columns. */\n readonly unencodedTextColumn?: (key: string) => PhysicalTextColumn | undefined;\n /** Project every column under an ordinal alias, using a distinct prefix per table. */\n readonly fields: (prefix: string) => Statement.Fragment;\n readonly decode: (\n row: Readonly<Record<string, unknown>>,\n prefix: string,\n ) => Record<string, unknown>;\n readonly value: (key: string, value: unknown) => Statement.Fragment;\n /** Complete INSERT prefix; append conflict/returning clauses in the owning operation. */\n readonly insert: (values: Readonly<Record<string, unknown>>) => Statement.Fragment;\n /** Complete UPDATE ... SET prefix; the owning operation supplies its predicate. */\n readonly update: (values: Readonly<Record<string, unknown>>) => Statement.Fragment;\n}\n\nexport type NativeSqlTables = (table: object) => SqlTable;\n\n/** Prefixes are short ASCII identifiers so ordinal aliases cannot be truncated\n * by PostgreSQL. Callers distinguish tables by prefix, never by physical names. */\nexport const nativeSqlAlias = (prefix: string, index: number): string => {\n if (!/^[A-Za-z_][A-Za-z0-9_]{0,39}$/.test(prefix))\n throw PersistenceMappingError.make({ operation: \"mapping\", cause: \"Invalid SQL alias prefix\" });\n\n return `${prefix}${index}`;\n};\n\nconst directTable = (client: SqlClient, physical: object): SqlTable => {\n if (!(physical instanceof Table))\n throw PersistenceMappingError.make({ operation: \"mapping\", cause: \"Expected a SQL table\" });\n\n const quote = (name: string) => {\n if (name.includes(\"\\0\"))\n throw PersistenceMappingError.make({ operation: \"mapping\", cause: \"NUL in SQL identifier\" });\n\n return client.literal(identifier(name));\n };\n\n const name =\n physical.schema === undefined\n ? quote(physical.name)\n : client`${quote(physical.schema)}.${quote(physical.name)}`;\n\n const columns = Object.entries(physical.columns);\n\n const getColumn = (key: string) => {\n const column = Object.hasOwn(physical.columns, key) ? physical.columns[key] : undefined;\n\n if (column === undefined)\n throw PersistenceMappingError.make({ operation: \"column\", cause: \"Missing SQL column\" });\n\n return column;\n };\n\n const value = (key: string, input: unknown): Statement.Fragment => {\n const mapped = getColumn(key);\n\n if (Statement.isFragment(input)) return input;\n const encoded = input === null ? null : mapped.mapToDriverValue(input);\n\n return Statement.fragment([\n Statement.parameter(\n typeof encoded === \"boolean\"\n ? client.onDialectOrElse({ sqlite: () => Number(encoded), orElse: () => encoded })\n : encoded,\n ),\n ]);\n };\n\n const entries = (values: Readonly<Record<string, unknown>>) =>\n Object.entries(values).filter(\n ([key, input]) => Object.hasOwn(physical.columns, key) && input !== undefined,\n );\n\n const bind = (alias?: string): SqlTable => {\n const reference = alias === undefined ? name : quote(alias);\n const column = (key: string) => client`${reference}.${quote(getColumn(key).options.name)}`;\n\n return {\n name: alias === undefined ? name : client`${name} AS ${reference}`,\n as: bind,\n column,\n unencodedTextColumn: (key) =>\n getColumn(key).options.type === \"text\"\n ? {\n table: {\n name: physical.name,\n ...(physical.schema === undefined ? {} : { schema: physical.schema }),\n },\n name: getColumn(key).options.name,\n }\n : undefined,\n value,\n fields: (prefix) =>\n client.join(\n \", \",\n false,\n )(\n columns.map(\n ([key], index) => client`${column(key)} AS ${quote(nativeSqlAlias(prefix, index))}`,\n ),\n ),\n decode: (row, prefix) =>\n Object.fromEntries(\n columns.map(([key, field], index) => {\n const alias = nativeSqlAlias(prefix, index);\n\n if (!Object.hasOwn(row, alias))\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"Missing SQL projection field\",\n });\n\n return [key, field.decode(row[alias])];\n }),\n ),\n insert: (values) => {\n const fields = entries(values);\n\n if (fields.length === 0) return client`INSERT INTO ${name} DEFAULT VALUES`;\n\n return client`INSERT INTO ${name} (${client.join(\", \", false)(fields.map(([key]) => quote(getColumn(key).options.name)))}) VALUES (${client.join(\", \", false)(fields.map(([key, input]) => value(key, input)))})`;\n },\n update: (values) => {\n const fields = entries(values);\n\n if (fields.length === 0)\n throw PersistenceMappingError.make({ operation: \"mapping\", cause: \"Empty SQL update\" });\n\n return client`UPDATE ${name} SET ${client.join(\", \", false)(fields.map(([key, input]) => client`${quote(getColumn(key).options.name)} = ${value(key, input)}`))}`;\n },\n };\n };\n\n return bind();\n};\n\n/** Cache physical metadata only; insert/update materialize values and hooks at\n * each call. Construction acquires no client and owns no transaction. */\nexport const makeNativeSqlTables = (\n client: SqlClient,\n convert: (client: SqlClient, table: object) => SqlTable = directTable,\n): NativeSqlTables => {\n const tables = new WeakMap<object, SqlTable>();\n\n return (table) => {\n const existing = tables.get(table);\n\n if (existing !== undefined) return existing;\n const mapped = convert(client, table);\n\n tables.set(table, mapped);\n\n return mapped;\n };\n};\n"],"mappings":";;;;;;AASA,MAAa,cAAiB,UAC5B,OAAO,IAAI;CACT,KAAK;CACL,QAAQ,UAAU,wBAAwB,KAAK;EAAE,WAAW;EAAW;CAAM,CAAC;AAChF,CAAC;;;AA6BH,MAAa,kBAAkB,QAAgB,UAA0B;CACvE,IAAI,CAAC,gCAAgC,KAAK,MAAM,GAC9C,MAAM,wBAAwB,KAAK;EAAE,WAAW;EAAW,OAAO;CAA2B,CAAC;CAEhG,OAAO,GAAG,SAAS;AACrB;AAEA,MAAM,eAAe,QAAmB,aAA+B;CACrE,IAAI,EAAE,oBAAoB,QACxB,MAAM,wBAAwB,KAAK;EAAE,WAAW;EAAW,OAAO;CAAuB,CAAC;CAE5F,MAAM,SAAS,SAAiB;EAC9B,IAAI,KAAK,SAAS,IAAI,GACpB,MAAM,wBAAwB,KAAK;GAAE,WAAW;GAAW,OAAO;EAAwB,CAAC;EAE7F,OAAO,OAAO,QAAQ,WAAW,IAAI,CAAC;CACxC;CAEA,MAAM,OACJ,SAAS,WAAW,KAAA,IAChB,MAAM,SAAS,IAAI,IACnB,MAAM,GAAG,MAAM,SAAS,MAAM,EAAE,GAAG,MAAM,SAAS,IAAI;CAE5D,MAAM,UAAU,OAAO,QAAQ,SAAS,OAAO;CAE/C,MAAM,aAAa,QAAgB;EACjC,MAAM,SAAS,OAAO,OAAO,SAAS,SAAS,GAAG,IAAI,SAAS,QAAQ,OAAO,KAAA;EAE9E,IAAI,WAAW,KAAA,GACb,MAAM,wBAAwB,KAAK;GAAE,WAAW;GAAU,OAAO;EAAqB,CAAC;EAEzF,OAAO;CACT;CAEA,MAAM,SAAS,KAAa,UAAuC;EACjE,MAAM,SAAS,UAAU,GAAG;EAE5B,IAAIA,YAAU,WAAW,KAAK,GAAG,OAAO;EACxC,MAAM,UAAU,UAAU,OAAO,OAAO,OAAO,iBAAiB,KAAK;EAErE,OAAOA,YAAU,SAAS,CACxBA,YAAU,UACR,OAAO,YAAY,YACf,OAAO,gBAAgB;GAAE,cAAc,OAAO,OAAO;GAAG,cAAc;EAAQ,CAAC,IAC/E,OACN,CACF,CAAC;CACH;CAEA,MAAM,WAAW,WACf,OAAO,QAAQ,MAAM,CAAC,CAAC,QACpB,CAAC,KAAK,WAAW,OAAO,OAAO,SAAS,SAAS,GAAG,KAAK,UAAU,KAAA,CACtE;CAEF,MAAM,QAAQ,UAA6B;EACzC,MAAM,YAAY,UAAU,KAAA,IAAY,OAAO,MAAM,KAAK;EAC1D,MAAM,UAAU,QAAgB,MAAM,GAAG,UAAU,GAAG,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,IAAI;EAEvF,OAAO;GACL,MAAM,UAAU,KAAA,IAAY,OAAO,MAAM,GAAG,KAAK,MAAM;GACvD,IAAI;GACJ;GACA,sBAAsB,QACpB,UAAU,GAAG,CAAC,CAAC,QAAQ,SAAS,SAC5B;IACE,OAAO;KACL,MAAM,SAAS;KACf,GAAI,SAAS,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,SAAS,OAAO;IACrE;IACA,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ;GAC/B,IACA,KAAA;GACN;GACA,SAAS,WACP,OAAO,KACL,MACA,KACF,CAAC,CACC,QAAQ,KACL,CAAC,MAAM,UAAU,MAAM,GAAG,OAAO,GAAG,EAAE,MAAM,MAAM,eAAe,QAAQ,KAAK,CAAC,GAClF,CACF;GACF,SAAS,KAAK,WACZ,OAAO,YACL,QAAQ,KAAK,CAAC,KAAK,QAAQ,UAAU;IACnC,MAAM,QAAQ,eAAe,QAAQ,KAAK;IAE1C,IAAI,CAAC,OAAO,OAAO,KAAK,KAAK,GAC3B,MAAM,wBAAwB,KAAK;KACjC,WAAW;KACX,OAAO;IACT,CAAC;IAEH,OAAO,CAAC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC;GACvC,CAAC,CACH;GACF,SAAS,WAAW;IAClB,MAAM,SAAS,QAAQ,MAAM;IAE7B,IAAI,OAAO,WAAW,GAAG,OAAO,MAAM,eAAe,KAAK;IAE1D,OAAO,MAAM,eAAe,KAAK,IAAI,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,SAAS,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,YAAY,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,WAAW,MAAM,KAAK,KAAK,CAAC,CAAC,EAAE;GACjN;GACA,SAAS,WAAW;IAClB,MAAM,SAAS,QAAQ,MAAM;IAE7B,IAAI,OAAO,WAAW,GACpB,MAAM,wBAAwB,KAAK;KAAE,WAAW;KAAW,OAAO;IAAmB,CAAC;IAExF,OAAO,MAAM,UAAU,KAAK,OAAO,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,KAAK,WAAW,MAAM,GAAG,MAAM,UAAU,GAAG,CAAC,CAAC,QAAQ,IAAI,EAAE,KAAK,MAAM,KAAK,KAAK,GAAG,CAAC;GAChK;EACF;CACF;CAEA,OAAO,KAAK;AACd;;;AAIA,MAAa,uBACX,QACA,UAA0D,gBACtC;CACpB,MAAM,yBAAS,IAAI,QAA0B;CAE7C,QAAQ,UAAU;EAChB,MAAM,WAAW,OAAO,IAAI,KAAK;EAEjC,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,MAAM,SAAS,QAAQ,QAAQ,KAAK;EAEpC,OAAO,IAAI,OAAO,MAAM;EAExB,OAAO;CACT;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "../mapping-error.mjs";
|
|
2
|
+
import "./owner.mjs";
|
|
3
|
+
import "./query-compiler.mjs";
|
|
4
|
+
import "./flow.mjs";
|
|
5
|
+
import "./registration.mjs";
|
|
6
|
+
import { Context, Effect, Layer } from "effect";
|
|
7
|
+
import { SecurityRevision } from "@yielded/auth/Sessions";
|
|
8
|
+
import { OAuthAccountRevision, OAuthLinkAccess } from "@yielded/auth/OAuth";
|