@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,5 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
3
|
|
|
4
4
|
import { PersistenceConfigurationError, type PersistenceApi } from "./configuration";
|
|
5
5
|
import { makeComposedPasskeys } from "./passkeys";
|
|
@@ -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(
|
|
41
|
+
return yield* makeSqlDatabase(dialect);
|
|
42
42
|
}),
|
|
43
43
|
}),
|
|
44
44
|
table,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Predicate } from "effect";
|
|
2
|
-
import
|
|
3
|
-
import type { SqlError } from "effect/
|
|
2
|
+
import { SqlClient } from "effect/sql/SqlClient";
|
|
3
|
+
import type { SqlError } from "effect/sql/SqlError";
|
|
4
4
|
|
|
5
5
|
import { PersistenceMappingError } from "./mapping-error";
|
|
6
6
|
import type { QueryOperations } from "./query-operations";
|
|
@@ -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 = (
|
|
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> };
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { type Context, Effect } from "effect";
|
|
2
|
-
import type * as SqlClient from "effect/
|
|
2
|
+
import type * as SqlClient from "effect/sql/SqlClient";
|
|
3
3
|
|
|
4
4
|
/** Standalone operations must observe their own durable commit. */
|
|
5
5
|
export const requireStandalone = <Failure>(
|
|
6
6
|
unavailable: () => Failure,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 (
|
|
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
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)
|
|
25
|
-
|
|
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
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
+
);
|
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
/* oxlint-disable no-explicit-any -- existing storage kernels erase foreign table shapes; domain errors remain typed. */
|
|
9
9
|
/* oxlint-disable no-explicit-any -- private execution bridge retains exact public driver wrappers. */
|
|
10
10
|
import { Context, Effect } from "effect";
|
|
11
|
-
import type * as SqlClient from "effect/
|
|
12
|
-
import type * as SqlError from "effect/
|
|
13
|
-
import type { Statement } from "effect/
|
|
11
|
+
import type * as SqlClient from "effect/sql/SqlClient";
|
|
12
|
+
import type * as SqlError from "effect/sql/SqlError";
|
|
13
|
+
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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,10 +1,10 @@
|
|
|
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";
|
|
5
|
-
import type * as SqlClient from "effect/
|
|
6
|
-
import type { SqlError } from "effect/
|
|
7
|
-
import type { Statement } from "effect/
|
|
4
|
+
import { Cause, Context, Effect } from "effect";
|
|
5
|
+
import type * as SqlClient from "effect/sql/SqlClient";
|
|
6
|
+
import type { SqlError } from "effect/sql/SqlError";
|
|
7
|
+
import type { Statement } from "effect/sql/Statement";
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
10
|
SqlExpression as SQL,
|
|
@@ -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;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Layer } from "effect";
|
|
2
|
-
import { SqlClient } from "effect/unstable/sql";
|
|
3
|
-
import { OAuthUnavailable } from "@yielded/auth/OAuth";
|
|
4
|
-
import { Persistence } from "@yielded/auth/OAuthApp";
|
|
5
|
-
//#region src/internal/oauth-app.d.ts
|
|
6
|
-
export declare const OAuthAppPersistence: {
|
|
7
|
-
layer: Layer.Layer<Persistence, OAuthUnavailable, SqlClient.SqlClient>;
|
|
8
|
-
migration: string;
|
|
9
|
-
};
|
|
10
|
-
//#endregion
|
|
11
|
-
//# sourceMappingURL=oauth-app.d.mts.map
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { requireStandalone } from "./standalone.mjs";
|
|
2
|
-
import { Effect, Layer, Schema } from "effect";
|
|
3
|
-
import { SqlClient } from "effect/unstable/sql";
|
|
4
|
-
import { OAuthUnavailable } from "@yielded/auth/OAuth";
|
|
5
|
-
import { Persistence, Record } from "@yielded/auth/OAuthApp";
|
|
6
|
-
//#region src/internal/oauth-app.ts
|
|
7
|
-
const codec = Schema.fromJsonString(Record);
|
|
8
|
-
const Row = Schema.Struct({
|
|
9
|
-
payload: Schema.String,
|
|
10
|
-
version: Schema.String
|
|
11
|
-
});
|
|
12
|
-
const OAuthAppPersistence = {
|
|
13
|
-
layer: Layer.effect(Persistence, Effect.gen(function* () {
|
|
14
|
-
const sql = yield* SqlClient.SqlClient;
|
|
15
|
-
if (!sql.onDialectOrElse({
|
|
16
|
-
sqlite: () => true,
|
|
17
|
-
pg: () => true,
|
|
18
|
-
orElse: () => false
|
|
19
|
-
})) return yield* OAuthUnavailable.make({});
|
|
20
|
-
const noTransaction = requireStandalone(() => OAuthUnavailable.make({}), sql);
|
|
21
|
-
const failure = (effect) => effect.pipe(Effect.mapError(() => OAuthUnavailable.make({})));
|
|
22
|
-
return Persistence.of({
|
|
23
|
-
get: Effect.fn("OAuthAppPersistence.get")(function* (namespace, key) {
|
|
24
|
-
yield* noTransaction;
|
|
25
|
-
const rows = yield* sql`SELECT payload, version FROM yielded_oauth_app WHERE namespace = ${namespace} AND record_key = ${key}`;
|
|
26
|
-
if (rows.length === 0) return void 0;
|
|
27
|
-
if (rows.length !== 1) return yield* OAuthUnavailable.make({});
|
|
28
|
-
const row = yield* Schema.decodeEffect(Row)(rows[0]);
|
|
29
|
-
const value = yield* Schema.decodeEffect(codec)(row.payload);
|
|
30
|
-
if (value.version !== row.version) return yield* OAuthUnavailable.make({});
|
|
31
|
-
return value;
|
|
32
|
-
}, failure),
|
|
33
|
-
insert: Effect.fn("OAuthAppPersistence.insert")(function* (namespace, key, value) {
|
|
34
|
-
yield* noTransaction;
|
|
35
|
-
const payload = yield* Schema.encodeEffect(codec)(value);
|
|
36
|
-
return (yield* sql`INSERT INTO yielded_oauth_app (namespace, record_key, version, payload) VALUES (${namespace}, ${key}, ${value.version}, ${payload}) ON CONFLICT (namespace, record_key) DO NOTHING RETURNING version`).length === 1;
|
|
37
|
-
}, failure),
|
|
38
|
-
compareAndSet: Effect.fn("OAuthAppPersistence.compareAndSet")(function* (namespace, key, version, value) {
|
|
39
|
-
yield* noTransaction;
|
|
40
|
-
if (value.version === version) return yield* OAuthUnavailable.make({});
|
|
41
|
-
const payload = yield* Schema.encodeEffect(codec)(value);
|
|
42
|
-
return (yield* sql`UPDATE yielded_oauth_app SET version = ${value.version}, payload = ${payload} WHERE namespace = ${namespace} AND record_key = ${key} AND version = ${version} RETURNING version`).length === 1;
|
|
43
|
-
}, failure)
|
|
44
|
-
});
|
|
45
|
-
})),
|
|
46
|
-
migration: `CREATE TABLE yielded_oauth_app (
|
|
47
|
-
namespace TEXT NOT NULL,
|
|
48
|
-
record_key TEXT NOT NULL,
|
|
49
|
-
version TEXT NOT NULL,
|
|
50
|
-
payload TEXT NOT NULL,
|
|
51
|
-
PRIMARY KEY (namespace, record_key)
|
|
52
|
-
)`
|
|
53
|
-
};
|
|
54
|
-
//#endregion
|
|
55
|
-
export { OAuthAppPersistence };
|
|
56
|
-
|
|
57
|
-
//# sourceMappingURL=oauth-app.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-app.mjs","names":[],"sources":["../../src/internal/oauth-app.ts"],"sourcesContent":["import { OAuthUnavailable } from \"@yielded/auth/OAuth\";\nimport { Persistence, Record } from \"@yielded/auth/OAuthApp\";\nimport { Effect, Layer, Schema } from \"effect\";\nimport { SqlClient } from \"effect/unstable/sql\";\n\nimport { requireStandalone } from \"./standalone\";\n\nconst codec = Schema.fromJsonString(Record);\nconst Row = Schema.Struct({ payload: Schema.String, version: Schema.String });\n\n/** Apply with the application's migrations. SQLite (including D1) and PostgreSQL.\n * Each transition is one conditional statement, so D1 needs no interactive\n * transaction and parallel workers share the same refresh/code claim.\n */\nconst migration = `CREATE TABLE yielded_oauth_app (\n namespace TEXT NOT NULL,\n record_key TEXT NOT NULL,\n version TEXT NOT NULL,\n payload TEXT NOT NULL,\n PRIMARY KEY (namespace, record_key)\n)`;\n\nconst layer = Layer.effect(\n Persistence,\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n if (!sql.onDialectOrElse({ sqlite: () => true, pg: () => true, orElse: () => false }))\n return yield* OAuthUnavailable.make({});\n\n const noTransaction = requireStandalone(() => OAuthUnavailable.make({}), sql);\n\n const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(Effect.mapError(() => OAuthUnavailable.make({})));\n\n return Persistence.of({\n get: Effect.fn(\"OAuthAppPersistence.get\")(function* (namespace, key) {\n yield* noTransaction;\n\n const rows = yield* sql<{\n payload: string;\n version: string;\n }>`SELECT payload, version FROM yielded_oauth_app WHERE namespace = ${namespace} AND record_key = ${key}`;\n\n if (rows.length === 0) return undefined;\n if (rows.length !== 1) return yield* OAuthUnavailable.make({});\n const row = yield* Schema.decodeEffect(Row)(rows[0]);\n const value = yield* Schema.decodeEffect(codec)(row.payload);\n\n if (value.version !== row.version) return yield* OAuthUnavailable.make({});\n\n return value;\n }, failure),\n insert: Effect.fn(\"OAuthAppPersistence.insert\")(function* (namespace, key, value) {\n yield* noTransaction;\n const payload = yield* Schema.encodeEffect(codec)(value);\n\n const rows =\n yield* sql`INSERT INTO yielded_oauth_app (namespace, record_key, version, payload) VALUES (${namespace}, ${key}, ${value.version}, ${payload}) ON CONFLICT (namespace, record_key) DO NOTHING RETURNING version`;\n\n return rows.length === 1;\n }, failure),\n compareAndSet: Effect.fn(\"OAuthAppPersistence.compareAndSet\")(function* (\n namespace,\n key,\n version,\n value,\n ) {\n yield* noTransaction;\n if (value.version === version) return yield* OAuthUnavailable.make({});\n const payload = yield* Schema.encodeEffect(codec)(value);\n\n const rows =\n yield* sql`UPDATE yielded_oauth_app SET version = ${value.version}, payload = ${payload} WHERE namespace = ${namespace} AND record_key = ${key} AND version = ${version} RETURNING version`;\n\n return rows.length === 1;\n }, failure),\n });\n }),\n);\n\nexport const OAuthAppPersistence = { layer, migration };\n"],"mappings":";;;;;;AAOA,MAAM,QAAQ,OAAO,eAAe,MAAM;AAC1C,MAAM,MAAM,OAAO,OAAO;CAAE,SAAS,OAAO;CAAQ,SAAS,OAAO;AAAO,CAAC;AAyE5E,MAAa,sBAAsB;CAAE,OA3DvB,MAAM,OAClB,aACA,OAAO,IAAI,aAAa;EACtB,MAAM,MAAM,OAAO,UAAU;EAE7B,IAAI,CAAC,IAAI,gBAAgB;GAAE,cAAc;GAAM,UAAU;GAAM,cAAc;EAAM,CAAC,GAClF,OAAO,OAAO,iBAAiB,KAAK,CAAC,CAAC;EAExC,MAAM,gBAAgB,wBAAwB,iBAAiB,KAAK,CAAC,CAAC,GAAG,GAAG;EAE5E,MAAM,WAAoB,WACxB,OAAO,KAAK,OAAO,eAAe,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC;EAE9D,OAAO,YAAY,GAAG;GACpB,KAAK,OAAO,GAAG,yBAAyB,CAAC,CAAC,WAAW,WAAW,KAAK;IACnE,OAAO;IAEP,MAAM,OAAO,OAAO,GAGlB,oEAAoE,UAAU,oBAAoB;IAEpG,IAAI,KAAK,WAAW,GAAG,OAAO,KAAA;IAC9B,IAAI,KAAK,WAAW,GAAG,OAAO,OAAO,iBAAiB,KAAK,CAAC,CAAC;IAC7D,MAAM,MAAM,OAAO,OAAO,aAAa,GAAG,CAAC,CAAC,KAAK,EAAE;IACnD,MAAM,QAAQ,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,IAAI,OAAO;IAE3D,IAAI,MAAM,YAAY,IAAI,SAAS,OAAO,OAAO,iBAAiB,KAAK,CAAC,CAAC;IAEzE,OAAO;GACT,GAAG,OAAO;GACV,QAAQ,OAAO,GAAG,4BAA4B,CAAC,CAAC,WAAW,WAAW,KAAK,OAAO;IAChF,OAAO;IACP,MAAM,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,KAAK;IAKvD,QAAO,OAFE,GAAG,mFAAmF,UAAU,IAAI,IAAI,IAAI,MAAM,QAAQ,IAAI,QAAQ,oEAAA,CAEnI,WAAW;GACzB,GAAG,OAAO;GACV,eAAe,OAAO,GAAG,mCAAmC,CAAC,CAAC,WAC5D,WACA,KACA,SACA,OACA;IACA,OAAO;IACP,IAAI,MAAM,YAAY,SAAS,OAAO,OAAO,iBAAiB,KAAK,CAAC,CAAC;IACrE,MAAM,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,KAAK;IAKvD,QAAO,OAFE,GAAG,0CAA0C,MAAM,QAAQ,cAAc,QAAQ,qBAAqB,UAAU,oBAAoB,IAAI,iBAAiB,QAAQ,oBAAA,CAE9J,WAAW;GACzB,GAAG,OAAO;EACZ,CAAC;CACH,CAAC,CAGkC;CAAO;;;;;;;AAAU"}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { OAuthUnavailable } from "@yielded/auth/OAuth";
|
|
2
|
-
import { Persistence, Record } from "@yielded/auth/OAuthApp";
|
|
3
|
-
import { Effect, Layer, Schema } from "effect";
|
|
4
|
-
import { SqlClient } from "effect/unstable/sql";
|
|
5
|
-
|
|
6
|
-
import { requireStandalone } from "./standalone";
|
|
7
|
-
|
|
8
|
-
const codec = Schema.fromJsonString(Record);
|
|
9
|
-
const Row = Schema.Struct({ payload: Schema.String, version: Schema.String });
|
|
10
|
-
|
|
11
|
-
/** Apply with the application's migrations. SQLite (including D1) and PostgreSQL.
|
|
12
|
-
* Each transition is one conditional statement, so D1 needs no interactive
|
|
13
|
-
* transaction and parallel workers share the same refresh/code claim.
|
|
14
|
-
*/
|
|
15
|
-
const migration = `CREATE TABLE yielded_oauth_app (
|
|
16
|
-
namespace TEXT NOT NULL,
|
|
17
|
-
record_key TEXT NOT NULL,
|
|
18
|
-
version TEXT NOT NULL,
|
|
19
|
-
payload TEXT NOT NULL,
|
|
20
|
-
PRIMARY KEY (namespace, record_key)
|
|
21
|
-
)`;
|
|
22
|
-
|
|
23
|
-
const layer = Layer.effect(
|
|
24
|
-
Persistence,
|
|
25
|
-
Effect.gen(function* () {
|
|
26
|
-
const sql = yield* SqlClient.SqlClient;
|
|
27
|
-
|
|
28
|
-
if (!sql.onDialectOrElse({ sqlite: () => true, pg: () => true, orElse: () => false }))
|
|
29
|
-
return yield* OAuthUnavailable.make({});
|
|
30
|
-
|
|
31
|
-
const noTransaction = requireStandalone(() => OAuthUnavailable.make({}), sql);
|
|
32
|
-
|
|
33
|
-
const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
34
|
-
effect.pipe(Effect.mapError(() => OAuthUnavailable.make({})));
|
|
35
|
-
|
|
36
|
-
return Persistence.of({
|
|
37
|
-
get: Effect.fn("OAuthAppPersistence.get")(function* (namespace, key) {
|
|
38
|
-
yield* noTransaction;
|
|
39
|
-
|
|
40
|
-
const rows = yield* sql<{
|
|
41
|
-
payload: string;
|
|
42
|
-
version: string;
|
|
43
|
-
}>`SELECT payload, version FROM yielded_oauth_app WHERE namespace = ${namespace} AND record_key = ${key}`;
|
|
44
|
-
|
|
45
|
-
if (rows.length === 0) return undefined;
|
|
46
|
-
if (rows.length !== 1) return yield* OAuthUnavailable.make({});
|
|
47
|
-
const row = yield* Schema.decodeEffect(Row)(rows[0]);
|
|
48
|
-
const value = yield* Schema.decodeEffect(codec)(row.payload);
|
|
49
|
-
|
|
50
|
-
if (value.version !== row.version) return yield* OAuthUnavailable.make({});
|
|
51
|
-
|
|
52
|
-
return value;
|
|
53
|
-
}, failure),
|
|
54
|
-
insert: Effect.fn("OAuthAppPersistence.insert")(function* (namespace, key, value) {
|
|
55
|
-
yield* noTransaction;
|
|
56
|
-
const payload = yield* Schema.encodeEffect(codec)(value);
|
|
57
|
-
|
|
58
|
-
const rows =
|
|
59
|
-
yield* sql`INSERT INTO yielded_oauth_app (namespace, record_key, version, payload) VALUES (${namespace}, ${key}, ${value.version}, ${payload}) ON CONFLICT (namespace, record_key) DO NOTHING RETURNING version`;
|
|
60
|
-
|
|
61
|
-
return rows.length === 1;
|
|
62
|
-
}, failure),
|
|
63
|
-
compareAndSet: Effect.fn("OAuthAppPersistence.compareAndSet")(function* (
|
|
64
|
-
namespace,
|
|
65
|
-
key,
|
|
66
|
-
version,
|
|
67
|
-
value,
|
|
68
|
-
) {
|
|
69
|
-
yield* noTransaction;
|
|
70
|
-
if (value.version === version) return yield* OAuthUnavailable.make({});
|
|
71
|
-
const payload = yield* Schema.encodeEffect(codec)(value);
|
|
72
|
-
|
|
73
|
-
const rows =
|
|
74
|
-
yield* sql`UPDATE yielded_oauth_app SET version = ${value.version}, payload = ${payload} WHERE namespace = ${namespace} AND record_key = ${key} AND version = ${version} RETURNING version`;
|
|
75
|
-
|
|
76
|
-
return rows.length === 1;
|
|
77
|
-
}, failure),
|
|
78
|
-
});
|
|
79
|
-
}),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
export const OAuthAppPersistence = { layer, migration };
|