@yielded/auth-persistence 0.1.0-beta.30 → 0.1.0-beta.31
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 +3 -6
- package/dist/Adapter.d.mts +1 -1
- package/dist/internal/configuration.d.mts +18 -8
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/models/password-model.d.mts +2 -1
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/passkey-managed.mjs +4 -2
- package/dist/internal/passkey-managed.mjs.map +1 -1
- package/dist/internal/password-native.mjs +14 -0
- package/dist/internal/password-native.mjs.map +1 -1
- package/dist/internal/password-registration-native.d.mts +1 -1
- package/dist/internal/persistence.d.mts +3 -0
- package/dist/internal/persistence.mjs +4 -6
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/registration.mjs +17 -8
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-native-authority.mjs +2 -1
- package/dist/internal/session-native-authority.mjs.map +1 -1
- package/dist/internal/session-native-login.mjs +2 -1
- package/dist/internal/session-native-login.mjs.map +1 -1
- package/dist/internal/session-native-pending-read.mjs +1 -1
- package/dist/internal/session-native-pending-read.mjs.map +1 -1
- package/dist/internal/session-native-state.mjs +21 -8
- package/dist/internal/session-native-state.mjs.map +1 -1
- package/dist/internal/session-native-stateful.mjs +2 -1
- package/dist/internal/session-native-stateful.mjs.map +1 -1
- package/dist/internal/session-native-step-up.mjs +4 -2
- package/dist/internal/session-native-step-up.mjs.map +1 -1
- package/dist/internal/sqlite-policy-guard.mjs +23 -0
- package/dist/internal/sqlite-policy-guard.mjs.map +1 -0
- package/dist/internal/storage-mapping.d.mts +9 -5
- package/dist/internal/storage-mapping.mjs +16 -4
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/package.json +1 -1
- package/src/internal/configuration.ts +26 -8
- package/src/internal/models/password-model.ts +2 -1
- package/src/internal/passkey-managed.ts +8 -2
- package/src/internal/password-native.ts +28 -2
- package/src/internal/persistence.ts +174 -164
- package/src/internal/registration.ts +53 -40
- package/src/internal/session-native-authority.ts +3 -1
- package/src/internal/session-native-login.ts +3 -1
- package/src/internal/session-native-pending-read.ts +1 -1
- package/src/internal/session-native-state.ts +29 -10
- package/src/internal/session-native-stateful.ts +3 -1
- package/src/internal/session-native-step-up.ts +5 -2
- package/src/internal/sqlite-policy-guard.ts +34 -0
- package/src/internal/storage-mapping.ts +40 -4
package/README.md
CHANGED
|
@@ -29,12 +29,9 @@ connection and migration runner.
|
|
|
29
29
|
|
|
30
30
|
The composed Layer covers password sign-in and management, email address verification
|
|
31
31
|
and changes, phone sign-in, and passkey sign-in and management with stateful sessions
|
|
32
|
-
on PostgreSQL and SQLite.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
physical columns and expressions and supply transaction or atomic-batch execution.
|
|
36
|
-
Plain reads use no transaction. Protected mutations retain their named final checks
|
|
37
|
-
when an application joins additional work to the owner.
|
|
32
|
+
on PostgreSQL and SQLite. Adapter authors use typed table mappings or
|
|
33
|
+
`NativeSqlTables` through `/Adapter`. Use the explicit coordinators when auth and
|
|
34
|
+
application writes must share one commit; independent transaction owners are not atomic.
|
|
38
35
|
See [persistence examples](../../docs/src/content/docs/reference/adapters.md#runnable-examples) for all
|
|
39
36
|
four ownership models and their current limits.
|
|
40
37
|
|
package/dist/Adapter.d.mts
CHANGED
|
@@ -14,11 +14,11 @@ import { OAuthProxySqlTable, makeOAuthProxyPersistence } from "./internal/oauth-
|
|
|
14
14
|
import { StorageRole, StorageTable, storageTables } from "./internal/storage-tables.mjs";
|
|
15
15
|
import { BoundPersistence, ClaimsCodec, Definition, MappingInput, PersistenceApi, PersistenceConfigurationError } from "./internal/configuration.mjs";
|
|
16
16
|
import { NativeSqlTables, SqlTable, makeNativeSqlTables, nativeSqlAlias } from "./internal/native-sql-table.mjs";
|
|
17
|
+
import { SqlBatchCommit, SqlNativeCommit, captureSqlBatchStatements, makeSqlCommitExecutor } from "./internal/sql-commit.mjs";
|
|
17
18
|
import { Backend, createPersistence } from "./internal/persistence.mjs";
|
|
18
19
|
import { makeMappings } from "./internal/storage-mapping.mjs";
|
|
19
20
|
import { PasswordRegistrationAuthority } from "./internal/registration-contract.mjs";
|
|
20
21
|
import { PhysicalStorageTable, StorageValidation, validateStorage, validateStorageBatch, withStorageValidation } from "./internal/storage-validation.mjs";
|
|
21
|
-
import { SqlBatchCommit, SqlNativeCommit, captureSqlBatchStatements, makeSqlCommitExecutor } from "./internal/sql-commit.mjs";
|
|
22
22
|
import { PasskeyNativeMapping, PasskeyNativeRead } from "./internal/passkey-native-state.mjs";
|
|
23
23
|
import { makeBatchPasskeyServices, makeNativePasskeyCredentialServices, makeNativePasskeyServices } from "./internal/passkey-native.mjs";
|
|
24
24
|
import { NativePasskeyCeremonyMapping, makeNativePasskeyCeremonyServices } from "./internal/passkey-native-ceremony.mjs";
|
|
@@ -78,6 +78,19 @@ export interface ProvisioningId<Id extends string> {
|
|
|
78
78
|
readonly authNamespace: Id;
|
|
79
79
|
readonly service: "PersistenceProvisioning";
|
|
80
80
|
}
|
|
81
|
+
export interface SubjectProvisioningInput<Registration> {
|
|
82
|
+
readonly requestId: string;
|
|
83
|
+
readonly identifier: LoginIdentifier;
|
|
84
|
+
readonly registration: Registration;
|
|
85
|
+
}
|
|
86
|
+
/** Return application subject values for insertion by the auth commit owner.
|
|
87
|
+
* Include the allocated native ID, active status, and initial security revision.
|
|
88
|
+
* Preparing values may allocate IDs, but must not write to the database or perform
|
|
89
|
+
* external side effects. Interactive drivers also accept an existing insert callback.
|
|
90
|
+
*/
|
|
91
|
+
export type SubjectProvisioning<Registration> = {
|
|
92
|
+
readonly values: (input: SubjectProvisioningInput<Registration>) => Effect.Effect<Readonly<Record<string, unknown>>, PasswordUnavailable>;
|
|
93
|
+
} | ((input: SubjectProvisioningInput<Registration>) => Effect.Effect<SubjectId, PasswordUnavailable>);
|
|
81
94
|
export type Provisioning<A extends {
|
|
82
95
|
readonly strategies: Readonly<Record<string, Strategy>>;
|
|
83
96
|
}> = { readonly [K in keyof A["strategies"] as A["strategies"][K] extends {
|
|
@@ -85,11 +98,7 @@ export type Provisioning<A extends {
|
|
|
85
98
|
kind: "password";
|
|
86
99
|
management: true;
|
|
87
100
|
};
|
|
88
|
-
} ? K : never]:
|
|
89
|
-
readonly requestId: string;
|
|
90
|
-
readonly identifier: LoginIdentifier;
|
|
91
|
-
readonly registration: Registration<A["strategies"][K]>;
|
|
92
|
-
}) => Effect.Effect<SubjectId, PasswordUnavailable>; };
|
|
101
|
+
} ? K : never]: SubjectProvisioning<Registration<A["strategies"][K]>>; };
|
|
93
102
|
export type ProvisioningRequirement<A extends {
|
|
94
103
|
readonly namespace: string;
|
|
95
104
|
readonly strategies: Readonly<Record<string, Strategy>>;
|
|
@@ -148,9 +157,10 @@ export interface BoundPersistence<T extends object, R, C extends ClaimsCodec, Id
|
|
|
148
157
|
readonly Config: Context.Service<ConfigId<A["namespace"]>, StorageLayout<T, Roles<C, Id, A>>> & {
|
|
149
158
|
readonly layer: (value: StorageLayout<T, Roles<C, Id, A>>) => Layer.Layer<ConfigId<A["namespace"]>>;
|
|
150
159
|
};
|
|
151
|
-
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
160
|
+
/** Supply subject values through `{ values }` on every driver, including D1.
|
|
161
|
+
* Interactive drivers also accept callbacks that insert through the owning
|
|
162
|
+
* SqlClient and return the new subject ID. Never open an independent transaction
|
|
163
|
+
* or perform external side effects while preparing a subject.
|
|
154
164
|
*/
|
|
155
165
|
readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;
|
|
156
166
|
/** Capture dependencies at acquisition; validate SQL metadata and configure storage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Crypto, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string | undefined;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: (input: {\n readonly requestId: string;\n readonly identifier: LoginIdentifier;\n readonly registration: Registration<A[\"strategies\"][K]>;\n }) => Effect.Effect<SubjectId, PasswordUnavailable>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles = \"proofs\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | KeyId<A[\"sessions\"][\"PendingAuthentication\"]>\n | KeyId<A[\"sessions\"][\"SessionStepUpPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionCleanup\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never ? never : PasskeyManagementPersistence)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"pending\"\n | (Enabled<A, \"password\"> extends never ? never : \"passwords\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\")\n | (Enabled<A, \"passkey\"> extends never ? never : \"passkeyCredentials\" | \"passkeyFlows\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Create only the application subject. Runs inside the owning auth SQL transaction;\n * use that SqlClient (including Drizzle over it), and return the new subject ID.\n * Do not perform external side effects or open an independent transaction here.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n /** Capture dependencies at acquisition; validate SQL metadata and configure storage\n * once on the first service operation. Initialization failures become that port's\n * unavailable error. Failure or interruption remains cached until layer reacquisition.\n */\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | Crypto.Crypto\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n /** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */\n readonly prefix: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AA+BA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"configuration.mjs","names":[],"sources":["../../src/internal/configuration.ts"],"sourcesContent":["import type { EmailAction, EmailAddressPersistence } from \"@yielded/auth/Email\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasskeyActionChallenge,\n PasskeyConfig,\n PasskeyCredentials,\n PasskeyManagementPersistence,\n PasskeyManagementPolicy,\n PasskeyMethodPolicy,\n PasskeyPersistence,\n PasskeyConfigurationError,\n} from \"@yielded/auth/Passkey\";\nimport type {\n PasswordAction,\n PasswordPersistence,\n PasswordUnavailable,\n} from \"@yielded/auth/Password\";\nimport type { PhoneSignInTargets } from \"@yielded/auth/PhoneOtp\";\nimport type { ProofPersistence } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport type {\n AuthenticationAuthority,\n AuthenticationRequirement,\n make as makeSessions,\n} from \"@yielded/auth/Sessions\";\nimport { type Context, type Crypto, type Effect, type Layer, Schema } from \"effect\";\nimport type { SqlClient } from \"effect/sql\";\n\nimport type { PersistenceMappingError } from \"./mapping-error\";\nimport type { StorageRole } from \"./storage-tables\";\n\nexport class PersistenceConfigurationError extends Schema.TaggedError<PersistenceConfigurationError>()(\n \"PersistenceConfigurationError\",\n { reason: Schema.String },\n) {}\n\nexport type ClaimsCodec = Schema.Codec<unknown, unknown, never, never>;\n\nexport interface PasskeyFeature {\n readonly kind: \"passkey\";\n readonly moduleId: string;\n readonly policy: Effect.Effect<PasskeyMethodPolicy, PasskeyConfigurationError, PasskeyConfig>;\n readonly management?: boolean;\n readonly managementPolicy?: PasskeyManagementPolicy;\n}\n\nexport interface Strategy {\n readonly strategy: object;\n readonly persistence?:\n | PasskeyFeature\n | {\n readonly kind: \"password\" | \"phone\" | \"email\";\n readonly moduleId: string;\n readonly lifecycle?: boolean;\n readonly management?: boolean;\n readonly addresses?: boolean;\n };\n readonly RegistrationAuthority?: Context.Key<unknown, unknown>;\n}\n\nexport interface Definition<C extends ClaimsCodec, Id extends string> {\n readonly namespace: string;\n readonly claims: C;\n readonly sessionMode?: string | undefined;\n readonly sessions: ReturnType<typeof makeSessions<C, Id>>;\n readonly strategies: Readonly<Record<string, Strategy>>;\n}\n\ntype KeyId<T> = T extends Context.Key<infer Id, infer _Service> ? Id : never;\ntype Enabled<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n Kind extends \"password\" | \"phone\" | \"email\" | \"passkey\",\n> = Extract<A[\"strategies\"][keyof A[\"strategies\"]], { persistence: { kind: Kind } }>;\n\ntype ManagedPassword<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"password\">, { persistence: { management: true } }>;\n\ntype ManagedPasskey<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n Extract<Enabled<A, \"passkey\">, { persistence: { management: true } }>;\n\nexport type PasskeyRequirement<\n A extends { readonly strategies: Readonly<Record<string, Strategy>> },\n> = Enabled<A, \"passkey\"> extends never ? never : PasskeyConfig;\n\ntype RegistrationKey<S> = S extends { readonly RegistrationAuthority: infer K } ? K : never;\ntype Registration<S> =\n RegistrationKey<S> extends Context.Key<infer _Id, infer Service>\n ? Service extends { readonly register: (input: infer Input, ...args: never[]) => unknown }\n ? Input extends { readonly registration: infer Value }\n ? Value\n : never\n : never\n : never;\n\nexport interface ProvisioningId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceProvisioning\";\n}\n\nexport interface SubjectProvisioningInput<Registration> {\n readonly requestId: string;\n readonly identifier: LoginIdentifier;\n readonly registration: Registration;\n}\n\n/** Return application subject values for insertion by the auth commit owner.\n * Include the allocated native ID, active status, and initial security revision.\n * Preparing values may allocate IDs, but must not write to the database or perform\n * external side effects. Interactive drivers also accept an existing insert callback.\n */\nexport type SubjectProvisioning<Registration> =\n | {\n readonly values: (\n input: SubjectProvisioningInput<Registration>,\n ) => Effect.Effect<Readonly<Record<string, unknown>>, PasswordUnavailable>;\n }\n | ((\n input: SubjectProvisioningInput<Registration>,\n ) => Effect.Effect<SubjectId, PasswordUnavailable>);\n\nexport type Provisioning<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> = {\n readonly [\n K in keyof A[\"strategies\"] as A[\"strategies\"][K] extends {\n persistence: { kind: \"password\"; management: true };\n }\n ? K\n : never\n ]: SubjectProvisioning<Registration<A[\"strategies\"][K]>>;\n};\n\nexport type ProvisioningRequirement<\n A extends { readonly namespace: string; readonly strategies: Readonly<Record<string, Strategy>> },\n> = ManagedPassword<A> extends never ? never : ProvisioningId<A[\"namespace\"]>;\n\ntype ProofRoles = \"proofs\";\n\ntype UsesProofs<A extends { readonly strategies: Readonly<Record<string, Strategy>> }> =\n | Enabled<A, \"phone\">\n | Enabled<A, \"email\">\n | ManagedPassword<A>;\n\nexport type Ports<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | AuthenticationAuthority\n | KeyId<A[\"sessions\"][\"StatefulSessionPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionRepository\"]>\n | KeyId<A[\"sessions\"][\"PendingAuthentication\"]>\n | KeyId<A[\"sessions\"][\"SessionStepUpPersistence\"]>\n | KeyId<A[\"sessions\"][\"SessionCleanup\"]>\n | (Enabled<A, \"password\"> extends never ? never : PasswordPersistence)\n | KeyId<RegistrationKey<ManagedPassword<A>>>\n | (Enabled<A, \"passkey\"> extends never ? never : PasskeyPersistence | PasskeyCredentials)\n | (ManagedPasskey<A> extends never ? never : PasskeyManagementPersistence)\n | (Enabled<A, \"email\"> extends never ? never : EmailAddressPersistence)\n | (UsesProofs<A> extends never ? never : ProofPersistence)\n | (Enabled<A, \"phone\"> extends never ? never : PhoneSignInTargets);\n\nexport type Roles<C extends ClaimsCodec, Id extends string, A extends Definition<C, Id>> =\n | \"identifiers\"\n | \"credentials\"\n | \"sessions\"\n | \"pending\"\n | (Enabled<A, \"password\"> extends never ? never : \"passwords\")\n | (Enabled<A, \"email\"> extends never ? never : \"emailCredentials\")\n | (Enabled<A, \"passkey\"> extends never ? never : \"passkeyCredentials\" | \"passkeyFlows\")\n | (UsesProofs<A> extends never ? never : ProofRoles);\n\nexport interface SubjectOptions<T extends object, NativeId> {\n readonly table: T;\n readonly id: string;\n readonly status: string;\n readonly activeValue: unknown;\n readonly securityRevision: string;\n readonly idCodec: Schema.Codec<SubjectId, NativeId>;\n readonly requirements: (\n row: Readonly<Record<string, unknown>>,\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n /** Defaults to sign-in requirements. Recovery can have a distinct application policy. */\n readonly actionRequirements?: (\n row: Readonly<Record<string, unknown>>,\n action: PasswordAction | EmailAction | PasskeyActionChallenge[\"action\"],\n ) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;\n}\n\n/** Codecs at the application-owned subject and timestamp boundary. */\nexport interface SubjectMapping {\n readonly table: object;\n readonly id: string;\n readonly status: string;\n readonly securityRevision: string;\n readonly activeValue: unknown;\n readonly requirements: SubjectOptions<object, unknown>[\"requirements\"];\n readonly actionRequirements: NonNullable<SubjectOptions<object, unknown>[\"actionRequirements\"]>;\n readonly toSubject: (value: unknown) => Effect.Effect<SubjectId, PersistenceMappingError>;\n readonly toNative: (value: SubjectId) => Effect.Effect<unknown, PersistenceMappingError>;\n readonly toSubjectSync: (value: unknown) => SubjectId;\n readonly toNativeSync: (value: SubjectId) => unknown;\n}\n\nexport interface MappingInput {\n readonly tables: Partial<Record<StorageRole, object>>;\n readonly subjects: SubjectMapping;\n readonly encodeInstant: (millis: number) => unknown;\n readonly decodeInstant: (value: unknown) => Effect.Effect<number, PersistenceMappingError>;\n readonly decodeInstantSync: (value: unknown) => number;\n}\n\nexport interface StorageLayout<\n T extends object,\n Role extends StorageRole = StorageRole,\n> extends MappingInput {\n readonly namespace: string;\n readonly schema: Readonly<Record<Role, T>>;\n}\n\nexport interface ConfigId<Id extends string> {\n readonly authNamespace: Id;\n readonly service: \"PersistenceConfig\";\n}\n\nexport interface TimestampOptions<Instant> {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n}\n\nexport interface BoundPersistence<\n T extends object,\n R,\n C extends ClaimsCodec,\n Id extends string,\n A extends Definition<C, Id>,\n> {\n readonly Config: Context.Service<ConfigId<A[\"namespace\"]>, StorageLayout<T, Roles<C, Id, A>>> & {\n readonly layer: (\n value: StorageLayout<T, Roles<C, Id, A>>,\n ) => Layer.Layer<ConfigId<A[\"namespace\"]>>;\n };\n /** Supply subject values through `{ values }` on every driver, including D1.\n * Interactive drivers also accept callbacks that insert through the owning\n * SqlClient and return the new subject ID. Never open an independent transaction\n * or perform external side effects while preparing a subject.\n */\n readonly Provisioning: Context.Service<ProvisioningRequirement<A>, Provisioning<A>>;\n /** Capture dependencies at acquisition; validate SQL metadata and configure storage\n * once on the first service operation. Initialization failures become that port's\n * unavailable error. Failure or interruption remains cached until layer reacquisition.\n */\n readonly layer: Layer.Layer<\n Ports<C, Id, A>,\n PersistenceConfigurationError,\n | Crypto.Crypto\n | ConfigId<A[\"namespace\"]>\n | ProvisioningRequirement<A>\n | PasskeyRequirement<A>\n | SqlClient.SqlClient\n | R\n >;\n readonly managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n /** Stable SQL identifier prefix; reuse the deployed prefix for existing tables. */\n readonly prefix: string;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n readonly map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Readonly<Record<Roles<C, Id, A>, T>>;\n readonly timestamps?: TimestampOptions<Instant>;\n }) => StorageLayout<T, Roles<C, Id, A>>;\n}\n\n/** Shared configuration shape. The backend chooses table types and service requirements. */\nexport interface PersistenceApi<T extends object, R = never> {\n readonly make: <\n C extends ClaimsCodec,\n const Id extends string,\n const A extends Definition<C, Id>,\n >(\n auth: A & Definition<C, Id>,\n ) => BoundPersistence<T, R, C, Id, A>;\n}\n"],"mappings":";;AA+BA,IAAa,gCAAb,cAAmD,OAAO,YAA2C,CAAC,CACpG,iCACA,EAAE,QAAQ,OAAO,OAAO,CAC1B,CAAC,CAAC,CAAC"}
|
|
@@ -15,7 +15,8 @@ export interface PasswordSubjectTable<Subject extends TableModel, _NativeSubject
|
|
|
15
15
|
readonly securityRevision: ColumnKey<Subject>;
|
|
16
16
|
readonly isActiveStatus: (value: unknown) => boolean;
|
|
17
17
|
readonly decodeRequirement: (row: Subject["select"]) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;
|
|
18
|
-
/** Current policy for this exact credential mutation
|
|
18
|
+
/** Current policy for this exact credential mutation. Interactive drivers hold
|
|
19
|
+
* the subject lock; D1 guards the physical application columns through commit. */
|
|
19
20
|
readonly decodeActionRequirement: (row: Subject["select"], action: PasswordAction) => Effect.Effect<AuthenticationRequirement, PersistenceMappingError>;
|
|
20
21
|
readonly nextSecurityRevision?: (current: SecurityRevision) => Effect.Effect<SecurityRevision, PersistenceMappingError>;
|
|
21
22
|
readonly nextSecurityRevisionSync?: (current: SecurityRevision) => SecurityRevision;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-model.mjs","names":[],"sources":["../../../src/internal/models/password-model.ts"],"sourcesContent":["import { type LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasswordAction,\n PasswordCredentialSnapshot,\n PasswordReplacement,\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 \"../table-model\";\nimport type { PersistenceMappingError, SubjectIdCodec } from \"./common\";\nimport type { ProofClock } from \"./proof-model\";\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 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}\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};\n\nexport interface PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> {\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 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 /** All semantic replacements still bump subject revision. This flag declares\n * authoritative verification's invalidation latency; cookie exposure is separate. */\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n readonly clock: ProofClock<Expression>;\n readonly d1?: { readonly primary: true };\n}\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 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 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 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 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 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 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 readonly mode: \"atomic\";\n readonly provisioning: PasswordRegistrationProvisioning<Registration, Subject, NativeSubjectId> &\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\nexport type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> =\n PasswordPersistenceMapping<\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 unknown,\n Expression\n>;\n"],"mappings":";;AA6HA,MAAa,8BAA2D;CACtE,YAAY;CACZ,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;AAChB;AAmEA,MAAa,0CAAmF;CAC9F,YAAY;CACZ,mBAAmB;CACnB,qBAAqB;AACvB"}
|
|
1
|
+
{"version":3,"file":"password-model.mjs","names":[],"sources":["../../../src/internal/models/password-model.ts"],"sourcesContent":["import { type LoginIdentifier } from \"@yielded/auth/Identity\";\nimport type {\n PasswordAction,\n PasswordCredentialSnapshot,\n PasswordReplacement,\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 \"../table-model\";\nimport type { PersistenceMappingError, SubjectIdCodec } from \"./common\";\nimport type { ProofClock } from \"./proof-model\";\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. Interactive drivers hold\n * the subject lock; D1 guards the physical application columns through commit. */\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 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}\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};\n\nexport interface PasswordPersistenceMapping<\n Subject extends Table,\n Identifier extends Table,\n Credential extends Table,\n AuthorityCredential extends Table,\n NativeSubjectId,\n Expression extends SqlExpression = SqlExpression,\n> {\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 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 /** All semantic replacements still bump subject revision. This flag declares\n * authoritative verification's invalidation latency; cookie exposure is separate. */\n readonly sessionInvalidation: \"same-authority-immediate\" | \"original-absolute-expiry\";\n readonly clock: ProofClock<Expression>;\n readonly d1?: { readonly primary: true };\n}\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 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 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 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 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 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 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 readonly mode: \"atomic\";\n readonly provisioning: PasswordRegistrationProvisioning<Registration, Subject, NativeSubjectId> &\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\nexport type AnyPasswordPersistenceMapping<Expression extends SqlExpression = SqlExpression> =\n PasswordPersistenceMapping<\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 unknown,\n Expression\n>;\n"],"mappings":";;AA8HA,MAAa,8BAA2D;CACtE,YAAY;CACZ,qBAAqB;CACrB,mBAAmB;CACnB,cAAc;AAChB;AAmEA,MAAa,0CAAmF;CAC9F,YAAY;CACZ,mBAAmB;CACnB,qBAAqB;AACvB"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PersistenceConfigurationError } from "./configuration.mjs";
|
|
2
2
|
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
3
3
|
import { anySqlCondition } from "./sql-change.mjs";
|
|
4
|
+
import { SqlBatchCommit } from "./sql-commit.mjs";
|
|
4
5
|
import { requiredPasskeyCredentialConstraints, requiredPasskeyPersistenceConstraints } from "./models/passkey-model.mjs";
|
|
5
|
-
import { makeNativePasskeyServices } from "./passkey-native.mjs";
|
|
6
|
+
import { makeBatchPasskeyServices, makeNativePasskeyServices } from "./passkey-native.mjs";
|
|
6
7
|
import { makePasskeyNativeManagement } from "./passkey-native-write.mjs";
|
|
7
8
|
import { storageTables } from "./storage-tables.mjs";
|
|
8
9
|
import { Context, Effect, Schema } from "effect";
|
|
@@ -177,6 +178,7 @@ const mappings = Effect.fnUntraced(function* (storage, tables, passwordModules,
|
|
|
177
178
|
});
|
|
178
179
|
/** Managed and mapped tables feed the same native passkey statements. */
|
|
179
180
|
const makeManagedPasskeys = Effect.fnUntraced(function* (input, tables) {
|
|
181
|
+
const batch = yield* SqlBatchCommit;
|
|
180
182
|
const policies = yield* Effect.forEach(input.features, (feature) => Effect.map(feature.policy, (policy) => ({
|
|
181
183
|
feature,
|
|
182
184
|
policy
|
|
@@ -186,7 +188,7 @@ const makeManagedPasskeys = Effect.fnUntraced(function* (input, tables) {
|
|
|
186
188
|
const managers = /* @__PURE__ */ new Map();
|
|
187
189
|
for (const feature of input.features) {
|
|
188
190
|
const selected = feature.management ? mapping.management(feature) : mapping.base(feature);
|
|
189
|
-
const base = yield* makeNativePasskeyServices(tables, selected);
|
|
191
|
+
const base = yield* batch === void 0 ? makeNativePasskeyServices(tables, selected) : makeBatchPasskeyServices(tables, selected);
|
|
190
192
|
services.set(feature.moduleId, base);
|
|
191
193
|
if (feature.management) managers.set(feature.moduleId, makePasskeyNativeManagement(base, mapping.management(feature)).passkeyManagementPersistence);
|
|
192
194
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkey-managed.mjs","names":[],"sources":["../../src/internal/passkey-managed.ts"],"sourcesContent":["import * as M from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport {\n PersistenceConfigurationError,\n type MappingInput,\n type PasskeyFeature,\n} from \"./configuration\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"./models/passkey-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeNativePasskeyServices } from \"./passkey-native\";\nimport type { PasskeyNativeMapping, PasskeyNativeRead } from \"./passkey-native-state\";\nimport {\n makePasskeyNativeManagement,\n type NativePasskeyManagementMapping,\n} from \"./passkey-native-write\";\nimport { anySqlCondition } from \"./sql-change\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nexport interface ComposedPasskeyInput {\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n}\n\nconst profileJson = Schema.fromJsonString(M.PasskeyProfile);\n\nconst credentialData = M.PasskeyCredential.mapFields(\n ({ revision: _revision, active: _active, requirement: _requirement, ...fields }) => fields,\n);\n\nconst configurationError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappings = Effect.fnUntraced(function* (\n storage: MappingInput,\n tables: NativeSqlTables,\n passwordModules: ReadonlyArray<string>,\n signInProfiles: ReadonlyArray<M.PasskeyProfile>,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const table = (role: StorageRole) => {\n const value = storage.tables[role];\n\n if (value === undefined) throw configurationError(`Missing ${role} mapping`);\n\n return value;\n };\n\n const mapped = <Role extends StorageRole>(role: Role) => ({\n table: table(role),\n // Managed storage validation owns these physical column keys.\n ...(Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])) as {\n readonly [Key in keyof (typeof storageTables)[Role][\"columns\"]]: Key;\n }),\n });\n\n const t = (role: StorageRole) => tables(table(role));\n\n const eq = (role: StorageRole, key: string, value: unknown) =>\n sql`${t(role).column(key)} = ${t(role).value(key, value)}`;\n\n const s = storage.subjects;\n const subject = tables(s.table);\n const active = (value: unknown) => value === true;\n\n const read: PasskeyNativeRead = {\n subject: {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n decodeId: (row) => row[s.id],\n decodeRequirement: s.requirements,\n isActiveStatus: (value) => Object.is(value, s.activeValue),\n activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,\n },\n credential: {\n ...mapped(\"passkeyCredentials\"),\n status: \"active\",\n decode: (row) =>\n Schema.decodeUnknownSync(credentialData)({\n ...row,\n profile: Schema.decodeUnknownSync(profileJson)(row.profile),\n }),\n decodeSubjectId: (row) => row.subjectId,\n isActiveStatus: active,\n activeCondition: eq(\"passkeyCredentials\", \"active\", true),\n },\n authority: {\n ...mapped(\"credentials\"),\n status: \"active\",\n isActiveStatus: active,\n activeCondition: eq(\"credentials\", \"active\", true),\n },\n subjectIds: { toNative: s.toNativeSync, toSubject: s.toSubjectSync, equals: Object.is },\n constraints: requiredPasskeyCredentialConstraints,\n };\n\n const base = (feature: PasskeyFeature): PasskeyNativeMapping => ({\n moduleId: feature.moduleId,\n read,\n flow: { ...mapped(\"passkeyFlows\"), encodeInsert: () => ({}) },\n clock: {\n encodeInstant: storage.encodeInstant,\n decodeInstant: storage.decodeInstantSync,\n engineNowMillis: sql.onDialectOrElse({\n pg: () => sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)`,\n orElse: () => sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`,\n }),\n toMillis: (expression: Fragment) => expression,\n fromMillis: (expression: Fragment) => expression,\n },\n telemetry: {\n lastUsedAt: \"lastUsedAt\",\n encodeBackupEligible: (value) => value,\n encodeBackupState: (value) => value,\n },\n constraints: requiredPasskeyPersistenceConstraints,\n });\n\n const metadata = (nativeId: unknown) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} and ${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)})`;\n\n const management = (feature: PasskeyFeature): NativePasskeyManagementMapping => {\n const policy = feature.managementPolicy;\n\n if (policy === undefined)\n throw configurationError(`Missing passkey management policy for ${feature.moduleId}`);\n\n const c = t(\"passkeyCredentials\"),\n f = t(\"credentials\");\n\n return {\n ...base(feature),\n write: {\n credential: {\n name: \"name\",\n createdAt: \"createdAt\",\n encodeInsert: ({ credential, subjectId, summary, marker }) => ({\n credentialId: credential.credentialId,\n subjectId,\n rpId: credential.rpId,\n protocolCredentialId: credential.protocolCredentialId,\n userHandle: credential.userHandle,\n credentialKey: \"\",\n publicKey: credential.publicKey,\n algorithm: credential.algorithm,\n profile: Schema.encodeSync(profileJson)(credential.profile),\n credentialRevision: marker,\n active: true,\n primarySignIn: credential.primarySignIn,\n enrollmentUserVerified: credential.enrollmentUserVerified,\n backupEligible: credential.backupEligible,\n backupState: credential.backupState,\n counter: credential.counter,\n name: summary.name,\n createdAt: storage.encodeInstant(summary.createdAtMillis),\n }),\n encodePrimarySignIn: (value) => value,\n encodeEnrollmentUserVerified: (value) => value,\n encodeBackupEligible: (value) => value,\n activeStatus: true,\n removedStatus: false,\n },\n authority: {\n encodeInsert: ({ subjectId, credential, marker }) => ({\n subjectId,\n credentialId: credential.credentialId,\n revision: marker,\n active: true,\n }),\n activeStatus: true,\n removedStatus: false,\n },\n policy: {\n subjectColumns: subject.keys,\n management: () => policy,\n requirement: s.actionRequirements,\n metadata,\n action: metadata,\n remainingSignIn: (nativeId, excluded, row) =>\n Effect.gen(function* () {\n const requirement = yield* Schema.decodeEffect(M.PasskeyRequirement)(\n yield* s.requirements(row),\n );\n\n const alternatives: Fragment[] = [];\n\n if (\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n alternative.factors.every((factor) => factor === \"possession\"),\n )\n ) {\n alternatives.push(sql`exists(select 1 from ${c.name} join ${f.name} on ${f.column(\"credentialId\")} = ${c.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${c.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n where ${eq(\"passkeyCredentials\", \"subjectId\", nativeId)} and ${c.column(\"credentialId\")} <> ${c.value(\"credentialId\", excluded)} and ${eq(\"passkeyCredentials\", \"active\", true)} and ${eq(\"passkeyCredentials\", \"primarySignIn\", true)} and ${eq(\"passkeyCredentials\", \"enrollmentUserVerified\", true)} and ${anySqlCondition(\n signInProfiles.map((profile) => eq(\"passkeyCredentials\", \"rpId\", profile.rpId)),\n )})`);\n }\n if (\n passwordModules.length > 0 &&\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n !alternative.userVerified &&\n !alternative.phishingResistant &&\n alternative.factors.every((factor) => factor === \"knowledge\"),\n )\n ) {\n const p = t(\"passwords\"),\n i = t(\"identifiers\");\n\n alternatives.push(sql`exists(select 1 from ${p.name} join ${f.name} on ${f.column(\"credentialId\")} = ${p.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${p.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n join ${i.name} on ${eq(\"identifiers\", \"subjectId\", nativeId)} and ${eq(\"identifiers\", \"active\", true)} and ${eq(\"identifiers\", \"namespace\", \"email\")}\n where ${eq(\"passwords\", \"subjectId\", nativeId)} and ${p.column(\"credentialId\")} <> ${p.value(\"credentialId\", excluded)} and ${anySqlCondition(\n passwordModules.map((module) => eq(\"passwords\", \"moduleId\", module)),\n )})`);\n }\n\n return anySqlCondition(alternatives);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause }),\n ),\n ),\n },\n },\n invalidation: {\n window: {\n trigger: \"credential-change\",\n existingSessions: \"immediate\",\n maximumExposureMillis: 0,\n oldAuthenticationEvidence: \"rejected\",\n },\n mutations: [],\n postcondition: ({ subjectId, securityRevision }) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, subjectId)} and ${subject.column(s.securityRevision)} = ${subject.value(s.securityRevision, securityRevision)})`,\n },\n };\n };\n\n return { base, management };\n});\n\n/** Managed and mapped tables feed the same native passkey statements. */\nexport const makeManagedPasskeys = Effect.fnUntraced(\n function* (input: ComposedPasskeyInput, tables: NativeSqlTables) {\n const policies = yield* Effect.forEach(input.features, (feature) =>\n Effect.map(feature.policy, (policy) => ({ feature, policy })),\n );\n\n const mapping = yield* mappings(\n input.storage,\n tables,\n input.passwordModules,\n policies.flatMap(({ feature, policy }) => (feature.management ? [] : policy.profiles)),\n );\n\n const services = new Map<\n string,\n Effect.Success<ReturnType<typeof makeNativePasskeyServices>>\n >();\n\n const managers = new Map<string, M.PasskeyManagementPersistence[\"Service\"]>();\n\n for (const feature of input.features) {\n const selected = feature.management ? mapping.management(feature) : mapping.base(feature);\n const base = yield* makeNativePasskeyServices(tables, selected);\n\n services.set(feature.moduleId, base);\n if (feature.management)\n managers.set(\n feature.moduleId,\n makePasskeyNativeManagement(base, mapping.management(feature))\n .passkeyManagementPersistence,\n );\n }\n\n const service = (moduleId: string) => {\n const value = services.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const manager = (moduleId: string) => {\n const value = managers.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const first = services.values().next().value;\n\n if (first === undefined) return yield* configurationError(\"A passkey module is required\");\n\n let context: Context.Context<never> = Context.make(M.PasskeyCredentials, {\n lookup: (input) => first.passkeyCredentials.lookup(input),\n listForSubject: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyCredentials.listForSubject(input),\n ),\n }).pipe(\n Context.add(M.PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(service(input.ceremony.moduleId), (value) =>\n value.passkeyPersistence.issue(input, prepare),\n ),\n context: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.context(input),\n ),\n consume: (input, prepare) =>\n Effect.flatMap(service(input.access.moduleId), (value) =>\n value.passkeyPersistence.consume(input, prepare),\n ),\n cleanup: (input, prepare) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.cleanup(input, prepare),\n ),\n }),\n );\n\n if (managers.size > 0)\n context = context.pipe(\n Context.add(M.PasskeyManagementPersistence, {\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager(input.ceremony.moduleId), (value) =>\n value.completeEnrollment(input, prepare),\n ),\n list: (input) => Effect.flatMap(manager(input.moduleId), (value) => value.list(input)),\n inspectRemove: (input) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.inspectRemove(input)),\n rename: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.rename(input, prepare)),\n remove: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.remove(input, prepare)),\n }),\n );\n\n return context;\n },\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : configurationError(\"Unable to configure passkey persistence\"),\n ),\n);\n"],"mappings":";;;;;;;;;;;AAiCA,MAAM,cAAc,OAAO,eAAe,EAAE,cAAc;AAE1D,MAAM,iBAAiB,EAAE,kBAAkB,WACxC,EAAE,UAAU,WAAW,QAAQ,SAAS,aAAa,cAAc,GAAG,aAAa,MACtF;AAEA,MAAM,sBAAsB,WAAmB,8BAA8B,KAAK,EAAE,OAAO,CAAC;AAE5F,MAAM,WAAW,OAAO,WAAW,WACjC,SACA,QACA,iBACA,gBACA;CACA,MAAM,OAAO,OAAO,UAAU,UAAA,CAAW,kBAAkB;CAE3D,MAAM,SAAS,SAAsB;EACnC,MAAM,QAAQ,QAAQ,OAAO;EAE7B,IAAI,UAAU,KAAA,GAAW,MAAM,mBAAmB,WAAW,KAAK,SAAS;EAE3E,OAAO;CACT;CAEA,MAAM,UAAoC,UAAgB;EACxD,OAAO,MAAM,IAAI;EAEjB,GAAI,OAAO,YAAY,OAAO,KAAK,cAAc,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;CAG1F;CAEA,MAAM,KAAK,SAAsB,OAAO,MAAM,IAAI,CAAC;CAEnD,MAAM,MAAM,MAAmB,KAAa,UAC1C,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK;CAEzD,MAAM,IAAI,QAAQ;CAClB,MAAM,UAAU,OAAO,EAAE,KAAK;CAC9B,MAAM,UAAU,UAAmB,UAAU;CAE7C,MAAM,OAA0B;EAC9B,SAAS;GACP,OAAO,EAAE;GACT,IAAI,EAAE;GACN,QAAQ,EAAE;GACV,kBAAkB,EAAE;GACpB,WAAW,QAAQ,IAAI,EAAE;GACzB,mBAAmB,EAAE;GACrB,iBAAiB,UAAU,OAAO,GAAG,OAAO,EAAE,WAAW;GACzD,iBAAiB,GAAG,GAAG,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,QAAQ,EAAE,WAAW;EAC5F;EACA,YAAY;GACV,GAAG,OAAO,oBAAoB;GAC9B,QAAQ;GACR,SAAS,QACP,OAAO,kBAAkB,cAAc,CAAC,CAAC;IACvC,GAAG;IACH,SAAS,OAAO,kBAAkB,WAAW,CAAC,CAAC,IAAI,OAAO;GAC5D,CAAC;GACH,kBAAkB,QAAQ,IAAI;GAC9B,gBAAgB;GAChB,iBAAiB,GAAG,sBAAsB,UAAU,IAAI;EAC1D;EACA,WAAW;GACT,GAAG,OAAO,aAAa;GACvB,QAAQ;GACR,gBAAgB;GAChB,iBAAiB,GAAG,eAAe,UAAU,IAAI;EACnD;EACA,YAAY;GAAE,UAAU,EAAE;GAAc,WAAW,EAAE;GAAe,QAAQ,OAAO;EAAG;EACtF,aAAa;CACf;CAEA,MAAM,QAAQ,aAAmD;EAC/D,UAAU,QAAQ;EAClB;EACA,MAAM;GAAE,GAAG,OAAO,cAAc;GAAG,qBAAqB,CAAC;EAAG;EAC5D,OAAO;GACL,eAAe,QAAQ;GACvB,eAAe,QAAQ;GACvB,iBAAiB,IAAI,gBAAgB;IACnC,UAAU,GAAG;IACb,cAAc,GAAG;GACnB,CAAC;GACD,WAAW,eAAyB;GACpC,aAAa,eAAyB;EACxC;EACA,WAAW;GACT,YAAY;GACZ,uBAAuB,UAAU;GACjC,oBAAoB,UAAU;EAChC;EACA,aAAa;CACf;CAEA,MAAM,YAAY,aAChB,GAAG,wBAAwB,QAAQ,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,MAAM,EAAE,IAAI,QAAQ,EAAE,OAAO,QAAQ,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE;CAEvL,MAAM,cAAc,YAA4D;EAC9E,MAAM,SAAS,QAAQ;EAEvB,IAAI,WAAW,KAAA,GACb,MAAM,mBAAmB,yCAAyC,QAAQ,UAAU;EAEtF,MAAM,IAAI,EAAE,oBAAoB,GAC9B,IAAI,EAAE,aAAa;EAErB,OAAO;GACL,GAAG,KAAK,OAAO;GACf,OAAO;IACL,YAAY;KACV,MAAM;KACN,WAAW;KACX,eAAe,EAAE,YAAY,WAAW,SAAS,cAAc;MAC7D,cAAc,WAAW;MACzB;MACA,MAAM,WAAW;MACjB,sBAAsB,WAAW;MACjC,YAAY,WAAW;MACvB,eAAe;MACf,WAAW,WAAW;MACtB,WAAW,WAAW;MACtB,SAAS,OAAO,WAAW,WAAW,CAAC,CAAC,WAAW,OAAO;MAC1D,oBAAoB;MACpB,QAAQ;MACR,eAAe,WAAW;MAC1B,wBAAwB,WAAW;MACnC,gBAAgB,WAAW;MAC3B,aAAa,WAAW;MACxB,SAAS,WAAW;MACpB,MAAM,QAAQ;MACd,WAAW,QAAQ,cAAc,QAAQ,eAAe;KAC1D;KACA,sBAAsB,UAAU;KAChC,+BAA+B,UAAU;KACzC,uBAAuB,UAAU;KACjC,cAAc;KACd,eAAe;IACjB;IACA,WAAW;KACT,eAAe,EAAE,WAAW,YAAY,cAAc;MACpD;MACA,cAAc,WAAW;MACzB,UAAU;MACV,QAAQ;KACV;KACA,cAAc;KACd,eAAe;IACjB;IACA,QAAQ;KACN,gBAAgB,QAAQ;KACxB,kBAAkB;KAClB,aAAa,EAAE;KACf;KACA,QAAQ;KACR,kBAAkB,UAAU,UAAU,QACpC,OAAO,IAAI,aAAa;MACtB,MAAM,cAAc,OAAO,OAAO,aAAa,EAAE,kBAAkB,CAAC,CAClE,OAAO,EAAE,aAAa,GAAG,CAC3B;MAEA,MAAM,eAA2B,CAAC;MAElC,IACE,YAAY,aAAa,MACtB,gBACC,YAAY,uBAAuB,KACnC,YAAY,QAAQ,OAAO,WAAW,WAAW,YAAY,CACjE,GAEA,aAAa,KAAK,GAAG,wBAAwB,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,EAAE,KAAK,EAAE,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE;wBAClS,GAAG,sBAAsB,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,QAAQ,EAAE,OAAO,GAAG,sBAAsB,UAAU,IAAI,EAAE,OAAO,GAAG,sBAAsB,iBAAiB,IAAI,EAAE,OAAO,GAAG,sBAAsB,0BAA0B,IAAI,EAAE,OAAO,gBAC5S,eAAe,KAAK,YAAY,GAAG,sBAAsB,QAAQ,QAAQ,IAAI,CAAC,CAChF,EAAE,EAAE;MAEN,IACE,gBAAgB,SAAS,KACzB,YAAY,aAAa,MACtB,gBACC,YAAY,uBAAuB,KACnC,CAAC,YAAY,gBACb,CAAC,YAAY,qBACb,YAAY,QAAQ,OAAO,WAAW,WAAW,WAAW,CAChE,GACA;OACA,MAAM,IAAI,EAAE,WAAW,GACrB,IAAI,EAAE,aAAa;OAErB,aAAa,KAAK,GAAG,wBAAwB,EAAE,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,OAAO,cAAc,EAAE,KAAK,EAAE,OAAO,cAAc,EAAE,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,UAAU,EAAE,KAAK,EAAE,OAAO,oBAAoB,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE;uBACnS,EAAE,KAAK,MAAM,GAAG,eAAe,aAAa,QAAQ,EAAE,OAAO,GAAG,eAAe,UAAU,IAAI,EAAE,OAAO,GAAG,eAAe,aAAa,OAAO,EAAE;wBAC7I,GAAG,aAAa,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,cAAc,EAAE,MAAM,EAAE,MAAM,gBAAgB,QAAQ,EAAE,OAAO,gBAC5H,gBAAgB,KAAK,WAAW,GAAG,aAAa,YAAY,MAAM,CAAC,CACrE,EAAE,EAAE;MACN;MAEA,OAAO,gBAAgB,YAAY;KACrC,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UACf,wBAAwB,KAAK;MAAE,WAAW;MAAW;KAAM,CAAC,CAC9D,CACF;IACJ;GACF;GACA,cAAc;IACZ,QAAQ;KACN,SAAS;KACT,kBAAkB;KAClB,uBAAuB;KACvB,2BAA2B;IAC7B;IACA,WAAW,CAAC;IACZ,gBAAgB,EAAE,WAAW,uBAC3B,GAAG,wBAAwB,QAAQ,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE,EAAE,KAAK,QAAQ,MAAM,EAAE,IAAI,SAAS,EAAE,OAAO,QAAQ,OAAO,EAAE,gBAAgB,EAAE,KAAK,QAAQ,MAAM,EAAE,kBAAkB,gBAAgB,EAAE;GACjN;EACF;CACF;CAEA,OAAO;EAAE;EAAM;CAAW;AAC5B,CAAC;;AAGD,MAAa,sBAAsB,OAAO,WACxC,WAAW,OAA6B,QAAyB;CAC/D,MAAM,WAAW,OAAO,OAAO,QAAQ,MAAM,WAAW,YACtD,OAAO,IAAI,QAAQ,SAAS,YAAY;EAAE;EAAS;CAAO,EAAE,CAC9D;CAEA,MAAM,UAAU,OAAO,SACrB,MAAM,SACN,QACA,MAAM,iBACN,SAAS,SAAS,EAAE,SAAS,aAAc,QAAQ,aAAa,CAAC,IAAI,OAAO,QAAS,CACvF;CAEA,MAAM,2BAAW,IAAI,IAGnB;CAEF,MAAM,2BAAW,IAAI,IAAuD;CAE5E,KAAK,MAAM,WAAW,MAAM,UAAU;EACpC,MAAM,WAAW,QAAQ,aAAa,QAAQ,WAAW,OAAO,IAAI,QAAQ,KAAK,OAAO;EACxF,MAAM,OAAO,OAAO,0BAA0B,QAAQ,QAAQ;EAE9D,SAAS,IAAI,QAAQ,UAAU,IAAI;EACnC,IAAI,QAAQ,YACV,SAAS,IACP,QAAQ,UACR,4BAA4B,MAAM,QAAQ,WAAW,OAAO,CAAC,CAAC,CAC3D,4BACL;CACJ;CAEA,MAAM,WAAW,aAAqB;EACpC,MAAM,QAAQ,SAAS,IAAI,QAAQ;EAEnC,OAAO,UAAU,KAAA,IACb,OAAO,KAAK,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACzC,OAAO,QAAQ,KAAK;CAC1B;CAEA,MAAM,WAAW,aAAqB;EACpC,MAAM,QAAQ,SAAS,IAAI,QAAQ;EAEnC,OAAO,UAAU,KAAA,IACb,OAAO,KAAK,EAAE,mBAAmB,KAAK,CAAC,CAAC,CAAC,IACzC,OAAO,QAAQ,KAAK;CAC1B;CAEA,MAAM,QAAQ,SAAS,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;CAEvC,IAAI,UAAU,KAAA,GAAW,OAAO,OAAO,mBAAmB,8BAA8B;CAExF,IAAI,UAAkC,QAAQ,KAAK,EAAE,oBAAoB;EACvE,SAAS,UAAU,MAAM,mBAAmB,OAAO,KAAK;EACxD,iBAAiB,UACf,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,eAAe,KAAK,CAC/C;CACJ,CAAC,CAAC,CAAC,KACD,QAAQ,IAAI,EAAE,oBAAoB;EAChC,QAAQ,OAAO,YACb,OAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,IAAI,UAChD,MAAM,mBAAmB,MAAM,OAAO,OAAO,CAC/C;EACF,UAAU,UACR,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,QAAQ,KAAK,CACxC;EACF,UAAU,OAAO,YACf,OAAO,QAAQ,QAAQ,MAAM,OAAO,QAAQ,IAAI,UAC9C,MAAM,mBAAmB,QAAQ,OAAO,OAAO,CACjD;EACF,UAAU,OAAO,YACf,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UACvC,MAAM,mBAAmB,QAAQ,OAAO,OAAO,CACjD;CACJ,CAAC,CACH;CAEA,IAAI,SAAS,OAAO,GAClB,UAAU,QAAQ,KAChB,QAAQ,IAAI,EAAE,8BAA8B;EAC1C,qBAAqB,OAAO,YAC1B,OAAO,QAAQ,QAAQ,MAAM,SAAS,QAAQ,IAAI,UAChD,MAAM,mBAAmB,OAAO,OAAO,CACzC;EACF,OAAO,UAAU,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,KAAK,KAAK,CAAC;EACrF,gBAAgB,UACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,cAAc,KAAK,CAAC;EAC/E,SAAS,OAAO,YACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,OAAO,OAAO,OAAO,CAAC;EACjF,SAAS,OAAO,YACd,OAAO,QAAQ,QAAQ,MAAM,QAAQ,IAAI,UAAU,MAAM,OAAO,OAAO,OAAO,CAAC;CACnF,CAAC,CACH;CAEF,OAAO;AACT,GACA,OAAO,UAAU,UACf,OAAO,GAAG,6BAA6B,CAAC,CAAC,KAAK,IAC1C,QACA,mBAAmB,yCAAyC,CAClE,CACF"}
|
|
1
|
+
{"version":3,"file":"passkey-managed.mjs","names":[],"sources":["../../src/internal/passkey-managed.ts"],"sourcesContent":["import * as M from \"@yielded/auth/Passkey\";\nimport { Context, Effect, Schema } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport {\n PersistenceConfigurationError,\n type MappingInput,\n type PasskeyFeature,\n} from \"./configuration\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport {\n requiredPasskeyCredentialConstraints,\n requiredPasskeyPersistenceConstraints,\n} from \"./models/passkey-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { makeBatchPasskeyServices, makeNativePasskeyServices } from \"./passkey-native\";\nimport type { PasskeyNativeMapping, PasskeyNativeRead } from \"./passkey-native-state\";\nimport {\n makePasskeyNativeManagement,\n type NativePasskeyManagementMapping,\n} from \"./passkey-native-write\";\nimport { anySqlCondition } from \"./sql-change\";\nimport { SqlBatchCommit } from \"./sql-commit\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\n\nexport interface ComposedPasskeyInput {\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n}\n\nconst profileJson = Schema.fromJsonString(M.PasskeyProfile);\n\nconst credentialData = M.PasskeyCredential.mapFields(\n ({ revision: _revision, active: _active, requirement: _requirement, ...fields }) => fields,\n);\n\nconst configurationError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappings = Effect.fnUntraced(function* (\n storage: MappingInput,\n tables: NativeSqlTables,\n passwordModules: ReadonlyArray<string>,\n signInProfiles: ReadonlyArray<M.PasskeyProfile>,\n) {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const table = (role: StorageRole) => {\n const value = storage.tables[role];\n\n if (value === undefined) throw configurationError(`Missing ${role} mapping`);\n\n return value;\n };\n\n const mapped = <Role extends StorageRole>(role: Role) => ({\n table: table(role),\n // Managed storage validation owns these physical column keys.\n ...(Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])) as {\n readonly [Key in keyof (typeof storageTables)[Role][\"columns\"]]: Key;\n }),\n });\n\n const t = (role: StorageRole) => tables(table(role));\n\n const eq = (role: StorageRole, key: string, value: unknown) =>\n sql`${t(role).column(key)} = ${t(role).value(key, value)}`;\n\n const s = storage.subjects;\n const subject = tables(s.table);\n const active = (value: unknown) => value === true;\n\n const read: PasskeyNativeRead = {\n subject: {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n decodeId: (row) => row[s.id],\n decodeRequirement: s.requirements,\n isActiveStatus: (value) => Object.is(value, s.activeValue),\n activeCondition: sql`${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)}`,\n },\n credential: {\n ...mapped(\"passkeyCredentials\"),\n status: \"active\",\n decode: (row) =>\n Schema.decodeUnknownSync(credentialData)({\n ...row,\n profile: Schema.decodeUnknownSync(profileJson)(row.profile),\n }),\n decodeSubjectId: (row) => row.subjectId,\n isActiveStatus: active,\n activeCondition: eq(\"passkeyCredentials\", \"active\", true),\n },\n authority: {\n ...mapped(\"credentials\"),\n status: \"active\",\n isActiveStatus: active,\n activeCondition: eq(\"credentials\", \"active\", true),\n },\n subjectIds: { toNative: s.toNativeSync, toSubject: s.toSubjectSync, equals: Object.is },\n constraints: requiredPasskeyCredentialConstraints,\n };\n\n const base = (feature: PasskeyFeature): PasskeyNativeMapping => ({\n moduleId: feature.moduleId,\n read,\n flow: { ...mapped(\"passkeyFlows\"), encodeInsert: () => ({}) },\n clock: {\n encodeInstant: storage.encodeInstant,\n decodeInstant: storage.decodeInstantSync,\n engineNowMillis: sql.onDialectOrElse({\n pg: () => sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)`,\n orElse: () => sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`,\n }),\n toMillis: (expression: Fragment) => expression,\n fromMillis: (expression: Fragment) => expression,\n },\n telemetry: {\n lastUsedAt: \"lastUsedAt\",\n encodeBackupEligible: (value) => value,\n encodeBackupState: (value) => value,\n },\n constraints: requiredPasskeyPersistenceConstraints,\n });\n\n const metadata = (nativeId: unknown) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, nativeId)} and ${subject.column(s.status)} = ${subject.value(s.status, s.activeValue)})`;\n\n const management = (feature: PasskeyFeature): NativePasskeyManagementMapping => {\n const policy = feature.managementPolicy;\n\n if (policy === undefined)\n throw configurationError(`Missing passkey management policy for ${feature.moduleId}`);\n\n const c = t(\"passkeyCredentials\"),\n f = t(\"credentials\");\n\n return {\n ...base(feature),\n write: {\n credential: {\n name: \"name\",\n createdAt: \"createdAt\",\n encodeInsert: ({ credential, subjectId, summary, marker }) => ({\n credentialId: credential.credentialId,\n subjectId,\n rpId: credential.rpId,\n protocolCredentialId: credential.protocolCredentialId,\n userHandle: credential.userHandle,\n credentialKey: \"\",\n publicKey: credential.publicKey,\n algorithm: credential.algorithm,\n profile: Schema.encodeSync(profileJson)(credential.profile),\n credentialRevision: marker,\n active: true,\n primarySignIn: credential.primarySignIn,\n enrollmentUserVerified: credential.enrollmentUserVerified,\n backupEligible: credential.backupEligible,\n backupState: credential.backupState,\n counter: credential.counter,\n name: summary.name,\n createdAt: storage.encodeInstant(summary.createdAtMillis),\n }),\n encodePrimarySignIn: (value) => value,\n encodeEnrollmentUserVerified: (value) => value,\n encodeBackupEligible: (value) => value,\n activeStatus: true,\n removedStatus: false,\n },\n authority: {\n encodeInsert: ({ subjectId, credential, marker }) => ({\n subjectId,\n credentialId: credential.credentialId,\n revision: marker,\n active: true,\n }),\n activeStatus: true,\n removedStatus: false,\n },\n policy: {\n subjectColumns: subject.keys,\n management: () => policy,\n requirement: s.actionRequirements,\n metadata,\n action: metadata,\n remainingSignIn: (nativeId, excluded, row) =>\n Effect.gen(function* () {\n const requirement = yield* Schema.decodeEffect(M.PasskeyRequirement)(\n yield* s.requirements(row),\n );\n\n const alternatives: Fragment[] = [];\n\n if (\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n alternative.factors.every((factor) => factor === \"possession\"),\n )\n ) {\n alternatives.push(sql`exists(select 1 from ${c.name} join ${f.name} on ${f.column(\"credentialId\")} = ${c.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${c.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n where ${eq(\"passkeyCredentials\", \"subjectId\", nativeId)} and ${c.column(\"credentialId\")} <> ${c.value(\"credentialId\", excluded)} and ${eq(\"passkeyCredentials\", \"active\", true)} and ${eq(\"passkeyCredentials\", \"primarySignIn\", true)} and ${eq(\"passkeyCredentials\", \"enrollmentUserVerified\", true)} and ${anySqlCondition(\n signInProfiles.map((profile) => eq(\"passkeyCredentials\", \"rpId\", profile.rpId)),\n )})`);\n }\n if (\n passwordModules.length > 0 &&\n requirement.alternatives.some(\n (alternative) =>\n alternative.minimumCredentials === 1 &&\n !alternative.userVerified &&\n !alternative.phishingResistant &&\n alternative.factors.every((factor) => factor === \"knowledge\"),\n )\n ) {\n const p = t(\"passwords\"),\n i = t(\"identifiers\");\n\n alternatives.push(sql`exists(select 1 from ${p.name} join ${f.name} on ${f.column(\"credentialId\")} = ${p.column(\"credentialId\")} and ${f.column(\"subjectId\")} = ${f.value(\"subjectId\", nativeId)} and ${f.column(\"revision\")} = ${p.column(\"credentialRevision\")} and ${eq(\"credentials\", \"active\", true)}\n join ${i.name} on ${eq(\"identifiers\", \"subjectId\", nativeId)} and ${eq(\"identifiers\", \"active\", true)} and ${eq(\"identifiers\", \"namespace\", \"email\")}\n where ${eq(\"passwords\", \"subjectId\", nativeId)} and ${p.column(\"credentialId\")} <> ${p.value(\"credentialId\", excluded)} and ${anySqlCondition(\n passwordModules.map((module) => eq(\"passwords\", \"moduleId\", module)),\n )})`);\n }\n\n return anySqlCondition(alternatives);\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause }),\n ),\n ),\n },\n },\n invalidation: {\n window: {\n trigger: \"credential-change\",\n existingSessions: \"immediate\",\n maximumExposureMillis: 0,\n oldAuthenticationEvidence: \"rejected\",\n },\n mutations: [],\n postcondition: ({ subjectId, securityRevision }) =>\n sql`exists(select 1 from ${subject.name} where ${subject.column(s.id)} = ${subject.value(s.id, subjectId)} and ${subject.column(s.securityRevision)} = ${subject.value(s.securityRevision, securityRevision)})`,\n },\n };\n };\n\n return { base, management };\n});\n\n/** Managed and mapped tables feed the same native passkey statements. */\nexport const makeManagedPasskeys = Effect.fnUntraced(\n function* (input: ComposedPasskeyInput, tables: NativeSqlTables) {\n const batch = yield* SqlBatchCommit;\n\n const policies = yield* Effect.forEach(input.features, (feature) =>\n Effect.map(feature.policy, (policy) => ({ feature, policy })),\n );\n\n const mapping = yield* mappings(\n input.storage,\n tables,\n input.passwordModules,\n policies.flatMap(({ feature, policy }) => (feature.management ? [] : policy.profiles)),\n );\n\n const services = new Map<\n string,\n Effect.Success<ReturnType<typeof makeNativePasskeyServices>>\n >();\n\n const managers = new Map<string, M.PasskeyManagementPersistence[\"Service\"]>();\n\n for (const feature of input.features) {\n const selected = feature.management ? mapping.management(feature) : mapping.base(feature);\n\n const base = yield* batch === undefined\n ? makeNativePasskeyServices(tables, selected)\n : makeBatchPasskeyServices(tables, selected);\n\n services.set(feature.moduleId, base);\n if (feature.management)\n managers.set(\n feature.moduleId,\n makePasskeyNativeManagement(base, mapping.management(feature))\n .passkeyManagementPersistence,\n );\n }\n\n const service = (moduleId: string) => {\n const value = services.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const manager = (moduleId: string) => {\n const value = managers.get(moduleId);\n\n return value === undefined\n ? Effect.fail(M.PasskeyUnavailable.make({}))\n : Effect.succeed(value);\n };\n\n const first = services.values().next().value;\n\n if (first === undefined) return yield* configurationError(\"A passkey module is required\");\n\n let context: Context.Context<never> = Context.make(M.PasskeyCredentials, {\n lookup: (input) => first.passkeyCredentials.lookup(input),\n listForSubject: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyCredentials.listForSubject(input),\n ),\n }).pipe(\n Context.add(M.PasskeyPersistence, {\n issue: (input, prepare) =>\n Effect.flatMap(service(input.ceremony.moduleId), (value) =>\n value.passkeyPersistence.issue(input, prepare),\n ),\n context: (input) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.context(input),\n ),\n consume: (input, prepare) =>\n Effect.flatMap(service(input.access.moduleId), (value) =>\n value.passkeyPersistence.consume(input, prepare),\n ),\n cleanup: (input, prepare) =>\n Effect.flatMap(service(input.moduleId), (value) =>\n value.passkeyPersistence.cleanup(input, prepare),\n ),\n }),\n );\n\n if (managers.size > 0)\n context = context.pipe(\n Context.add(M.PasskeyManagementPersistence, {\n completeEnrollment: (input, prepare) =>\n Effect.flatMap(manager(input.ceremony.moduleId), (value) =>\n value.completeEnrollment(input, prepare),\n ),\n list: (input) => Effect.flatMap(manager(input.moduleId), (value) => value.list(input)),\n inspectRemove: (input) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.inspectRemove(input)),\n rename: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.rename(input, prepare)),\n remove: (input, prepare) =>\n Effect.flatMap(manager(input.moduleId), (value) => value.remove(input, prepare)),\n }),\n );\n\n return context;\n },\n Effect.mapError((error) =>\n Schema.is(PersistenceConfigurationError)(error)\n ? error\n : configurationError(\"Unable to configure passkey persistence\"),\n ),\n);\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
2
|
+
import { nativeSqlAlias } from "./native-sql-table.mjs";
|
|
2
3
|
import { sqlBatchAssertion } from "./d1-planning.mjs";
|
|
3
4
|
import { exactSqlText, executeSqlChange } from "./sql-change.mjs";
|
|
4
5
|
import { allocatePasswordNextSecurityRevision, allocatePasswordValue, passwordEvidenceSatisfiedAt, passwordProofRedemptionMatches, snapshotPasswordMutation, validatePasswordMutation } from "./password-policy.mjs";
|
|
@@ -6,7 +7,9 @@ import { requireStandalone } from "./standalone.mjs";
|
|
|
6
7
|
import { CurrentSqlCommit, SqlBatchCommit, appendSqlBatchStatement, makeSqlCommitExecutor, registerSqlBatchPostcondition, registerSqlCommitReceipt, registerSqlPostcondition } from "./sql-commit.mjs";
|
|
7
8
|
import { makeNativeProofStore } from "./proof-native.mjs";
|
|
8
9
|
import { makePasswordCredentials, samePasswordCredentialSnapshot } from "./password-credentials.mjs";
|
|
10
|
+
import { guardSqlitePolicy } from "./sqlite-policy-guard.mjs";
|
|
9
11
|
import { Crypto, DateTime, Effect, Option, Redacted, Schema } from "effect";
|
|
12
|
+
import { fragment, parameter } from "effect/sql/Statement";
|
|
10
13
|
import { CurrentCommitJournal } from "@yielded/auth/Hooks";
|
|
11
14
|
import * as P from "@yielded/auth/Password";
|
|
12
15
|
import { ProofRedemptionInput } from "@yielded/auth/Proofs";
|
|
@@ -29,6 +32,7 @@ const makeNativePasswordServices = Effect.fnUntraced(function* (tables, mapping,
|
|
|
29
32
|
const credential = tables(c.table).as("password_credential");
|
|
30
33
|
const authority = tables(a.table).as("password_authority");
|
|
31
34
|
const rawSubject = tables(s.table), rawPassword = tables(c.table), rawAuthority = tables(a.table);
|
|
35
|
+
const policyColumns = rawSubject.keys.filter((key) => key !== s.id && key !== s.status && key !== s.securityRevision);
|
|
32
36
|
const now = tables.expression(mapping.clock.engineNowMillis);
|
|
33
37
|
const lock = sql.onDialectOrElse({
|
|
34
38
|
sqlite: () => sql``,
|
|
@@ -94,6 +98,10 @@ const makeNativePasswordServices = Effect.fnUntraced(function* (tables, mapping,
|
|
|
94
98
|
return {
|
|
95
99
|
nativeId,
|
|
96
100
|
subjectRow,
|
|
101
|
+
policyValues: batch === void 0 ? [] : policyColumns.map((key) => ({
|
|
102
|
+
column: rawSubject.selectedColumn(key),
|
|
103
|
+
value: fragment([parameter(rows[0][nativeSqlAlias("password_subject_", rawSubject.keys.indexOf(key))])])
|
|
104
|
+
})),
|
|
97
105
|
identifierRow,
|
|
98
106
|
passwordRow,
|
|
99
107
|
revision,
|
|
@@ -159,6 +167,12 @@ const makeNativePasswordServices = Effect.fnUntraced(function* (tables, mapping,
|
|
|
159
167
|
const freshUntil = (requirement) => {
|
|
160
168
|
return [...new Set(input.authorization.evidence.proofs.map((proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis))].filter((time) => time > state.now).sort((a, b) => a - b).find((time) => !passwordEvidenceSatisfiedAt(input.authorization.evidence, requirement, time));
|
|
161
169
|
};
|
|
170
|
+
if (batch !== void 0) yield* guardSqlitePolicy({
|
|
171
|
+
name: "password-subject-policy",
|
|
172
|
+
table: rawSubject.name,
|
|
173
|
+
owner: subjectKey(rawSubject, s.id, nativeId),
|
|
174
|
+
values: state.policyValues
|
|
175
|
+
});
|
|
162
176
|
const requestedDeadline = freshUntil(input.authorization.requirement), currentDeadline = freshUntil(requirement);
|
|
163
177
|
ensure(requestedDeadline !== void 0 && currentDeadline !== void 0);
|
|
164
178
|
const deadline = Math.min(requestedDeadline, currentDeadline);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-native.mjs","names":[],"sources":["../../src/internal/password-native.ts"],"sourcesContent":["import { CurrentCommitJournal, type LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport * as P from \"@yielded/auth/Password\";\nimport { ProofRedemptionInput } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport { SecurityRevision, type AuthenticationRequirement } from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option, Redacted, Schema, Crypto } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyPasswordPersistenceMapping } from \"./models/password-model\";\nimport type { AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { NativeSqlTables, SqlTable } from \"./native-sql-table\";\nimport { makePasswordCredentials, samePasswordCredentialSnapshot } from \"./password-credentials\";\nimport {\n allocatePasswordNextSecurityRevision,\n allocatePasswordValue,\n passwordEvidenceSatisfiedAt,\n passwordProofRedemptionMatches,\n snapshotPasswordMutation,\n validatePasswordMutation,\n} from \"./password-policy\";\nimport { makeNativeProofStore } from \"./proof-native\";\nimport { exactSqlText, executeSqlChange } from \"./sql-change\";\nimport {\n appendSqlBatchStatement,\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n registerSqlBatchPostcondition,\n registerSqlCommitReceipt,\n registerSqlPostcondition,\n SqlBatchCommit,\n} from \"./sql-commit\";\nimport { requireStandalone } from \"./standalone\";\n\nconst unavailable = () => P.PasswordUnavailable.make({});\n\nconst ensure: (condition: unknown) => asserts condition = (condition) => {\n if (!condition) throw unavailable();\n};\n\nexport const makeNativePasswordServices = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: AnyPasswordPersistenceMapping,\n proofs?: AnyProofPersistenceMapping,\n): Effect.fn.Return<\n { readonly passwordPersistence: P.PasswordPersistence[\"Service\"] },\n P.PasswordUnavailable,\n SqlClient | LifecycleHooks | Crypto.Crypto | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n const sql = (yield* SqlClient).withoutTransforms();\n const executor = yield* makeSqlCommitExecutor(unavailable);\n const crypto = yield* Crypto.Crypto;\n const parent = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const s = mapping.subject,\n i = mapping.identifier,\n c = mapping.credential,\n a = mapping.authorityCredential;\n\n const subject = tables(s.table).as(\"password_subject\");\n const identifier = tables(i.table).as(\"password_identifier\");\n const credential = tables(c.table).as(\"password_credential\");\n const authority = tables(a.table).as(\"password_authority\");\n\n const rawSubject = tables(s.table),\n rawPassword = tables(c.table),\n rawAuthority = tables(a.table);\n\n const now = tables.expression(mapping.clock.engineNowMillis);\n const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });\n\n const exact = (table: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, table.column(key), table.value(key, value));\n\n const subjectKey = (table: SqlTable, key: string, nativeId: unknown) =>\n sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const run = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.run(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n : executor\n .batch(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const read = Effect.fnUntraced(function* (\n moduleId: string,\n subjectId: SubjectId,\n wanted?: LoginIdentifier,\n locked?: Record<string, unknown>,\n ) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n // Without one wanted identifier, retired identifiers would multiply authority\n // rows; read authority separately so the result grows linearly.\n const joined = wanted !== undefined;\n\n const rows =\n yield* sql`select ${subject.fields(\"password_subject_\")}, ${identifier.fields(\"password_identifier_\")}, ${credential.fields(\"password_credential_\")}${joined ? sql`, ${authority.fields(\"password_authority_\")}` : sql``}, ${now} as engine_now\n from ${subject.name}\n left join ${identifier.name} on ${subjectKey(identifier, i.subjectId, nativeId)} ${wanted === undefined ? sql`` : sql`and ${exact(identifier, i.namespace, wanted.namespace)} and ${exact(identifier, i.value, wanted.value)}`}\n left join ${credential.name} on ${subjectKey(credential, c.subjectId, nativeId)} and ${exact(credential, c.moduleId, moduleId)}\n ${joined ? sql`left join ${authority.name} on ${subjectKey(authority, a.subjectId, nativeId)}` : sql``}\n where ${subjectKey(subject, s.id, nativeId)} order by ${identifier.column(i.namespace)}, ${identifier.column(i.value)}${joined ? sql`, ${authority.column(a.credentialId)}` : sql``} limit 4097`;\n\n const authorityRows = joined\n ? rows\n : yield* sql`select ${authority.fields(\"password_authority_\")} from ${authority.name} where ${subjectKey(authority, a.subjectId, nativeId)} order by ${authority.column(a.credentialId)} limit 4097`;\n\n ensure(rows.length <= 4096 && authorityRows.length <= 4096);\n if (rows[0] === undefined) return undefined;\n const subjectRow = subject.decode(rows[0], \"password_subject_\");\n const decodedId = yield* mapping.subjectId.toSubject(subjectRow[s.id]);\n\n ensure(\n decodedId === subjectId &&\n (locked === undefined || subjectRow[s.securityRevision] === locked[s.securityRevision]),\n );\n const password = credential.decode(rows[0], \"password_credential_\");\n const passwordRow = password[c.credentialId] === null ? undefined : password;\n\n const factors = new Map<\n string,\n {\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n readonly active: boolean;\n }\n >();\n\n let identifierRow: Record<string, unknown> | undefined;\n\n for (const row of authorityRows) {\n const factor = authority.decode(row, \"password_authority_\");\n\n if (\n factor[a.credentialId] !== null &&\n (a.status === undefined || a.isActiveStatus?.(factor[a.status]) === true)\n ) {\n ensure(mapping.subjectId.equals(factor[a.subjectId], nativeId));\n const id = yield* Schema.decodeUnknownEffect(Schema.String)(factor[a.credentialId]);\n const revision = yield* Schema.decodeUnknownEffect(SecurityRevision)(factor[a.revision]);\n const old = factors.get(id);\n\n ensure(old === undefined || old.revision === revision);\n factors.set(id, { credentialId: id, revision, active: true });\n }\n }\n for (const row of rows) {\n const entry = identifier.decode(row, \"password_identifier_\");\n\n if (\n entry[i.namespace] !== null &&\n mapping.subjectId.equals(entry[i.subjectId], nativeId) &&\n mapping.identifier.isCurrent(entry)\n )\n identifierRow ??= entry;\n }\n ensure(factors.size <= 64);\n\n const revision = {\n subjectId,\n securityRevision: yield* Schema.decodeUnknownEffect(SecurityRevision)(\n subjectRow[s.securityRevision],\n ),\n credentials: [...factors.values()].map(({ credentialId, revision }) => ({\n credentialId,\n revision,\n })),\n };\n\n const snapshot =\n identifierRow === undefined || passwordRow === undefined\n ? undefined\n : yield* mapping.credential\n .decode({\n moduleId,\n subject: subjectRow,\n identifier: identifierRow,\n credential: passwordRow,\n })\n .pipe(Effect.flatMap(P.snapshotPasswordCredential));\n\n if (snapshot !== undefined)\n ensure(\n snapshot.revision.subjectId === subjectId &&\n snapshot.credentialId === passwordRow?.[c.credentialId],\n );\n\n return {\n nativeId,\n subjectRow,\n identifierRow,\n passwordRow,\n revision,\n snapshot: snapshot === undefined ? undefined : { ...snapshot, revision },\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0].engine_now)),\n };\n });\n\n const prepare = <V, A>(value: V, project: P.PreparePasswordCommit<V, A>) =>\n Effect.gen(function* () {\n const journal = yield* CurrentCommitJournal;\n\n const receipt = project(value, journal);\n\n ensure(receipt?._tag === \"PreparedCommit\" && Effect.isEffect(receipt.read));\n yield* registerSqlCommitReceipt(receipt);\n\n return receipt;\n });\n\n const mutation = <A>(\n original: P.PasswordMutationInput,\n action: P.PasswordAction,\n project: P.PreparePasswordCommit<P.PasswordMutationDecision, A>,\n redemption?: Parameters<P.PasswordPersistence[\"Service\"][\"resetWithProof\"]>[0][\"redemption\"],\n ) =>\n run(\n Effect.gen(function* () {\n const input = yield* snapshotPasswordMutation(original);\n const nativeId = yield* mapping.subjectId.toNative(input.expectedRevision.subjectId);\n\n const rows =\n yield* sql`select ${rawSubject.fields(\"password_lock_\")} from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} limit 2 ${batch === undefined ? lock : sql``}`;\n\n if (rows.length === 0) return yield* prepare(\"rejected\" as const, project);\n ensure(rows.length === 1);\n const locked = rawSubject.decode(rows[0]!, \"password_lock_\");\n\n const state = yield* read(\n input.moduleId,\n input.expectedRevision.subjectId,\n input.credential?.identifier,\n locked,\n );\n\n if (state === undefined) return yield* prepare(\"rejected\" as const, project);\n const requirement = yield* s.decodeActionRequirement(state.subjectRow, action);\n\n const valid = yield* validatePasswordMutation(\n mapping,\n input,\n action,\n {\n subject: {\n active: s.isActiveStatus(state.subjectRow[s.status]),\n securityRevision: state.revision.securityRevision,\n },\n identifierCurrent: state.identifierRow !== undefined,\n credentials: state.revision.credentials.map((entry) => ({ ...entry, active: true })),\n snapshot: Effect.succeed(state.snapshot),\n requirement: Effect.succeed(requirement),\n },\n state.now,\n );\n\n if (\n valid === undefined ||\n (action === \"add-password\"\n ? state.passwordRow !== undefined\n : state.snapshot === undefined ||\n input.credential === undefined ||\n !samePasswordCredentialSnapshot(state.snapshot, input.credential))\n )\n return yield* prepare(\"rejected\" as const, project);\n let proofValidity = sql`1 = 1`;\n\n if (redemption !== undefined) {\n if (proofs === undefined) return yield* unavailable();\n\n const captured = {\n ...redemption,\n input: yield* Schema.decodeEffect(ProofRedemptionInput)(redemption.input),\n };\n\n if (!passwordProofRedemptionMatches({ ...input, redemption: captured }))\n return yield* prepare(\"rejected\" as const, project);\n // Inherit the physical owner; application owners retain the exact consumed\n // proof absence check after their callback.\n const proof = yield* makeNativeProofStore(tables, proofs, batch !== undefined);\n const redeemed = yield* proof.redeemLocked(captured.input);\n\n const proofReceipt = captured.prepare(\n redeemed.decision,\n yield* CurrentCommitJournal,\n (value) => value,\n );\n\n yield* registerSqlCommitReceipt(proofReceipt);\n if (redeemed.decision === \"rejected\") return yield* prepare(\"rejected\" as const, project);\n proofValidity = redeemed.validUntil;\n }\n const mode = \"interactive\";\n\n const credentialId =\n input.credential?.credentialId ??\n (yield* allocatePasswordValue(\n mode,\n mapping.allocateCredentialId,\n mapping.allocateCredentialIdSync,\n ));\n\n const credentialRevision = yield* allocatePasswordValue(\n mode,\n mapping.allocateRevision,\n mapping.allocateRevisionSync,\n );\n\n const verifierVersion = yield* allocatePasswordValue(\n mode,\n mapping.allocateRevision,\n mapping.allocateRevisionSync,\n );\n\n const nextSecurityRevision = yield* allocatePasswordNextSecurityRevision(\n mapping,\n mode,\n input.expectedRevision.securityRevision,\n );\n\n ensure(\n nextSecurityRevision !== input.expectedRevision.securityRevision &&\n credentialRevision !== input.credential?.credentialRevision &&\n verifierVersion !== input.credential?.verifierVersion,\n );\n\n const freshUntil = (requirement: AuthenticationRequirement) => {\n const deadlines = [\n ...new Set(\n input.authorization.evidence.proofs.map(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis,\n ),\n ),\n ]\n .filter((time) => time > state.now)\n .sort((a, b) => a - b);\n\n return deadlines.find(\n (time) => !passwordEvidenceSatisfiedAt(input.authorization.evidence, requirement, time),\n );\n };\n\n const requestedDeadline = freshUntil(input.authorization.requirement),\n currentDeadline = freshUntil(requirement);\n\n ensure(requestedDeadline !== undefined && currentDeadline !== undefined);\n const deadline = Math.min(requestedDeadline, currentDeadline);\n const freshness = sql`${now} >= ${state.now} and ${now} < ${deadline} and ${proofValidity}`;\n\n const stage = Effect.fnUntraced(function* (statement: Fragment) {\n if (batch === undefined) ensure((yield* executeSqlChange(sql, statement)) === 1);\n else {\n yield* appendSqlBatchStatement(sql`${statement}`);\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = 1`));\n }\n });\n\n if (batch !== undefined) {\n if (\n s.d1ActiveStatusValue === undefined ||\n (a.status !== undefined && a.d1ActiveStatusValue === undefined)\n )\n return yield* unavailable();\n\n const factors = state.revision.credentials.map(\n (factor) =>\n sql`exists(select 1 from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, factor.credentialId)} and ${exact(rawAuthority, a.revision, factor.revision)} ${a.status === undefined ? sql`` : sql`and ${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`})`,\n );\n\n const active =\n a.status === undefined\n ? sql`1 = 1`\n : sql`${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`;\n\n const condition = sql.and([\n sql`exists(select 1 from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, input.expectedRevision.securityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, s.d1ActiveStatusValue)})`,\n sql`(select count(*) from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${active}) = ${factors.length}`,\n ...factors,\n freshness,\n ]);\n\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n if (input.credential !== undefined) {\n if (i.d1CurrentCondition === undefined) return yield* unavailable();\n yield* appendSqlBatchStatement(\n sqlBatchAssertion(\n sql,\n sql`exists(select 1 from ${tables(i.table).name} where ${exact(tables(i.table), i.namespace, input.credential.identifier.namespace)} and ${exact(tables(i.table), i.value, input.credential.identifier.value)} and ${subjectKey(tables(i.table), i.subjectId, nativeId)} and ${tables.expression(i.d1CurrentCondition({ identifier: input.credential.identifier, nativeSubjectId: nativeId }))})`,\n ),\n );\n }\n }\n const revisions = { credentialId, credentialRevision, verifierVersion };\n\n if (input.credential === undefined) {\n yield* stage(\n sql`${rawPassword.insert(c.encodeInsert({ moduleId: input.moduleId, subjectId: nativeId, replacement: input.replacement, ...revisions }))}`,\n );\n yield* stage(\n sql`${rawAuthority.insert(a.encodeInsert({ subjectId: nativeId, credentialId, revision: credentialRevision }))}`,\n );\n } else {\n const old = input.credential;\n\n yield* stage(\n sql`${rawPassword.update(c.encodeReplacement({ replacement: input.replacement, credentialRevision, verifierVersion }))} where ${subjectKey(rawPassword, c.subjectId, nativeId)} and ${exact(rawPassword, c.moduleId, input.moduleId)} and ${exact(rawPassword, c.credentialId, old.credentialId)} and ${exact(rawPassword, c.credentialRevision, old.credentialRevision)} and ${exact(rawPassword, c.verifierVersion, old.verifierVersion)} and ${exact(rawPassword, c.verifier, Redacted.value(old.verifier))} and ${exact(rawPassword, c.normalization, old.normalization)} and ${freshness}`,\n );\n yield* stage(\n sql`${rawAuthority.update(a.encodeRevision(credentialRevision))} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, old.credentialId)} and ${exact(rawAuthority, a.revision, old.credentialRevision)}`,\n );\n }\n\n const expectedFactors = state.revision.credentials\n .filter((factor) => factor.credentialId !== credentialId)\n .concat({ credentialId, revision: credentialRevision });\n\n const finalPassword = sql`exists(select 1 from ${rawPassword.name} where ${subjectKey(rawPassword, c.subjectId, nativeId)} and ${exact(rawPassword, c.moduleId, input.moduleId)} and ${exact(rawPassword, c.credentialId, credentialId)} and ${exact(rawPassword, c.credentialRevision, credentialRevision)} and ${exact(rawPassword, c.verifierVersion, verifierVersion)} and ${exact(rawPassword, c.verifier, Redacted.value(input.replacement.verifier))} and ${exact(rawPassword, c.normalization, input.replacement.normalization)})`;\n\n const canGuardResult =\n (a.status === undefined || a.d1ActiveStatusValue !== undefined) &&\n (input.credential === undefined || i.d1CurrentCondition !== undefined);\n\n let resultingState: Fragment | undefined;\n\n if (canGuardResult) {\n const active =\n a.status === undefined\n ? sql`1 = 1`\n : sql`${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`;\n\n const conditions: Array<Fragment> = [\n finalPassword,\n freshness,\n sql`(select count(*) from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${active}) = ${expectedFactors.length}`,\n ...expectedFactors.map(\n (factor) =>\n sql`exists(select 1 from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, factor.credentialId)} and ${exact(rawAuthority, a.revision, factor.revision)} and ${active})`,\n ),\n ];\n\n if (input.credential !== undefined) {\n const expected = input.credential;\n const identifierTable = tables(i.table);\n\n ensure(i.d1CurrentCondition !== undefined);\n conditions.push(\n sql`exists(select 1 from ${identifierTable.name} where ${exact(identifierTable, i.namespace, expected.identifier.namespace)} and ${exact(identifierTable, i.value, expected.identifier.value)} and ${subjectKey(identifierTable, i.subjectId, nativeId)} and ${tables.expression(i.d1CurrentCondition({ identifier: expected.identifier, nativeSubjectId: nativeId }))})`,\n sql`exists(select 1 from ${identifierTable.name} where ${exact(identifierTable, i.namespace, expected.identifier.namespace)} and ${exact(identifierTable, i.value, expected.identifier.value)} and ${subjectKey(identifierTable, i.subjectId, nativeId)} and ${exact(identifierTable, i.bindingRevision, expected.identifierBindingRevision)} and ${expected.identifierVerifiedAtMillis === undefined ? sql`${identifierTable.column(i.verifiedAt)} is null` : sql`${identifierTable.column(i.verifiedAt)} = ${identifierTable.value(i.verifiedAt, mapping.clock.encodeInstant(expected.identifierVerifiedAtMillis))}`})`,\n );\n }\n resultingState = sql.and(conditions);\n }\n yield* stage(\n sql`${rawSubject.update({ [s.securityRevision]: nextSecurityRevision })} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, input.expectedRevision.securityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, locked[s.status])} and ${freshness} and ${resultingState ?? sql`1 = 1`}`,\n );\n if (batch !== undefined) {\n ensure(resultingState !== undefined);\n yield* registerSqlBatchPostcondition({\n name: \"password-replacement-authority\",\n statement: sqlBatchAssertion(\n sql,\n sql.and([\n resultingState,\n sql`exists(select 1 from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, nextSecurityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, s.d1ActiveStatusValue)})`,\n ]),\n ),\n });\n } else if (Option.isSome(parent) || resultingState === undefined)\n yield* registerSqlPostcondition({\n name: \"password-replacement-authority\",\n check: Effect.gen(function* () {\n const final = yield* read(\n input.moduleId,\n input.expectedRevision.subjectId,\n input.credential?.identifier,\n );\n\n ensure(\n final !== undefined &&\n s.isActiveStatus(final.subjectRow[s.status]) &&\n final.revision.securityRevision === nextSecurityRevision &&\n final.revision.credentials.length === expectedFactors.length &&\n expectedFactors.every((factor) =>\n final.revision.credentials.some(\n (entry) =>\n entry.credentialId === factor.credentialId &&\n entry.revision === factor.revision,\n ),\n ) &&\n final.now >= state.now &&\n final.now < deadline,\n );\n const password = final.passwordRow;\n\n ensure(\n password !== undefined &&\n password[c.credentialId] === credentialId &&\n password[c.credentialRevision] === credentialRevision &&\n password[c.verifierVersion] === verifierVersion &&\n password[c.verifier] === Redacted.value(input.replacement.verifier) &&\n password[c.normalization] === input.replacement.normalization,\n );\n if (input.credential !== undefined)\n ensure(\n final.snapshot !== undefined &&\n final.snapshot.identifierBindingRevision ===\n input.credential.identifierBindingRevision &&\n final.snapshot.identifierVerifiedAtMillis ===\n input.credential.identifierVerifiedAtMillis,\n );\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepare(\"changed\" as const, project);\n }),\n );\n\n const credentials = yield* makePasswordCredentials(tables, mapping, {\n mode: \"interactive\",\n locking: false,\n coordinated: Option.isSome(parent),\n standaloneGuard: requireStandalone(unavailable, sql.transactionService),\n });\n\n const passwordPersistence: P.PasswordPersistence[\"Service\"] = {\n ...credentials,\n readForSubject: (input) =>\n executor.read(\n Effect.map(read(input.moduleId, input.subjectId), (state) =>\n state === undefined || !s.isActiveStatus(state.subjectRow[s.status])\n ? Option.none()\n : Option.fromUndefinedOr(state.snapshot),\n ),\n ),\n recoveryTarget: (input) =>\n executor.read(\n Effect.gen(function* () {\n const rows =\n yield* sql`select ${identifier.fields(\"password_recovery_\")} from ${identifier.name} where ${exact(identifier, i.namespace, input.identifier.namespace)} and ${exact(identifier, i.value, input.identifier.value)} limit 2`;\n\n if (rows.length === 0) return Option.none();\n ensure(rows.length === 1);\n const row = identifier.decode(rows[0]!, \"password_recovery_\");\n\n if (!i.isCurrent(row)) return Option.none();\n const subjectId = yield* mapping.subjectId.toSubject(row[i.subjectId]);\n const state = yield* read(input.moduleId, subjectId, input.identifier);\n\n return state === undefined ||\n !s.isActiveStatus(state.subjectRow[s.status]) ||\n state.snapshot?.identifierVerifiedAtMillis === undefined\n ? Option.none()\n : Option.some(state.snapshot);\n }),\n ),\n addIfAbsent: (input, project) => mutation(input, \"add-password\", project),\n replaceIfCurrent: (input, project) => mutation(input, \"change-password\", project),\n resetWithProof: (input, project) =>\n mutation(input, \"reset-password\", project, input.redemption),\n };\n\n return { passwordPersistence };\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"password-native.mjs","names":[],"sources":["../../src/internal/password-native.ts"],"sourcesContent":["import { CurrentCommitJournal, type LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport type { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport * as P from \"@yielded/auth/Password\";\nimport { ProofRedemptionInput } from \"@yielded/auth/Proofs\";\nimport type { SubjectId } from \"@yielded/auth/Schema\";\nimport { SecurityRevision, type AuthenticationRequirement } from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Option, Redacted, Schema, Crypto } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport { fragment, parameter, type Fragment } from \"effect/sql/Statement\";\n\nimport { sqlBatchAssertion } from \"./d1-planning\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport type { AnyPasswordPersistenceMapping } from \"./models/password-model\";\nimport type { AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport { nativeSqlAlias, type NativeSqlTables, type SqlTable } from \"./native-sql-table\";\nimport { makePasswordCredentials, samePasswordCredentialSnapshot } from \"./password-credentials\";\nimport {\n allocatePasswordNextSecurityRevision,\n allocatePasswordValue,\n passwordEvidenceSatisfiedAt,\n passwordProofRedemptionMatches,\n snapshotPasswordMutation,\n validatePasswordMutation,\n} from \"./password-policy\";\nimport { makeNativeProofStore } from \"./proof-native\";\nimport { exactSqlText, executeSqlChange } from \"./sql-change\";\nimport {\n appendSqlBatchStatement,\n CurrentSqlCommit,\n makeSqlCommitExecutor,\n registerSqlBatchPostcondition,\n registerSqlCommitReceipt,\n registerSqlPostcondition,\n SqlBatchCommit,\n} from \"./sql-commit\";\nimport { guardSqlitePolicy } from \"./sqlite-policy-guard\";\nimport { requireStandalone } from \"./standalone\";\n\nconst unavailable = () => P.PasswordUnavailable.make({});\n\nconst ensure: (condition: unknown) => asserts condition = (condition) => {\n if (!condition) throw unavailable();\n};\n\nexport const makeNativePasswordServices = Effect.fnUntraced(function* (\n tables: NativeSqlTables,\n mapping: AnyPasswordPersistenceMapping,\n proofs?: AnyProofPersistenceMapping,\n): Effect.fn.Return<\n { readonly passwordPersistence: P.PasswordPersistence[\"Service\"] },\n P.PasswordUnavailable,\n SqlClient | LifecycleHooks | Crypto.Crypto | SqlBatchCommit\n> {\n const batch = yield* SqlBatchCommit;\n const sql = (yield* SqlClient).withoutTransforms();\n const executor = yield* makeSqlCommitExecutor(unavailable);\n const crypto = yield* Crypto.Crypto;\n const parent = yield* Effect.serviceOption(CurrentSqlCommit);\n\n const s = mapping.subject,\n i = mapping.identifier,\n c = mapping.credential,\n a = mapping.authorityCredential;\n\n const subject = tables(s.table).as(\"password_subject\");\n const identifier = tables(i.table).as(\"password_identifier\");\n const credential = tables(c.table).as(\"password_credential\");\n const authority = tables(a.table).as(\"password_authority\");\n\n const rawSubject = tables(s.table),\n rawPassword = tables(c.table),\n rawAuthority = tables(a.table);\n\n // Action policy may inspect any application column. Authority columns have\n // separate checks and securityRevision intentionally changes in this mutation.\n const policyColumns = rawSubject.keys.filter(\n (key) => key !== s.id && key !== s.status && key !== s.securityRevision,\n );\n\n const now = tables.expression(mapping.clock.engineNowMillis);\n const lock = sql.onDialectOrElse({ sqlite: () => sql``, orElse: () => sql`for update` });\n\n const exact = (table: SqlTable, key: string, value: unknown) =>\n exactSqlText(sql, table.column(key), table.value(key, value));\n\n const subjectKey = (table: SqlTable, key: string, nativeId: unknown) =>\n sql`${table.column(key)} = ${table.value(key, nativeId)}`;\n\n const run = <A, E, R>(work: Effect.Effect<A, E, R>) =>\n batch === undefined\n ? executor.run(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n : executor\n .batch(work.pipe(Effect.provideService(Crypto.Crypto, crypto)))\n .pipe(Effect.provideService(SqlBatchCommit, batch));\n\n const read = Effect.fnUntraced(function* (\n moduleId: string,\n subjectId: SubjectId,\n wanted?: LoginIdentifier,\n locked?: Record<string, unknown>,\n ) {\n const nativeId = yield* mapping.subjectId.toNative(subjectId);\n\n // Without one wanted identifier, retired identifiers would multiply authority\n // rows; read authority separately so the result grows linearly.\n const joined = wanted !== undefined;\n\n const rows =\n yield* sql`select ${subject.fields(\"password_subject_\")}, ${identifier.fields(\"password_identifier_\")}, ${credential.fields(\"password_credential_\")}${joined ? sql`, ${authority.fields(\"password_authority_\")}` : sql``}, ${now} as engine_now\n from ${subject.name}\n left join ${identifier.name} on ${subjectKey(identifier, i.subjectId, nativeId)} ${wanted === undefined ? sql`` : sql`and ${exact(identifier, i.namespace, wanted.namespace)} and ${exact(identifier, i.value, wanted.value)}`}\n left join ${credential.name} on ${subjectKey(credential, c.subjectId, nativeId)} and ${exact(credential, c.moduleId, moduleId)}\n ${joined ? sql`left join ${authority.name} on ${subjectKey(authority, a.subjectId, nativeId)}` : sql``}\n where ${subjectKey(subject, s.id, nativeId)} order by ${identifier.column(i.namespace)}, ${identifier.column(i.value)}${joined ? sql`, ${authority.column(a.credentialId)}` : sql``} limit 4097`;\n\n const authorityRows = joined\n ? rows\n : yield* sql`select ${authority.fields(\"password_authority_\")} from ${authority.name} where ${subjectKey(authority, a.subjectId, nativeId)} order by ${authority.column(a.credentialId)} limit 4097`;\n\n ensure(rows.length <= 4096 && authorityRows.length <= 4096);\n if (rows[0] === undefined) return undefined;\n const subjectRow = subject.decode(rows[0], \"password_subject_\");\n const decodedId = yield* mapping.subjectId.toSubject(subjectRow[s.id]);\n\n ensure(\n decodedId === subjectId &&\n (locked === undefined || subjectRow[s.securityRevision] === locked[s.securityRevision]),\n );\n const password = credential.decode(rows[0], \"password_credential_\");\n const passwordRow = password[c.credentialId] === null ? undefined : password;\n\n const factors = new Map<\n string,\n {\n readonly credentialId: string;\n readonly revision: SecurityRevision;\n readonly active: boolean;\n }\n >();\n\n let identifierRow: Record<string, unknown> | undefined;\n\n for (const row of authorityRows) {\n const factor = authority.decode(row, \"password_authority_\");\n\n if (\n factor[a.credentialId] !== null &&\n (a.status === undefined || a.isActiveStatus?.(factor[a.status]) === true)\n ) {\n ensure(mapping.subjectId.equals(factor[a.subjectId], nativeId));\n const id = yield* Schema.decodeUnknownEffect(Schema.String)(factor[a.credentialId]);\n const revision = yield* Schema.decodeUnknownEffect(SecurityRevision)(factor[a.revision]);\n const old = factors.get(id);\n\n ensure(old === undefined || old.revision === revision);\n factors.set(id, { credentialId: id, revision, active: true });\n }\n }\n for (const row of rows) {\n const entry = identifier.decode(row, \"password_identifier_\");\n\n if (\n entry[i.namespace] !== null &&\n mapping.subjectId.equals(entry[i.subjectId], nativeId) &&\n mapping.identifier.isCurrent(entry)\n )\n identifierRow ??= entry;\n }\n ensure(factors.size <= 64);\n\n const revision = {\n subjectId,\n securityRevision: yield* Schema.decodeUnknownEffect(SecurityRevision)(\n subjectRow[s.securityRevision],\n ),\n credentials: [...factors.values()].map(({ credentialId, revision }) => ({\n credentialId,\n revision,\n })),\n };\n\n const snapshot =\n identifierRow === undefined || passwordRow === undefined\n ? undefined\n : yield* mapping.credential\n .decode({\n moduleId,\n subject: subjectRow,\n identifier: identifierRow,\n credential: passwordRow,\n })\n .pipe(Effect.flatMap(P.snapshotPasswordCredential));\n\n if (snapshot !== undefined)\n ensure(\n snapshot.revision.subjectId === subjectId &&\n snapshot.credentialId === passwordRow?.[c.credentialId],\n );\n\n return {\n nativeId,\n subjectRow,\n policyValues:\n batch === undefined\n ? []\n : policyColumns.map((key) => ({\n column: rawSubject.selectedColumn(key),\n value: fragment([\n parameter(\n rows[0][nativeSqlAlias(\"password_subject_\", rawSubject.keys.indexOf(key))],\n ),\n ]),\n })),\n identifierRow,\n passwordRow,\n revision,\n snapshot: snapshot === undefined ? undefined : { ...snapshot, revision },\n now: yield* Schema.decodeEffect(Schema.Int)(Number(rows[0].engine_now)),\n };\n });\n\n const prepare = <V, A>(value: V, project: P.PreparePasswordCommit<V, A>) =>\n Effect.gen(function* () {\n const journal = yield* CurrentCommitJournal;\n\n const receipt = project(value, journal);\n\n ensure(receipt?._tag === \"PreparedCommit\" && Effect.isEffect(receipt.read));\n yield* registerSqlCommitReceipt(receipt);\n\n return receipt;\n });\n\n const mutation = <A>(\n original: P.PasswordMutationInput,\n action: P.PasswordAction,\n project: P.PreparePasswordCommit<P.PasswordMutationDecision, A>,\n redemption?: Parameters<P.PasswordPersistence[\"Service\"][\"resetWithProof\"]>[0][\"redemption\"],\n ) =>\n run(\n Effect.gen(function* () {\n const input = yield* snapshotPasswordMutation(original);\n const nativeId = yield* mapping.subjectId.toNative(input.expectedRevision.subjectId);\n\n const rows =\n yield* sql`select ${rawSubject.fields(\"password_lock_\")} from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} limit 2 ${batch === undefined ? lock : sql``}`;\n\n if (rows.length === 0) return yield* prepare(\"rejected\" as const, project);\n ensure(rows.length === 1);\n const locked = rawSubject.decode(rows[0]!, \"password_lock_\");\n\n const state = yield* read(\n input.moduleId,\n input.expectedRevision.subjectId,\n input.credential?.identifier,\n locked,\n );\n\n if (state === undefined) return yield* prepare(\"rejected\" as const, project);\n const requirement = yield* s.decodeActionRequirement(state.subjectRow, action);\n\n const valid = yield* validatePasswordMutation(\n mapping,\n input,\n action,\n {\n subject: {\n active: s.isActiveStatus(state.subjectRow[s.status]),\n securityRevision: state.revision.securityRevision,\n },\n identifierCurrent: state.identifierRow !== undefined,\n credentials: state.revision.credentials.map((entry) => ({ ...entry, active: true })),\n snapshot: Effect.succeed(state.snapshot),\n requirement: Effect.succeed(requirement),\n },\n state.now,\n );\n\n if (\n valid === undefined ||\n (action === \"add-password\"\n ? state.passwordRow !== undefined\n : state.snapshot === undefined ||\n input.credential === undefined ||\n !samePasswordCredentialSnapshot(state.snapshot, input.credential))\n )\n return yield* prepare(\"rejected\" as const, project);\n let proofValidity = sql`1 = 1`;\n\n if (redemption !== undefined) {\n if (proofs === undefined) return yield* unavailable();\n\n const captured = {\n ...redemption,\n input: yield* Schema.decodeEffect(ProofRedemptionInput)(redemption.input),\n };\n\n if (!passwordProofRedemptionMatches({ ...input, redemption: captured }))\n return yield* prepare(\"rejected\" as const, project);\n // Inherit the physical owner; application owners retain the exact consumed\n // proof absence check after their callback.\n const proof = yield* makeNativeProofStore(tables, proofs, batch !== undefined);\n const redeemed = yield* proof.redeemLocked(captured.input);\n\n const proofReceipt = captured.prepare(\n redeemed.decision,\n yield* CurrentCommitJournal,\n (value) => value,\n );\n\n yield* registerSqlCommitReceipt(proofReceipt);\n if (redeemed.decision === \"rejected\") return yield* prepare(\"rejected\" as const, project);\n proofValidity = redeemed.validUntil;\n }\n const mode = \"interactive\";\n\n const credentialId =\n input.credential?.credentialId ??\n (yield* allocatePasswordValue(\n mode,\n mapping.allocateCredentialId,\n mapping.allocateCredentialIdSync,\n ));\n\n const credentialRevision = yield* allocatePasswordValue(\n mode,\n mapping.allocateRevision,\n mapping.allocateRevisionSync,\n );\n\n const verifierVersion = yield* allocatePasswordValue(\n mode,\n mapping.allocateRevision,\n mapping.allocateRevisionSync,\n );\n\n const nextSecurityRevision = yield* allocatePasswordNextSecurityRevision(\n mapping,\n mode,\n input.expectedRevision.securityRevision,\n );\n\n ensure(\n nextSecurityRevision !== input.expectedRevision.securityRevision &&\n credentialRevision !== input.credential?.credentialRevision &&\n verifierVersion !== input.credential?.verifierVersion,\n );\n\n const freshUntil = (requirement: AuthenticationRequirement) => {\n const deadlines = [\n ...new Set(\n input.authorization.evidence.proofs.map(\n (proof) => DateTime.toEpochMillis(proof.verifiedAt) + requirement.maximumAgeMillis,\n ),\n ),\n ]\n .filter((time) => time > state.now)\n .sort((a, b) => a - b);\n\n return deadlines.find(\n (time) => !passwordEvidenceSatisfiedAt(input.authorization.evidence, requirement, time),\n );\n };\n\n if (batch !== undefined)\n yield* guardSqlitePolicy({\n name: \"password-subject-policy\",\n table: rawSubject.name,\n owner: subjectKey(rawSubject, s.id, nativeId),\n values: state.policyValues,\n });\n\n const requestedDeadline = freshUntil(input.authorization.requirement),\n currentDeadline = freshUntil(requirement);\n\n ensure(requestedDeadline !== undefined && currentDeadline !== undefined);\n const deadline = Math.min(requestedDeadline, currentDeadline);\n const freshness = sql`${now} >= ${state.now} and ${now} < ${deadline} and ${proofValidity}`;\n\n const stage = Effect.fnUntraced(function* (statement: Fragment) {\n if (batch === undefined) ensure((yield* executeSqlChange(sql, statement)) === 1);\n else {\n yield* appendSqlBatchStatement(sql`${statement}`);\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, sql`changes() = 1`));\n }\n });\n\n if (batch !== undefined) {\n if (\n s.d1ActiveStatusValue === undefined ||\n (a.status !== undefined && a.d1ActiveStatusValue === undefined)\n )\n return yield* unavailable();\n\n const factors = state.revision.credentials.map(\n (factor) =>\n sql`exists(select 1 from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, factor.credentialId)} and ${exact(rawAuthority, a.revision, factor.revision)} ${a.status === undefined ? sql`` : sql`and ${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`})`,\n );\n\n const active =\n a.status === undefined\n ? sql`1 = 1`\n : sql`${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`;\n\n const condition = sql.and([\n sql`exists(select 1 from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, input.expectedRevision.securityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, s.d1ActiveStatusValue)})`,\n sql`(select count(*) from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${active}) = ${factors.length}`,\n ...factors,\n freshness,\n ]);\n\n yield* appendSqlBatchStatement(sqlBatchAssertion(sql, condition));\n if (input.credential !== undefined) {\n if (i.d1CurrentCondition === undefined) return yield* unavailable();\n yield* appendSqlBatchStatement(\n sqlBatchAssertion(\n sql,\n sql`exists(select 1 from ${tables(i.table).name} where ${exact(tables(i.table), i.namespace, input.credential.identifier.namespace)} and ${exact(tables(i.table), i.value, input.credential.identifier.value)} and ${subjectKey(tables(i.table), i.subjectId, nativeId)} and ${tables.expression(i.d1CurrentCondition({ identifier: input.credential.identifier, nativeSubjectId: nativeId }))})`,\n ),\n );\n }\n }\n const revisions = { credentialId, credentialRevision, verifierVersion };\n\n if (input.credential === undefined) {\n yield* stage(\n sql`${rawPassword.insert(c.encodeInsert({ moduleId: input.moduleId, subjectId: nativeId, replacement: input.replacement, ...revisions }))}`,\n );\n yield* stage(\n sql`${rawAuthority.insert(a.encodeInsert({ subjectId: nativeId, credentialId, revision: credentialRevision }))}`,\n );\n } else {\n const old = input.credential;\n\n yield* stage(\n sql`${rawPassword.update(c.encodeReplacement({ replacement: input.replacement, credentialRevision, verifierVersion }))} where ${subjectKey(rawPassword, c.subjectId, nativeId)} and ${exact(rawPassword, c.moduleId, input.moduleId)} and ${exact(rawPassword, c.credentialId, old.credentialId)} and ${exact(rawPassword, c.credentialRevision, old.credentialRevision)} and ${exact(rawPassword, c.verifierVersion, old.verifierVersion)} and ${exact(rawPassword, c.verifier, Redacted.value(old.verifier))} and ${exact(rawPassword, c.normalization, old.normalization)} and ${freshness}`,\n );\n yield* stage(\n sql`${rawAuthority.update(a.encodeRevision(credentialRevision))} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, old.credentialId)} and ${exact(rawAuthority, a.revision, old.credentialRevision)}`,\n );\n }\n\n const expectedFactors = state.revision.credentials\n .filter((factor) => factor.credentialId !== credentialId)\n .concat({ credentialId, revision: credentialRevision });\n\n const finalPassword = sql`exists(select 1 from ${rawPassword.name} where ${subjectKey(rawPassword, c.subjectId, nativeId)} and ${exact(rawPassword, c.moduleId, input.moduleId)} and ${exact(rawPassword, c.credentialId, credentialId)} and ${exact(rawPassword, c.credentialRevision, credentialRevision)} and ${exact(rawPassword, c.verifierVersion, verifierVersion)} and ${exact(rawPassword, c.verifier, Redacted.value(input.replacement.verifier))} and ${exact(rawPassword, c.normalization, input.replacement.normalization)})`;\n\n const canGuardResult =\n (a.status === undefined || a.d1ActiveStatusValue !== undefined) &&\n (input.credential === undefined || i.d1CurrentCondition !== undefined);\n\n let resultingState: Fragment | undefined;\n\n if (canGuardResult) {\n const active =\n a.status === undefined\n ? sql`1 = 1`\n : sql`${rawAuthority.column(a.status)} = ${rawAuthority.value(a.status, a.d1ActiveStatusValue)}`;\n\n const conditions: Array<Fragment> = [\n finalPassword,\n freshness,\n sql`(select count(*) from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${active}) = ${expectedFactors.length}`,\n ...expectedFactors.map(\n (factor) =>\n sql`exists(select 1 from ${rawAuthority.name} where ${subjectKey(rawAuthority, a.subjectId, nativeId)} and ${exact(rawAuthority, a.credentialId, factor.credentialId)} and ${exact(rawAuthority, a.revision, factor.revision)} and ${active})`,\n ),\n ];\n\n if (input.credential !== undefined) {\n const expected = input.credential;\n const identifierTable = tables(i.table);\n\n ensure(i.d1CurrentCondition !== undefined);\n conditions.push(\n sql`exists(select 1 from ${identifierTable.name} where ${exact(identifierTable, i.namespace, expected.identifier.namespace)} and ${exact(identifierTable, i.value, expected.identifier.value)} and ${subjectKey(identifierTable, i.subjectId, nativeId)} and ${tables.expression(i.d1CurrentCondition({ identifier: expected.identifier, nativeSubjectId: nativeId }))})`,\n sql`exists(select 1 from ${identifierTable.name} where ${exact(identifierTable, i.namespace, expected.identifier.namespace)} and ${exact(identifierTable, i.value, expected.identifier.value)} and ${subjectKey(identifierTable, i.subjectId, nativeId)} and ${exact(identifierTable, i.bindingRevision, expected.identifierBindingRevision)} and ${expected.identifierVerifiedAtMillis === undefined ? sql`${identifierTable.column(i.verifiedAt)} is null` : sql`${identifierTable.column(i.verifiedAt)} = ${identifierTable.value(i.verifiedAt, mapping.clock.encodeInstant(expected.identifierVerifiedAtMillis))}`})`,\n );\n }\n resultingState = sql.and(conditions);\n }\n yield* stage(\n sql`${rawSubject.update({ [s.securityRevision]: nextSecurityRevision })} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, input.expectedRevision.securityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, locked[s.status])} and ${freshness} and ${resultingState ?? sql`1 = 1`}`,\n );\n if (batch !== undefined) {\n ensure(resultingState !== undefined);\n yield* registerSqlBatchPostcondition({\n name: \"password-replacement-authority\",\n statement: sqlBatchAssertion(\n sql,\n sql.and([\n resultingState,\n sql`exists(select 1 from ${rawSubject.name} where ${subjectKey(rawSubject, s.id, nativeId)} and ${exact(rawSubject, s.securityRevision, nextSecurityRevision)} and ${rawSubject.column(s.status)} = ${rawSubject.value(s.status, s.d1ActiveStatusValue)})`,\n ]),\n ),\n });\n } else if (Option.isSome(parent) || resultingState === undefined)\n yield* registerSqlPostcondition({\n name: \"password-replacement-authority\",\n check: Effect.gen(function* () {\n const final = yield* read(\n input.moduleId,\n input.expectedRevision.subjectId,\n input.credential?.identifier,\n );\n\n ensure(\n final !== undefined &&\n s.isActiveStatus(final.subjectRow[s.status]) &&\n final.revision.securityRevision === nextSecurityRevision &&\n final.revision.credentials.length === expectedFactors.length &&\n expectedFactors.every((factor) =>\n final.revision.credentials.some(\n (entry) =>\n entry.credentialId === factor.credentialId &&\n entry.revision === factor.revision,\n ),\n ) &&\n final.now >= state.now &&\n final.now < deadline,\n );\n const password = final.passwordRow;\n\n ensure(\n password !== undefined &&\n password[c.credentialId] === credentialId &&\n password[c.credentialRevision] === credentialRevision &&\n password[c.verifierVersion] === verifierVersion &&\n password[c.verifier] === Redacted.value(input.replacement.verifier) &&\n password[c.normalization] === input.replacement.normalization,\n );\n if (input.credential !== undefined)\n ensure(\n final.snapshot !== undefined &&\n final.snapshot.identifierBindingRevision ===\n input.credential.identifierBindingRevision &&\n final.snapshot.identifierVerifiedAtMillis ===\n input.credential.identifierVerifiedAtMillis,\n );\n }).pipe(\n Effect.mapError((cause) =>\n PersistenceMappingError.make({ operation: \"decode\", cause }),\n ),\n ),\n });\n\n return yield* prepare(\"changed\" as const, project);\n }),\n );\n\n const credentials = yield* makePasswordCredentials(tables, mapping, {\n mode: \"interactive\",\n locking: false,\n coordinated: Option.isSome(parent),\n standaloneGuard: requireStandalone(unavailable, sql.transactionService),\n });\n\n const passwordPersistence: P.PasswordPersistence[\"Service\"] = {\n ...credentials,\n readForSubject: (input) =>\n executor.read(\n Effect.map(read(input.moduleId, input.subjectId), (state) =>\n state === undefined || !s.isActiveStatus(state.subjectRow[s.status])\n ? Option.none()\n : Option.fromUndefinedOr(state.snapshot),\n ),\n ),\n recoveryTarget: (input) =>\n executor.read(\n Effect.gen(function* () {\n const rows =\n yield* sql`select ${identifier.fields(\"password_recovery_\")} from ${identifier.name} where ${exact(identifier, i.namespace, input.identifier.namespace)} and ${exact(identifier, i.value, input.identifier.value)} limit 2`;\n\n if (rows.length === 0) return Option.none();\n ensure(rows.length === 1);\n const row = identifier.decode(rows[0]!, \"password_recovery_\");\n\n if (!i.isCurrent(row)) return Option.none();\n const subjectId = yield* mapping.subjectId.toSubject(row[i.subjectId]);\n const state = yield* read(input.moduleId, subjectId, input.identifier);\n\n return state === undefined ||\n !s.isActiveStatus(state.subjectRow[s.status]) ||\n state.snapshot?.identifierVerifiedAtMillis === undefined\n ? Option.none()\n : Option.some(state.snapshot);\n }),\n ),\n addIfAbsent: (input, project) => mutation(input, \"add-password\", project),\n replaceIfCurrent: (input, project) => mutation(input, \"change-password\", project),\n resetWithProof: (input, project) =>\n mutation(input, \"reset-password\", project, input.redemption),\n };\n\n return { passwordPersistence };\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsCA,MAAM,oBAAoB,EAAE,oBAAoB,KAAK,CAAC,CAAC;AAEvD,MAAM,UAAqD,cAAc;CACvE,IAAI,CAAC,WAAW,MAAM,YAAY;AACpC;AAEA,MAAa,6BAA6B,OAAO,WAAW,WAC1D,QACA,SACA,QAKA;CACA,MAAM,QAAQ,OAAO;CACrB,MAAM,OAAO,OAAO,UAAA,CAAW,kBAAkB;CACjD,MAAM,WAAW,OAAO,sBAAsB,WAAW;CACzD,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,SAAS,OAAO,OAAO,cAAc,gBAAgB;CAE3D,MAAM,IAAI,QAAQ,SAChB,IAAI,QAAQ,YACZ,IAAI,QAAQ,YACZ,IAAI,QAAQ;CAEd,MAAM,UAAU,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,kBAAkB;CACrD,MAAM,aAAa,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,qBAAqB;CAC3D,MAAM,aAAa,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,qBAAqB;CAC3D,MAAM,YAAY,OAAO,EAAE,KAAK,CAAC,CAAC,GAAG,oBAAoB;CAEzD,MAAM,aAAa,OAAO,EAAE,KAAK,GAC/B,cAAc,OAAO,EAAE,KAAK,GAC5B,eAAe,OAAO,EAAE,KAAK;CAI/B,MAAM,gBAAgB,WAAW,KAAK,QACnC,QAAQ,QAAQ,EAAE,MAAM,QAAQ,EAAE,UAAU,QAAQ,EAAE,gBACzD;CAEA,MAAM,MAAM,OAAO,WAAW,QAAQ,MAAM,eAAe;CAC3D,MAAM,OAAO,IAAI,gBAAgB;EAAE,cAAc,GAAG;EAAI,cAAc,GAAG;CAAa,CAAC;CAEvF,MAAM,SAAS,OAAiB,KAAa,UAC3C,aAAa,KAAK,MAAM,OAAO,GAAG,GAAG,MAAM,MAAM,KAAK,KAAK,CAAC;CAE9D,MAAM,cAAc,OAAiB,KAAa,aAChD,GAAG,GAAG,MAAM,OAAO,GAAG,EAAE,KAAK,MAAM,MAAM,KAAK,QAAQ;CAExD,MAAM,OAAgB,SACpB,UAAU,KAAA,IACN,SAAS,IAAI,KAAK,KAAK,OAAO,eAAe,OAAO,QAAQ,MAAM,CAAC,CAAC,IACpE,SACG,MAAM,KAAK,KAAK,OAAO,eAAe,OAAO,QAAQ,MAAM,CAAC,CAAC,CAAC,CAC9D,KAAK,OAAO,eAAe,gBAAgB,KAAK,CAAC;CAE1D,MAAM,OAAO,OAAO,WAAW,WAC7B,UACA,WACA,QACA,QACA;EACA,MAAM,WAAW,OAAO,QAAQ,UAAU,SAAS,SAAS;EAI5D,MAAM,SAAS,WAAW,KAAA;EAE1B,MAAM,OACJ,OAAO,GAAG,UAAU,QAAQ,OAAO,mBAAmB,EAAE,IAAI,WAAW,OAAO,sBAAsB,EAAE,IAAI,WAAW,OAAO,sBAAsB,IAAI,SAAS,GAAG,KAAK,UAAU,OAAO,qBAAqB,MAAM,GAAG,GAAG,IAAI,IAAI;aAC1N,QAAQ,KAAK;kBACR,WAAW,KAAK,MAAM,WAAW,YAAY,EAAE,WAAW,QAAQ,EAAE,GAAG,WAAW,KAAA,IAAY,GAAG,KAAK,GAAG,OAAO,MAAM,YAAY,EAAE,WAAW,OAAO,SAAS,EAAE,OAAO,MAAM,YAAY,EAAE,OAAO,OAAO,KAAK,IAAI;kBACnN,WAAW,KAAK,MAAM,WAAW,YAAY,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,YAAY,EAAE,UAAU,QAAQ,EAAE;QAC7H,SAAS,GAAG,aAAa,UAAU,KAAK,MAAM,WAAW,WAAW,EAAE,WAAW,QAAQ,MAAM,GAAG,GAAG;cAC/F,WAAW,SAAS,EAAE,IAAI,QAAQ,EAAE,YAAY,WAAW,OAAO,EAAE,SAAS,EAAE,IAAI,WAAW,OAAO,EAAE,KAAK,IAAI,SAAS,GAAG,KAAK,UAAU,OAAO,EAAE,YAAY,MAAM,GAAG,GAAG;EAEtL,MAAM,gBAAgB,SAClB,OACA,OAAO,GAAG,UAAU,UAAU,OAAO,qBAAqB,EAAE,QAAQ,UAAU,KAAK,SAAS,WAAW,WAAW,EAAE,WAAW,QAAQ,EAAE,YAAY,UAAU,OAAO,EAAE,YAAY,EAAE;EAE1L,OAAO,KAAK,UAAU,QAAQ,cAAc,UAAU,IAAI;EAC1D,IAAI,KAAK,OAAO,KAAA,GAAW,OAAO,KAAA;EAClC,MAAM,aAAa,QAAQ,OAAO,KAAK,IAAI,mBAAmB;EAC9D,MAAM,YAAY,OAAO,QAAQ,UAAU,UAAU,WAAW,EAAE,GAAG;EAErE,OACE,cAAc,cACX,WAAW,KAAA,KAAa,WAAW,EAAE,sBAAsB,OAAO,EAAE,kBACzE;EACA,MAAM,WAAW,WAAW,OAAO,KAAK,IAAI,sBAAsB;EAClE,MAAM,cAAc,SAAS,EAAE,kBAAkB,OAAO,KAAA,IAAY;EAEpE,MAAM,0BAAU,IAAI,IAOlB;EAEF,IAAI;EAEJ,KAAK,MAAM,OAAO,eAAe;GAC/B,MAAM,SAAS,UAAU,OAAO,KAAK,qBAAqB;GAE1D,IACE,OAAO,EAAE,kBAAkB,SAC1B,EAAE,WAAW,KAAA,KAAa,EAAE,iBAAiB,OAAO,EAAE,OAAO,MAAM,OACpE;IACA,OAAO,QAAQ,UAAU,OAAO,OAAO,EAAE,YAAY,QAAQ,CAAC;IAC9D,MAAM,KAAK,OAAO,OAAO,oBAAoB,OAAO,MAAM,CAAC,CAAC,OAAO,EAAE,aAAa;IAClF,MAAM,WAAW,OAAO,OAAO,oBAAoB,gBAAgB,CAAC,CAAC,OAAO,EAAE,SAAS;IACvF,MAAM,MAAM,QAAQ,IAAI,EAAE;IAE1B,OAAO,QAAQ,KAAA,KAAa,IAAI,aAAa,QAAQ;IACrD,QAAQ,IAAI,IAAI;KAAE,cAAc;KAAI;KAAU,QAAQ;IAAK,CAAC;GAC9D;EACF;EACA,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,QAAQ,WAAW,OAAO,KAAK,sBAAsB;GAE3D,IACE,MAAM,EAAE,eAAe,QACvB,QAAQ,UAAU,OAAO,MAAM,EAAE,YAAY,QAAQ,KACrD,QAAQ,WAAW,UAAU,KAAK,GAElC,kBAAkB;EACtB;EACA,OAAO,QAAQ,QAAQ,EAAE;EAEzB,MAAM,WAAW;GACf;GACA,kBAAkB,OAAO,OAAO,oBAAoB,gBAAgB,CAAC,CACnE,WAAW,EAAE,iBACf;GACA,aAAa,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,cAAc,gBAAgB;IACtE;IACA;GACF,EAAE;EACJ;EAEA,MAAM,WACJ,kBAAkB,KAAA,KAAa,gBAAgB,KAAA,IAC3C,KAAA,IACA,OAAO,QAAQ,WACZ,OAAO;GACN;GACA,SAAS;GACT,YAAY;GACZ,YAAY;EACd,CAAC,CAAC,CACD,KAAK,OAAO,QAAQ,EAAE,0BAA0B,CAAC;EAE1D,IAAI,aAAa,KAAA,GACf,OACE,SAAS,SAAS,cAAc,aAC9B,SAAS,iBAAiB,cAAc,EAAE,aAC9C;EAEF,OAAO;GACL;GACA;GACA,cACE,UAAU,KAAA,IACN,CAAC,IACD,cAAc,KAAK,SAAS;IAC1B,QAAQ,WAAW,eAAe,GAAG;IACrC,OAAO,SAAS,CACd,UACE,KAAK,EAAE,CAAC,eAAe,qBAAqB,WAAW,KAAK,QAAQ,GAAG,CAAC,EAC1E,CACF,CAAC;GACH,EAAE;GACR;GACA;GACA;GACA,UAAU,aAAa,KAAA,IAAY,KAAA,IAAY;IAAE,GAAG;IAAU;GAAS;GACvE,KAAK,OAAO,OAAO,aAAa,OAAO,GAAG,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,UAAU,CAAC;EACxE;CACF,CAAC;CAED,MAAM,WAAiB,OAAU,YAC/B,OAAO,IAAI,aAAa;EAGtB,MAAM,UAAU,QAAQ,OAAO,OAFR,oBAEe;EAEtC,OAAO,SAAS,SAAS,oBAAoB,OAAO,SAAS,QAAQ,IAAI,CAAC;EAC1E,OAAO,yBAAyB,OAAO;EAEvC,OAAO;CACT,CAAC;CAEH,MAAM,YACJ,UACA,QACA,SACA,eAEA,IACE,OAAO,IAAI,aAAa;EACtB,MAAM,QAAQ,OAAO,yBAAyB,QAAQ;EACtD,MAAM,WAAW,OAAO,QAAQ,UAAU,SAAS,MAAM,iBAAiB,SAAS;EAEnF,MAAM,OACJ,OAAO,GAAG,UAAU,WAAW,OAAO,gBAAgB,EAAE,QAAQ,WAAW,KAAK,SAAS,WAAW,YAAY,EAAE,IAAI,QAAQ,EAAE,WAAW,UAAU,KAAA,IAAY,OAAO,GAAG;EAE7K,IAAI,KAAK,WAAW,GAAG,OAAO,OAAO,QAAQ,YAAqB,OAAO;EACzE,OAAO,KAAK,WAAW,CAAC;EACxB,MAAM,SAAS,WAAW,OAAO,KAAK,IAAK,gBAAgB;EAE3D,MAAM,QAAQ,OAAO,KACnB,MAAM,UACN,MAAM,iBAAiB,WACvB,MAAM,YAAY,YAClB,MACF;EAEA,IAAI,UAAU,KAAA,GAAW,OAAO,OAAO,QAAQ,YAAqB,OAAO;EAC3E,MAAM,cAAc,OAAO,EAAE,wBAAwB,MAAM,YAAY,MAAM;EAmB7E,KACE,OAlBmB,yBACnB,SACA,OACA,QACA;GACE,SAAS;IACP,QAAQ,EAAE,eAAe,MAAM,WAAW,EAAE,OAAO;IACnD,kBAAkB,MAAM,SAAS;GACnC;GACA,mBAAmB,MAAM,kBAAkB,KAAA;GAC3C,aAAa,MAAM,SAAS,YAAY,KAAK,WAAW;IAAE,GAAG;IAAO,QAAQ;GAAK,EAAE;GACnF,UAAU,OAAO,QAAQ,MAAM,QAAQ;GACvC,aAAa,OAAO,QAAQ,WAAW;EACzC,GACA,MAAM,GACR,OAGY,KAAA,MACT,WAAW,iBACR,MAAM,gBAAgB,KAAA,IACtB,MAAM,aAAa,KAAA,KACnB,MAAM,eAAe,KAAA,KACrB,CAAC,+BAA+B,MAAM,UAAU,MAAM,UAAU,IAEpE,OAAO,OAAO,QAAQ,YAAqB,OAAO;EACpD,IAAI,gBAAgB,GAAG;EAEvB,IAAI,eAAe,KAAA,GAAW;GAC5B,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,YAAY;GAEpD,MAAM,WAAW;IACf,GAAG;IACH,OAAO,OAAO,OAAO,aAAa,oBAAoB,CAAC,CAAC,WAAW,KAAK;GAC1E;GAEA,IAAI,CAAC,+BAA+B;IAAE,GAAG;IAAO,YAAY;GAAS,CAAC,GACpE,OAAO,OAAO,QAAQ,YAAqB,OAAO;GAIpD,MAAM,WAAW,QAAO,OADH,qBAAqB,QAAQ,QAAQ,UAAU,KAAA,CAAS,EAAA,CAC/C,aAAa,SAAS,KAAK;GAEzD,MAAM,eAAe,SAAS,QAC5B,SAAS,UACT,OAAO,uBACN,UAAU,KACb;GAEA,OAAO,yBAAyB,YAAY;GAC5C,IAAI,SAAS,aAAa,YAAY,OAAO,OAAO,QAAQ,YAAqB,OAAO;GACxF,gBAAgB,SAAS;EAC3B;EACA,MAAM,OAAO;EAEb,MAAM,eACJ,MAAM,YAAY,iBACjB,OAAO,sBACN,MACA,QAAQ,sBACR,QAAQ,wBACV;EAEF,MAAM,qBAAqB,OAAO,sBAChC,MACA,QAAQ,kBACR,QAAQ,oBACV;EAEA,MAAM,kBAAkB,OAAO,sBAC7B,MACA,QAAQ,kBACR,QAAQ,oBACV;EAEA,MAAM,uBAAuB,OAAO,qCAClC,SACA,MACA,MAAM,iBAAiB,gBACzB;EAEA,OACE,yBAAyB,MAAM,iBAAiB,oBAC9C,uBAAuB,MAAM,YAAY,sBACzC,oBAAoB,MAAM,YAAY,eAC1C;EAEA,MAAM,cAAc,gBAA2C;GAW7D,OAVkB,CAChB,GAAG,IAAI,IACL,MAAM,cAAc,SAAS,OAAO,KACjC,UAAU,SAAS,cAAc,MAAM,UAAU,IAAI,YAAY,gBACpE,CACF,CACF,CAAC,CACE,QAAQ,SAAS,OAAO,MAAM,GAAG,CAAC,CAClC,MAAM,GAAG,MAAM,IAAI,CAEP,CAAC,CAAC,MACd,SAAS,CAAC,4BAA4B,MAAM,cAAc,UAAU,aAAa,IAAI,CACxF;EACF;EAEA,IAAI,UAAU,KAAA,GACZ,OAAO,kBAAkB;GACvB,MAAM;GACN,OAAO,WAAW;GAClB,OAAO,WAAW,YAAY,EAAE,IAAI,QAAQ;GAC5C,QAAQ,MAAM;EAChB,CAAC;EAEH,MAAM,oBAAoB,WAAW,MAAM,cAAc,WAAW,GAClE,kBAAkB,WAAW,WAAW;EAE1C,OAAO,sBAAsB,KAAA,KAAa,oBAAoB,KAAA,CAAS;EACvE,MAAM,WAAW,KAAK,IAAI,mBAAmB,eAAe;EAC5D,MAAM,YAAY,GAAG,GAAG,IAAI,MAAM,MAAM,IAAI,OAAO,IAAI,KAAK,SAAS,OAAO;EAE5E,MAAM,QAAQ,OAAO,WAAW,WAAW,WAAqB;GAC9D,IAAI,UAAU,KAAA,GAAW,QAAQ,OAAO,iBAAiB,KAAK,SAAS,OAAO,CAAC;QAC1E;IACH,OAAO,wBAAwB,GAAG,GAAG,WAAW;IAChD,OAAO,wBAAwB,kBAAkB,KAAK,GAAG,eAAe,CAAC;GAC3E;EACF,CAAC;EAED,IAAI,UAAU,KAAA,GAAW;GACvB,IACE,EAAE,wBAAwB,KAAA,KACzB,EAAE,WAAW,KAAA,KAAa,EAAE,wBAAwB,KAAA,GAErD,OAAO,OAAO,YAAY;GAE5B,MAAM,UAAU,MAAM,SAAS,YAAY,KACxC,WACC,GAAG,wBAAwB,aAAa,KAAK,SAAS,WAAW,cAAc,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,cAAc,EAAE,cAAc,OAAO,YAAY,EAAE,OAAO,MAAM,cAAc,EAAE,UAAU,OAAO,QAAQ,EAAE,GAAG,EAAE,WAAW,KAAA,IAAY,GAAG,KAAK,GAAG,OAAO,aAAa,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,MAAM,EAAE,QAAQ,EAAE,mBAAmB,IAAI,EACzW;GAEA,MAAM,SACJ,EAAE,WAAW,KAAA,IACT,GAAG,UACH,GAAG,GAAG,aAAa,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,MAAM,EAAE,QAAQ,EAAE,mBAAmB;GAEjG,MAAM,YAAY,IAAI,IAAI;IACxB,GAAG,wBAAwB,WAAW,KAAK,SAAS,WAAW,YAAY,EAAE,IAAI,QAAQ,EAAE,OAAO,MAAM,YAAY,EAAE,kBAAkB,MAAM,iBAAiB,gBAAgB,EAAE,OAAO,WAAW,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE;IAC3Q,GAAG,yBAAyB,aAAa,KAAK,SAAS,WAAW,cAAc,EAAE,WAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,QAAQ;IACnI,GAAG;IACH;GACF,CAAC;GAED,OAAO,wBAAwB,kBAAkB,KAAK,SAAS,CAAC;GAChE,IAAI,MAAM,eAAe,KAAA,GAAW;IAClC,IAAI,EAAE,uBAAuB,KAAA,GAAW,OAAO,OAAO,YAAY;IAClE,OAAO,wBACL,kBACE,KACA,GAAG,wBAAwB,OAAO,EAAE,KAAK,CAAC,CAAC,KAAK,SAAS,MAAM,OAAO,EAAE,KAAK,GAAG,EAAE,WAAW,MAAM,WAAW,WAAW,SAAS,EAAE,OAAO,MAAM,OAAO,EAAE,KAAK,GAAG,EAAE,OAAO,MAAM,WAAW,WAAW,KAAK,EAAE,OAAO,WAAW,OAAO,EAAE,KAAK,GAAG,EAAE,WAAW,QAAQ,EAAE,OAAO,OAAO,WAAW,EAAE,mBAAmB;KAAE,YAAY,MAAM,WAAW;KAAY,iBAAiB;IAAS,CAAC,CAAC,EAAE,EACjY,CACF;GACF;EACF;EACA,MAAM,YAAY;GAAE;GAAc;GAAoB;EAAgB;EAEtE,IAAI,MAAM,eAAe,KAAA,GAAW;GAClC,OAAO,MACL,GAAG,GAAG,YAAY,OAAO,EAAE,aAAa;IAAE,UAAU,MAAM;IAAU,WAAW;IAAU,aAAa,MAAM;IAAa,GAAG;GAAU,CAAC,CAAC,GAC1I;GACA,OAAO,MACL,GAAG,GAAG,aAAa,OAAO,EAAE,aAAa;IAAE,WAAW;IAAU;IAAc,UAAU;GAAmB,CAAC,CAAC,GAC/G;EACF,OAAO;GACL,MAAM,MAAM,MAAM;GAElB,OAAO,MACL,GAAG,GAAG,YAAY,OAAO,EAAE,kBAAkB;IAAE,aAAa,MAAM;IAAa;IAAoB;GAAgB,CAAC,CAAC,EAAE,SAAS,WAAW,aAAa,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,aAAa,EAAE,UAAU,MAAM,QAAQ,EAAE,OAAO,MAAM,aAAa,EAAE,cAAc,IAAI,YAAY,EAAE,OAAO,MAAM,aAAa,EAAE,oBAAoB,IAAI,kBAAkB,EAAE,OAAO,MAAM,aAAa,EAAE,iBAAiB,IAAI,eAAe,EAAE,OAAO,MAAM,aAAa,EAAE,UAAU,SAAS,MAAM,IAAI,QAAQ,CAAC,EAAE,OAAO,MAAM,aAAa,EAAE,eAAe,IAAI,aAAa,EAAE,OAAO,WACtjB;GACA,OAAO,MACL,GAAG,GAAG,aAAa,OAAO,EAAE,eAAe,kBAAkB,CAAC,EAAE,SAAS,WAAW,cAAc,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,cAAc,EAAE,cAAc,IAAI,YAAY,EAAE,OAAO,MAAM,cAAc,EAAE,UAAU,IAAI,kBAAkB,GACrP;EACF;EAEA,MAAM,kBAAkB,MAAM,SAAS,YACpC,QAAQ,WAAW,OAAO,iBAAiB,YAAY,CAAC,CACxD,OAAO;GAAE;GAAc,UAAU;EAAmB,CAAC;EAExD,MAAM,gBAAgB,GAAG,wBAAwB,YAAY,KAAK,SAAS,WAAW,aAAa,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,aAAa,EAAE,UAAU,MAAM,QAAQ,EAAE,OAAO,MAAM,aAAa,EAAE,cAAc,YAAY,EAAE,OAAO,MAAM,aAAa,EAAE,oBAAoB,kBAAkB,EAAE,OAAO,MAAM,aAAa,EAAE,iBAAiB,eAAe,EAAE,OAAO,MAAM,aAAa,EAAE,UAAU,SAAS,MAAM,MAAM,YAAY,QAAQ,CAAC,EAAE,OAAO,MAAM,aAAa,EAAE,eAAe,MAAM,YAAY,aAAa,EAAE;EAExgB,MAAM,kBACH,EAAE,WAAW,KAAA,KAAa,EAAE,wBAAwB,KAAA,OACpD,MAAM,eAAe,KAAA,KAAa,EAAE,uBAAuB,KAAA;EAE9D,IAAI;EAEJ,IAAI,gBAAgB;GAClB,MAAM,SACJ,EAAE,WAAW,KAAA,IACT,GAAG,UACH,GAAG,GAAG,aAAa,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,MAAM,EAAE,QAAQ,EAAE,mBAAmB;GAEjG,MAAM,aAA8B;IAClC;IACA;IACA,GAAG,yBAAyB,aAAa,KAAK,SAAS,WAAW,cAAc,EAAE,WAAW,QAAQ,EAAE,OAAO,OAAO,MAAM,gBAAgB;IAC3I,GAAG,gBAAgB,KAChB,WACC,GAAG,wBAAwB,aAAa,KAAK,SAAS,WAAW,cAAc,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,cAAc,EAAE,cAAc,OAAO,YAAY,EAAE,OAAO,MAAM,cAAc,EAAE,UAAU,OAAO,QAAQ,EAAE,OAAO,OAAO,EAChP;GACF;GAEA,IAAI,MAAM,eAAe,KAAA,GAAW;IAClC,MAAM,WAAW,MAAM;IACvB,MAAM,kBAAkB,OAAO,EAAE,KAAK;IAEtC,OAAO,EAAE,uBAAuB,KAAA,CAAS;IACzC,WAAW,KACT,GAAG,wBAAwB,gBAAgB,KAAK,SAAS,MAAM,iBAAiB,EAAE,WAAW,SAAS,WAAW,SAAS,EAAE,OAAO,MAAM,iBAAiB,EAAE,OAAO,SAAS,WAAW,KAAK,EAAE,OAAO,WAAW,iBAAiB,EAAE,WAAW,QAAQ,EAAE,OAAO,OAAO,WAAW,EAAE,mBAAmB;KAAE,YAAY,SAAS;KAAY,iBAAiB;IAAS,CAAC,CAAC,EAAE,IACvW,GAAG,wBAAwB,gBAAgB,KAAK,SAAS,MAAM,iBAAiB,EAAE,WAAW,SAAS,WAAW,SAAS,EAAE,OAAO,MAAM,iBAAiB,EAAE,OAAO,SAAS,WAAW,KAAK,EAAE,OAAO,WAAW,iBAAiB,EAAE,WAAW,QAAQ,EAAE,OAAO,MAAM,iBAAiB,EAAE,iBAAiB,SAAS,yBAAyB,EAAE,OAAO,SAAS,+BAA+B,KAAA,IAAY,GAAG,GAAG,gBAAgB,OAAO,EAAE,UAAU,EAAE,YAAY,GAAG,GAAG,gBAAgB,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,MAAM,EAAE,YAAY,QAAQ,MAAM,cAAc,SAAS,0BAA0B,CAAC,IAAI,EACzlB;GACF;GACA,iBAAiB,IAAI,IAAI,UAAU;EACrC;EACA,OAAO,MACL,GAAG,GAAG,WAAW,OAAO,GAAG,EAAE,mBAAmB,qBAAqB,CAAC,EAAE,SAAS,WAAW,YAAY,EAAE,IAAI,QAAQ,EAAE,OAAO,MAAM,YAAY,EAAE,kBAAkB,MAAM,iBAAiB,gBAAgB,EAAE,OAAO,WAAW,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,MAAM,EAAE,QAAQ,OAAO,EAAE,OAAO,EAAE,OAAO,UAAU,OAAO,kBAAkB,GAAG,SAClV;EACA,IAAI,UAAU,KAAA,GAAW;GACvB,OAAO,mBAAmB,KAAA,CAAS;GACnC,OAAO,8BAA8B;IACnC,MAAM;IACN,WAAW,kBACT,KACA,IAAI,IAAI,CACN,gBACA,GAAG,wBAAwB,WAAW,KAAK,SAAS,WAAW,YAAY,EAAE,IAAI,QAAQ,EAAE,OAAO,MAAM,YAAY,EAAE,kBAAkB,oBAAoB,EAAE,OAAO,WAAW,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE,EAC1P,CAAC,CACH;GACF,CAAC;EACH,OAAO,IAAI,OAAO,OAAO,MAAM,KAAK,mBAAmB,KAAA,GACrD,OAAO,yBAAyB;GAC9B,MAAM;GACN,OAAO,OAAO,IAAI,aAAa;IAC7B,MAAM,QAAQ,OAAO,KACnB,MAAM,UACN,MAAM,iBAAiB,WACvB,MAAM,YAAY,UACpB;IAEA,OACE,UAAU,KAAA,KACR,EAAE,eAAe,MAAM,WAAW,EAAE,OAAO,KAC3C,MAAM,SAAS,qBAAqB,wBACpC,MAAM,SAAS,YAAY,WAAW,gBAAgB,UACtD,gBAAgB,OAAO,WACrB,MAAM,SAAS,YAAY,MACxB,UACC,MAAM,iBAAiB,OAAO,gBAC9B,MAAM,aAAa,OAAO,QAC9B,CACF,KACA,MAAM,OAAO,MAAM,OACnB,MAAM,MAAM,QAChB;IACA,MAAM,WAAW,MAAM;IAEvB,OACE,aAAa,KAAA,KACX,SAAS,EAAE,kBAAkB,gBAC7B,SAAS,EAAE,wBAAwB,sBACnC,SAAS,EAAE,qBAAqB,mBAChC,SAAS,EAAE,cAAc,SAAS,MAAM,MAAM,YAAY,QAAQ,KAClE,SAAS,EAAE,mBAAmB,MAAM,YAAY,aACpD;IACA,IAAI,MAAM,eAAe,KAAA,GACvB,OACE,MAAM,aAAa,KAAA,KACjB,MAAM,SAAS,8BACb,MAAM,WAAW,6BACnB,MAAM,SAAS,+BACb,MAAM,WAAW,0BACvB;GACJ,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UACf,wBAAwB,KAAK;IAAE,WAAW;IAAU;GAAM,CAAC,CAC7D,CACF;EACF,CAAC;EAEH,OAAO,OAAO,QAAQ,WAAoB,OAAO;CACnD,CAAC,CACH;CA8CF,OAAO,EAAE,qBAAA;EApCP,GAAG,OARsB,wBAAwB,QAAQ,SAAS;GAClE,MAAM;GACN,SAAS;GACT,aAAa,OAAO,OAAO,MAAM;GACjC,iBAAiB,kBAAkB,aAAa,IAAI,kBAAkB;EACxE,CAAC;EAIC,iBAAiB,UACf,SAAS,KACP,OAAO,IAAI,KAAK,MAAM,UAAU,MAAM,SAAS,IAAI,UACjD,UAAU,KAAA,KAAa,CAAC,EAAE,eAAe,MAAM,WAAW,EAAE,OAAO,IAC/D,OAAO,KAAK,IACZ,OAAO,gBAAgB,MAAM,QAAQ,CAC3C,CACF;EACF,iBAAiB,UACf,SAAS,KACP,OAAO,IAAI,aAAa;GACtB,MAAM,OACJ,OAAO,GAAG,UAAU,WAAW,OAAO,oBAAoB,EAAE,QAAQ,WAAW,KAAK,SAAS,MAAM,YAAY,EAAE,WAAW,MAAM,WAAW,SAAS,EAAE,OAAO,MAAM,YAAY,EAAE,OAAO,MAAM,WAAW,KAAK,EAAE;GAEpN,IAAI,KAAK,WAAW,GAAG,OAAO,OAAO,KAAK;GAC1C,OAAO,KAAK,WAAW,CAAC;GACxB,MAAM,MAAM,WAAW,OAAO,KAAK,IAAK,oBAAoB;GAE5D,IAAI,CAAC,EAAE,UAAU,GAAG,GAAG,OAAO,OAAO,KAAK;GAC1C,MAAM,YAAY,OAAO,QAAQ,UAAU,UAAU,IAAI,EAAE,UAAU;GACrE,MAAM,QAAQ,OAAO,KAAK,MAAM,UAAU,WAAW,MAAM,UAAU;GAErE,OAAO,UAAU,KAAA,KACf,CAAC,EAAE,eAAe,MAAM,WAAW,EAAE,OAAO,KAC5C,MAAM,UAAU,+BAA+B,KAAA,IAC7C,OAAO,KAAK,IACZ,OAAO,KAAK,MAAM,QAAQ;EAChC,CAAC,CACH;EACF,cAAc,OAAO,YAAY,SAAS,OAAO,gBAAgB,OAAO;EACxE,mBAAmB,OAAO,YAAY,SAAS,OAAO,mBAAmB,OAAO;EAChF,iBAAiB,OAAO,YACtB,SAAS,OAAO,kBAAkB,SAAS,MAAM,UAAU;CAGpC,EAAE;AAC/B,CAAC"}
|