@yielded/auth-persistence 0.1.0-beta.10 → 0.1.0-beta.12
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 +7 -7
- package/dist/Adapter.d.mts +2 -2
- package/dist/Adapter.mjs +4 -4
- package/dist/index.d.mts +1 -2
- package/dist/index.mjs +1 -2
- package/dist/internal/PasswordPreparedPostconditions.d.mts +1 -1
- package/dist/internal/configuration.d.mts +2 -2
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/email-kernel.d.mts +6 -6
- package/dist/internal/email-kernel.mjs +8 -6
- package/dist/internal/email-kernel.mjs.map +1 -1
- package/dist/internal/models/email-model.d.mts +1 -1
- package/dist/internal/oauth-server.d.mts +1 -1
- package/dist/internal/oauth-server.mjs +2 -2
- package/dist/internal/oauth-server.mjs.map +1 -1
- package/dist/internal/passkey/state.mjs +3 -2
- package/dist/internal/passkey/state.mjs.map +1 -1
- package/dist/internal/passkey/target.d.mts +1 -1
- package/dist/internal/passkey/target.mjs +21 -12
- package/dist/internal/passkey/target.mjs.map +1 -1
- package/dist/internal/passkey/write-state.mjs +3 -2
- package/dist/internal/passkey/write-state.mjs.map +1 -1
- package/dist/internal/passkey/write-target.mjs +6 -6
- package/dist/internal/passkey/write-target.mjs.map +1 -1
- package/dist/internal/passkey-kernel.d.mts +13 -16
- package/dist/internal/passkeys.mjs +9 -8
- package/dist/internal/passkeys.mjs.map +1 -1
- package/dist/internal/password-kernel.d.mts +4 -4
- package/dist/internal/password-kernel.mjs +3 -2
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +4 -3
- package/dist/internal/persistence.mjs +23 -27
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/proof-kernel.d.mts +3 -3
- package/dist/internal/proof-kernel.mjs +2 -1
- package/dist/internal/proof-kernel.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +1 -1
- package/dist/internal/registration.mjs +6 -1
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-kernel.d.mts +20 -10
- package/dist/internal/session-kernel.mjs +155 -147
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-persistence.mjs +3 -4
- package/dist/internal/sql-persistence.mjs.map +1 -1
- package/dist/internal/sql-query.mjs +4 -2
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/standalone.d.mts +2 -4
- package/dist/internal/standalone.mjs +2 -2
- package/dist/internal/standalone.mjs.map +1 -1
- package/dist/internal/storage-mapping.mjs +2 -2
- package/dist/internal/storage-validation.mjs +4 -2
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +8 -9
- package/dist/internal/transaction-execution-kernel.mjs +9 -7
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
- package/dist/internal/transaction-kernel.d.mts +9 -5
- package/dist/internal/transaction-kernel.mjs +5 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +6 -1
- package/src/index.ts +0 -1
- package/src/internal/configuration.ts +1 -1
- package/src/internal/email-kernel.ts +31 -36
- package/src/internal/oauth-server.ts +2 -2
- package/src/internal/passkey/state.ts +3 -2
- package/src/internal/passkey/target.ts +37 -44
- package/src/internal/passkey/write-state.ts +3 -2
- package/src/internal/passkey/write-target.ts +5 -15
- package/src/internal/passkeys.ts +5 -10
- package/src/internal/password-kernel.ts +3 -3
- package/src/internal/persistence.ts +78 -69
- package/src/internal/proof-kernel.ts +3 -3
- package/src/internal/query-operations.ts +1 -1
- package/src/internal/registration.ts +22 -7
- package/src/internal/session-kernel.ts +1123 -1140
- package/src/internal/sql-persistence.ts +2 -2
- package/src/internal/sql-query.ts +6 -4
- package/src/internal/standalone.ts +6 -8
- package/src/internal/storage-validation.ts +20 -16
- package/src/internal/transaction-execution-kernel.ts +20 -18
- package/src/internal/transaction-kernel.ts +10 -4
- package/dist/internal/oauth-app.d.mts +0 -11
- package/dist/internal/oauth-app.mjs +0 -57
- package/dist/internal/oauth-app.mjs.map +0 -1
- package/src/internal/oauth-app.ts +0 -82
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-kernel.mjs","names":[],"sources":["../../src/internal/transaction-kernel.ts"],"sourcesContent":["import { type CommitJournal, type PreparedCommit } from \"@yielded/auth/Hooks\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\nimport { Cause, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\";\nimport type { SqlError } from \"effect/unstable/sql/SqlError\";\nimport type { Statement } from \"effect/unstable/sql/Statement\";\n\nimport type {\n SqlExpression as SQL,\n QueryFailure,\n QueryOperations,\n SqlFragment,\n SqlColumn,\n} from \"./query-operations\";\n\ntype Table = object;\n\nexport type Row = Record<string, any>;\n\n/** Only query-builder/table shapes are erased here. Installed native\n * queries have captured clients, and their error channel remains explicit. */\ntype NativeQuery<A> = Effect.Effect<A, QueryFailure | SqlError>;\n\nexport interface TransactionNativeDatabase {\n readonly $client: SqlClient.SqlClient & {\n readonly batch: (statements: ReadonlyArray<Statement<any>>) => Effect.Effect<any, SqlError>;\n };\n readonly transaction: <A, E, R>(\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError, R>;\n}\n\nexport interface Observation {\n readonly table: Table;\n readonly where: SQL;\n rows: ReadonlyArray<Row>;\n}\n\n/** Expressions deliberately do not replace observed values. Read only semantic\n * columns when telemetry changes atomically, and state the physical result. */\nexport interface GuardedUpdate {\n readonly rows: number;\n readonly postcondition: SQL;\n}\n\nexport interface TransactionOwner<Failure> {\n readonly database: any;\n readonly journal: CommitJournal;\n readonly marker: string;\n readonly batch: boolean;\n readonly observations: Observation[];\n readonly postconditions: Array<SQL | (() => SQL)>;\n readonly statements: Statement<any>[];\n readonly guards: PreparedCommit<void>[];\n exact(table: Table, row: Row): SQL;\n read(\n table: Table,\n where: SQL,\n options?: {\n readonly lock?: boolean;\n readonly limit?: number;\n readonly observe?: boolean;\n readonly admissionOnly?: boolean;\n readonly takeOnly?: boolean;\n readonly orderBy?: any;\n readonly columns?: ReadonlyArray<string>;\n },\n ): Effect.Effect<Observation, Failure>;\n write(query: any): Effect.Effect<void, Failure>;\n insert(\n table: Table,\n values: Row,\n key: Row,\n absent?: boolean,\n ): Effect.Effect<Observation, Failure>;\n update(table: Table, key: Row, values: Row): Effect.Effect<void, Failure>;\n updateGuarded(\n table: Table,\n where: SQL,\n values: Row,\n guard: GuardedUpdate,\n ): Effect.Effect<void, Failure>;\n /** Internal SQL only, run once after application work and before final guards. */\n finalUpdate(\n table: Table,\n where: SQL,\n values: Row,\n guard: GuardedUpdate,\n ): Effect.Effect<void, Failure>;\n remove(table: Table, key: Row): Effect.Effect<void, Failure>;\n check(condition: SQL): Effect.Effect<boolean, Failure>;\n now(clock: { readonly engineNowMillis: SQL }): Effect.Effect<number, Failure>;\n finish(): Effect.Effect<void, Failure>;\n}\n\nexport const makeTransactionKernel = <\n Fragment extends SqlFragment = SqlFragment,\n Column extends SqlColumn = SqlColumn,\n>(\n operations: QueryOperations<Fragment, Column>,\n) => {\n const { eq, getTableColumns, isNull, sql, balancedD1And, compactD1GeneratedStatement } =\n operations;\n\n const isUnavailableCause = <Failure>(cause: Cause.Cause<unknown>, unavailable: () => Failure) => {\n const expected = unavailable();\n\n const tag =\n typeof expected === \"object\" && expected !== null && \"_tag\" in expected\n ? expected._tag\n : undefined;\n\n return (\n tag !== undefined &&\n cause.reasons.length > 0 &&\n cause.reasons.every(\n (reason) =>\n Cause.isFailReason(reason) &&\n typeof reason.error === \"object\" &&\n reason.error !== null &&\n \"_tag\" in reason.error &&\n reason.error._tag === tag,\n )\n );\n };\n\n /** Report once, before an adapter replaces the raw cause with its public\n * unavailable error. Already translated failures stay quiet at outer owners. */\n const reportTransactionFailure = <A, E, R, Failure>(\n effect: Effect.Effect<A, E, R>,\n unavailable: () => Failure,\n ): Effect.Effect<A, E, R> =>\n Effect.catchCause(effect, (cause) =>\n isUnavailableCause(cause, unavailable)\n ? Effect.failCause(cause)\n : reportAuthFailure(\"auth-persistence\", cause).pipe(\n Effect.andThen(Effect.failCause(cause)),\n ),\n );\n\n const both = (...parts: ReadonlyArray<SQL | undefined>) => balancedD1And(...parts) ?? sql`1 = 1`;\n\n const makeTransactionRows = <Failure>(unavailable: () => Failure) => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const col = (table: Table, key: string) => {\n const value = getTableColumns(table)[key];\n\n invariant(value !== undefined);\n\n return value;\n };\n\n const equal = (table: Table, values: Row) =>\n both(\n ...Object.entries(values).map(([key, value]) =>\n value === null ? isNull(col(table, key)) : eq(col(table, key), value),\n ),\n );\n\n /** Mapped driver values use plain data, Dates and binary views. Application\n * identity classes are reconstructed by the mapped codecs, never cloned here. */\n const copiedRow = (row: Row) => {\n const seen = new WeakSet<object>();\n\n const supported = (value: unknown): void => {\n if (value === null || typeof value !== \"object\" || seen.has(value)) return;\n seen.add(value);\n if (value instanceof Date || value instanceof ArrayBuffer || ArrayBuffer.isView(value))\n return;\n invariant(\n Array.isArray(value) ||\n Object.getPrototypeOf(value) === Object.prototype ||\n Object.getPrototypeOf(value) === null,\n );\n for (const item of Object.values(value)) supported(item);\n };\n\n supported(row);\n\n return structuredClone(row);\n };\n\n const nativeKey = (value: unknown): string => {\n const packed = (tag: string, text: string) => tag + text.length + \":\" + text;\n\n if (value instanceof Date) return packed(\"date\", value.toISOString());\n if (typeof value === \"bigint\") return packed(\"bigint\", value.toString());\n\n const bytes =\n value instanceof ArrayBuffer\n ? new Uint8Array(value)\n : ArrayBuffer.isView(value)\n ? new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n : undefined;\n\n if (bytes !== undefined) return packed(\"bytes\", Array.from(bytes).join(\",\"));\n if (value === null || typeof value !== \"object\") return packed(typeof value, String(value));\n if (Array.isArray(value)) return packed(\"array\", value.map(nativeKey).join(\"\"));\n invariant(\n Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null,\n );\n\n return packed(\n \"object\",\n Object.entries(value)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, item]) => nativeKey(name) + nativeKey(item))\n .join(\"\"),\n );\n };\n\n const matchesNativeRow = (table: Table, row: Row, key: Row) =>\n Object.entries(key).every(([name, value]) => {\n if (value === null || row[name] === null) return value === row[name];\n const column = col(table, name);\n\n return (\n nativeKey(column.mapToDriverValue(row[name])) ===\n nativeKey(column.mapToDriverValue(value))\n );\n });\n\n return { col, equal, copiedRow, matchesNativeRow };\n };\n\n const makeTransactionOwner = <Failure>(\n database: any,\n journal: CommitJournal,\n marker: string,\n unavailable: () => Failure,\n configuration: {\n readonly client?: any;\n readonly batch: boolean;\n readonly locking: boolean;\n readonly mysql: boolean;\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n },\n ): TransactionOwner<Failure> => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const { col, equal, matchesNativeRow } = makeTransactionRows(unavailable);\n let phase: \"open\" | \"finishing\" | \"finished\" = \"open\";\n let poisoned = false;\n const open = () => invariant(phase === \"open\" && !poisoned);\n\n const result = <A, E>(effect: Effect.Effect<A, E>) =>\n effect.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (exit._tag === \"Failure\") poisoned = true;\n }),\n ),\n );\n\n const observations: Observation[] = [];\n const statements: Statement<any>[] = [];\n const postconditions: Array<SQL | (() => SQL)> = [];\n const guards: PreparedCommit<void>[] = [];\n const finalUpdates: Array<{ readonly query: any; readonly guard: GuardedUpdate }> = [];\n\n const exactRow = (table: Table, row: Row) =>\n both(\n ...Object.entries(row).map(([key, value]) => {\n if (typeof value !== \"string\") return equal(table, { [key]: value });\n\n const column = col(table, key),\n bound = sql.param(value, column);\n\n return configuration.dialect === \"mysql\"\n ? sql`binary ${column} = binary ${bound}`\n : configuration.dialect === \"pg\"\n ? sql`convert_to(cast(${column} as text), 'UTF8') = convert_to(cast(${bound} as text), 'UTF8')`\n : sql`cast(${column} as blob) = cast(${bound} as blob)`;\n }),\n );\n\n const toStatement = (query: any): Statement<any> => {\n const rendered = query.toSQL();\n const client = configuration.client ?? database.$client;\n\n return compactD1GeneratedStatement(\n client,\n client.unsafe(rendered.sql, rendered.params),\n unavailable,\n );\n };\n\n const assertion = (condition: SQL) =>\n database\n .select({\n value: sql`case when ${condition} then 1 else json_extract('[]', ${\"$[oauth-owner-\" + marker + \"]\"}) end`,\n })\n .from(sql`(select 1) as oauth_assertion`);\n\n const observedConditions = (table: Table, where: SQL, rows: ReadonlyArray<Row>) => [\n sql`(select count(*) from ${table} where ${where}) = ${rows.length}`,\n ...rows.map(\n (row) => sql`exists(select 1 from ${table} where ${both(where, exactRow(table, row))})`,\n ),\n ];\n\n // All chunks remain inside the same native owner or atomic D1 batch. A single\n // wide row is compacted; separate observations cannot accumulate unbounded\n // bind parameters or expression depth in one final SELECT.\n const chunks = (conditions: ReadonlyArray<SQL>) => {\n const output: SQL[] = [];\n let pending: SQL[] = [];\n\n for (const condition of conditions) {\n const candidate = [...pending, condition];\n const rendered = assertion(both(...candidate)).toSQL();\n\n if (\n pending.length > 0 &&\n (rendered.params.length > 96 || new TextEncoder().encode(rendered.sql).length > 48_000)\n ) {\n output.push(both(...pending));\n pending = [condition];\n } else pending = candidate;\n }\n if (pending.length > 0) output.push(both(...pending));\n\n return output;\n };\n\n const appendAssertions = (conditions: ReadonlyArray<SQL>) => {\n for (const condition of chunks(conditions))\n statements.push(toStatement(assertion(condition)));\n };\n\n const observed = (table: Table, where: SQL, rows: ReadonlyArray<Row>) => {\n const observation = { table, where, rows };\n\n observations.push(observation);\n\n return observation;\n };\n\n const queryCondition = (condition: SQL) =>\n database\n .select({\n value: sql`case when ${condition} then 1 else 0 end`.mapWith(Number).as(\"value\"),\n })\n .from(sql`(select 1) as oauth_guard`);\n\n const conditionHolds = (condition: SQL) =>\n Effect.gen(function* () {\n const query = queryCondition(condition);\n\n const rows: ReadonlyArray<Row> = yield* configuration.dialect === \"sqlite\"\n ? toStatement(query)\n : (query as NativeQuery<Row[]>);\n\n invariant(rows.length === 1);\n\n return rows[0]!.value === 1;\n });\n\n const guardedQuery = (table: Table, where: SQL, values: Row, guard: GuardedUpdate) => {\n invariant(Number.isSafeInteger(guard.rows) && guard.rows > 0 && guard.rows <= 128);\n\n return { query: database.update(table).set(values).where(where), guard: { ...guard } };\n };\n\n const applyGuarded = (entry: { readonly query: any; readonly guard: GuardedUpdate }) =>\n Effect.gen(function* () {\n if (configuration.batch) {\n statements.push(toStatement(entry.query));\n statements.push(toStatement(assertion(sql`changes() = ${entry.guard.rows}`)));\n appendAssertions([entry.guard.postcondition]);\n } else {\n if (configuration.mysql) {\n // mysql2 reports matched rows (FOUND_ROWS), including a telemetry no-op.\n const changed = yield* entry.query as NativeQuery<{ readonly affectedRows: number }>;\n\n invariant(changed.affectedRows === entry.guard.rows);\n } else {\n const changed = yield* entry.query.returning({ value: sql`1` }) as NativeQuery<\n ReadonlyArray<Row>\n >;\n\n invariant(changed.length === entry.guard.rows);\n }\n invariant(yield* conditionHolds(entry.guard.postcondition));\n }\n });\n\n const owner: TransactionOwner<Failure> = {\n database,\n journal,\n marker,\n batch: configuration.batch,\n observations,\n statements,\n postconditions,\n guards,\n exact: exactRow,\n read: (table, where, options = {}) =>\n result(\n Effect.gen(function* () {\n open();\n const limit = options.limit ?? 64;\n\n let query = database\n .select(\n options.columns === undefined\n ? undefined\n : Object.fromEntries(options.columns.map((key) => [key, col(table, key)])),\n )\n .from(table)\n .where(where)\n .limit(options.takeOnly ? limit : limit + 1);\n\n if (options.orderBy !== undefined) query = query.orderBy(options.orderBy);\n if (options.lock !== false && configuration.locking) query = query.for(\"update\");\n const rows: Row[] = yield* query as NativeQuery<Row[]>;\n\n invariant(rows.length <= limit);\n if (configuration.batch && (options.observe !== false || options.admissionOnly))\n appendAssertions(observedConditions(table, where, rows));\n\n return options.observe === false || options.admissionOnly\n ? { table, where, rows }\n : observed(table, where, rows);\n }),\n ),\n write: (query) =>\n result(\n Effect.gen(function* () {\n open();\n if (configuration.batch) statements.push(toStatement(query));\n else yield* query as NativeQuery<unknown>;\n }),\n ),\n insert: (table, values, key, absent = false) =>\n result(\n Effect.gen(function* () {\n open();\n let query = database.insert(table).values(values);\n\n if (absent)\n query = configuration.mysql\n ? query.onDuplicateKeyUpdate({\n set: { [Object.keys(key)[0]!]: col(table, Object.keys(key)[0]!) },\n })\n : query.onConflictDoNothing();\n yield* owner.write(query);\n const where = equal(table, key);\n\n if (configuration.batch) return observed(table, where, [values]);\n const found = yield* owner.read(table, where, { limit: 1 });\n const markerColumn = Object.keys(values).find((key) => values[key] === marker);\n\n if (!absent || (markerColumn !== undefined && found.rows[0]?.[markerColumn] === marker))\n invariant(\n yield* owner.check(\n sql`exists(select 1 from ${table} where ${both(where, exactRow(table, values))})`,\n ),\n );\n\n return found;\n }),\n ),\n update: (table, key, values) =>\n result(\n Effect.gen(function* () {\n open();\n yield* owner.write(database.update(table).set(values).where(equal(table, key)));\n for (const observation of observations)\n if (observation.table === table)\n observation.rows = observation.rows.map((row) =>\n matchesNativeRow(table, row, key) ? { ...row, ...values } : row,\n );\n }),\n ),\n updateGuarded: (table, where, values, guard) =>\n result(\n Effect.gen(function* () {\n open();\n const entry = guardedQuery(table, where, values, guard);\n\n yield* applyGuarded(entry);\n postconditions.push(entry.guard.postcondition);\n }),\n ),\n finalUpdate: (table, where, values, guard) =>\n result(\n Effect.sync(() => {\n open();\n invariant(finalUpdates.length < 128);\n finalUpdates.push(guardedQuery(table, where, values, guard));\n }),\n ),\n remove: (table, key) =>\n result(\n Effect.gen(function* () {\n open();\n yield* owner.write(database.delete(table).where(equal(table, key)));\n for (const observation of observations)\n if (observation.table === table)\n observation.rows = observation.rows.filter(\n (row) => !matchesNativeRow(table, row, key),\n );\n }),\n ),\n check: (condition) =>\n result(\n Effect.gen(function* () {\n open();\n const holds = yield* conditionHolds(condition);\n\n if (configuration.batch)\n statements.push(toStatement(assertion(holds ? condition : sql`not (${condition})`)));\n\n return holds;\n }),\n ),\n now: (clock) =>\n result(\n Effect.gen(function* () {\n open();\n\n const rows: Row[] = yield* database\n .select({\n value: sql`${clock.engineNowMillis}`.mapWith(Number),\n })\n .from(sql`(select 1) as oauth_clock`) as NativeQuery<Row[]>;\n\n const value = rows[0]?.value;\n\n invariant(Number.isSafeInteger(value) && value >= 0);\n\n return value as number;\n }),\n ),\n finish: () =>\n result(\n Effect.gen(function* () {\n open();\n phase = \"finishing\";\n Object.freeze(observations);\n Object.freeze(postconditions);\n Object.freeze(guards);\n Object.freeze(finalUpdates);\n\n // Close registration before any late-bound SQL predicate is materialized.\n // No callback is invoked after the first final write.\n const conditions: SQL[] = observations.flatMap((observation) =>\n observedConditions(observation.table, observation.where, observation.rows),\n );\n\n conditions.push(\n ...postconditions.map((condition) =>\n typeof condition === \"function\" ? condition() : condition,\n ),\n );\n conditions.push(...finalUpdates.map((entry) => entry.guard.postcondition));\n for (const guard of guards) {\n const status = yield* Effect.result(guard.read);\n\n invariant(status._tag === \"Failure\" && status.failure._tag === \"CommitPending\");\n }\n invariant(!poisoned);\n for (const entry of finalUpdates) yield* applyGuarded(entry);\n if (configuration.batch) appendAssertions(conditions);\n else\n for (const condition of chunks(conditions))\n invariant(yield* conditionHolds(condition));\n invariant(!poisoned);\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n phase = \"finished\";\n }),\n ),\n ),\n ),\n };\n\n return owner;\n };\n\n return { reportTransactionFailure, both, makeTransactionRows, makeTransactionOwner };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transaction-kernel.mjs","names":[],"sources":["../../src/internal/transaction-kernel.ts"],"sourcesContent":["import { type CommitJournal, type PreparedCommit } from \"@yielded/auth/Hooks\";\nimport { reportAuthFailure } from \"@yielded/auth/Persistence\";\n/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */\nimport { Cause, Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\nimport type { Statement } from \"effect/sql/Statement\";\n\nimport type {\n SqlExpression as SQL,\n QueryFailure,\n QueryOperations,\n SqlFragment,\n SqlColumn,\n} from \"./query-operations\";\n\ntype Table = object;\n\nexport type Row = Record<string, any>;\n\n/** Only query-builder/table shapes are erased here. Installed native\n * queries have captured clients, and their error channel remains explicit. */\ntype NativeQuery<A> = Effect.Effect<A, QueryFailure | SqlError>;\n\nexport interface TransactionNativeDatabase {\n readonly $client: SqlClient.SqlClient & {\n readonly batch: (statements: ReadonlyArray<Statement<any>>) => Effect.Effect<any, SqlError>;\n };\n readonly transaction: <A, E, R>(\n body: (transaction: any) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SqlError, R>;\n}\n\n/** Root native database required only while constructing standalone services.\n * Active transactions remain owned by their coordinator and journal. */\nexport class NativeDatabase extends Context.Service<NativeDatabase, TransactionNativeDatabase>()(\n \"effect-auth/persistence/NativeDatabase\",\n) {}\n\nexport interface Observation {\n readonly table: Table;\n readonly where: SQL;\n rows: ReadonlyArray<Row>;\n}\n\n/** Expressions deliberately do not replace observed values. Read only semantic\n * columns when telemetry changes atomically, and state the physical result. */\nexport interface GuardedUpdate {\n readonly rows: number;\n readonly postcondition: SQL;\n}\n\nexport interface TransactionOwner<Failure> {\n readonly database: any;\n readonly journal: CommitJournal;\n readonly marker: string;\n readonly batch: boolean;\n readonly observations: Observation[];\n readonly postconditions: Array<SQL | (() => SQL)>;\n readonly statements: Statement<any>[];\n readonly guards: PreparedCommit<void>[];\n exact(table: Table, row: Row): SQL;\n read(\n table: Table,\n where: SQL,\n options?: {\n readonly lock?: boolean;\n readonly limit?: number;\n readonly observe?: boolean;\n readonly admissionOnly?: boolean;\n readonly takeOnly?: boolean;\n readonly orderBy?: any;\n readonly columns?: ReadonlyArray<string>;\n },\n ): Effect.Effect<Observation, Failure>;\n write(query: any): Effect.Effect<void, Failure>;\n insert(\n table: Table,\n values: Row,\n key: Row,\n absent?: boolean,\n ): Effect.Effect<Observation, Failure>;\n update(table: Table, key: Row, values: Row): Effect.Effect<void, Failure>;\n updateGuarded(\n table: Table,\n where: SQL,\n values: Row,\n guard: GuardedUpdate,\n ): Effect.Effect<void, Failure>;\n /** Internal SQL only, run once after application work and before final guards. */\n finalUpdate(\n table: Table,\n where: SQL,\n values: Row,\n guard: GuardedUpdate,\n ): Effect.Effect<void, Failure>;\n remove(table: Table, key: Row): Effect.Effect<void, Failure>;\n check(condition: SQL): Effect.Effect<boolean, Failure>;\n now(clock: { readonly engineNowMillis: SQL }): Effect.Effect<number, Failure>;\n finish(): Effect.Effect<void, Failure>;\n}\n\nexport const makeTransactionKernel = <\n Fragment extends SqlFragment = SqlFragment,\n Column extends SqlColumn = SqlColumn,\n>(\n operations: QueryOperations<Fragment, Column>,\n) => {\n const { eq, getTableColumns, isNull, sql, balancedD1And, compactD1GeneratedStatement } =\n operations;\n\n const isUnavailableCause = <Failure>(cause: Cause.Cause<unknown>, unavailable: () => Failure) => {\n const expected = unavailable();\n\n const tag =\n typeof expected === \"object\" && expected !== null && \"_tag\" in expected\n ? expected._tag\n : undefined;\n\n return (\n tag !== undefined &&\n cause.reasons.length > 0 &&\n cause.reasons.every(\n (reason) =>\n Cause.isFailReason(reason) &&\n typeof reason.error === \"object\" &&\n reason.error !== null &&\n \"_tag\" in reason.error &&\n reason.error._tag === tag,\n )\n );\n };\n\n /** Report once, before an adapter replaces the raw cause with its public\n * unavailable error. Already translated failures stay quiet at outer owners. */\n const reportTransactionFailure = <A, E, R, Failure>(\n effect: Effect.Effect<A, E, R>,\n unavailable: () => Failure,\n ): Effect.Effect<A, E, R> =>\n Effect.catchCause(effect, (cause) =>\n isUnavailableCause(cause, unavailable)\n ? Effect.failCause(cause)\n : reportAuthFailure(\"auth-persistence\", cause).pipe(\n Effect.andThen(Effect.failCause(cause)),\n ),\n );\n\n const both = (...parts: ReadonlyArray<SQL | undefined>) => balancedD1And(...parts) ?? sql`1 = 1`;\n\n const makeTransactionRows = <Failure>(unavailable: () => Failure) => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const col = (table: Table, key: string) => {\n const value = getTableColumns(table)[key];\n\n invariant(value !== undefined);\n\n return value;\n };\n\n const equal = (table: Table, values: Row) =>\n both(\n ...Object.entries(values).map(([key, value]) =>\n value === null ? isNull(col(table, key)) : eq(col(table, key), value),\n ),\n );\n\n /** Mapped driver values use plain data, Dates and binary views. Application\n * identity classes are reconstructed by the mapped codecs, never cloned here. */\n const copiedRow = (row: Row) => {\n const seen = new WeakSet<object>();\n\n const supported = (value: unknown): void => {\n if (value === null || typeof value !== \"object\" || seen.has(value)) return;\n seen.add(value);\n if (value instanceof Date || value instanceof ArrayBuffer || ArrayBuffer.isView(value))\n return;\n invariant(\n Array.isArray(value) ||\n Object.getPrototypeOf(value) === Object.prototype ||\n Object.getPrototypeOf(value) === null,\n );\n for (const item of Object.values(value)) supported(item);\n };\n\n supported(row);\n\n return structuredClone(row);\n };\n\n const nativeKey = (value: unknown): string => {\n const packed = (tag: string, text: string) => tag + text.length + \":\" + text;\n\n if (value instanceof Date) return packed(\"date\", value.toISOString());\n if (typeof value === \"bigint\") return packed(\"bigint\", value.toString());\n\n const bytes =\n value instanceof ArrayBuffer\n ? new Uint8Array(value)\n : ArrayBuffer.isView(value)\n ? new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n : undefined;\n\n if (bytes !== undefined) return packed(\"bytes\", Array.from(bytes).join(\",\"));\n if (value === null || typeof value !== \"object\") return packed(typeof value, String(value));\n if (Array.isArray(value)) return packed(\"array\", value.map(nativeKey).join(\"\"));\n invariant(\n Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null,\n );\n\n return packed(\n \"object\",\n Object.entries(value)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, item]) => nativeKey(name) + nativeKey(item))\n .join(\"\"),\n );\n };\n\n const matchesNativeRow = (table: Table, row: Row, key: Row) =>\n Object.entries(key).every(([name, value]) => {\n if (value === null || row[name] === null) return value === row[name];\n const column = col(table, name);\n\n return (\n nativeKey(column.mapToDriverValue(row[name])) ===\n nativeKey(column.mapToDriverValue(value))\n );\n });\n\n return { col, equal, copiedRow, matchesNativeRow };\n };\n\n const makeTransactionOwner = <Failure>(\n database: any,\n journal: CommitJournal,\n marker: string,\n unavailable: () => Failure,\n configuration: {\n readonly client?: any;\n readonly batch: boolean;\n readonly locking: boolean;\n readonly mysql: boolean;\n readonly dialect: \"pg\" | \"mysql\" | \"sqlite\";\n },\n ): TransactionOwner<Failure> => {\n const invariant: (value: unknown) => asserts value = (value) => {\n if (!value) throw unavailable();\n };\n\n const { col, equal, matchesNativeRow } = makeTransactionRows(unavailable);\n let phase: \"open\" | \"finishing\" | \"finished\" = \"open\";\n let poisoned = false;\n const open = () => invariant(phase === \"open\" && !poisoned);\n\n const result = <A, E>(effect: Effect.Effect<A, E>) =>\n effect.pipe(\n (operation) => reportTransactionFailure(operation, unavailable),\n Effect.mapError(unavailable),\n Effect.catchDefect(() => Effect.fail(unavailable())),\n Effect.onExit((exit) =>\n Effect.sync(() => {\n if (exit._tag === \"Failure\") poisoned = true;\n }),\n ),\n );\n\n const observations: Observation[] = [];\n const statements: Statement<any>[] = [];\n const postconditions: Array<SQL | (() => SQL)> = [];\n const guards: PreparedCommit<void>[] = [];\n const finalUpdates: Array<{ readonly query: any; readonly guard: GuardedUpdate }> = [];\n\n const exactRow = (table: Table, row: Row) =>\n both(\n ...Object.entries(row).map(([key, value]) => {\n if (typeof value !== \"string\") return equal(table, { [key]: value });\n\n const column = col(table, key),\n bound = sql.param(value, column);\n\n return configuration.dialect === \"mysql\"\n ? sql`binary ${column} = binary ${bound}`\n : configuration.dialect === \"pg\"\n ? sql`convert_to(cast(${column} as text), 'UTF8') = convert_to(cast(${bound} as text), 'UTF8')`\n : sql`cast(${column} as blob) = cast(${bound} as blob)`;\n }),\n );\n\n const toStatement = (query: any): Statement<any> => {\n const rendered = query.toSQL();\n const client = configuration.client ?? database.$client;\n\n return compactD1GeneratedStatement(\n client,\n client.unsafe(rendered.sql, rendered.params),\n unavailable,\n );\n };\n\n const assertion = (condition: SQL) =>\n database\n .select({\n value: sql`case when ${condition} then 1 else json_extract('[]', ${\"$[oauth-owner-\" + marker + \"]\"}) end`,\n })\n .from(sql`(select 1) as oauth_assertion`);\n\n const observedConditions = (table: Table, where: SQL, rows: ReadonlyArray<Row>) => [\n sql`(select count(*) from ${table} where ${where}) = ${rows.length}`,\n ...rows.map(\n (row) => sql`exists(select 1 from ${table} where ${both(where, exactRow(table, row))})`,\n ),\n ];\n\n // All chunks remain inside the same native owner or atomic D1 batch. A single\n // wide row is compacted; separate observations cannot accumulate unbounded\n // bind parameters or expression depth in one final SELECT.\n const chunks = (conditions: ReadonlyArray<SQL>) => {\n const output: SQL[] = [];\n let pending: SQL[] = [];\n\n for (const condition of conditions) {\n const candidate = [...pending, condition];\n const rendered = assertion(both(...candidate)).toSQL();\n\n if (\n pending.length > 0 &&\n (rendered.params.length > 96 || new TextEncoder().encode(rendered.sql).length > 48_000)\n ) {\n output.push(both(...pending));\n pending = [condition];\n } else pending = candidate;\n }\n if (pending.length > 0) output.push(both(...pending));\n\n return output;\n };\n\n const appendAssertions = (conditions: ReadonlyArray<SQL>) => {\n for (const condition of chunks(conditions))\n statements.push(toStatement(assertion(condition)));\n };\n\n const observed = (table: Table, where: SQL, rows: ReadonlyArray<Row>) => {\n const observation = { table, where, rows };\n\n observations.push(observation);\n\n return observation;\n };\n\n const queryCondition = (condition: SQL) =>\n database\n .select({\n value: sql`case when ${condition} then 1 else 0 end`.mapWith(Number).as(\"value\"),\n })\n .from(sql`(select 1) as oauth_guard`);\n\n const conditionHolds = (condition: SQL) =>\n Effect.gen(function* () {\n const query = queryCondition(condition);\n\n const rows: ReadonlyArray<Row> = yield* configuration.dialect === \"sqlite\"\n ? toStatement(query)\n : (query as NativeQuery<Row[]>);\n\n invariant(rows.length === 1);\n\n return rows[0]!.value === 1;\n });\n\n const guardedQuery = (table: Table, where: SQL, values: Row, guard: GuardedUpdate) => {\n invariant(Number.isSafeInteger(guard.rows) && guard.rows > 0 && guard.rows <= 128);\n\n return { query: database.update(table).set(values).where(where), guard: { ...guard } };\n };\n\n const applyGuarded = (entry: { readonly query: any; readonly guard: GuardedUpdate }) =>\n Effect.gen(function* () {\n if (configuration.batch) {\n statements.push(toStatement(entry.query));\n statements.push(toStatement(assertion(sql`changes() = ${entry.guard.rows}`)));\n appendAssertions([entry.guard.postcondition]);\n } else {\n if (configuration.mysql) {\n // mysql2 reports matched rows (FOUND_ROWS), including a telemetry no-op.\n const changed = yield* entry.query as NativeQuery<{ readonly affectedRows: number }>;\n\n invariant(changed.affectedRows === entry.guard.rows);\n } else {\n const changed = yield* entry.query.returning({ value: sql`1` }) as NativeQuery<\n ReadonlyArray<Row>\n >;\n\n invariant(changed.length === entry.guard.rows);\n }\n invariant(yield* conditionHolds(entry.guard.postcondition));\n }\n });\n\n const owner: TransactionOwner<Failure> = {\n database,\n journal,\n marker,\n batch: configuration.batch,\n observations,\n statements,\n postconditions,\n guards,\n exact: exactRow,\n read: (table, where, options = {}) =>\n result(\n Effect.gen(function* () {\n open();\n const limit = options.limit ?? 64;\n\n let query = database\n .select(\n options.columns === undefined\n ? undefined\n : Object.fromEntries(options.columns.map((key) => [key, col(table, key)])),\n )\n .from(table)\n .where(where)\n .limit(options.takeOnly ? limit : limit + 1);\n\n if (options.orderBy !== undefined) query = query.orderBy(options.orderBy);\n if (options.lock !== false && configuration.locking) query = query.for(\"update\");\n const rows: Row[] = yield* query as NativeQuery<Row[]>;\n\n invariant(rows.length <= limit);\n if (configuration.batch && (options.observe !== false || options.admissionOnly))\n appendAssertions(observedConditions(table, where, rows));\n\n return options.observe === false || options.admissionOnly\n ? { table, where, rows }\n : observed(table, where, rows);\n }),\n ),\n write: (query) =>\n result(\n Effect.gen(function* () {\n open();\n if (configuration.batch) statements.push(toStatement(query));\n else yield* query as NativeQuery<unknown>;\n }),\n ),\n insert: (table, values, key, absent = false) =>\n result(\n Effect.gen(function* () {\n open();\n let query = database.insert(table).values(values);\n\n if (absent)\n query = configuration.mysql\n ? query.onDuplicateKeyUpdate({\n set: { [Object.keys(key)[0]!]: col(table, Object.keys(key)[0]!) },\n })\n : query.onConflictDoNothing();\n yield* owner.write(query);\n const where = equal(table, key);\n\n if (configuration.batch) return observed(table, where, [values]);\n const found = yield* owner.read(table, where, { limit: 1 });\n const markerColumn = Object.keys(values).find((key) => values[key] === marker);\n\n if (!absent || (markerColumn !== undefined && found.rows[0]?.[markerColumn] === marker))\n invariant(\n yield* owner.check(\n sql`exists(select 1 from ${table} where ${both(where, exactRow(table, values))})`,\n ),\n );\n\n return found;\n }),\n ),\n update: (table, key, values) =>\n result(\n Effect.gen(function* () {\n open();\n yield* owner.write(database.update(table).set(values).where(equal(table, key)));\n for (const observation of observations)\n if (observation.table === table)\n observation.rows = observation.rows.map((row) =>\n matchesNativeRow(table, row, key) ? { ...row, ...values } : row,\n );\n }),\n ),\n updateGuarded: (table, where, values, guard) =>\n result(\n Effect.gen(function* () {\n open();\n const entry = guardedQuery(table, where, values, guard);\n\n yield* applyGuarded(entry);\n postconditions.push(entry.guard.postcondition);\n }),\n ),\n finalUpdate: (table, where, values, guard) =>\n result(\n Effect.sync(() => {\n open();\n invariant(finalUpdates.length < 128);\n finalUpdates.push(guardedQuery(table, where, values, guard));\n }),\n ),\n remove: (table, key) =>\n result(\n Effect.gen(function* () {\n open();\n yield* owner.write(database.delete(table).where(equal(table, key)));\n for (const observation of observations)\n if (observation.table === table)\n observation.rows = observation.rows.filter(\n (row) => !matchesNativeRow(table, row, key),\n );\n }),\n ),\n check: (condition) =>\n result(\n Effect.gen(function* () {\n open();\n const holds = yield* conditionHolds(condition);\n\n if (configuration.batch)\n statements.push(toStatement(assertion(holds ? condition : sql`not (${condition})`)));\n\n return holds;\n }),\n ),\n now: (clock) =>\n result(\n Effect.gen(function* () {\n open();\n\n const rows: Row[] = yield* database\n .select({\n value: sql`${clock.engineNowMillis}`.mapWith(Number),\n })\n .from(sql`(select 1) as oauth_clock`) as NativeQuery<Row[]>;\n\n const value = rows[0]?.value;\n\n invariant(Number.isSafeInteger(value) && value >= 0);\n\n return value as number;\n }),\n ),\n finish: () =>\n result(\n Effect.gen(function* () {\n open();\n phase = \"finishing\";\n Object.freeze(observations);\n Object.freeze(postconditions);\n Object.freeze(guards);\n Object.freeze(finalUpdates);\n\n // Close registration before any late-bound SQL predicate is materialized.\n // No callback is invoked after the first final write.\n const conditions: SQL[] = observations.flatMap((observation) =>\n observedConditions(observation.table, observation.where, observation.rows),\n );\n\n conditions.push(\n ...postconditions.map((condition) =>\n typeof condition === \"function\" ? condition() : condition,\n ),\n );\n conditions.push(...finalUpdates.map((entry) => entry.guard.postcondition));\n for (const guard of guards) {\n const status = yield* Effect.result(guard.read);\n\n invariant(status._tag === \"Failure\" && status.failure._tag === \"CommitPending\");\n }\n invariant(!poisoned);\n for (const entry of finalUpdates) yield* applyGuarded(entry);\n if (configuration.batch) appendAssertions(conditions);\n else\n for (const condition of chunks(conditions))\n invariant(yield* conditionHolds(condition));\n invariant(!poisoned);\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n phase = \"finished\";\n }),\n ),\n ),\n ),\n };\n\n return owner;\n };\n\n return { reportTransactionFailure, both, makeTransactionRows, makeTransactionOwner };\n};\n"],"mappings":"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"}
|
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.12","dependencies":{"@noble/hashes":"2.3.0","@yielded/auth":"0.1.0-beta.12","@yielded/auth-crypto":"0.1.0-beta.12"},"devDependencies":{"@effect/platform-node":"4.0.0","@effect/sql-libsql":"4.0.0","@effect/sql-sqlite-node":"4.0.0","@effect/vitest":"4.0.0","effect":"4.0.0","typescript":"7.0.2","vite-plus":"0.3.3"},"peerDependencies":{"effect":"^4.0.0"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./Adapter":{"types":"./dist/Adapter.d.mts","default":"./dist/Adapter.mjs"}},"description":"Direct Effect SQL persistence and shared storage contracts 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"}}
|
package/src/Adapter.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/** Shared storage contracts and kernels for persistence adapter authors.
|
|
1
|
+
/** Shared storage contracts and kernels for persistence adapter authors.
|
|
2
|
+
* Standalone constructors require the matching Current*Sql or NativeDatabase
|
|
3
|
+
* service during acquisition. Provide a coordinator's exact transaction when
|
|
4
|
+
* constructing bound services; do not provide the root database around their
|
|
5
|
+
* later operations, where Current*Sql identifies an active transaction. */
|
|
2
6
|
export {
|
|
3
7
|
type EmailSubjectReadTable,
|
|
4
8
|
type EmailSubjectTable,
|
|
@@ -271,6 +275,7 @@ export {
|
|
|
271
275
|
type GuardedUpdate,
|
|
272
276
|
type Observation,
|
|
273
277
|
type Row,
|
|
278
|
+
NativeDatabase,
|
|
274
279
|
type TransactionNativeDatabase,
|
|
275
280
|
type TransactionOwner,
|
|
276
281
|
makeTransactionKernel,
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { AuthPersistence } from "./internal/sql-persistence";
|
|
2
|
-
export { OAuthAppPersistence } from "./internal/oauth-app";
|
|
3
2
|
export { OAuthServerPersistence } from "./internal/oauth-server";
|
|
4
3
|
export { PersistenceConfigurationError } from "./internal/configuration";
|
|
5
4
|
export { PersistenceMappingError } from "./internal/mapping-error";
|
|
@@ -25,7 +25,7 @@ import type {
|
|
|
25
25
|
make as makeSessions,
|
|
26
26
|
} from "@yielded/auth/Sessions";
|
|
27
27
|
import { type Context, type Effect, type Layer, Schema } from "effect";
|
|
28
|
-
import type { SqlClient } from "effect/
|
|
28
|
+
import type { SqlClient } from "effect/sql";
|
|
29
29
|
|
|
30
30
|
import type { PersistenceMappingError } from "./mapping-error";
|
|
31
31
|
import type { StorageRole } from "./storage-tables";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
type SecurityRevision,
|
|
33
33
|
} from "@yielded/auth/Sessions";
|
|
34
34
|
import { Cause, Context, DateTime, Effect, Option, Schema } from "effect";
|
|
35
|
-
import type * as SqlError from "effect/
|
|
35
|
+
import type * as SqlError from "effect/sql/SqlError";
|
|
36
36
|
|
|
37
37
|
import { isMappedConstraintConflict, PersistenceMappingError } from "./mapping-error";
|
|
38
38
|
import {
|
|
@@ -379,41 +379,36 @@ export const makeEmailKernel = <
|
|
|
379
379
|
return snapshot;
|
|
380
380
|
});
|
|
381
381
|
|
|
382
|
-
const makeSqlEmailSignInTargets = Effect.fn("makeSqlEmailSignInTargets")(function (
|
|
383
|
-
database: EmailSqlDatabase,
|
|
382
|
+
const makeSqlEmailSignInTargets = Effect.fn("makeSqlEmailSignInTargets")(function* (
|
|
384
383
|
mapping: SignInMapping,
|
|
385
384
|
configuration: EmailSqlConfiguration,
|
|
386
|
-
): Effect.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
: Effect.fail(unavailable())
|
|
414
|
-
).pipe(Effect.provideService(CurrentEmailSql, database), translateFailure),
|
|
415
|
-
}),
|
|
416
|
-
);
|
|
385
|
+
): Effect.fn.Return<EmailSignInTargets["Service"], never, CurrentEmailSql> {
|
|
386
|
+
const database = yield* CurrentEmailSql;
|
|
387
|
+
|
|
388
|
+
return EmailSignInTargets.of({
|
|
389
|
+
lookup: (input) =>
|
|
390
|
+
(validEmailSignInConstraints(mapping)
|
|
391
|
+
? safeRead(
|
|
392
|
+
database,
|
|
393
|
+
configuration,
|
|
394
|
+
Effect.gen(function* () {
|
|
395
|
+
const row = (yield* emailLookupRows(mapping, input.moduleId, input.identifier))[0];
|
|
396
|
+
|
|
397
|
+
if (row === undefined) return Option.none();
|
|
398
|
+
|
|
399
|
+
const snapshot = yield* decodeEmailSnapshot(
|
|
400
|
+
mapping,
|
|
401
|
+
input.moduleId,
|
|
402
|
+
input.identifier,
|
|
403
|
+
row,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
return snapshot === undefined ? Option.none() : Option.some(snapshot);
|
|
407
|
+
}),
|
|
408
|
+
)
|
|
409
|
+
: Effect.fail(unavailable())
|
|
410
|
+
).pipe(Effect.provideService(CurrentEmailSql, database), translateFailure),
|
|
411
|
+
});
|
|
417
412
|
});
|
|
418
413
|
|
|
419
414
|
const readSubject = Effect.fn("DrizzleEmail.readSubject")(function* (
|
|
@@ -1224,10 +1219,10 @@ export const makeEmailKernel = <
|
|
|
1224
1219
|
});
|
|
1225
1220
|
|
|
1226
1221
|
const makeSqlEmailAddressPersistence = Effect.fn("makeSqlEmailAddressPersistence")(function* (
|
|
1227
|
-
database: EmailSqlDatabase,
|
|
1228
1222
|
mapping: AddressMapping,
|
|
1229
1223
|
configuration: EmailSqlConfiguration,
|
|
1230
|
-
): Effect.fn.Return<EmailAddressPersistence["Service"], never, LifecycleHooks> {
|
|
1224
|
+
): Effect.fn.Return<EmailAddressPersistence["Service"], never, LifecycleHooks | CurrentEmailSql> {
|
|
1225
|
+
const database = yield* CurrentEmailSql;
|
|
1231
1226
|
const hooks = yield* LifecycleHooks;
|
|
1232
1227
|
|
|
1233
1228
|
return EmailAddressPersistence.of({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Persistence, Record, Unavailable } from "@yielded/auth/OAuthServer";
|
|
2
2
|
import { Effect, Layer, Schema } from "effect";
|
|
3
|
-
import { SqlClient } from "effect/
|
|
3
|
+
import { SqlClient } from "effect/sql";
|
|
4
4
|
|
|
5
5
|
import { requireStandalone } from "./standalone";
|
|
6
6
|
|
|
@@ -33,7 +33,7 @@ const layer = Layer.effect(
|
|
|
33
33
|
|
|
34
34
|
if (!sql.onDialectOrElse({ sqlite: () => true, pg: () => true, orElse: () => false }))
|
|
35
35
|
return yield* Unavailable.make({});
|
|
36
|
-
const standalone = requireStandalone(() => Unavailable.make({}), sql);
|
|
36
|
+
const standalone = requireStandalone(() => Unavailable.make({}), sql.transactionService);
|
|
37
37
|
|
|
38
38
|
const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
39
39
|
effect.pipe(Effect.mapError(() => Unavailable.make({})));
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
PasskeyMethodPolicy,
|
|
16
16
|
snapshotPasskeySync,
|
|
17
17
|
} from "@yielded/auth/Passkey";
|
|
18
|
-
import { Context, Effect,
|
|
18
|
+
import { Context, Effect, Schema } from "effect";
|
|
19
|
+
import { Base64Url } from "effect/encoding";
|
|
19
20
|
|
|
20
21
|
import {
|
|
21
22
|
requiredPasskeyCredentialConstraints,
|
|
@@ -47,7 +48,7 @@ export const makePasskeyStateKernel = (
|
|
|
47
48
|
|
|
48
49
|
const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);
|
|
49
50
|
|
|
50
|
-
const nonce = () =>
|
|
51
|
+
const nonce = () => Base64Url.encode(randomBytes(32));
|
|
51
52
|
|
|
52
53
|
const encoder = new TextEncoder();
|
|
53
54
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
|
|
2
|
-
|
|
3
1
|
import { type PreparedCommit, LifecycleHooks } from "@yielded/auth/Hooks";
|
|
2
|
+
/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
|
|
4
3
|
import {
|
|
5
4
|
PasskeyConfigurationError,
|
|
6
5
|
type PasskeyUnavailable,
|
|
@@ -28,7 +27,7 @@ import {
|
|
|
28
27
|
} from "@yielded/auth/Passkey";
|
|
29
28
|
import { SubjectId } from "@yielded/auth/Schema";
|
|
30
29
|
import { Effect, Schema } from "effect";
|
|
31
|
-
import type { Statement } from "effect/
|
|
30
|
+
import type { Statement } from "effect/sql/Statement";
|
|
32
31
|
|
|
33
32
|
import type { PersistenceMappingError } from "../mapping-error";
|
|
34
33
|
import type {
|
|
@@ -44,6 +43,7 @@ import type {
|
|
|
44
43
|
TransactionTargetConfiguration,
|
|
45
44
|
makeTransactionExecutionKernel,
|
|
46
45
|
} from "../transaction-execution-kernel";
|
|
46
|
+
import type { NativeDatabase } from "../transaction-kernel";
|
|
47
47
|
import type { makePasskeyCredentialsKernel } from "./credentials";
|
|
48
48
|
import type { makePasskeyFlowKernel } from "./flow";
|
|
49
49
|
import type { makePasskeyRegistrationCeremonyKernel } from "./registration-ceremony";
|
|
@@ -114,22 +114,21 @@ export const makePasskeyTargetKernel = (
|
|
|
114
114
|
} = execution;
|
|
115
115
|
|
|
116
116
|
const sqlClientPasskeyStandaloneGuard = (
|
|
117
|
-
|
|
118
|
-
) => sqlClientTransactionStandaloneGuard(unavailable,
|
|
117
|
+
transactionService: Parameters<typeof sqlClientTransactionStandaloneGuard>[1],
|
|
118
|
+
) => sqlClientTransactionStandaloneGuard(unavailable, transactionService);
|
|
119
119
|
|
|
120
120
|
const emptyHooks: LifecycleHooks["Service"] = {
|
|
121
121
|
before: () => Effect.void,
|
|
122
122
|
after: () => Effect.succeed([]),
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
-
const makePasskeyExecution = (
|
|
126
|
-
database: Parameters<typeof makeTransactionExecution>[1],
|
|
127
|
-
hooks: LifecycleHooks["Service"],
|
|
125
|
+
const makePasskeyExecution = Effect.fnUntraced(function* (
|
|
128
126
|
configuration: PasskeyTargetConfiguration,
|
|
129
|
-
): PasskeyExecution
|
|
130
|
-
const
|
|
127
|
+
): Effect.fn.Return<PasskeyExecution, never, LifecycleHooks | NativeDatabase> {
|
|
128
|
+
const hooks = yield* LifecycleHooks;
|
|
129
|
+
|
|
130
|
+
const execution = yield* makeTransactionExecution(
|
|
131
131
|
CurrentPasskeyTransaction,
|
|
132
|
-
database,
|
|
133
132
|
configuration,
|
|
134
133
|
unavailable,
|
|
135
134
|
nonce,
|
|
@@ -140,7 +139,7 @@ export const makePasskeyTargetKernel = (
|
|
|
140
139
|
run: (operation, mutation) =>
|
|
141
140
|
execution.run(operation, mutation).pipe(Effect.provideService(LifecycleHooks, hooks)),
|
|
142
141
|
};
|
|
143
|
-
};
|
|
142
|
+
});
|
|
144
143
|
|
|
145
144
|
const issueInput = Schema.Struct({ ceremony: PasskeyCeremony, policy: PasskeyMethodPolicy });
|
|
146
145
|
|
|
@@ -422,77 +421,71 @@ export const makePasskeyTargetKernel = (
|
|
|
422
421
|
);
|
|
423
422
|
|
|
424
423
|
const makeTargetPasskeyCredentials = <M, RSetup>(
|
|
425
|
-
database: any,
|
|
426
424
|
source: PasskeyMappingSource<M, RSetup>,
|
|
427
425
|
configuration: PasskeyTargetConfiguration,
|
|
428
426
|
): Effect.Effect<
|
|
429
427
|
PasskeyCredentialServices,
|
|
430
428
|
PasskeyConfigurationError | PersistenceMappingError,
|
|
431
|
-
RSetup
|
|
429
|
+
RSetup | NativeDatabase
|
|
432
430
|
> =>
|
|
433
|
-
Effect.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
431
|
+
Effect.gen(function* () {
|
|
432
|
+
const mapping = yield* capturedMapping(source, "read", configuration);
|
|
433
|
+
|
|
434
|
+
const execution = yield* makePasskeyExecution(configuration).pipe(
|
|
435
|
+
Effect.provideService(LifecycleHooks, emptyHooks),
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
return { passkeyCredentials: credentialService(mapping, execution) };
|
|
439
|
+
});
|
|
439
440
|
|
|
440
441
|
const makeTargetPasskeyEnrollmentContext = <M, RSetup>(
|
|
441
|
-
database: any,
|
|
442
442
|
source: PasskeyMappingSource<M, RSetup>,
|
|
443
443
|
configuration: PasskeyTargetConfiguration,
|
|
444
444
|
): Effect.Effect<
|
|
445
445
|
PasskeyEnrollmentContextServices,
|
|
446
446
|
PasskeyConfigurationError | PersistenceMappingError,
|
|
447
|
-
RSetup
|
|
447
|
+
RSetup | NativeDatabase
|
|
448
448
|
> =>
|
|
449
|
-
Effect.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
449
|
+
Effect.gen(function* () {
|
|
450
|
+
const mapping = yield* capturedMapping(source, "context", configuration);
|
|
451
|
+
|
|
452
|
+
const execution = yield* makePasskeyExecution(configuration).pipe(
|
|
453
|
+
Effect.provideService(LifecycleHooks, emptyHooks),
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
return { passkeyEnrollmentContext: enrollmentService(mapping, execution) };
|
|
457
|
+
});
|
|
455
458
|
|
|
456
459
|
const makeTargetPasskeyPersistence = <M, RSetup>(
|
|
457
|
-
database: any,
|
|
458
460
|
source: PasskeyMappingSource<M, RSetup>,
|
|
459
461
|
configuration: PasskeyTargetConfiguration,
|
|
460
462
|
): Effect.Effect<
|
|
461
463
|
PasskeyPersistenceServices,
|
|
462
464
|
PasskeyConfigurationError | PersistenceMappingError,
|
|
463
|
-
RSetup | LifecycleHooks
|
|
465
|
+
RSetup | LifecycleHooks | NativeDatabase
|
|
464
466
|
> =>
|
|
465
467
|
Effect.gen(function* () {
|
|
466
468
|
const mapping = yield* capturedMapping(source, "assertion", configuration);
|
|
467
|
-
const
|
|
469
|
+
const execution = yield* makePasskeyExecution(configuration);
|
|
468
470
|
|
|
469
471
|
return {
|
|
470
|
-
passkeyPersistence: makePasskeyPersistence(
|
|
471
|
-
mapping,
|
|
472
|
-
makePasskeyExecution(database, hooks, configuration),
|
|
473
|
-
configuration,
|
|
474
|
-
),
|
|
472
|
+
passkeyPersistence: makePasskeyPersistence(mapping, execution, configuration),
|
|
475
473
|
};
|
|
476
474
|
});
|
|
477
475
|
|
|
478
476
|
const makeTargetPasskeyRegistration = <M, RSetup>(
|
|
479
|
-
database: any,
|
|
480
477
|
source: PasskeyMappingSource<M, RSetup>,
|
|
481
478
|
configuration: PasskeyTargetConfiguration,
|
|
482
479
|
): Effect.Effect<
|
|
483
480
|
PasskeyRegistrationCeremonyServices,
|
|
484
481
|
PasskeyConfigurationError | PersistenceMappingError,
|
|
485
|
-
RSetup | LifecycleHooks
|
|
482
|
+
RSetup | LifecycleHooks | NativeDatabase
|
|
486
483
|
> =>
|
|
487
484
|
Effect.gen(function* () {
|
|
488
485
|
const mapping = yield* capturedMapping(source, "registration", configuration);
|
|
489
|
-
const
|
|
486
|
+
const execution = yield* makePasskeyExecution(configuration);
|
|
490
487
|
|
|
491
|
-
return registrationServices(
|
|
492
|
-
mapping,
|
|
493
|
-
makePasskeyExecution(database, hooks, configuration),
|
|
494
|
-
configuration,
|
|
495
|
-
);
|
|
488
|
+
return registrationServices(mapping, execution, configuration);
|
|
496
489
|
});
|
|
497
490
|
|
|
498
491
|
const coordinateTargetPasskey = <M, RSetup, Transaction, A, E, R>(
|
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
} from "@yielded/auth/Passkey";
|
|
21
21
|
import { TokenDigest, type SubjectId } from "@yielded/auth/Schema";
|
|
22
22
|
import { SessionInvalidationWindow } from "@yielded/auth/Sessions";
|
|
23
|
-
import { DateTime, Effect,
|
|
23
|
+
import { DateTime, Effect, Schema } from "effect";
|
|
24
|
+
import { Base64Url } from "effect/encoding";
|
|
24
25
|
|
|
25
26
|
import type { PersistenceMappingError } from "../mapping-error";
|
|
26
27
|
import type { QueryOperations } from "../query-operations";
|
|
@@ -86,7 +87,7 @@ export const makePasskeyWriteStateKernel = (
|
|
|
86
87
|
value: S["Type"],
|
|
87
88
|
) =>
|
|
88
89
|
TokenDigest.make(
|
|
89
|
-
|
|
90
|
+
Base64Url.encode(sha256(new TextEncoder().encode(jsonStorage(schema).encode(value)))),
|
|
90
91
|
);
|
|
91
92
|
|
|
92
93
|
const currentSubject = Effect.fn("passkey.currentWriteSubject")(function* (
|
|
@@ -12,7 +12,7 @@ import * as M from "@yielded/auth/Passkey";
|
|
|
12
12
|
import { SubjectId } from "@yielded/auth/Schema";
|
|
13
13
|
import { SessionInvalidationWindow } from "@yielded/auth/Sessions";
|
|
14
14
|
import { Effect, Layer, Schema } from "effect";
|
|
15
|
-
import type { Statement } from "effect/
|
|
15
|
+
import type { Statement } from "effect/sql/Statement";
|
|
16
16
|
|
|
17
17
|
import type { PasskeyMappingSource } from "../models/passkey-model";
|
|
18
18
|
import type {
|
|
@@ -341,35 +341,25 @@ export const makePasskeyWriteTargetKernel = (
|
|
|
341
341
|
});
|
|
342
342
|
|
|
343
343
|
const makeTargetPasskeyManagement = <M, RSetup>(
|
|
344
|
-
database: any,
|
|
345
344
|
source: PasskeyMappingSource<M, RSetup>,
|
|
346
345
|
configuration: PasskeyTargetConfiguration,
|
|
347
346
|
) =>
|
|
348
347
|
Effect.gen(function* () {
|
|
349
348
|
const mapping = yield* writeMapping(source, configuration, false);
|
|
350
|
-
const
|
|
349
|
+
const execution = yield* makePasskeyExecution(configuration);
|
|
351
350
|
|
|
352
|
-
return managementServices(
|
|
353
|
-
mapping,
|
|
354
|
-
makePasskeyExecution(database, hooks, configuration),
|
|
355
|
-
configuration,
|
|
356
|
-
);
|
|
351
|
+
return managementServices(mapping, execution, configuration);
|
|
357
352
|
});
|
|
358
353
|
|
|
359
354
|
const makeTargetPasskeyRegistrationWriter = <M, R, RSetup>(
|
|
360
|
-
database: any,
|
|
361
355
|
source: PasskeyMappingSource<M, RSetup>,
|
|
362
356
|
configuration: PasskeyTargetConfiguration,
|
|
363
357
|
) =>
|
|
364
358
|
Effect.gen(function* () {
|
|
365
359
|
const mapping = yield* writeMapping(source, configuration, true);
|
|
366
|
-
const
|
|
360
|
+
const execution = yield* makePasskeyExecution(configuration);
|
|
367
361
|
|
|
368
|
-
return registrationServices<R>(
|
|
369
|
-
mapping,
|
|
370
|
-
makePasskeyExecution(database, hooks, configuration),
|
|
371
|
-
configuration,
|
|
372
|
-
);
|
|
362
|
+
return registrationServices<R>(mapping, execution, configuration);
|
|
373
363
|
});
|
|
374
364
|
|
|
375
365
|
const coordinateTargetPasskeyManagement = <M, RSetup, Transaction, A, E, R>(
|
package/src/internal/passkeys.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
PasskeyUnavailable,
|
|
13
13
|
} from "@yielded/auth/Passkey";
|
|
14
14
|
import { Context, Effect, Schema } from "effect";
|
|
15
|
-
import { SqlClient } from "effect/
|
|
15
|
+
import { SqlClient } from "effect/sql";
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
PersistenceConfigurationError,
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
import { makePasskeyKernel } from "./passkey-kernel";
|
|
34
34
|
import type { PasskeyTargetConfiguration } from "./passkey/target";
|
|
35
35
|
import type { Backend } from "./persistence";
|
|
36
|
-
import
|
|
36
|
+
import { CurrentProofSql } from "./proof-kernel";
|
|
37
37
|
import type { SqlExpression as SQL, QueryOperations, TableModel } from "./query-operations";
|
|
38
38
|
import { requireStandalone } from "./standalone";
|
|
39
39
|
import { storageTables, type StorageRole } from "./storage-tables";
|
|
@@ -386,7 +386,7 @@ export const makeComposedPasskeys = (
|
|
|
386
386
|
};
|
|
387
387
|
|
|
388
388
|
const make: Backend<object, never>["passkeys"] = Effect.fn("AuthPersistence.passkeys")(
|
|
389
|
-
function* ({
|
|
389
|
+
function* ({ storage, namespace, dialect, features, passwordModules }) {
|
|
390
390
|
const client = yield* SqlClient.SqlClient;
|
|
391
391
|
|
|
392
392
|
const policies = yield* Effect.forEach(features, (feature) =>
|
|
@@ -409,11 +409,10 @@ export const makeComposedPasskeys = (
|
|
|
409
409
|
mode: "interactive",
|
|
410
410
|
dialect,
|
|
411
411
|
locking: dialect === "pg",
|
|
412
|
-
standaloneGuard: () => requireStandalone(unavailable, client),
|
|
412
|
+
standaloneGuard: () => requireStandalone(unavailable, client.transactionService),
|
|
413
413
|
};
|
|
414
414
|
|
|
415
|
-
|
|
416
|
-
const native = database as ProofSqlDatabase;
|
|
415
|
+
const native = yield* CurrentProofSql;
|
|
417
416
|
|
|
418
417
|
yield* client.withTransaction(
|
|
419
418
|
Effect.gen(function* () {
|
|
@@ -492,7 +491,6 @@ export const makeComposedPasskeys = (
|
|
|
492
491
|
);
|
|
493
492
|
|
|
494
493
|
const credential = yield* makeTargetPasskeyCredentials<ReadMapping, never>(
|
|
495
|
-
database,
|
|
496
494
|
m.read,
|
|
497
495
|
configuration,
|
|
498
496
|
);
|
|
@@ -510,7 +508,6 @@ export const makeComposedPasskeys = (
|
|
|
510
508
|
});
|
|
511
509
|
|
|
512
510
|
const services = yield* makeTargetPasskeyManagement<ManagementMapping, never>(
|
|
513
|
-
database,
|
|
514
511
|
mapping,
|
|
515
512
|
configuration,
|
|
516
513
|
);
|
|
@@ -520,7 +517,6 @@ export const makeComposedPasskeys = (
|
|
|
520
517
|
enrollment.set(
|
|
521
518
|
feature.moduleId,
|
|
522
519
|
(yield* makeTargetPasskeyEnrollmentContext<ManagementMapping, never>(
|
|
523
|
-
database,
|
|
524
520
|
mapping,
|
|
525
521
|
configuration,
|
|
526
522
|
)).passkeyEnrollmentContext,
|
|
@@ -529,7 +525,6 @@ export const makeComposedPasskeys = (
|
|
|
529
525
|
persistence.set(
|
|
530
526
|
feature.moduleId,
|
|
531
527
|
(yield* makeTargetPasskeyPersistence<BaseMapping, never>(
|
|
532
|
-
database,
|
|
533
528
|
m.base(feature),
|
|
534
529
|
configuration,
|
|
535
530
|
)).passkeyPersistence,
|