@yielded/auth-persistence 0.1.0-beta.8 → 0.1.0-beta.9
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 +4 -0
- package/dist/DrizzleD1.d.mts +7 -7
- package/dist/DrizzleLibsql.d.mts +31 -31
- package/dist/DrizzleLibsql.mjs +7 -22
- package/dist/DrizzleLibsql.mjs.map +1 -1
- package/dist/DrizzleMysql2.d.mts +8 -8
- package/dist/DrizzlePglite.d.mts +15 -15
- package/dist/DrizzlePostgres.d.mts +13 -13
- package/dist/DrizzleSqliteBun.d.mts +24 -24
- package/dist/DrizzleSqliteDo.d.mts +29 -29
- package/dist/DrizzleSqliteNode.d.mts +22 -22
- package/dist/DrizzleSqliteWasm.d.mts +24 -24
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/drizzle/D1BatchStatements.d.mts +1 -2
- package/dist/drizzle/SuppliedService.d.mts +1 -2
- package/dist/drizzle/d1-emails.d.mts +5 -6
- package/dist/drizzle/d1-identity.d.mts +2 -3
- package/dist/drizzle/d1-oauth.d.mts +10 -11
- package/dist/drizzle/d1-passkey.d.mts +5 -6
- package/dist/drizzle/d1-password-prepared.d.mts +2 -3
- package/dist/drizzle/d1-passwords.d.mts +4 -5
- package/dist/drizzle/d1-phone.d.mts +2 -3
- package/dist/drizzle/d1-planning.d.mts +1 -2
- package/dist/drizzle/d1-proofs.d.mts +2 -3
- package/dist/drizzle/d1-sessions.d.mts +10 -11
- package/dist/drizzle/d1-totp.d.mts +2 -3
- package/dist/drizzle/email-model.d.mts +30 -31
- package/dist/drizzle/email-registration.d.mts +1 -2
- package/dist/drizzle/email-target.d.mts +3 -4
- package/dist/drizzle/model.d.mts +41 -41
- package/dist/drizzle/model.mjs +3 -5
- package/dist/drizzle/model.mjs.map +1 -1
- package/dist/drizzle/mysql-emails.d.mts +7 -8
- package/dist/drizzle/mysql-identity.d.mts +2 -3
- package/dist/drizzle/mysql-password-prepared.d.mts +3 -4
- package/dist/drizzle/mysql-passwords.d.mts +6 -7
- package/dist/drizzle/mysql-proofs.d.mts +3 -4
- package/dist/drizzle/mysql-sessions.d.mts +15 -16
- package/dist/drizzle/mysql.d.mts +8 -9
- package/dist/drizzle/oauth-connected-model.d.mts +21 -22
- package/dist/drizzle/oauth-connected-reference.d.mts +1 -2
- package/dist/drizzle/oauth-connected-state.d.mts +1950 -7
- package/dist/drizzle/oauth-connected-target.d.mts +7 -8
- package/dist/drizzle/oauth-flow.d.mts +22 -2
- package/dist/drizzle/oauth-model.d.mts +45 -46
- package/dist/drizzle/oauth-owner.d.mts +10 -2
- package/dist/drizzle/oauth-target.d.mts +9 -10
- package/dist/drizzle/passkey-model.d.mts +34 -35
- package/dist/drizzle/passkey-registration-ceremony-model.d.mts +5 -6
- package/dist/drizzle/passkey-write-model.d.mts +13 -14
- package/dist/drizzle/password-model.d.mts +29 -30
- package/dist/drizzle/password-prepared-model.d.mts +10 -11
- package/dist/drizzle/password-prepared-target.d.mts +1 -2
- package/dist/drizzle/password-registration.d.mts +1 -2
- package/dist/drizzle/password-target.d.mts +2 -3
- package/dist/drizzle/pg-emails.d.mts +7 -8
- package/dist/drizzle/pg-password-prepared.d.mts +3 -4
- package/dist/drizzle/pg-passwords.d.mts +6 -7
- package/dist/drizzle/pg-proofs.d.mts +3 -4
- package/dist/drizzle/pg-sessions.d.mts +15 -16
- package/dist/drizzle/pg.d.mts +10 -11
- package/dist/drizzle/phone-model.d.mts +9 -10
- package/dist/drizzle/phone-target.d.mts +1 -2
- package/dist/drizzle/proof-model.d.mts +25 -26
- package/dist/drizzle/proof-target.d.mts +1 -2
- package/dist/drizzle/session-model.d.mts +25 -26
- package/dist/drizzle/session-target.d.mts +4 -5
- package/dist/drizzle/sqlite.d.mts +8 -9
- package/dist/drizzle/standalone-guard.mjs +3 -5
- package/dist/drizzle/standalone-guard.mjs.map +1 -1
- package/dist/drizzle/step-up-model.d.mts +6 -7
- package/dist/drizzle/totp-model.d.mts +7 -8
- package/dist/drizzle/totp-state.d.mts +2 -3
- package/dist/drizzle/totp-target.d.mts +1 -2
- package/dist/drizzle/transaction-mysql.mjs +1 -0
- package/dist/drizzle/transaction-mysql.mjs.map +1 -1
- package/dist/drizzle/transaction-owner.d.mts +3 -2
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/internal/configuration.d.mts +19 -19
- package/dist/internal/mapping-error.d.mts +2 -3
- package/dist/internal/oauth-app.d.mts +1 -2
- package/dist/internal/oauth-app.mjs +3 -4
- package/dist/internal/oauth-app.mjs.map +1 -1
- package/dist/internal/oauth-server.d.mts +10 -0
- package/dist/internal/oauth-server.mjs +66 -0
- package/dist/internal/oauth-server.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +2 -3
- package/dist/internal/passkeys.mjs +2 -1
- package/dist/internal/passkeys.mjs.map +1 -1
- package/dist/internal/persistence.mjs +3 -2
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/sql-persistence.d.mts +1 -2
- package/dist/internal/sql-table.d.mts +6 -7
- package/dist/internal/sql-table.mjs +12 -2
- package/dist/internal/sql-table.mjs.map +1 -1
- package/dist/internal/standalone.mjs +13 -0
- package/dist/internal/standalone.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +5 -6
- package/dist/internal/transaction-execution-kernel.d.mts +2 -3
- package/dist/internal/transaction-execution-kernel.mjs +3 -5
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
- package/dist/internal/transaction-kernel.d.mts +49 -2
- package/package.json +1 -1
- package/src/DrizzleLibsql.ts +10 -54
- package/src/drizzle/model.ts +8 -10
- package/src/drizzle/standalone-guard.ts +4 -9
- package/src/drizzle/transaction-mysql.ts +2 -0
- package/src/index.ts +1 -0
- package/src/internal/oauth-app.ts +4 -5
- package/src/internal/oauth-server.ts +89 -0
- package/src/internal/passkeys.ts +2 -6
- package/src/internal/persistence.ts +3 -5
- package/src/internal/sql-table.ts +17 -2
- package/src/internal/standalone.ts +30 -0
- package/src/internal/transaction-execution-kernel.ts +3 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/internal/persistence.ts"],"sourcesContent":["import { EmailAddressPersistence, EmailUnavailable } from \"@yielded/auth/Email\";\nimport { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport type { PasskeyConfig } from \"@yielded/auth/Passkey\";\nimport { PasswordPersistence, PasswordUnavailable } from \"@yielded/auth/Password\";\nimport { digest, hooksLayer, randomId } from \"@yielded/auth/Persistence\";\nimport { PhoneAdmission, PhoneSignInTargets, PhoneOtpUnavailable } from \"@yielded/auth/PhoneOtp\";\nimport { ProofPersistence, ProofUnavailable } from \"@yielded/auth/Proofs\";\nimport { AuthenticationAuthority, SessionUnavailable } from \"@yielded/auth/Sessions\";\nimport type { Table } from \"drizzle-orm\";\nimport { Context, Effect, Layer, Option, Schema } from \"effect\";\nimport { SqlClient } from \"effect/unstable/sql\";\n\nimport {\n PersistenceConfigurationError,\n type ClaimsCodec,\n type ConfigId,\n type Definition,\n type PersistenceApi,\n type Ports,\n type Roles,\n type Provisioning,\n type ProvisioningRequirement,\n type StorageLayout,\n type SubjectMapping,\n type SubjectOptions,\n type PasskeyFeature,\n type PasskeyRequirement,\n type MappingInput,\n} from \"./configuration\";\nimport { makeEmailKernel, type EmailSqlDatabase } from \"./email-kernel\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { makePasswordKernel, type PasswordSqlDatabase } from \"./password-kernel\";\nimport { makePhoneKernel, CurrentPhoneTransaction } from \"./phone-kernel\";\nimport { makeProofKernel, type ProofSqlDatabase, type ProofSqlQuery } from \"./proof-kernel\";\nimport type { QueryOperations } from \"./query-operations\";\nimport { makeRegistrationAuthority, type CreateSubject } from \"./registration\";\nimport { makeSessionKernel, type SessionSqlDatabase } from \"./session-kernel\";\nimport { makeMappings } from \"./storage-mapping\";\nimport {\n tableDefinition,\n storageTables,\n type StorageRole,\n type StorageTable,\n} from \"./storage-tables\";\nimport { validateStorage } from \"./storage-validation\";\nimport { makeTransactionExecutionKernel } from \"./transaction-execution-kernel\";\nimport { makeTransactionKernel, type TransactionNativeDatabase } from \"./transaction-kernel\";\n\nexport interface Backend<T extends object, R> {\n readonly makeTable: (definition: StorageTable) => T;\n readonly describe: (table: T) => StorageTable;\n readonly acquire: Effect.Effect<object, PersistenceConfigurationError, R | SqlClient.SqlClient>;\n readonly operations: QueryOperations;\n readonly passkeys: (input: {\n readonly database: object;\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n }) => Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n PasskeyConfig | LifecycleHooks | SqlClient.SqlClient\n >;\n}\n\nconst configError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappingError = (cause: unknown) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause });\n\nconst timestampKeys = new Set([\n \"verifiedAt\",\n \"createdAt\",\n \"lastIssueAt\",\n \"issuedAt\",\n \"expiresAt\",\n \"absoluteExpiresAt\",\n \"retentionUntil\",\n \"claimDeadline\",\n \"retryAt\",\n \"admittedAt\",\n \"deadline\",\n \"occurredAt\",\n \"dedupUntil\",\n]);\n\nexport const createPersistence = <T extends object, R>(\n backend: Backend<T, R>,\n): PersistenceApi<T, R> => {\n const proofKernel = makeProofKernel(backend.operations);\n const passwordKernel = makePasswordKernel(backend.operations, proofKernel);\n const emailKernel = makeEmailKernel(backend.operations, proofKernel);\n const sessionKernel = makeSessionKernel(backend.operations);\n const transactionKernel = makeTransactionKernel(backend.operations);\n const executionKernel = makeTransactionExecutionKernel(transactionKernel);\n const phoneKernel = makePhoneKernel(backend.operations, transactionKernel);\n\n const make = <C extends ClaimsCodec, const Id extends string, const A extends Definition<C, Id>>(\n auth: A & Definition<C, Id>,\n ) => {\n const features = Object.values(auth.strategies).map((strategy) => strategy.persistence);\n const phone = features.some((feature) => feature?.kind === \"phone\");\n const password = features.some((feature) => feature?.kind === \"password\");\n\n const passkeys = features.filter(\n (feature): feature is PasskeyFeature => feature?.kind === \"passkey\",\n );\n\n const management = features.some(\n (feature) => feature?.kind === \"password\" && feature.management,\n );\n\n const email = features.some((feature) => feature?.kind === \"email\" && feature.addresses);\n const proofs = phone || management || email;\n\n const roles: StorageRole[] = [\"identifiers\", \"credentials\", \"sessions\", \"sessionFlows\"];\n\n if (password)\n roles.push(\n \"passwords\",\n \"passwordAttempts\",\n \"passwordScopes\",\n \"passwordCharges\",\n \"passwordCommands\",\n );\n if (management) roles.push(\"passwordRegistrations\");\n if (email) roles.push(\"emailCredentials\", \"emailCommands\");\n if (phone) roles.push(\"phoneState\");\n if (passkeys.length > 0)\n roles.push(\n \"passkeyCredentials\",\n \"passkeyOwnership\",\n \"passkeyHandles\",\n \"passkeyModules\",\n \"passkeyFlows\",\n \"passkeyAdmissions\",\n \"passkeyCharges\",\n );\n if (passkeys.some((feature) => feature.management)) roles.push(\"passkeyCommands\");\n if (proofs)\n roles.push(\n \"proofRequests\",\n \"proofSeries\",\n \"proofGenerations\",\n \"proofContinuations\",\n \"proofScopes\",\n \"proofAbuse\",\n \"proofFailures\",\n \"proofCommands\",\n );\n\n const ConfigKey = Context.Service<\n ConfigId<A[\"namespace\"]>,\n StorageLayout<T, Roles<C, Id, A>>\n >()(`@yielded/auth-persistence/${auth.namespace}/Config`);\n\n const Config = Object.assign(ConfigKey, {\n layer: (value: StorageLayout<T, Roles<C, Id, A>>) => Layer.succeed(ConfigKey, value),\n });\n\n const ProvisioningKey = Context.Service<ProvisioningRequirement<A>, Provisioning<A>>()(\n `@yielded/auth-persistence/${auth.namespace}/Provisioning`,\n );\n\n // Capability metadata controls the conditional requirement, and core has\n // already decoded each registration with its strategy's Schema.\n const provisioners = management\n ? ProvisioningKey.pipe(\n Effect.map((value) => value as Readonly<Record<string, CreateSubject>>),\n )\n : Effect.succeed<Readonly<Record<string, CreateSubject>>>({});\n\n const layout = <N, Instant>(\n options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<StorageRole, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n },\n managed: boolean,\n ): StorageLayout<T, Roles<C, Id, A>> => {\n const subject = options.subjects;\n\n if (passkeys.length > 0 && options.timestamps !== undefined)\n throw configError(\n \"Composed passkey storage uses integer milliseconds; use an explicit passkey adapter for a custom timestamp representation\",\n );\n const description = backend.describe(subject.table);\n const idColumn = description.columns[subject.id];\n\n for (const key of [subject.id, subject.status, subject.securityRevision]) {\n if (description.columns[key] === undefined)\n throw configError(`Missing subject column ${key}`);\n }\n if (subject.activeValue === undefined)\n throw configError(\"An explicit active subject value is required\");\n\n if (idColumn === undefined || idColumn.type === \"boolean\")\n throw configError(\"Subject ID must map a text or integer column\");\n\n const prefix =\n options.prefix ?? `auth_${digest(auth.namespace).slice(0, 12).replaceAll(\"-\", \"_\")}`;\n\n const schema: Partial<Record<StorageRole, T>> = { ...options.tables };\n\n for (const role of roles) {\n if (schema[role] === undefined && managed) {\n const definition = tableDefinition(role, `${prefix}_${role}`, idColumn.type);\n\n const adjusted =\n options.timestamps === undefined\n ? definition\n : {\n ...definition,\n columns: Object.fromEntries(\n Object.entries(definition.columns).map(([key, column]) => [\n key,\n timestampKeys.has(key)\n ? { ...column, type: options.timestamps?.type ?? column.type }\n : column,\n ]),\n ),\n };\n\n schema[role] = backend.makeTable(adjusted);\n }\n }\n for (const role of roles) {\n if (schema[role] === undefined) throw configError(`Missing ${role} table`);\n }\n const instantCodec = options.timestamps?.codec;\n\n const decodeInstant =\n instantCodec === undefined\n ? Schema.decodeUnknownEffect(Schema.Int)\n : Schema.decodeUnknownEffect(instantCodec);\n\n const decodeInstantSync =\n instantCodec === undefined\n ? Schema.decodeUnknownSync(Schema.Int)\n : Schema.decodeUnknownSync(instantCodec);\n\n const subjects: SubjectMapping = {\n table: subject.table,\n id: subject.id,\n status: subject.status,\n securityRevision: subject.securityRevision,\n activeValue: subject.activeValue,\n requirements: subject.requirements,\n actionRequirements: subject.actionRequirements ?? subject.requirements,\n toSubject: (id) =>\n Schema.decodeUnknownEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toNative: (id) =>\n Schema.encodeEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toSubjectSync: Schema.decodeUnknownSync(subject.idCodec),\n toNativeSync: Schema.encodeSync(subject.idCodec),\n };\n\n return Object.freeze({\n namespace: auth.namespace,\n schema: Object.freeze(schema) as Readonly<Record<Roles<C, Id, A>, T>>,\n tables: schema,\n subjects,\n encodeInstant:\n instantCodec === undefined ? (millis: number) => millis : Schema.encodeSync(instantCodec),\n decodeInstant: (value: unknown) => decodeInstant(value).pipe(Effect.mapError(mappingError)),\n decodeInstantSync,\n });\n };\n\n const services = Layer.effectContext(\n Effect.gen(function* () {\n const storage = yield* ConfigKey;\n const hooks = yield* LifecycleHooks;\n const client = yield* SqlClient.SqlClient;\n const database = yield* backend.acquire;\n\n const dialect = client.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* configError(\"Use an explicit adapter for this SQL dialect\");\n if (auth.sessionMode !== \"stateful\")\n return yield* configError(\"The composed layer currently requires stateful sessions\");\n if (\n features.some(\n (feature) =>\n feature === undefined ||\n (\"lifecycle\" in feature && feature.lifecycle) ||\n (feature.kind === \"email\" && !feature.addresses),\n )\n )\n return yield* configError(\n \"Use explicit services for strategies without composed persistence support\",\n );\n if (storage.namespace !== auth.namespace)\n return yield* configError(\n \"Persistence configuration belongs to a different Auth definition\",\n );\n for (const role of roles) {\n const table = (storage.schema as Partial<Record<StorageRole, T>>)[role];\n\n if (table === undefined) return yield* configError(`Missing ${role} table`);\n const description = backend.describe(table);\n const columns = description.columns;\n\n for (const name of Object.keys(storageTables[role].columns)) {\n if (columns[name] === undefined)\n return yield* configError(`Missing ${role}.${name} column`);\n }\n yield* validateStorage(client, dialect, description, storageTables[role].unique);\n }\n yield* validateStorage(client, dialect, backend.describe(storage.subjects.table as T), [\n [storage.subjects.id],\n ]);\n const mappings = makeMappings(storage);\n\n const standalone = <E>(error: () => E) =>\n Effect.serviceOption(client.transactionService).pipe(\n Effect.flatMap((value) => (Option.isNone(value) ? Effect.void : Effect.fail(error()))),\n );\n\n // Backend validation owns the foreign query-builder shape, never the decoded rows.\n const native = database as ProofSqlDatabase &\n PasswordSqlDatabase &\n EmailSqlDatabase &\n SessionSqlDatabase &\n TransactionNativeDatabase;\n\n const options = {\n mode: \"interactive\" as const,\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => SessionUnavailable.make({})),\n };\n\n const sessionMapping = mappings.sessions(auth.claims);\n\n const sessionServices = yield* sessionKernel.makeSqlStatefulSessions(\n native,\n sessionMapping,\n options,\n );\n\n const authority = yield* sessionKernel.makeSqlAuthenticationAuthority<C[\"Type\"]>(\n native,\n { ...mappings.authority(), isConstraintConflict: () => false },\n options,\n );\n\n let context: Context.Context<never> = Context.make(AuthenticationAuthority, authority).pipe(\n Context.add(\n auth.sessions.StatefulSessionPersistence,\n sessionServices.statefulSessionPersistence,\n ),\n Context.add(auth.sessions.SessionRepository, sessionServices.sessionRepository),\n );\n\n const proofConfiguration = proofs\n ? {\n mapping: mappings.proofs(),\n configuration: {\n mode: \"interactive\" as const,\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => ProofUnavailable.make({})),\n insertIfAbsent: (query: ProofSqlQuery) => query.onConflictDoNothing(),\n },\n }\n : undefined;\n\n if (proofConfiguration !== undefined) {\n context = Context.add(\n context,\n ProofPersistence,\n yield* proofKernel.makeSqlProofPersistence(\n native,\n proofConfiguration.mapping,\n proofConfiguration.configuration,\n ),\n );\n }\n\n if (password) {\n const persistence = yield* passwordKernel.makeSqlPasswordPersistence(\n native,\n mappings.passwords(),\n {\n mode: \"interactive\",\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => PasswordUnavailable.make({})),\n insertIfAbsent: (query) => query.onConflictDoNothing(),\n proof: proofConfiguration,\n },\n );\n\n context = Context.add(context, PasswordPersistence, persistence);\n }\n const creators = yield* provisioners;\n\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n const create = creators[name];\n\n if (key === undefined || create === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n const registration = yield* makeRegistrationAuthority(\n native,\n mappings,\n backend.operations,\n standalone(() => PasswordUnavailable.make({})),\n create,\n );\n\n context = Context.add(context, key, registration);\n }\n if (email) {\n context = Context.add(\n context,\n EmailAddressPersistence,\n yield* emailKernel.makeSqlEmailAddressPersistence(native, mappings.emails(), {\n mode: \"interactive\",\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => EmailUnavailable.make({})),\n proof: proofConfiguration,\n }),\n );\n }\n if (phone) {\n const { column, eq } = backend.operations;\n\n const mapping = (moduleId: string) => ({\n moduleId,\n subject: {\n table: storage.subjects.table,\n id: storage.subjects.id,\n securityRevision: storage.subjects.securityRevision,\n activeCondition: eq(\n column(storage.subjects.table as Table, storage.subjects.status),\n storage.subjects.activeValue,\n ),\n },\n subjectIds: {\n toNative: storage.subjects.toNativeSync,\n toSubject: storage.subjects.toSubjectSync,\n },\n identifier: {\n table: mappings.table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n revision: \"revision\",\n verifiedAt: \"verifiedAt\",\n activeCondition: eq(column(mappings.table(\"identifiers\"), \"active\"), true),\n },\n credential: {\n table: mappings.table(\"credentials\"),\n id: \"credentialId\",\n subjectId: \"subjectId\",\n revision: \"revision\",\n activeCondition: eq(column(mappings.table(\"credentials\"), \"active\"), true),\n },\n state: {\n table: mappings.table(\"phoneState\"),\n scope: \"scope\",\n state: \"state\",\n version: \"version\",\n encodeInsert: (row: object) => row,\n },\n admission: {\n windowMillis: 60_000,\n networkRequests: 10,\n networkAttempts: 100,\n maximumMessages: 10,\n requestRetentionMillis: 86_400_000,\n },\n engineNowMillis: backend.operations\n .sql`${dialect === \"pg\" ? backend.operations.sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)` : backend.operations.sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`}`,\n encodeInstant: storage.encodeInstant,\n });\n\n const execution = executionKernel.makeTransactionExecution(\n CurrentPhoneTransaction,\n native,\n {\n mode: \"interactive\",\n dialect,\n locking: dialect === \"pg\",\n standaloneGuard: () => standalone(() => PhoneOtpUnavailable.make({})),\n },\n () => PhoneOtpUnavailable.make({}),\n randomId,\n );\n\n const run = <Value, E, Requirements>(\n work: Effect.Effect<Value, E, Requirements>,\n mutation = true,\n ) =>\n execution.admit.pipe(\n Effect.andThen(execution.run(work, mutation)),\n Effect.provideService(LifecycleHooks, hooks),\n );\n\n const allowed = (moduleId: string) =>\n features.some((feature) => feature?.kind === \"phone\" && feature.moduleId === moduleId);\n\n const admission = PhoneAdmission.of({\n admit: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.admitPhone(mapping(request.moduleId), request))\n : Effect.fail(PhoneOtpUnavailable.make({})),\n cleanup: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.cleanupPhoneAdmission(mapping(request.moduleId), request))\n : Effect.fail(PhoneOtpUnavailable.make({})),\n });\n\n const targets = PhoneSignInTargets.of({\n lookup: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.lookupPhone(mapping(request.moduleId), request), false)\n : Effect.fail(PhoneOtpUnavailable.make({})),\n });\n\n context = context.pipe(\n Context.add(PhoneAdmission, admission),\n Context.add(PhoneSignInTargets, targets),\n );\n }\n\n if (passkeys.length > 0) {\n // Auth capability metadata determines whether PasskeyConfig is required.\n const services = backend.passkeys({\n database,\n storage,\n namespace: auth.namespace,\n dialect,\n features: passkeys,\n passwordModules: features.flatMap((feature) =>\n feature?.kind === \"password\" ? [feature.moduleId] : [],\n ),\n }) as Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n PasskeyRequirement<A> | LifecycleHooks | SqlClient.SqlClient\n >;\n\n context = Context.merge(context, yield* services);\n }\n\n // The checked capability metadata above determines exactly these service keys.\n return context as Context.Context<Ports<C, Id, A>>;\n }),\n ).pipe(Layer.provide(hooksLayer));\n\n return {\n Config,\n Provisioning: ProvisioningKey,\n layer: services,\n managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, true),\n map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Partial<Record<StorageRole, T>>;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, false),\n };\n };\n\n return { make };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistence.mjs","names":[],"sources":["../../src/internal/persistence.ts"],"sourcesContent":["import { EmailAddressPersistence, EmailUnavailable } from \"@yielded/auth/Email\";\nimport { LifecycleHooks } from \"@yielded/auth/Hooks\";\nimport type { PasskeyConfig } from \"@yielded/auth/Passkey\";\nimport { PasswordPersistence, PasswordUnavailable } from \"@yielded/auth/Password\";\nimport { digest, hooksLayer, randomId } from \"@yielded/auth/Persistence\";\nimport { PhoneAdmission, PhoneSignInTargets, PhoneOtpUnavailable } from \"@yielded/auth/PhoneOtp\";\nimport { ProofPersistence, ProofUnavailable } from \"@yielded/auth/Proofs\";\nimport { AuthenticationAuthority, SessionUnavailable } from \"@yielded/auth/Sessions\";\nimport type { Table } from \"drizzle-orm\";\nimport { Context, Effect, Layer, Schema } from \"effect\";\nimport { SqlClient } from \"effect/unstable/sql\";\n\nimport {\n PersistenceConfigurationError,\n type ClaimsCodec,\n type ConfigId,\n type Definition,\n type PersistenceApi,\n type Ports,\n type Roles,\n type Provisioning,\n type ProvisioningRequirement,\n type StorageLayout,\n type SubjectMapping,\n type SubjectOptions,\n type PasskeyFeature,\n type PasskeyRequirement,\n type MappingInput,\n} from \"./configuration\";\nimport { makeEmailKernel, type EmailSqlDatabase } from \"./email-kernel\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { makePasswordKernel, type PasswordSqlDatabase } from \"./password-kernel\";\nimport { makePhoneKernel, CurrentPhoneTransaction } from \"./phone-kernel\";\nimport { makeProofKernel, type ProofSqlDatabase, type ProofSqlQuery } from \"./proof-kernel\";\nimport type { QueryOperations } from \"./query-operations\";\nimport { makeRegistrationAuthority, type CreateSubject } from \"./registration\";\nimport { makeSessionKernel, type SessionSqlDatabase } from \"./session-kernel\";\nimport { requireStandalone } from \"./standalone\";\nimport { makeMappings } from \"./storage-mapping\";\nimport {\n tableDefinition,\n storageTables,\n type StorageRole,\n type StorageTable,\n} from \"./storage-tables\";\nimport { validateStorage } from \"./storage-validation\";\nimport { makeTransactionExecutionKernel } from \"./transaction-execution-kernel\";\nimport { makeTransactionKernel, type TransactionNativeDatabase } from \"./transaction-kernel\";\n\nexport interface Backend<T extends object, R> {\n readonly makeTable: (definition: StorageTable) => T;\n readonly describe: (table: T) => StorageTable;\n readonly acquire: Effect.Effect<object, PersistenceConfigurationError, R | SqlClient.SqlClient>;\n readonly operations: QueryOperations;\n readonly passkeys: (input: {\n readonly database: object;\n readonly storage: MappingInput;\n readonly namespace: string;\n readonly dialect: \"pg\" | \"sqlite\";\n readonly features: ReadonlyArray<PasskeyFeature>;\n readonly passwordModules: ReadonlyArray<string>;\n }) => Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n PasskeyConfig | LifecycleHooks | SqlClient.SqlClient\n >;\n}\n\nconst configError = (reason: string) => PersistenceConfigurationError.make({ reason });\n\nconst mappingError = (cause: unknown) =>\n PersistenceMappingError.make({ operation: \"mapping\", cause });\n\nconst timestampKeys = new Set([\n \"verifiedAt\",\n \"createdAt\",\n \"lastIssueAt\",\n \"issuedAt\",\n \"expiresAt\",\n \"absoluteExpiresAt\",\n \"retentionUntil\",\n \"claimDeadline\",\n \"retryAt\",\n \"admittedAt\",\n \"deadline\",\n \"occurredAt\",\n \"dedupUntil\",\n]);\n\nexport const createPersistence = <T extends object, R>(\n backend: Backend<T, R>,\n): PersistenceApi<T, R> => {\n const proofKernel = makeProofKernel(backend.operations);\n const passwordKernel = makePasswordKernel(backend.operations, proofKernel);\n const emailKernel = makeEmailKernel(backend.operations, proofKernel);\n const sessionKernel = makeSessionKernel(backend.operations);\n const transactionKernel = makeTransactionKernel(backend.operations);\n const executionKernel = makeTransactionExecutionKernel(transactionKernel);\n const phoneKernel = makePhoneKernel(backend.operations, transactionKernel);\n\n const make = <C extends ClaimsCodec, const Id extends string, const A extends Definition<C, Id>>(\n auth: A & Definition<C, Id>,\n ) => {\n const features = Object.values(auth.strategies).map((strategy) => strategy.persistence);\n const phone = features.some((feature) => feature?.kind === \"phone\");\n const password = features.some((feature) => feature?.kind === \"password\");\n\n const passkeys = features.filter(\n (feature): feature is PasskeyFeature => feature?.kind === \"passkey\",\n );\n\n const management = features.some(\n (feature) => feature?.kind === \"password\" && feature.management,\n );\n\n const email = features.some((feature) => feature?.kind === \"email\" && feature.addresses);\n const proofs = phone || management || email;\n\n const roles: StorageRole[] = [\"identifiers\", \"credentials\", \"sessions\", \"sessionFlows\"];\n\n if (password)\n roles.push(\n \"passwords\",\n \"passwordAttempts\",\n \"passwordScopes\",\n \"passwordCharges\",\n \"passwordCommands\",\n );\n if (management) roles.push(\"passwordRegistrations\");\n if (email) roles.push(\"emailCredentials\", \"emailCommands\");\n if (phone) roles.push(\"phoneState\");\n if (passkeys.length > 0)\n roles.push(\n \"passkeyCredentials\",\n \"passkeyOwnership\",\n \"passkeyHandles\",\n \"passkeyModules\",\n \"passkeyFlows\",\n \"passkeyAdmissions\",\n \"passkeyCharges\",\n );\n if (passkeys.some((feature) => feature.management)) roles.push(\"passkeyCommands\");\n if (proofs)\n roles.push(\n \"proofRequests\",\n \"proofSeries\",\n \"proofGenerations\",\n \"proofContinuations\",\n \"proofScopes\",\n \"proofAbuse\",\n \"proofFailures\",\n \"proofCommands\",\n );\n\n const ConfigKey = Context.Service<\n ConfigId<A[\"namespace\"]>,\n StorageLayout<T, Roles<C, Id, A>>\n >()(`@yielded/auth-persistence/${auth.namespace}/Config`);\n\n const Config = Object.assign(ConfigKey, {\n layer: (value: StorageLayout<T, Roles<C, Id, A>>) => Layer.succeed(ConfigKey, value),\n });\n\n const ProvisioningKey = Context.Service<ProvisioningRequirement<A>, Provisioning<A>>()(\n `@yielded/auth-persistence/${auth.namespace}/Provisioning`,\n );\n\n // Capability metadata controls the conditional requirement, and core has\n // already decoded each registration with its strategy's Schema.\n const provisioners = management\n ? ProvisioningKey.pipe(\n Effect.map((value) => value as Readonly<Record<string, CreateSubject>>),\n )\n : Effect.succeed<Readonly<Record<string, CreateSubject>>>({});\n\n const layout = <N, Instant>(\n options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<StorageRole, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n },\n managed: boolean,\n ): StorageLayout<T, Roles<C, Id, A>> => {\n const subject = options.subjects;\n\n if (passkeys.length > 0 && options.timestamps !== undefined)\n throw configError(\n \"Composed passkey storage uses integer milliseconds; use an explicit passkey adapter for a custom timestamp representation\",\n );\n const description = backend.describe(subject.table);\n const idColumn = description.columns[subject.id];\n\n for (const key of [subject.id, subject.status, subject.securityRevision]) {\n if (description.columns[key] === undefined)\n throw configError(`Missing subject column ${key}`);\n }\n if (subject.activeValue === undefined)\n throw configError(\"An explicit active subject value is required\");\n\n if (idColumn === undefined || idColumn.type === \"boolean\")\n throw configError(\"Subject ID must map a text or integer column\");\n\n const prefix =\n options.prefix ?? `auth_${digest(auth.namespace).slice(0, 12).replaceAll(\"-\", \"_\")}`;\n\n const schema: Partial<Record<StorageRole, T>> = { ...options.tables };\n\n for (const role of roles) {\n if (schema[role] === undefined && managed) {\n const definition = tableDefinition(role, `${prefix}_${role}`, idColumn.type);\n\n const adjusted =\n options.timestamps === undefined\n ? definition\n : {\n ...definition,\n columns: Object.fromEntries(\n Object.entries(definition.columns).map(([key, column]) => [\n key,\n timestampKeys.has(key)\n ? { ...column, type: options.timestamps?.type ?? column.type }\n : column,\n ]),\n ),\n };\n\n schema[role] = backend.makeTable(adjusted);\n }\n }\n for (const role of roles) {\n if (schema[role] === undefined) throw configError(`Missing ${role} table`);\n }\n const instantCodec = options.timestamps?.codec;\n\n const decodeInstant =\n instantCodec === undefined\n ? Schema.decodeUnknownEffect(Schema.Int)\n : Schema.decodeUnknownEffect(instantCodec);\n\n const decodeInstantSync =\n instantCodec === undefined\n ? Schema.decodeUnknownSync(Schema.Int)\n : Schema.decodeUnknownSync(instantCodec);\n\n const subjects: SubjectMapping = {\n table: subject.table,\n id: subject.id,\n status: subject.status,\n securityRevision: subject.securityRevision,\n activeValue: subject.activeValue,\n requirements: subject.requirements,\n actionRequirements: subject.actionRequirements ?? subject.requirements,\n toSubject: (id) =>\n Schema.decodeUnknownEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toNative: (id) =>\n Schema.encodeEffect(subject.idCodec)(id).pipe(Effect.mapError(mappingError)),\n toSubjectSync: Schema.decodeUnknownSync(subject.idCodec),\n toNativeSync: Schema.encodeSync(subject.idCodec),\n };\n\n return Object.freeze({\n namespace: auth.namespace,\n schema: Object.freeze(schema) as Readonly<Record<Roles<C, Id, A>, T>>,\n tables: schema,\n subjects,\n encodeInstant:\n instantCodec === undefined ? (millis: number) => millis : Schema.encodeSync(instantCodec),\n decodeInstant: (value: unknown) => decodeInstant(value).pipe(Effect.mapError(mappingError)),\n decodeInstantSync,\n });\n };\n\n const services = Layer.effectContext(\n Effect.gen(function* () {\n const storage = yield* ConfigKey;\n const hooks = yield* LifecycleHooks;\n const client = yield* SqlClient.SqlClient;\n const database = yield* backend.acquire;\n\n const dialect = client.onDialectOrElse({\n pg: () => \"pg\" as const,\n sqlite: () => \"sqlite\" as const,\n orElse: () => undefined,\n });\n\n if (dialect === undefined)\n return yield* configError(\"Use an explicit adapter for this SQL dialect\");\n if (auth.sessionMode !== \"stateful\")\n return yield* configError(\"The composed layer currently requires stateful sessions\");\n if (\n features.some(\n (feature) =>\n feature === undefined ||\n (\"lifecycle\" in feature && feature.lifecycle) ||\n (feature.kind === \"email\" && !feature.addresses),\n )\n )\n return yield* configError(\n \"Use explicit services for strategies without composed persistence support\",\n );\n if (storage.namespace !== auth.namespace)\n return yield* configError(\n \"Persistence configuration belongs to a different Auth definition\",\n );\n for (const role of roles) {\n const table = (storage.schema as Partial<Record<StorageRole, T>>)[role];\n\n if (table === undefined) return yield* configError(`Missing ${role} table`);\n const description = backend.describe(table);\n const columns = description.columns;\n\n for (const name of Object.keys(storageTables[role].columns)) {\n if (columns[name] === undefined)\n return yield* configError(`Missing ${role}.${name} column`);\n }\n yield* validateStorage(client, dialect, description, storageTables[role].unique);\n }\n yield* validateStorage(client, dialect, backend.describe(storage.subjects.table as T), [\n [storage.subjects.id],\n ]);\n const mappings = makeMappings(storage);\n\n const standalone = <E>(error: () => E) => requireStandalone(error, client);\n\n // Backend validation owns the foreign query-builder shape, never the decoded rows.\n const native = database as ProofSqlDatabase &\n PasswordSqlDatabase &\n EmailSqlDatabase &\n SessionSqlDatabase &\n TransactionNativeDatabase;\n\n const options = {\n mode: \"interactive\" as const,\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => SessionUnavailable.make({})),\n };\n\n const sessionMapping = mappings.sessions(auth.claims);\n\n const sessionServices = yield* sessionKernel.makeSqlStatefulSessions(\n native,\n sessionMapping,\n options,\n );\n\n const authority = yield* sessionKernel.makeSqlAuthenticationAuthority<C[\"Type\"]>(\n native,\n { ...mappings.authority(), isConstraintConflict: () => false },\n options,\n );\n\n let context: Context.Context<never> = Context.make(AuthenticationAuthority, authority).pipe(\n Context.add(\n auth.sessions.StatefulSessionPersistence,\n sessionServices.statefulSessionPersistence,\n ),\n Context.add(auth.sessions.SessionRepository, sessionServices.sessionRepository),\n );\n\n const proofConfiguration = proofs\n ? {\n mapping: mappings.proofs(),\n configuration: {\n mode: \"interactive\" as const,\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => ProofUnavailable.make({})),\n insertIfAbsent: (query: ProofSqlQuery) => query.onConflictDoNothing(),\n },\n }\n : undefined;\n\n if (proofConfiguration !== undefined) {\n context = Context.add(\n context,\n ProofPersistence,\n yield* proofKernel.makeSqlProofPersistence(\n native,\n proofConfiguration.mapping,\n proofConfiguration.configuration,\n ),\n );\n }\n\n if (password) {\n const persistence = yield* passwordKernel.makeSqlPasswordPersistence(\n native,\n mappings.passwords(),\n {\n mode: \"interactive\",\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => PasswordUnavailable.make({})),\n insertIfAbsent: (query) => query.onConflictDoNothing(),\n proof: proofConfiguration,\n },\n );\n\n context = Context.add(context, PasswordPersistence, persistence);\n }\n const creators = yield* provisioners;\n\n for (const [name, strategy] of Object.entries(auth.strategies)) {\n if (strategy.persistence?.kind !== \"password\" || !strategy.persistence.management)\n continue;\n const key = strategy.RegistrationAuthority;\n const create = creators[name];\n\n if (key === undefined || create === undefined)\n return yield* configError(`Missing subject provisioning for ${name}`);\n\n const registration = yield* makeRegistrationAuthority(\n native,\n mappings,\n backend.operations,\n standalone(() => PasswordUnavailable.make({})),\n create,\n );\n\n context = Context.add(context, key, registration);\n }\n if (email) {\n context = Context.add(\n context,\n EmailAddressPersistence,\n yield* emailKernel.makeSqlEmailAddressPersistence(native, mappings.emails(), {\n mode: \"interactive\",\n locking: dialect === \"pg\",\n standaloneGuard: standalone(() => EmailUnavailable.make({})),\n proof: proofConfiguration,\n }),\n );\n }\n if (phone) {\n const { column, eq } = backend.operations;\n\n const mapping = (moduleId: string) => ({\n moduleId,\n subject: {\n table: storage.subjects.table,\n id: storage.subjects.id,\n securityRevision: storage.subjects.securityRevision,\n activeCondition: eq(\n column(storage.subjects.table as Table, storage.subjects.status),\n storage.subjects.activeValue,\n ),\n },\n subjectIds: {\n toNative: storage.subjects.toNativeSync,\n toSubject: storage.subjects.toSubjectSync,\n },\n identifier: {\n table: mappings.table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n revision: \"revision\",\n verifiedAt: \"verifiedAt\",\n activeCondition: eq(column(mappings.table(\"identifiers\"), \"active\"), true),\n },\n credential: {\n table: mappings.table(\"credentials\"),\n id: \"credentialId\",\n subjectId: \"subjectId\",\n revision: \"revision\",\n activeCondition: eq(column(mappings.table(\"credentials\"), \"active\"), true),\n },\n state: {\n table: mappings.table(\"phoneState\"),\n scope: \"scope\",\n state: \"state\",\n version: \"version\",\n encodeInsert: (row: object) => row,\n },\n admission: {\n windowMillis: 60_000,\n networkRequests: 10,\n networkAttempts: 100,\n maximumMessages: 10,\n requestRetentionMillis: 86_400_000,\n },\n engineNowMillis: backend.operations\n .sql`${dialect === \"pg\" ? backend.operations.sql`cast(extract(epoch from clock_timestamp()) * 1000 as bigint)` : backend.operations.sql`cast(round((julianday('now') - 2440587.5) * 86400000) as integer)`}`,\n encodeInstant: storage.encodeInstant,\n });\n\n const execution = executionKernel.makeTransactionExecution(\n CurrentPhoneTransaction,\n native,\n {\n mode: \"interactive\",\n dialect,\n locking: dialect === \"pg\",\n standaloneGuard: () => standalone(() => PhoneOtpUnavailable.make({})),\n },\n () => PhoneOtpUnavailable.make({}),\n randomId,\n );\n\n const run = <Value, E, Requirements>(\n work: Effect.Effect<Value, E, Requirements>,\n mutation = true,\n ) =>\n execution.admit.pipe(\n Effect.andThen(execution.run(work, mutation)),\n Effect.provideService(LifecycleHooks, hooks),\n );\n\n const allowed = (moduleId: string) =>\n features.some((feature) => feature?.kind === \"phone\" && feature.moduleId === moduleId);\n\n const admission = PhoneAdmission.of({\n admit: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.admitPhone(mapping(request.moduleId), request))\n : Effect.fail(PhoneOtpUnavailable.make({})),\n cleanup: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.cleanupPhoneAdmission(mapping(request.moduleId), request))\n : Effect.fail(PhoneOtpUnavailable.make({})),\n });\n\n const targets = PhoneSignInTargets.of({\n lookup: (request) =>\n allowed(request.moduleId)\n ? run(phoneKernel.lookupPhone(mapping(request.moduleId), request), false)\n : Effect.fail(PhoneOtpUnavailable.make({})),\n });\n\n context = context.pipe(\n Context.add(PhoneAdmission, admission),\n Context.add(PhoneSignInTargets, targets),\n );\n }\n\n if (passkeys.length > 0) {\n // Auth capability metadata determines whether PasskeyConfig is required.\n const services = backend.passkeys({\n database,\n storage,\n namespace: auth.namespace,\n dialect,\n features: passkeys,\n passwordModules: features.flatMap((feature) =>\n feature?.kind === \"password\" ? [feature.moduleId] : [],\n ),\n }) as Effect.Effect<\n Context.Context<never>,\n PersistenceConfigurationError,\n PasskeyRequirement<A> | LifecycleHooks | SqlClient.SqlClient\n >;\n\n context = Context.merge(context, yield* services);\n }\n\n // The checked capability metadata above determines exactly these service keys.\n return context as Context.Context<Ports<C, Id, A>>;\n }),\n ).pipe(Layer.provide(hooksLayer));\n\n return {\n Config,\n Provisioning: ProvisioningKey,\n layer: services,\n managed: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables?: Partial<Record<Roles<C, Id, A>, T>>;\n readonly prefix?: string;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, true),\n map: <N, Instant = number>(options: {\n readonly subjects: SubjectOptions<T, N>;\n readonly tables: Partial<Record<StorageRole, T>>;\n readonly timestamps?: {\n readonly type: \"text\" | \"integer\";\n readonly codec: Schema.Codec<number, Instant>;\n };\n }) => layout(options, false),\n };\n };\n\n return { make };\n};\n"],"mappings":"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"}
|
|
@@ -3,9 +3,8 @@ import { PersistenceApi } from "./configuration.mjs";
|
|
|
3
3
|
import { Table } from "./sql-table.mjs";
|
|
4
4
|
//#region src/internal/sql-persistence.d.ts
|
|
5
5
|
declare const table: (definition: StorageTable) => Table;
|
|
6
|
-
declare const AuthPersistence: PersistenceApi<Table> & {
|
|
6
|
+
export declare const AuthPersistence: PersistenceApi<Table> & {
|
|
7
7
|
readonly table: typeof table;
|
|
8
8
|
};
|
|
9
9
|
//#endregion
|
|
10
|
-
export { AuthPersistence };
|
|
11
10
|
//# sourceMappingURL=sql-persistence.d.mts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
//#region src/internal/sql-table.d.ts
|
|
2
|
-
type Dialect = "pg" | "sqlite";
|
|
3
|
-
interface Compiler {
|
|
2
|
+
export type Dialect = "pg" | "sqlite";
|
|
3
|
+
export interface Compiler {
|
|
4
4
|
readonly dialect: Dialect;
|
|
5
5
|
readonly parameters: unknown[];
|
|
6
6
|
}
|
|
7
|
-
declare class Fragment {
|
|
7
|
+
export declare class Fragment {
|
|
8
8
|
readonly render: (compiler: Compiler) => string;
|
|
9
9
|
readonly decode: (value: unknown) => unknown;
|
|
10
10
|
constructor(render: (compiler: Compiler) => string, decode?: (value: unknown) => unknown);
|
|
@@ -12,12 +12,12 @@ declare class Fragment {
|
|
|
12
12
|
as(_name: string): Fragment;
|
|
13
13
|
getSQL(): Fragment;
|
|
14
14
|
}
|
|
15
|
-
interface ColumnOptions {
|
|
15
|
+
export interface ColumnOptions {
|
|
16
16
|
readonly name: string;
|
|
17
17
|
readonly type: "text" | "integer" | "boolean";
|
|
18
18
|
readonly nullable?: boolean;
|
|
19
19
|
}
|
|
20
|
-
declare class Column extends Fragment {
|
|
20
|
+
export declare class Column extends Fragment {
|
|
21
21
|
readonly tableName: string;
|
|
22
22
|
readonly options: ColumnOptions;
|
|
23
23
|
readonly schema?: string | undefined;
|
|
@@ -25,7 +25,7 @@ declare class Column extends Fragment {
|
|
|
25
25
|
mapToDriverValue(value: unknown): unknown;
|
|
26
26
|
}
|
|
27
27
|
/** Plain SQL identifiers and column representations, independent of an ORM. */
|
|
28
|
-
declare class Table extends Fragment {
|
|
28
|
+
export declare class Table extends Fragment {
|
|
29
29
|
readonly name: string;
|
|
30
30
|
readonly unique: ReadonlyArray<ReadonlyArray<string>>;
|
|
31
31
|
readonly schema?: string | undefined;
|
|
@@ -33,5 +33,4 @@ declare class Table extends Fragment {
|
|
|
33
33
|
constructor(name: string, columns: Readonly<Record<string, ColumnOptions>>, unique: ReadonlyArray<ReadonlyArray<string>>, schema?: string | undefined);
|
|
34
34
|
}
|
|
35
35
|
//#endregion
|
|
36
|
-
export { Column, ColumnOptions, Compiler, Dialect, Fragment, Table };
|
|
37
36
|
//# sourceMappingURL=sql-table.d.mts.map
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
2
|
+
import { Option, Schema, SchemaGetter } from "effect";
|
|
2
3
|
//#region src/internal/sql-table.ts
|
|
4
|
+
const SqlInteger = Schema.Union([
|
|
5
|
+
Schema.Int,
|
|
6
|
+
Schema.BigInt,
|
|
7
|
+
Schema.String.check(Schema.isPattern(/^-?\d+$/))
|
|
8
|
+
]).pipe(Schema.decodeTo(Schema.Int, {
|
|
9
|
+
decode: SchemaGetter.transform(Number),
|
|
10
|
+
encode: SchemaGetter.passthrough()
|
|
11
|
+
}));
|
|
12
|
+
const decodeInteger = Schema.decodeUnknownOption(SqlInteger);
|
|
3
13
|
const identifier = (name) => "\"" + name.replaceAll("\"", "\"\"") + "\"";
|
|
4
14
|
var Fragment = class Fragment {
|
|
5
15
|
render;
|
|
@@ -34,8 +44,8 @@ var Column = class extends Fragment {
|
|
|
34
44
|
});
|
|
35
45
|
}
|
|
36
46
|
if (options.type === "integer") {
|
|
37
|
-
const number =
|
|
38
|
-
if (
|
|
47
|
+
const number = decodeInteger(value);
|
|
48
|
+
if (Option.isSome(number)) return number.value;
|
|
39
49
|
throw PersistenceMappingError.make({
|
|
40
50
|
operation: "decode",
|
|
41
51
|
cause: "SQL integer is outside the safe range"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { PersistenceMappingError } from \"./mapping-error\";\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport interface Compiler {\n readonly dialect: Dialect;\n readonly parameters: unknown[];\n}\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport class Fragment {\n constructor(\n readonly render: (compiler: Compiler) => string,\n readonly decode: (value: unknown) => unknown = (value) => value,\n ) {}\n\n mapWith(decode: (value: unknown) => unknown): Fragment {\n return new Fragment(this.render, decode);\n }\n\n as(_name: string): Fragment {\n return this;\n }\n\n getSQL(): Fragment {\n return this;\n }\n}\n\nexport interface ColumnOptions {\n readonly name: string;\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column extends Fragment {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {\n super(\n () =>\n `${schema === undefined ? \"\" : identifier(schema) + \".\"}${identifier(tableName)}.${identifier(options.name)}`,\n (value) => {\n if (value === null) return value;\n if (options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (options.type === \"integer\") {\n const number =
|
|
1
|
+
{"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { Option, Schema, SchemaGetter } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nconst SqlInteger = Schema.Union([\n Schema.Int,\n Schema.BigInt,\n Schema.String.check(Schema.isPattern(/^-?\\d+$/)),\n]).pipe(\n Schema.decodeTo(Schema.Int, {\n decode: SchemaGetter.transform(Number),\n encode: SchemaGetter.passthrough(),\n }),\n);\n\nconst decodeInteger = Schema.decodeUnknownOption(SqlInteger);\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport interface Compiler {\n readonly dialect: Dialect;\n readonly parameters: unknown[];\n}\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport class Fragment {\n constructor(\n readonly render: (compiler: Compiler) => string,\n readonly decode: (value: unknown) => unknown = (value) => value,\n ) {}\n\n mapWith(decode: (value: unknown) => unknown): Fragment {\n return new Fragment(this.render, decode);\n }\n\n as(_name: string): Fragment {\n return this;\n }\n\n getSQL(): Fragment {\n return this;\n }\n}\n\nexport interface ColumnOptions {\n readonly name: string;\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column extends Fragment {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {\n super(\n () =>\n `${schema === undefined ? \"\" : identifier(schema) + \".\"}${identifier(tableName)}.${identifier(options.name)}`,\n (value) => {\n if (value === null) return value;\n if (options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (options.type === \"integer\") {\n const number = decodeInteger(value);\n\n if (Option.isSome(number)) return number.value;\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"SQL integer is outside the safe range\",\n });\n }\n\n return value;\n },\n );\n }\n\n mapToDriverValue(value: unknown): unknown {\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table extends Fragment {\n readonly columns: Readonly<Record<string, Column>>;\n\n constructor(\n readonly name: string,\n columns: Readonly<Record<string, ColumnOptions>>,\n readonly unique: ReadonlyArray<ReadonlyArray<string>>,\n readonly schema?: string,\n ) {\n super(() => (schema === undefined ? \"\" : identifier(schema) + \".\") + identifier(name));\n this.columns = Object.fromEntries(\n Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]),\n );\n }\n}\n"],"mappings":";;;AAIA,MAAM,aAAa,OAAO,MAAM;CAC9B,OAAO;CACP,OAAO;CACP,OAAO,OAAO,MAAM,OAAO,UAAU,SAAS,CAAC;AACjD,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,OAAO,KAAK;CAC1B,QAAQ,aAAa,UAAU,MAAM;CACrC,QAAQ,aAAa,YAAY;AACnC,CAAC,CACH;AAEA,MAAM,gBAAgB,OAAO,oBAAoB,UAAU;AAU3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;AAE/E,IAAa,WAAb,MAAa,SAAS;CAET;CACA;CAFX,YACE,QACA,UAAgD,UAAU,OAC1D;EAFS,KAAA,SAAA;EACA,KAAA,SAAA;CACR;CAEH,QAAQ,QAA+C;EACrD,OAAO,IAAI,SAAS,KAAK,QAAQ,MAAM;CACzC;CAEA,GAAG,OAAyB;EAC1B,OAAO;CACT;CAEA,SAAmB;EACjB,OAAO;CACT;AACF;AAQA,IAAa,SAAb,cAA4B,SAAS;CAExB;CACA;CACA;CAHX,YACE,WACA,SACA,QACA;EACA,YAEI,GAAG,WAAW,KAAA,IAAY,KAAK,WAAW,MAAM,IAAI,MAAM,WAAW,SAAS,EAAE,GAAG,WAAW,QAAQ,IAAI,MAC3G,UAAU;GACT,IAAI,UAAU,MAAM,OAAO;GAC3B,IAAI,QAAQ,SAAS,WAAW;IAC9B,IAAI,UAAU,QAAQ,UAAU,GAAG,OAAO;IAC1C,IAAI,UAAU,SAAS,UAAU,GAAG,OAAO;IAC3C,MAAM,wBAAwB,KAAK;KAAE,WAAW;KAAU,OAAO;IAAsB,CAAC;GAC1F;GACA,IAAI,QAAQ,SAAS,WAAW;IAC9B,MAAM,SAAS,cAAc,KAAK;IAElC,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,OAAO;IACzC,MAAM,wBAAwB,KAAK;KACjC,WAAW;KACX,OAAO;IACT,CAAC;GACH;GAEA,OAAO;EACT,CACF;EA1BS,KAAA,YAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;CAyBX;CAEA,iBAAiB,OAAyB;EACxC,OAAO;CACT;AACF;;AAGA,IAAa,QAAb,cAA2B,SAAS;CAIvB;CAEA;CACA;CANX;CAEA,YACE,MACA,SACA,QACA,QACA;EACA,aAAa,WAAW,KAAA,IAAY,KAAK,WAAW,MAAM,IAAI,OAAO,WAAW,IAAI,CAAC;EAL5E,KAAA,OAAA;EAEA,KAAA,SAAA;EACA,KAAA,SAAA;EAGT,KAAK,UAAU,OAAO,YACpB,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,KAAK,IAAI,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,CAC1F;CACF;AACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
//#region src/internal/standalone.ts
|
|
3
|
+
/** Standalone operations must observe their own durable commit. */
|
|
4
|
+
const requireStandalone = (unavailable, client) => Effect.withFiber((fiber) => {
|
|
5
|
+
const services = fiber.context.mapUnsafe;
|
|
6
|
+
if (client !== void 0 && services.has(client.transactionService.key)) return Effect.fail(unavailable());
|
|
7
|
+
for (const key of services.keys()) if (key.startsWith("@effect/sql-libsql/LibsqlClient/LibsqlTransaction/")) return Effect.fail(unavailable());
|
|
8
|
+
return Effect.void;
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { requireStandalone };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=standalone.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standalone.mjs","names":[],"sources":["../../src/internal/standalone.ts"],"sourcesContent":["import { type Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\";\n\n/** Standalone operations must observe their own durable commit. */\nexport const requireStandalone = <Failure>(\n unavailable: () => Failure,\n client?: {\n readonly transactionService: Context.Key<\n SqlClient.TransactionConnection,\n SqlClient.TransactionConnection.Service\n >;\n },\n): Effect.Effect<void, Failure> =>\n Effect.withFiber((fiber) => {\n const services = fiber.context.mapUnsafe;\n\n if (client !== undefined && services.has(client.transactionService.key))\n return Effect.fail(unavailable());\n\n // rc.117 libSQL uses a private per-client marker that is not exposed through\n // SqlClient.transactionService. Conservatively reject every ambient libSQL\n // transaction, including another client's, before issuing standalone writes.\n // Recheck this upstream detail whenever the Effect driver is upgraded.\n for (const key of services.keys()) {\n if (key.startsWith(\"@effect/sql-libsql/LibsqlClient/LibsqlTransaction/\"))\n return Effect.fail(unavailable());\n }\n\n return Effect.void;\n });\n"],"mappings":";;;AAIA,MAAa,qBACX,aACA,WAOA,OAAO,WAAW,UAAU;CAC1B,MAAM,WAAW,MAAM,QAAQ;CAE/B,IAAI,WAAW,KAAA,KAAa,SAAS,IAAI,OAAO,mBAAmB,GAAG,GACpE,OAAO,OAAO,KAAK,YAAY,CAAC;CAMlC,KAAK,MAAM,OAAO,SAAS,KAAK,GAC9B,IAAI,IAAI,WAAW,oDAAoD,GACrE,OAAO,OAAO,KAAK,YAAY,CAAC;CAGpC,OAAO,OAAO;AAChB,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
//#region src/internal/storage-tables.d.ts
|
|
3
|
-
declare const StorageColumn: Schema.Struct<{
|
|
3
|
+
export declare const StorageColumn: Schema.Struct<{
|
|
4
4
|
readonly name: Schema.NonEmptyString;
|
|
5
5
|
readonly type: Schema.Literals<readonly ["text", "integer", "boolean"]>;
|
|
6
6
|
readonly nullable: Schema.optionalKey<Schema.Boolean>;
|
|
7
7
|
}>;
|
|
8
|
-
declare const StorageTable: Schema.Struct<{
|
|
8
|
+
export declare const StorageTable: Schema.Struct<{
|
|
9
9
|
readonly name: Schema.NonEmptyString;
|
|
10
10
|
readonly schema: Schema.optionalKey<Schema.NonEmptyString>;
|
|
11
11
|
readonly columns: Schema.$Record<Schema.String, Schema.Struct<{
|
|
@@ -15,9 +15,9 @@ declare const StorageTable: Schema.Struct<{
|
|
|
15
15
|
}>>;
|
|
16
16
|
readonly unique: Schema.$Array<Schema.$Array<Schema.String>>;
|
|
17
17
|
}>;
|
|
18
|
-
type StorageTable = typeof StorageTable.Type;
|
|
18
|
+
export type StorageTable = typeof StorageTable.Type;
|
|
19
19
|
/** Logical roles are independent of table names and migration ownership. */
|
|
20
|
-
declare const storageTables: {
|
|
20
|
+
export declare const storageTables: {
|
|
21
21
|
readonly subjects: {
|
|
22
22
|
columns: {
|
|
23
23
|
id: {
|
|
@@ -908,7 +908,6 @@ declare const storageTables: {
|
|
|
908
908
|
unique: readonly (readonly ("dedupUntil" | "flowId" | "pendingDigest" | "state" | "subjectId")[])[];
|
|
909
909
|
};
|
|
910
910
|
};
|
|
911
|
-
type StorageRole = keyof typeof storageTables;
|
|
911
|
+
export type StorageRole = keyof typeof storageTables;
|
|
912
912
|
//#endregion
|
|
913
|
-
export { StorageColumn, StorageRole, StorageTable, storageTables };
|
|
914
913
|
//# sourceMappingURL=storage-tables.d.mts.map
|
|
@@ -5,14 +5,13 @@ import "effect/unstable/sql/SqlClient";
|
|
|
5
5
|
import * as SqlError$1 from "effect/unstable/sql/SqlError";
|
|
6
6
|
import { Statement } from "effect/unstable/sql/Statement";
|
|
7
7
|
//#region src/internal/transaction-execution-kernel.d.ts
|
|
8
|
-
interface TransactionTargetConfiguration<Failure> {
|
|
8
|
+
export interface TransactionTargetConfiguration<Failure> {
|
|
9
9
|
readonly mode: "interactive" | "synchronous" | "batch";
|
|
10
10
|
readonly dialect: "pg" | "mysql" | "sqlite";
|
|
11
11
|
readonly locking: boolean;
|
|
12
12
|
readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;
|
|
13
13
|
readonly transaction?: <A, E, R>(database: any, body: (transaction: any) => Effect.Effect<A, E, R>) => Effect.Effect<A, E | SqlError$1.SqlError | Failure, R>;
|
|
14
14
|
}
|
|
15
|
-
type TransactionCoordinatorError<E, Failure> = E | Failure | HookConfigurationError | SqlError$1.SqlError;
|
|
15
|
+
export type TransactionCoordinatorError<E, Failure> = E | Failure | HookConfigurationError | SqlError$1.SqlError;
|
|
16
16
|
//#endregion
|
|
17
|
-
export { TransactionCoordinatorError, TransactionTargetConfiguration };
|
|
18
17
|
//# sourceMappingURL=transaction-execution-kernel.d.mts.map
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requireStandalone } from "./standalone.mjs";
|
|
2
|
+
import { Context, Effect } from "effect";
|
|
2
3
|
import { CurrentCommitJournal, coordinateCommit, hasCommitScope } from "@yielded/auth/Hooks";
|
|
3
4
|
//#region src/internal/transaction-execution-kernel.ts
|
|
4
5
|
const makeTransactionExecutionKernel = (transactions) => {
|
|
5
6
|
const { makeTransactionOwner, reportTransactionFailure } = transactions;
|
|
6
|
-
const sqlClientTransactionStandaloneGuard = (unavailable, database) =>
|
|
7
|
-
onNone: () => Effect.void,
|
|
8
|
-
onSome: () => Effect.fail(unavailable())
|
|
9
|
-
})));
|
|
7
|
+
const sqlClientTransactionStandaloneGuard = (unavailable, database) => requireStandalone(unavailable, database.$client);
|
|
10
8
|
const makeTransactionExecution = (ownerTag, database, configuration, unavailable, nonce, bound) => {
|
|
11
9
|
const invariant = (value) => {
|
|
12
10
|
if (!value) throw unavailable();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-execution-kernel.mjs","names":[],"sources":["../../src/internal/transaction-execution-kernel.ts"],"sourcesContent":["import {\n coordinateCommit,\n hasCommitScope,\n CurrentCommitJournal,\n type LifecycleHooks,\n type HookConfigurationError,\n} from \"@yielded/auth/Hooks\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\n/* oxlint-disable no-explicit-any -- private execution bridge retains exact public driver wrappers. */\nimport { Context, Effect, Option } from \"effect\";\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\";\nimport type * as SqlError from \"effect/unstable/sql/SqlError\";\nimport type { Statement } from \"effect/unstable/sql/Statement\";\n\nimport {\n type TransactionNativeDatabase,\n type TransactionOwner,\n type makeTransactionKernel,\n} from \"./transaction-kernel\";\n\nexport interface TransactionTargetConfiguration<Failure> {\n readonly mode: \"interactive\" | \"synchronous\" | \"batch\";\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n readonly locking: boolean;\n readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;\n readonly transaction?: <A, E, R>(\n database: any,\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError.SqlError | Failure, R>;\n}\n\nexport type TransactionCoordinatorError<E, Failure> =\n | E\n | Failure\n | HookConfigurationError\n | SqlError.SqlError;\n\nexport interface TransactionBound<Failure> {\n readonly owner: TransactionOwner<Failure>;\n used: boolean;\n poisoned: boolean;\n active: boolean;\n inFlight: number;\n}\n\nexport interface TransactionExecution<Failure, OwnerId, Requirements = LifecycleHooks> {\n readonly bound: boolean;\n readonly active: () => boolean;\n readonly admit: Effect.Effect<void, Failure>;\n readonly poison: () => void;\n readonly run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation?: boolean,\n ) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;\n}\n\nexport const makeTransactionExecutionKernel = (\n transactions: Pick<\n ReturnType<typeof makeTransactionKernel>,\n \"makeTransactionOwner\" | \"reportTransactionFailure\"\n >,\n) => {\n const { makeTransactionOwner, reportTransactionFailure } = transactions;\n\n const sqlClientTransactionStandaloneGuard = <Failure>(\n unavailable: () => Failure,\n database: {\n readonly $client: Pick<SqlClient.SqlClient, \"transactionService\">;\n },\n ) =>\n Effect.serviceOption(database.$client.transactionService).pipe(\n Effect.flatMap(\n Option.match({ onNone: () => Effect.void, onSome: () => Effect.fail(unavailable()) }),\n ),\n );\n\n const makeTransactionExecution = <Failure, OwnerId>(\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n database: TransactionNativeDatabase,\n configuration: TransactionTargetConfiguration<Failure>,\n unavailable: () => Failure,\n nonce: () => string,\n bound?: TransactionBound<Failure>,\n ): TransactionExecution<Failure, OwnerId> => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n return {\n bound: bound !== undefined,\n active: () => bound === undefined || bound.active,\n admit:\n bound !== undefined\n ? Effect.suspend(() => (bound.active ? Effect.void : Effect.fail(unavailable())))\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n }),\n poison: () => {\n if (bound !== undefined) bound.poisoned = true;\n },\n run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation = true,\n ): Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | LifecycleHooks> => {\n return Effect.suspend(() => {\n let entered = false;\n\n const work =\n bound !== undefined\n ? Effect.gen(function* () {\n if (!bound.active) return yield* Effect.fail(unavailable());\n entered = true;\n bound.inFlight++;\n if (!mutation)\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, bound.owner).pipe(\n Context.add(CurrentCommitJournal, bound.owner.journal),\n ),\n );\n invariant(!bound.used && !bound.poisoned);\n bound.used = true;\n\n const result = yield* coordinateCommit(\n (journal) =>\n Effect.gen(function* () {\n bound.owner.guards.push(journal.prepare(undefined));\n\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, { ...bound.owner, journal }).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n }),\n { mode: configuration.mode },\n );\n\n return result.value;\n }).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (entered) bound.inFlight--;\n if (exit._tag === \"Failure\") bound.poisoned = true;\n }),\n ),\n )\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const owner = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n locking: configuration.locking,\n mysql: configuration.dialect === \"mysql\",\n dialect: configuration.dialect,\n });\n\n const value = yield* Effect.provideContext(\n body,\n Context.make(ownerTag, owner).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n\n yield* owner.finish();\n if (configuration.mode === \"batch\")\n yield* tx.$client.batch(owner.statements);\n\n return value;\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value;\n });\n\n return work.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n });\n },\n };\n };\n\n const coordinateTransactionOwner = <\n Failure,\n OwnerId,\n Resources,\n AllocationError,\n Services,\n Transaction,\n A,\n E,\n R,\n >(\n database: TransactionNativeDatabase,\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n configuration: TransactionTargetConfiguration<Failure>,\n allocate: Effect.Effect<Resources, AllocationError>,\n unavailable: () => Failure,\n nonce: () => string,\n services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services,\n owner: (\n transaction: Transaction,\n services: Services,\n append: (statement: Statement<any>) => void,\n ) => Effect.Effect<A, E, R>,\n ): Effect.Effect<\n A,\n TransactionCoordinatorError<E, Failure>,\n Exclude<R, CurrentCommitJournal> | LifecycleHooks\n > => {\n return Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n\n // Allocation is independent of registration data and always precedes the physical owner.\n const resources = yield* allocate.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const state = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n dialect: configuration.dialect,\n mysql: configuration.dialect === \"mysql\",\n locking: configuration.locking,\n });\n\n const bound: TransactionBound<Failure> = {\n owner: state,\n used: false,\n poisoned: false,\n active: true,\n inFlight: 0,\n };\n\n return yield* Effect.gen(function* () {\n const value = yield* owner(\n tx as Transaction,\n services(\n makeTransactionExecution(\n ownerTag,\n tx,\n configuration,\n unavailable,\n nonce,\n bound,\n ),\n resources,\n ),\n (statement) => {\n if (!bound.active) throw unavailable();\n state.statements.push(statement);\n },\n );\n\n bound.active = false;\n if (bound.poisoned || bound.inFlight !== 0)\n return yield* Effect.fail(unavailable());\n yield* state.finish();\n if (configuration.mode === \"batch\") yield* tx.$client.batch(state.statements);\n\n return value;\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n bound.active = false;\n }),\n ),\n );\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value as A;\n });\n };\n\n return {\n sqlClientTransactionStandaloneGuard,\n makeTransactionExecution,\n coordinateTransactionOwner,\n };\n};\n"],"mappings":";;;AAwDA,MAAa,kCACX,iBAIG;CACH,MAAM,EAAE,sBAAsB,6BAA6B;CAE3D,MAAM,uCACJ,aACA,aAIA,OAAO,cAAc,SAAS,QAAQ,kBAAkB,CAAC,CAAC,KACxD,OAAO,QACL,OAAO,MAAM;EAAE,cAAc,OAAO;EAAM,cAAc,OAAO,KAAK,YAAY,CAAC;CAAE,CAAC,CACtF,CACF;CAEF,MAAM,4BACJ,UACA,UACA,eACA,aACA,OACA,UAC2C;EAC3C,MAAM,aAAgD,UAAU;GAC9D,IAAI,CAAC,OAAO,MAAM,YAAY;EAChC;EAEA,OAAO;GACL,OAAO,UAAU,KAAA;GACjB,cAAc,UAAU,KAAA,KAAa,MAAM;GAC3C,OACE,UAAU,KAAA,IACN,OAAO,cAAe,MAAM,SAAS,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC,CAAE,IAC9E,OAAO,IAAI,aAAa;IACtB,IAAI,OAAO,gBAAgB,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC;IAClE,OAAO,cAAc,gBAAgB,QAAQ;GAC/C,CAAC;GACP,cAAc;IACZ,IAAI,UAAU,KAAA,GAAW,MAAM,WAAW;GAC5C;GACA,MACE,MACA,WAAW,SACgF;IAC3F,OAAO,OAAO,cAAc;KAC1B,IAAI,UAAU;KAqFd,QAlFE,UAAU,KAAA,IACN,OAAO,IAAI,aAAa;MACtB,IAAI,CAAC,MAAM,QAAQ,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC;MAC1D,UAAU;MACV,MAAM;MACN,IAAI,CAAC,UACH,OAAO,OAAO,OAAO,eACnB,MACA,QAAQ,KAAK,UAAU,MAAM,KAAK,CAAC,CAAC,KAClC,QAAQ,IAAI,sBAAsB,MAAM,MAAM,OAAO,CACvD,CACF;MACF,UAAU,CAAC,MAAM,QAAQ,CAAC,MAAM,QAAQ;MACxC,MAAM,OAAO;MAiBb,QAAO,OAfe,kBACnB,YACC,OAAO,IAAI,aAAa;OACtB,MAAM,MAAM,OAAO,KAAK,QAAQ,QAAQ,KAAA,CAAS,CAAC;OAElD,OAAO,OAAO,OAAO,eACnB,MACA,QAAQ,KAAK,UAAU;QAAE,GAAG,MAAM;QAAO;OAAQ,CAAC,CAAC,CAAC,KAClD,QAAQ,IAAI,sBAAsB,OAAO,CAC3C,CACF;MACF,CAAC,GACH,EAAE,MAAM,cAAc,KAAK,CAC7B,EAAA,CAEc;KAChB,CAAC,CAAC,CAAC,KACD,OAAO,QAAQ,SACb,OAAO,WAAW;MAChB,IAAI,SAAS,MAAM;MACnB,IAAI,KAAK,SAAS,WAAW,MAAM,WAAW;KAChD,CAAC,CACH,CACF,IACA,OAAO,IAAI,aAAa;MACtB,IAAI,OAAO,gBAAgB,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC;MAClE,OAAO,cAAc,gBAAgB,QAAQ;MAC7C,MAAM,SAAS,MAAM;MAqCrB,QAAO,OAnCe,kBACnB,YAAY;OACX,MAAM,OAAO,OACX,OAAO,IAAI,aAAa;QACtB,MAAM,QAAQ,qBAAqB,IAAI,SAAS,QAAQ,aAAa;SACnE,QAAQ,SAAS;SACjB,OAAO,cAAc,SAAS;SAC9B,SAAS,cAAc;SACvB,OAAO,cAAc,YAAY;SACjC,SAAS,cAAc;QACzB,CAAC;QAED,MAAM,QAAQ,OAAO,OAAO,eAC1B,MACA,QAAQ,KAAK,UAAU,KAAK,CAAC,CAAC,KAC5B,QAAQ,IAAI,sBAAsB,OAAO,CAC3C,CACF;QAEA,OAAO,MAAM,OAAO;QACpB,IAAI,cAAc,SAAS,SACzB,OAAO,GAAG,QAAQ,MAAM,MAAM,UAAU;QAE1C,OAAO;OACT,CAAC;OAEH,OAAO,cAAc,SAAS,UAC1B,IAAI,QAAQ,IACZ,cAAc,gBAAgB,KAAA,IAC5B,cAAc,YAAY,UAAU,GAAG,IACvC,SAAS,YAAY,GAAG;MAChC,GACA,EAAE,MAAM,cAAc,KAAK,CAC7B,EAAA,CAEc;KAChB,CAAC,EAAA,CAEK,MACT,cAAc,yBAAyB,WAAW,WAAW,GAC9D,OAAO,SAAS,WAAW,GAC3B,OAAO,kBAAkB,OAAO,KAAK,YAAY,CAAC,CAAC,CACrD;IACF,CAAC;GACH;EACF;CACF;CAEA,MAAM,8BAWJ,UACA,UACA,eACA,UACA,aACA,OACA,UACA,UASG;EACH,OAAO,OAAO,IAAI,aAAa;GAC7B,IAAI,OAAO,gBAAgB,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC;GAClE,OAAO,cAAc,gBAAgB,QAAQ;GAG7C,MAAM,YAAY,OAAO,SAAS,MAC/B,cAAc,yBAAyB,WAAW,WAAW,GAC9D,OAAO,SAAS,WAAW,GAC3B,OAAO,kBAAkB,OAAO,KAAK,YAAY,CAAC,CAAC,CACrD;GAEA,MAAM,SAAS,MAAM;GAmErB,QAAO,OAjEe,kBACnB,YAAY;IACX,MAAM,OAAO,OACX,OAAO,IAAI,aAAa;KACtB,MAAM,QAAQ,qBAAqB,IAAI,SAAS,QAAQ,aAAa;MACnE,QAAQ,SAAS;MACjB,OAAO,cAAc,SAAS;MAC9B,SAAS,cAAc;MACvB,OAAO,cAAc,YAAY;MACjC,SAAS,cAAc;KACzB,CAAC;KAED,MAAM,QAAmC;MACvC,OAAO;MACP,MAAM;MACN,UAAU;MACV,QAAQ;MACR,UAAU;KACZ;KAEA,OAAO,OAAO,OAAO,IAAI,aAAa;MACpC,MAAM,QAAQ,OAAO,MACnB,IACA,SACE,yBACE,UACA,IACA,eACA,aACA,OACA,KACF,GACA,SACF,IACC,cAAc;OACb,IAAI,CAAC,MAAM,QAAQ,MAAM,YAAY;OACrC,MAAM,WAAW,KAAK,SAAS;MACjC,CACF;MAEA,MAAM,SAAS;MACf,IAAI,MAAM,YAAY,MAAM,aAAa,GACvC,OAAO,OAAO,OAAO,KAAK,YAAY,CAAC;MACzC,OAAO,MAAM,OAAO;MACpB,IAAI,cAAc,SAAS,SAAS,OAAO,GAAG,QAAQ,MAAM,MAAM,UAAU;MAE5E,OAAO;KACT,CAAC,CAAC,CAAC,KACD,OAAO,SACL,OAAO,WAAW;MAChB,MAAM,SAAS;KACjB,CAAC,CACH,CACF;IACF,CAAC;IAEH,OAAO,cAAc,SAAS,UAC1B,IAAI,QAAQ,IACZ,cAAc,gBAAgB,KAAA,IAC5B,cAAc,YAAY,UAAU,GAAG,IACvC,SAAS,YAAY,GAAG;GAChC,GACA,EAAE,MAAM,cAAc,KAAK,CAC7B,EAAA,CAEc;EAChB,CAAC;CACH;CAEA,OAAO;EACL;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"transaction-execution-kernel.mjs","names":[],"sources":["../../src/internal/transaction-execution-kernel.ts"],"sourcesContent":["import {\n coordinateCommit,\n hasCommitScope,\n CurrentCommitJournal,\n type LifecycleHooks,\n type HookConfigurationError,\n} from \"@yielded/auth/Hooks\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\n/* oxlint-disable no-explicit-any -- private execution bridge retains exact public driver wrappers. */\nimport { Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\";\nimport type * as SqlError from \"effect/unstable/sql/SqlError\";\nimport type { Statement } from \"effect/unstable/sql/Statement\";\n\nimport { requireStandalone } from \"./standalone\";\nimport {\n type TransactionNativeDatabase,\n type TransactionOwner,\n type makeTransactionKernel,\n} from \"./transaction-kernel\";\n\nexport interface TransactionTargetConfiguration<Failure> {\n readonly mode: \"interactive\" | \"synchronous\" | \"batch\";\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n readonly locking: boolean;\n readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;\n readonly transaction?: <A, E, R>(\n database: any,\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError.SqlError | Failure, R>;\n}\n\nexport type TransactionCoordinatorError<E, Failure> =\n | E\n | Failure\n | HookConfigurationError\n | SqlError.SqlError;\n\nexport interface TransactionBound<Failure> {\n readonly owner: TransactionOwner<Failure>;\n used: boolean;\n poisoned: boolean;\n active: boolean;\n inFlight: number;\n}\n\nexport interface TransactionExecution<Failure, OwnerId, Requirements = LifecycleHooks> {\n readonly bound: boolean;\n readonly active: () => boolean;\n readonly admit: Effect.Effect<void, Failure>;\n readonly poison: () => void;\n readonly run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation?: boolean,\n ) => Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | Requirements>;\n}\n\nexport const makeTransactionExecutionKernel = (\n transactions: Pick<\n ReturnType<typeof makeTransactionKernel>,\n \"makeTransactionOwner\" | \"reportTransactionFailure\"\n >,\n) => {\n const { makeTransactionOwner, reportTransactionFailure } = transactions;\n\n const sqlClientTransactionStandaloneGuard = <Failure>(\n unavailable: () => Failure,\n database: {\n readonly $client: Pick<SqlClient.SqlClient, \"transactionService\">;\n },\n ) => requireStandalone(unavailable, database.$client);\n\n const makeTransactionExecution = <Failure, OwnerId>(\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n database: TransactionNativeDatabase,\n configuration: TransactionTargetConfiguration<Failure>,\n unavailable: () => Failure,\n nonce: () => string,\n bound?: TransactionBound<Failure>,\n ): TransactionExecution<Failure, OwnerId> => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n return {\n bound: bound !== undefined,\n active: () => bound === undefined || bound.active,\n admit:\n bound !== undefined\n ? Effect.suspend(() => (bound.active ? Effect.void : Effect.fail(unavailable())))\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n }),\n poison: () => {\n if (bound !== undefined) bound.poisoned = true;\n },\n run: <A, E, R>(\n body: Effect.Effect<A, E, R>,\n mutation = true,\n ): Effect.Effect<A, Failure, Exclude<R, OwnerId | CurrentCommitJournal> | LifecycleHooks> => {\n return Effect.suspend(() => {\n let entered = false;\n\n const work =\n bound !== undefined\n ? Effect.gen(function* () {\n if (!bound.active) return yield* Effect.fail(unavailable());\n entered = true;\n bound.inFlight++;\n if (!mutation)\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, bound.owner).pipe(\n Context.add(CurrentCommitJournal, bound.owner.journal),\n ),\n );\n invariant(!bound.used && !bound.poisoned);\n bound.used = true;\n\n const result = yield* coordinateCommit(\n (journal) =>\n Effect.gen(function* () {\n bound.owner.guards.push(journal.prepare(undefined));\n\n return yield* Effect.provideContext(\n body,\n Context.make(ownerTag, { ...bound.owner, journal }).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n }),\n { mode: configuration.mode },\n );\n\n return result.value;\n }).pipe(\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (entered) bound.inFlight--;\n if (exit._tag === \"Failure\") bound.poisoned = true;\n }),\n ),\n )\n : Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const owner = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n locking: configuration.locking,\n mysql: configuration.dialect === \"mysql\",\n dialect: configuration.dialect,\n });\n\n const value = yield* Effect.provideContext(\n body,\n Context.make(ownerTag, owner).pipe(\n Context.add(CurrentCommitJournal, journal),\n ),\n );\n\n yield* owner.finish();\n if (configuration.mode === \"batch\")\n yield* tx.$client.batch(owner.statements);\n\n return value;\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value;\n });\n\n return work.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n });\n },\n };\n };\n\n const coordinateTransactionOwner = <\n Failure,\n OwnerId,\n Resources,\n AllocationError,\n Services,\n Transaction,\n A,\n E,\n R,\n >(\n database: TransactionNativeDatabase,\n ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,\n configuration: TransactionTargetConfiguration<Failure>,\n allocate: Effect.Effect<Resources, AllocationError>,\n unavailable: () => Failure,\n nonce: () => string,\n services: (execution: TransactionExecution<Failure, OwnerId>, resources: Resources) => Services,\n owner: (\n transaction: Transaction,\n services: Services,\n append: (statement: Statement<any>) => void,\n ) => Effect.Effect<A, E, R>,\n ): Effect.Effect<\n A,\n TransactionCoordinatorError<E, Failure>,\n Exclude<R, CurrentCommitJournal> | LifecycleHooks\n > => {\n return Effect.gen(function* () {\n if (yield* hasCommitScope) return yield* Effect.fail(unavailable());\n yield* configuration.standaloneGuard(database);\n\n // Allocation is independent of registration data and always precedes the physical owner.\n const resources = yield* allocate.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n );\n\n const marker = nonce();\n\n const result = yield* coordinateCommit(\n (journal) => {\n const run = (tx: TransactionNativeDatabase) =>\n Effect.gen(function* () {\n const state = makeTransactionOwner(tx, journal, marker, unavailable, {\n client: database.$client,\n batch: configuration.mode === \"batch\",\n dialect: configuration.dialect,\n mysql: configuration.dialect === \"mysql\",\n locking: configuration.locking,\n });\n\n const bound: TransactionBound<Failure> = {\n owner: state,\n used: false,\n poisoned: false,\n active: true,\n inFlight: 0,\n };\n\n return yield* Effect.gen(function* () {\n const value = yield* owner(\n tx as Transaction,\n services(\n makeTransactionExecution(\n ownerTag,\n tx,\n configuration,\n unavailable,\n nonce,\n bound,\n ),\n resources,\n ),\n (statement) => {\n if (!bound.active) throw unavailable();\n state.statements.push(statement);\n },\n );\n\n bound.active = false;\n if (bound.poisoned || bound.inFlight !== 0)\n return yield* Effect.fail(unavailable());\n yield* state.finish();\n if (configuration.mode === \"batch\") yield* tx.$client.batch(state.statements);\n\n return value;\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n bound.active = false;\n }),\n ),\n );\n });\n\n return configuration.mode === \"batch\"\n ? run(database)\n : configuration.transaction !== undefined\n ? configuration.transaction(database, run)\n : database.transaction(run);\n },\n { mode: configuration.mode },\n );\n\n return result.value as A;\n });\n };\n\n return {\n sqlClientTransactionStandaloneGuard,\n makeTransactionExecution,\n coordinateTransactionOwner,\n };\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,54 @@
|
|
|
1
1
|
import "./query-operations.mjs";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import { PreparedCommit } from "@yielded/auth/Hooks";
|
|
3
|
+
import { CommitJournal, PreparedCommit } from "@yielded/auth/Hooks";
|
|
4
4
|
import { SQL, Table } from "drizzle-orm";
|
|
5
5
|
import "effect/unstable/sql/SqlClient";
|
|
6
6
|
import { SqlError } from "effect/unstable/sql/SqlError";
|
|
7
|
-
import { Statement } from "effect/unstable/sql/Statement";
|
|
7
|
+
import { Statement } from "effect/unstable/sql/Statement";
|
|
8
|
+
//#region src/internal/transaction-kernel.d.ts
|
|
9
|
+
export type Row = Record<string, any>;
|
|
10
|
+
export interface Observation {
|
|
11
|
+
readonly table: Table;
|
|
12
|
+
readonly where: SQL;
|
|
13
|
+
rows: ReadonlyArray<Row>;
|
|
14
|
+
}
|
|
15
|
+
/** Expressions deliberately do not replace observed values. Read only semantic
|
|
16
|
+
* columns when telemetry changes atomically, and state the physical result. */
|
|
17
|
+
export interface GuardedUpdate {
|
|
18
|
+
readonly rows: number;
|
|
19
|
+
readonly postcondition: SQL;
|
|
20
|
+
}
|
|
21
|
+
export interface TransactionOwner<Failure> {
|
|
22
|
+
readonly database: any;
|
|
23
|
+
readonly journal: CommitJournal;
|
|
24
|
+
readonly marker: string;
|
|
25
|
+
readonly batch: boolean;
|
|
26
|
+
readonly observations: Observation[];
|
|
27
|
+
readonly postconditions: Array<SQL | (() => SQL)>;
|
|
28
|
+
readonly statements: Statement<any>[];
|
|
29
|
+
readonly guards: PreparedCommit<void>[];
|
|
30
|
+
exact(table: Table, row: Row): SQL;
|
|
31
|
+
read(table: Table, where: SQL, options?: {
|
|
32
|
+
readonly lock?: boolean;
|
|
33
|
+
readonly limit?: number;
|
|
34
|
+
readonly observe?: boolean;
|
|
35
|
+
readonly admissionOnly?: boolean;
|
|
36
|
+
readonly takeOnly?: boolean;
|
|
37
|
+
readonly orderBy?: any;
|
|
38
|
+
readonly columns?: ReadonlyArray<string>;
|
|
39
|
+
}): Effect.Effect<Observation, Failure>;
|
|
40
|
+
write(query: any): Effect.Effect<void, Failure>;
|
|
41
|
+
insert(table: Table, values: Row, key: Row, absent?: boolean): Effect.Effect<Observation, Failure>;
|
|
42
|
+
update(table: Table, key: Row, values: Row): Effect.Effect<void, Failure>;
|
|
43
|
+
updateGuarded(table: Table, where: SQL, values: Row, guard: GuardedUpdate): Effect.Effect<void, Failure>;
|
|
44
|
+
/** Internal SQL only, run once after application work and before final guards. */
|
|
45
|
+
finalUpdate(table: Table, where: SQL, values: Row, guard: GuardedUpdate): Effect.Effect<void, Failure>;
|
|
46
|
+
remove(table: Table, key: Row): Effect.Effect<void, Failure>;
|
|
47
|
+
check(condition: SQL): Effect.Effect<boolean, Failure>;
|
|
48
|
+
now(clock: {
|
|
49
|
+
readonly engineNowMillis: SQL;
|
|
50
|
+
}): Effect.Effect<number, Failure>;
|
|
51
|
+
finish(): Effect.Effect<void, Failure>;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//# sourceMappingURL=transaction-kernel.d.mts.map
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@yielded/auth-persistence","version":"0.1.0-beta.
|
|
1
|
+
{"name":"@yielded/auth-persistence","version":"0.1.0-beta.9","dependencies":{"@noble/hashes":"2.3.0","@yielded/auth":"0.1.0-beta.9"},"devDependencies":{"@effect/platform-bun":"4.0.0-rc.117","@effect/platform-node":"4.0.0-rc.117","@effect/sql-d1":"4.0.0-rc.117","@effect/sql-libsql":"4.0.0-rc.117","@effect/sql-mysql2":"4.0.0-rc.117","@effect/sql-pg":"4.0.0-rc.117","@effect/sql-pglite":"4.0.0-rc.117","@effect/sql-sqlite-bun":"4.0.0-rc.117","@effect/sql-sqlite-do":"4.0.0-rc.117","@effect/sql-sqlite-node":"4.0.0-rc.117","@effect/sql-sqlite-wasm":"4.0.0-rc.117","@effect/vitest":"4.0.0-rc.117","drizzle-orm":"1.0.0-rc.5-ab785fc","effect":"4.0.0-rc.117","typescript":"7.0.2","vite-plus":"1.0.0-rc.0"},"peerDependencies":{"@effect/sql-d1":">=4.0.0-rc.117","@effect/sql-libsql":">=4.0.0-rc.117","@effect/sql-mysql2":">=4.0.0-rc.117","@effect/sql-pg":">=4.0.0-rc.117","@effect/sql-pglite":">=4.0.0-rc.117","@effect/sql-sqlite-bun":">=4.0.0-rc.117","@effect/sql-sqlite-do":">=4.0.0-rc.117","@effect/sql-sqlite-node":">=4.0.0-rc.117","@effect/sql-sqlite-wasm":">=4.0.0-rc.117","drizzle-orm":">=1.0.0-rc.5-ab785fc","effect":"^4.0.0-rc.117"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./drizzle":{"types":"./dist/Drizzle.d.mts","default":"./dist/Drizzle.mjs"},"./drizzle/d1":{"types":"./dist/DrizzleD1.d.mts","default":"./dist/DrizzleD1.mjs"},"./drizzle/libsql":{"types":"./dist/DrizzleLibsql.d.mts","default":"./dist/DrizzleLibsql.mjs"},"./drizzle/mysql2":{"types":"./dist/DrizzleMysql2.d.mts","default":"./dist/DrizzleMysql2.mjs"},"./drizzle/pglite":{"types":"./dist/DrizzlePglite.d.mts","default":"./dist/DrizzlePglite.mjs"},"./drizzle/postgres":{"types":"./dist/DrizzlePostgres.d.mts","default":"./dist/DrizzlePostgres.mjs"},"./drizzle/sqlite-bun":{"types":"./dist/DrizzleSqliteBun.d.mts","default":"./dist/DrizzleSqliteBun.mjs"},"./drizzle/sqlite-do":{"types":"./dist/DrizzleSqliteDo.d.mts","default":"./dist/DrizzleSqliteDo.mjs"},"./drizzle/sqlite-node":{"types":"./dist/DrizzleSqliteNode.d.mts","default":"./dist/DrizzleSqliteNode.mjs"},"./drizzle/sqlite-wasm":{"types":"./dist/DrizzleSqliteWasm.d.mts","default":"./dist/DrizzleSqliteWasm.mjs"}},"description":"SQL persistence, Drizzle bindings, and managed schemas for Yielded Auth.","homepage":"https://yielded.dev/auth/","bugs":{"url":"https://github.com/yielded-dev/auth/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/yielded-dev/auth.git","directory":"packages/auth-persistence"},"files":["dist","src"],"type":"module","sideEffects":[],"publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests","example:totp":"bun test/fixtures/totp-sqlite-bun.ts"},"peerDependenciesMeta":{"@effect/sql-d1":{"optional":true},"@effect/sql-libsql":{"optional":true},"@effect/sql-mysql2":{"optional":true},"@effect/sql-pg":{"optional":true},"@effect/sql-pglite":{"optional":true},"@effect/sql-sqlite-bun":{"optional":true},"@effect/sql-sqlite-do":{"optional":true},"@effect/sql-sqlite-node":{"optional":true},"@effect/sql-sqlite-wasm":{"optional":true},"drizzle-orm":{"optional":true}}}
|
package/src/DrizzleLibsql.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { SessionUnavailable } from "@yielded/auth/Sessions";
|
|
|
6
6
|
import type { AnyRelations } from "drizzle-orm";
|
|
7
7
|
import { type EffectLibsqlDatabase, makeWithDefaults } from "drizzle-orm/effect-libsql";
|
|
8
8
|
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
9
|
-
import {
|
|
9
|
+
import { Effect } from "effect";
|
|
10
10
|
|
|
11
11
|
import type {
|
|
12
12
|
AuthStoreTables,
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
makeSqliteAuthStoreServices,
|
|
25
25
|
makeSqliteOAuthStateServices,
|
|
26
26
|
} from "./drizzle/sqlite";
|
|
27
|
+
import { requireStandalone as standalone } from "./internal/standalone";
|
|
27
28
|
|
|
28
29
|
export {
|
|
29
30
|
coordinateSqliteAuthStoreTransaction as coordinateAuthStoreTransaction,
|
|
@@ -41,61 +42,16 @@ import { makeSqlitePasswordTarget, sqlitePasswordConfiguration } from "./drizzle
|
|
|
41
42
|
import { makeSqliteProofTarget, sqliteProofConfiguration } from "./drizzle/sqlite-proofs";
|
|
42
43
|
import { makeSqliteSessionTarget, sqliteSessionConfiguration } from "./drizzle/sqlite-sessions";
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const LibsqlTransaction = Context.Service<unknown>(
|
|
49
|
-
"@effect/sql-libsql/LibsqlClient/LibsqlTransaction",
|
|
45
|
+
const requireStandalone = standalone(() =>
|
|
46
|
+
AuthStoreError.make({
|
|
47
|
+
message: "Use the decision consume API inside an outer database transaction",
|
|
48
|
+
}),
|
|
50
49
|
);
|
|
51
50
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
onSome: () =>
|
|
57
|
-
AuthStoreError.make({
|
|
58
|
-
message: "Use the decision consume API inside an outer database transaction",
|
|
59
|
-
}),
|
|
60
|
-
}),
|
|
61
|
-
),
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
const requireStandaloneSession = Effect.serviceOption(LibsqlTransaction).pipe(
|
|
65
|
-
Effect.flatMap(
|
|
66
|
-
Option.match({
|
|
67
|
-
onNone: () => Effect.void,
|
|
68
|
-
onSome: () => SessionUnavailable.make({}),
|
|
69
|
-
}),
|
|
70
|
-
),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const requireStandaloneProof = Effect.serviceOption(LibsqlTransaction).pipe(
|
|
74
|
-
Effect.flatMap(
|
|
75
|
-
Option.match({
|
|
76
|
-
onNone: () => Effect.void,
|
|
77
|
-
onSome: () => ProofUnavailable.make({}),
|
|
78
|
-
}),
|
|
79
|
-
),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const requireStandalonePassword = Effect.serviceOption(LibsqlTransaction).pipe(
|
|
83
|
-
Effect.flatMap(
|
|
84
|
-
Option.match({
|
|
85
|
-
onNone: () => Effect.void,
|
|
86
|
-
onSome: () => PasswordUnavailable.make({}),
|
|
87
|
-
}),
|
|
88
|
-
),
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const requireStandaloneEmail = Effect.serviceOption(LibsqlTransaction).pipe(
|
|
92
|
-
Effect.flatMap(
|
|
93
|
-
Option.match({
|
|
94
|
-
onNone: () => Effect.void,
|
|
95
|
-
onSome: () => EmailUnavailable.make({}),
|
|
96
|
-
}),
|
|
97
|
-
),
|
|
98
|
-
);
|
|
51
|
+
const requireStandaloneSession = standalone(() => SessionUnavailable.make({}));
|
|
52
|
+
const requireStandaloneProof = standalone(() => ProofUnavailable.make({}));
|
|
53
|
+
const requireStandalonePassword = standalone(() => PasswordUnavailable.make({}));
|
|
54
|
+
const requireStandaloneEmail = standalone(() => EmailUnavailable.make({}));
|
|
99
55
|
|
|
100
56
|
const sessionTarget = makeSqliteSessionTarget<EffectLibsqlDatabase<AnyRelations>>(
|
|
101
57
|
sqliteSessionConfiguration("interactive", requireStandaloneSession),
|