@yielded/auth-persistence 0.1.0-beta.11 → 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.
Files changed (66) hide show
  1. package/README.md +6 -1
  2. package/dist/Adapter.d.mts +2 -2
  3. package/dist/Adapter.mjs +4 -4
  4. package/dist/internal/PasswordPreparedPostconditions.d.mts +1 -1
  5. package/dist/internal/configuration.d.mts +1 -1
  6. package/dist/internal/email-kernel.d.mts +5 -5
  7. package/dist/internal/email-kernel.mjs +8 -6
  8. package/dist/internal/email-kernel.mjs.map +1 -1
  9. package/dist/internal/models/email-model.d.mts +1 -1
  10. package/dist/internal/oauth-server.mjs +1 -1
  11. package/dist/internal/oauth-server.mjs.map +1 -1
  12. package/dist/internal/passkey/target.mjs +21 -12
  13. package/dist/internal/passkey/target.mjs.map +1 -1
  14. package/dist/internal/passkey/write-target.mjs +6 -6
  15. package/dist/internal/passkey/write-target.mjs.map +1 -1
  16. package/dist/internal/passkey-kernel.d.mts +9 -12
  17. package/dist/internal/passkeys.mjs +8 -7
  18. package/dist/internal/passkeys.mjs.map +1 -1
  19. package/dist/internal/password-kernel.d.mts +3 -3
  20. package/dist/internal/password-kernel.mjs +3 -2
  21. package/dist/internal/password-kernel.mjs.map +1 -1
  22. package/dist/internal/persistence.d.mts +3 -2
  23. package/dist/internal/persistence.mjs +22 -26
  24. package/dist/internal/persistence.mjs.map +1 -1
  25. package/dist/internal/proof-kernel.d.mts +2 -2
  26. package/dist/internal/proof-kernel.mjs +2 -1
  27. package/dist/internal/proof-kernel.mjs.map +1 -1
  28. package/dist/internal/registration.mjs +6 -1
  29. package/dist/internal/registration.mjs.map +1 -1
  30. package/dist/internal/session-kernel.d.mts +19 -9
  31. package/dist/internal/session-kernel.mjs +155 -147
  32. package/dist/internal/session-kernel.mjs.map +1 -1
  33. package/dist/internal/sql-persistence.mjs +2 -3
  34. package/dist/internal/sql-persistence.mjs.map +1 -1
  35. package/dist/internal/sql-query.mjs +4 -2
  36. package/dist/internal/sql-query.mjs.map +1 -1
  37. package/dist/internal/standalone.d.mts +2 -4
  38. package/dist/internal/standalone.mjs +2 -2
  39. package/dist/internal/standalone.mjs.map +1 -1
  40. package/dist/internal/storage-mapping.mjs +2 -2
  41. package/dist/internal/storage-validation.mjs +4 -2
  42. package/dist/internal/storage-validation.mjs.map +1 -1
  43. package/dist/internal/transaction-execution-kernel.d.mts +6 -7
  44. package/dist/internal/transaction-execution-kernel.mjs +9 -7
  45. package/dist/internal/transaction-execution-kernel.mjs.map +1 -1
  46. package/dist/internal/transaction-kernel.d.mts +7 -3
  47. package/dist/internal/transaction-kernel.mjs +5 -2
  48. package/dist/internal/transaction-kernel.mjs.map +1 -1
  49. package/package.json +1 -1
  50. package/src/Adapter.ts +6 -1
  51. package/src/internal/email-kernel.ts +30 -35
  52. package/src/internal/oauth-server.ts +1 -1
  53. package/src/internal/passkey/target.ts +36 -43
  54. package/src/internal/passkey/write-target.ts +4 -14
  55. package/src/internal/passkeys.ts +4 -9
  56. package/src/internal/password-kernel.ts +2 -2
  57. package/src/internal/persistence.ts +77 -68
  58. package/src/internal/proof-kernel.ts +2 -2
  59. package/src/internal/registration.ts +22 -7
  60. package/src/internal/session-kernel.ts +1122 -1139
  61. package/src/internal/sql-persistence.ts +1 -1
  62. package/src/internal/sql-query.ts +5 -3
  63. package/src/internal/standalone.ts +5 -7
  64. package/src/internal/storage-validation.ts +20 -16
  65. package/src/internal/transaction-execution-kernel.ts +17 -15
  66. package/src/internal/transaction-kernel.ts +7 -1
@@ -38,7 +38,7 @@ export const AuthPersistence: PersistenceApi<Table> & { readonly table: typeof t
38
38
  reason: "Use an explicit adapter for this SQL dialect",
39
39
  });
40
40
 
41
- return makeSqlDatabase(sql, dialect);
41
+ return yield* makeSqlDatabase(dialect);
42
42
  }),
43
43
  }),
44
44
  table,
@@ -1,5 +1,5 @@
1
1
  import { Effect, Predicate } from "effect";
2
- import type { SqlClient } from "effect/sql/SqlClient";
2
+ import { SqlClient } from "effect/sql/SqlClient";
3
3
  import type { SqlError } from "effect/sql/SqlError";
4
4
 
5
5
  import { PersistenceMappingError } from "./mapping-error";
@@ -213,7 +213,9 @@ const compile = (state: QueryState, compiler: Compiler): string => {
213
213
  };
214
214
 
215
215
  /** Captures one Effect SQL client. Its transaction service remains the physical owner. */
216
- export const makeSqlDatabase = (client: SqlClient, dialect: Dialect): SqlDatabase => {
216
+ export const makeSqlDatabase = Effect.fnUntraced(function* (dialect: Dialect) {
217
+ const client = yield* SqlClient;
218
+
217
219
  const query = (state: QueryState): Query => {
218
220
  const execute = Effect.suspend(() => {
219
221
  const compiler: Compiler = { dialect, parameters: [] };
@@ -267,7 +269,7 @@ export const makeSqlDatabase = (client: SqlClient, dialect: Dialect): SqlDatabas
267
269
  };
268
270
 
269
271
  return database;
270
- };
272
+ });
271
273
 
272
274
  interface Query extends Effect.Effect<Row[], SqlError> {
273
275
  toSQL(): { readonly sql: string; readonly params: ReadonlyArray<unknown> };
@@ -4,17 +4,15 @@ import type * as SqlClient from "effect/sql/SqlClient";
4
4
  /** Standalone operations must observe their own durable commit. */
5
5
  export const requireStandalone = <Failure>(
6
6
  unavailable: () => Failure,
7
- client?: {
8
- readonly transactionService: Context.Key<
9
- SqlClient.TransactionConnection,
10
- SqlClient.TransactionConnection.Service
11
- >;
12
- },
7
+ transactionService?: Context.Key<
8
+ SqlClient.TransactionConnection,
9
+ SqlClient.TransactionConnection.Service
10
+ >,
13
11
  ): Effect.Effect<void, Failure> =>
14
12
  Effect.withFiber((fiber) => {
15
13
  const services = fiber.context.mapUnsafe;
16
14
 
17
- if (client !== undefined && services.has(client.transactionService.key))
15
+ if (transactionService !== undefined && services.has(transactionService.key))
18
16
  return Effect.fail(unavailable());
19
17
 
20
18
  // rc.117 libSQL uses a private per-client marker that is not exposed through
@@ -1,5 +1,5 @@
1
1
  import { Effect, Schema } from "effect";
2
- import type { SqlClient } from "effect/sql/SqlClient";
2
+ import { SqlClient } from "effect/sql/SqlClient";
3
3
 
4
4
  import { PersistenceConfigurationError } from "./configuration";
5
5
  import type { StorageTable } from "./storage-tables";
@@ -16,13 +16,14 @@ export const qualifiedTableName = (table: Pick<StorageTable, "name" | "schema">)
16
16
 
17
17
  /** Validate physical uniqueness before the first authentication operation. A
18
18
  * partial or expression index cannot establish these unconditional guarantees. */
19
- export const validateStorage = (
20
- client: SqlClient,
21
- dialect: "pg" | "sqlite",
22
- table: StorageTable,
23
- required: ReadonlyArray<ReadonlyArray<string>>,
24
- ) =>
25
- Effect.gen(function* () {
19
+ export const validateStorage = Effect.fnUntraced(
20
+ function* (
21
+ dialect: "pg" | "sqlite",
22
+ table: StorageTable,
23
+ required: ReadonlyArray<ReadonlyArray<string>>,
24
+ ) {
25
+ const client = yield* SqlClient;
26
+
26
27
  const physicalKeys: ReadonlyArray<ReadonlyArray<string>> = yield* dialect === "sqlite"
27
28
  ? Effect.gen(function* () {
28
29
  if (table.schema !== undefined && table.schema !== "main")
@@ -106,12 +107,15 @@ export const validateStorage = (
106
107
  reason: `Missing unique key on ${table.name} (${keys.join(", ")})`,
107
108
  });
108
109
  }
109
- }).pipe(
110
- Effect.mapError((error) =>
111
- Schema.is(PersistenceConfigurationError)(error)
112
- ? error
113
- : PersistenceConfigurationError.make({
114
- reason: `Cannot validate SQL storage for ${table.name}`,
115
- }),
110
+ },
111
+ (effect, _dialect, table) =>
112
+ effect.pipe(
113
+ Effect.mapError((error) =>
114
+ Schema.is(PersistenceConfigurationError)(error)
115
+ ? error
116
+ : PersistenceConfigurationError.make({
117
+ reason: `Cannot validate SQL storage for ${table.name}`,
118
+ }),
119
+ ),
116
120
  ),
117
- );
121
+ );
@@ -14,6 +14,7 @@ import type { Statement } from "effect/sql/Statement";
14
14
 
15
15
  import { requireStandalone } from "./standalone";
16
16
  import {
17
+ NativeDatabase,
17
18
  type TransactionNativeDatabase,
18
19
  type TransactionOwner,
19
20
  type makeTransactionKernel,
@@ -23,7 +24,10 @@ export interface TransactionTargetConfiguration<Failure> {
23
24
  readonly mode: "interactive" | "synchronous" | "batch";
24
25
  readonly dialect: "pg" | "mysql" | "sqlite";
25
26
  readonly locking: boolean;
26
- readonly standaloneGuard: (database: any) => Effect.Effect<void, Failure>;
27
+ /** The captured database's transaction marker; batch drivers may omit it. */
28
+ readonly standaloneGuard: (
29
+ transactionService: SqlClient.SqlClient["transactionService"] | undefined,
30
+ ) => Effect.Effect<void, Failure>;
27
31
  readonly transaction?: <A, E, R>(
28
32
  database: any,
29
33
  body: (transaction: any) => Effect.Effect<A, E, R>,
@@ -65,19 +69,18 @@ export const makeTransactionExecutionKernel = (
65
69
 
66
70
  const sqlClientTransactionStandaloneGuard = <Failure>(
67
71
  unavailable: () => Failure,
68
- database: {
69
- readonly $client: Pick<SqlClient.SqlClient, "transactionService">;
70
- },
71
- ) => requireStandalone(unavailable, database.$client);
72
+ transactionService: SqlClient.SqlClient["transactionService"] | undefined,
73
+ ) => requireStandalone(unavailable, transactionService);
72
74
 
73
- const makeTransactionExecution = <Failure, OwnerId>(
75
+ const makeTransactionExecution = Effect.fnUntraced(function* <Failure, OwnerId>(
74
76
  ownerTag: Context.Key<OwnerId, TransactionOwner<Failure>>,
75
- database: TransactionNativeDatabase,
76
77
  configuration: TransactionTargetConfiguration<Failure>,
77
78
  unavailable: () => Failure,
78
79
  nonce: () => string,
79
80
  bound?: TransactionBound<Failure>,
80
- ): TransactionExecution<Failure, OwnerId> => {
81
+ ): Effect.fn.Return<TransactionExecution<Failure, OwnerId>, never, NativeDatabase> {
82
+ const database = yield* NativeDatabase;
83
+
81
84
  const invariant: (value: unknown) => asserts value = (value) => {
82
85
  if (!value) throw unavailable();
83
86
  };
@@ -90,7 +93,7 @@ export const makeTransactionExecutionKernel = (
90
93
  ? Effect.suspend(() => (bound.active ? Effect.void : Effect.fail(unavailable())))
91
94
  : Effect.gen(function* () {
92
95
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
93
- yield* configuration.standaloneGuard(database);
96
+ yield* configuration.standaloneGuard(database.$client.transactionService);
94
97
  }),
95
98
  poison: () => {
96
99
  if (bound !== undefined) bound.poisoned = true;
@@ -144,7 +147,7 @@ export const makeTransactionExecutionKernel = (
144
147
  )
145
148
  : Effect.gen(function* () {
146
149
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
147
- yield* configuration.standaloneGuard(database);
150
+ yield* configuration.standaloneGuard(database.$client.transactionService);
148
151
  const marker = nonce();
149
152
 
150
153
  const result = yield* coordinateCommit(
@@ -193,7 +196,7 @@ export const makeTransactionExecutionKernel = (
193
196
  });
194
197
  },
195
198
  };
196
- };
199
+ });
197
200
 
198
201
  const coordinateTransactionOwner = <
199
202
  Failure,
@@ -225,7 +228,7 @@ export const makeTransactionExecutionKernel = (
225
228
  > => {
226
229
  return Effect.gen(function* () {
227
230
  if (yield* hasCommitScope) return yield* Effect.fail(unavailable());
228
- yield* configuration.standaloneGuard(database);
231
+ yield* configuration.standaloneGuard(database.$client.transactionService);
229
232
 
230
233
  // Allocation is independent of registration data and always precedes the physical owner.
231
234
  const resources = yield* allocate.pipe(
@@ -260,14 +263,13 @@ export const makeTransactionExecutionKernel = (
260
263
  const value = yield* owner(
261
264
  tx as Transaction,
262
265
  services(
263
- makeTransactionExecution(
266
+ yield* makeTransactionExecution(
264
267
  ownerTag,
265
- tx,
266
268
  configuration,
267
269
  unavailable,
268
270
  nonce,
269
271
  bound,
270
- ),
272
+ ).pipe(Effect.provideService(NativeDatabase, tx)),
271
273
  resources,
272
274
  ),
273
275
  (statement) => {
@@ -1,7 +1,7 @@
1
1
  import { type CommitJournal, type PreparedCommit } from "@yielded/auth/Hooks";
2
2
  import { reportAuthFailure } from "@yielded/auth/Persistence";
3
3
  /* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */
4
- import { Cause, Effect } from "effect";
4
+ import { Cause, Context, Effect } from "effect";
5
5
  import type * as SqlClient from "effect/sql/SqlClient";
6
6
  import type { SqlError } from "effect/sql/SqlError";
7
7
  import type { Statement } from "effect/sql/Statement";
@@ -31,6 +31,12 @@ export interface TransactionNativeDatabase {
31
31
  ) => Effect.Effect<A, E | SqlError, R>;
32
32
  }
33
33
 
34
+ /** Root native database required only while constructing standalone services.
35
+ * Active transactions remain owned by their coordinator and journal. */
36
+ export class NativeDatabase extends Context.Service<NativeDatabase, TransactionNativeDatabase>()(
37
+ "effect-auth/persistence/NativeDatabase",
38
+ ) {}
39
+
34
40
  export interface Observation {
35
41
  readonly table: Table;
36
42
  readonly where: SQL;