@yielded/auth-persistence 0.1.0-beta.10
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 +27 -0
- package/dist/Adapter.d.mts +30 -0
- package/dist/Adapter.mjs +25 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
- package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
- package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
- package/dist/internal/configuration.d.mts +174 -0
- package/dist/internal/configuration.mjs +7 -0
- package/dist/internal/configuration.mjs.map +1 -0
- package/dist/internal/email-kernel.d.mts +275 -0
- package/dist/internal/email-kernel.mjs +487 -0
- package/dist/internal/email-kernel.mjs.map +1 -0
- package/dist/internal/mapping-error.d.mts +10 -0
- package/dist/internal/mapping-error.mjs +30 -0
- package/dist/internal/mapping-error.mjs.map +1 -0
- package/dist/internal/models/common.d.mts +12 -0
- package/dist/internal/models/email-model.d.mts +353 -0
- package/dist/internal/models/email-model.mjs +21 -0
- package/dist/internal/models/email-model.mjs.map +1 -0
- package/dist/internal/models/passkey-model.d.mts +266 -0
- package/dist/internal/models/passkey-model.mjs +34 -0
- package/dist/internal/models/passkey-model.mjs.map +1 -0
- package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
- package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
- package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
- package/dist/internal/models/passkey-write-model.d.mts +189 -0
- package/dist/internal/models/passkey-write-model.mjs +13 -0
- package/dist/internal/models/passkey-write-model.mjs.map +1 -0
- package/dist/internal/models/password-model.d.mts +328 -0
- package/dist/internal/models/password-model.mjs +23 -0
- package/dist/internal/models/password-model.mjs.map +1 -0
- package/dist/internal/models/proof-model.d.mts +285 -0
- package/dist/internal/models/proof-model.mjs +17 -0
- package/dist/internal/models/proof-model.mjs.map +1 -0
- package/dist/internal/models/session-model.d.mts +199 -0
- package/dist/internal/models/session-model.mjs +20 -0
- package/dist/internal/models/session-model.mjs.map +1 -0
- package/dist/internal/models/step-up-model.d.mts +66 -0
- package/dist/internal/models/step-up-model.mjs +9 -0
- package/dist/internal/models/step-up-model.mjs.map +1 -0
- package/dist/internal/oauth-app.d.mts +11 -0
- package/dist/internal/oauth-app.mjs +57 -0
- package/dist/internal/oauth-app.mjs.map +1 -0
- package/dist/internal/oauth-server.d.mts +10 -0
- package/dist/internal/oauth-server.mjs +66 -0
- package/dist/internal/oauth-server.mjs.map +1 -0
- package/dist/internal/passkey/admission.d.mts +6 -0
- package/dist/internal/passkey/admission.mjs +216 -0
- package/dist/internal/passkey/admission.mjs.map +1 -0
- package/dist/internal/passkey/credentials.d.mts +14 -0
- package/dist/internal/passkey/credentials.mjs +249 -0
- package/dist/internal/passkey/credentials.mjs.map +1 -0
- package/dist/internal/passkey/enrollment.mjs +138 -0
- package/dist/internal/passkey/enrollment.mjs.map +1 -0
- package/dist/internal/passkey/flow.d.mts +21 -0
- package/dist/internal/passkey/flow.mjs +452 -0
- package/dist/internal/passkey/flow.mjs.map +1 -0
- package/dist/internal/passkey/management.mjs +231 -0
- package/dist/internal/passkey/management.mjs.map +1 -0
- package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
- package/dist/internal/passkey/registration-ceremony.mjs +129 -0
- package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
- package/dist/internal/passkey/registration-custody.d.mts +2 -0
- package/dist/internal/passkey/registration-custody.mjs +52 -0
- package/dist/internal/passkey/registration-custody.mjs.map +1 -0
- package/dist/internal/passkey/registration-write.mjs +211 -0
- package/dist/internal/passkey/registration-write.mjs.map +1 -0
- package/dist/internal/passkey/state.d.mts +10 -0
- package/dist/internal/passkey/state.mjs +234 -0
- package/dist/internal/passkey/state.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +19 -0
- package/dist/internal/passkey/target.mjs +190 -0
- package/dist/internal/passkey/target.mjs.map +1 -0
- package/dist/internal/passkey/write-state.d.mts +17 -0
- package/dist/internal/passkey/write-state.mjs +345 -0
- package/dist/internal/passkey/write-state.mjs.map +1 -0
- package/dist/internal/passkey/write-target.mjs +192 -0
- package/dist/internal/passkey/write-target.mjs.map +1 -0
- package/dist/internal/passkey-kernel.d.mts +5592 -0
- package/dist/internal/passkey-kernel.mjs +49 -0
- package/dist/internal/passkey-kernel.mjs.map +1 -0
- package/dist/internal/passkeys.d.mts +7 -0
- package/dist/internal/passkeys.mjs +368 -0
- package/dist/internal/passkeys.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +290 -0
- package/dist/internal/password-kernel.mjs +704 -0
- package/dist/internal/password-kernel.mjs.map +1 -0
- package/dist/internal/persistence.d.mts +25 -0
- package/dist/internal/persistence.mjs +291 -0
- package/dist/internal/persistence.mjs.map +1 -0
- package/dist/internal/phone-kernel.d.mts +162 -0
- package/dist/internal/phone-kernel.mjs +434 -0
- package/dist/internal/phone-kernel.mjs.map +1 -0
- package/dist/internal/proof-kernel.d.mts +93 -0
- package/dist/internal/proof-kernel.mjs +619 -0
- package/dist/internal/proof-kernel.mjs.map +1 -0
- package/dist/internal/query-operations.d.mts +61 -0
- package/dist/internal/registration-contract.d.mts +16 -0
- package/dist/internal/registration.mjs +66 -0
- package/dist/internal/registration.mjs.map +1 -0
- package/dist/internal/session-kernel.d.mts +48 -0
- package/dist/internal/session-kernel.mjs +737 -0
- package/dist/internal/session-kernel.mjs.map +1 -0
- package/dist/internal/sql-persistence.d.mts +10 -0
- package/dist/internal/sql-persistence.mjs +37 -0
- package/dist/internal/sql-persistence.mjs.map +1 -0
- package/dist/internal/sql-query.mjs +207 -0
- package/dist/internal/sql-query.mjs.map +1 -0
- package/dist/internal/sql-table.d.mts +36 -0
- package/dist/internal/sql-table.mjs +81 -0
- package/dist/internal/sql-table.mjs.map +1 -0
- package/dist/internal/standalone.d.mts +9 -0
- package/dist/internal/standalone.mjs +13 -0
- package/dist/internal/standalone.mjs.map +1 -0
- package/dist/internal/step-up-state.d.mts +49 -0
- package/dist/internal/step-up-state.mjs +32 -0
- package/dist/internal/step-up-state.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +686 -0
- package/dist/internal/storage-mapping.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +913 -0
- package/dist/internal/storage-tables.mjs +387 -0
- package/dist/internal/storage-tables.mjs.map +1 -0
- package/dist/internal/storage-validation.mjs +58 -0
- package/dist/internal/storage-validation.mjs.map +1 -0
- package/dist/internal/transaction-execution-kernel.d.mts +38 -0
- package/dist/internal/transaction-execution-kernel.mjs +116 -0
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
- package/dist/internal/transaction-kernel.d.mts +77 -0
- package/dist/internal/transaction-kernel.mjs +260 -0
- package/dist/internal/transaction-kernel.mjs.map +1 -0
- package/package.json +1 -0
- package/src/Adapter.ts +291 -0
- package/src/index.ts +6 -0
- package/src/internal/PasswordPreparedPostconditions.ts +30 -0
- package/src/internal/configuration.ts +282 -0
- package/src/internal/email-kernel.ts +1535 -0
- package/src/internal/mapping-error.ts +36 -0
- package/src/internal/models/common.ts +11 -0
- package/src/internal/models/email-model.ts +559 -0
- package/src/internal/models/passkey-model.ts +390 -0
- package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
- package/src/internal/models/passkey-write-model.ts +297 -0
- package/src/internal/models/password-model.ts +513 -0
- package/src/internal/models/proof-model.ts +424 -0
- package/src/internal/models/session-model.ts +375 -0
- package/src/internal/models/step-up-model.ts +135 -0
- package/src/internal/oauth-app.ts +82 -0
- package/src/internal/oauth-server.ts +89 -0
- package/src/internal/passkey/admission.ts +361 -0
- package/src/internal/passkey/credentials.ts +511 -0
- package/src/internal/passkey/enrollment.ts +326 -0
- package/src/internal/passkey/flow.ts +1062 -0
- package/src/internal/passkey/management.ts +421 -0
- package/src/internal/passkey/registration-ceremony.ts +283 -0
- package/src/internal/passkey/registration-custody.ts +91 -0
- package/src/internal/passkey/registration-write.ts +426 -0
- package/src/internal/passkey/state.ts +353 -0
- package/src/internal/passkey/target.ts +586 -0
- package/src/internal/passkey/write-state.ts +681 -0
- package/src/internal/passkey/write-target.ts +452 -0
- package/src/internal/passkey-kernel.ts +104 -0
- package/src/internal/passkeys.ts +606 -0
- package/src/internal/password-kernel.ts +2124 -0
- package/src/internal/persistence.ts +588 -0
- package/src/internal/phone-kernel.ts +846 -0
- package/src/internal/proof-kernel.ts +1954 -0
- package/src/internal/query-operations.ts +81 -0
- package/src/internal/registration-contract.ts +22 -0
- package/src/internal/registration.ts +161 -0
- package/src/internal/session-kernel.ts +2217 -0
- package/src/internal/sql-persistence.ts +45 -0
- package/src/internal/sql-query.ts +298 -0
- package/src/internal/sql-table.ts +104 -0
- package/src/internal/standalone.ts +30 -0
- package/src/internal/step-up-state.ts +139 -0
- package/src/internal/storage-mapping.ts +708 -0
- package/src/internal/storage-tables.ts +408 -0
- package/src/internal/storage-validation.ts +117 -0
- package/src/internal/transaction-execution-kernel.ts +313 -0
- package/src/internal/transaction-kernel.ts +593 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SqlExpression, TableModel } from "../query-operations.mjs";
|
|
2
|
+
import { PersistenceMappingError } from "../mapping-error.mjs";
|
|
3
|
+
import "./common.mjs";
|
|
4
|
+
import { D1SessionClockMapping, SessionAuthorityTables, SessionConstraintClassifier, SessionIdCodec, SignedSessionValidityTables, StatefulSessionTables } from "./session-model.mjs";
|
|
5
|
+
import { DateTime, Effect } from "effect";
|
|
6
|
+
import { SecurityRevision, SessionStepUpIntent } from "@yielded/auth/Sessions";
|
|
7
|
+
//#region src/internal/models/step-up-model.d.ts
|
|
8
|
+
type ColumnKey<T extends TableModel> = T["column"];
|
|
9
|
+
/** Distinct from login-pending storage. Snapshot is TEXT encoded by the core's
|
|
10
|
+
* SessionStepUpIntent schema; it contains no Claims and needs no consumer decoder. */
|
|
11
|
+
export interface SessionStepUpIntentTables<Intent extends TableModel, NativeSubjectId> {
|
|
12
|
+
readonly intent: {
|
|
13
|
+
readonly table: Intent["table"];
|
|
14
|
+
readonly digest: ColumnKey<Intent>;
|
|
15
|
+
readonly version: ColumnKey<Intent>;
|
|
16
|
+
readonly flowId: ColumnKey<Intent>;
|
|
17
|
+
readonly subjectId: ColumnKey<Intent>;
|
|
18
|
+
readonly bindingDigest: ColumnKey<Intent>;
|
|
19
|
+
readonly snapshot: ColumnKey<Intent>;
|
|
20
|
+
readonly expiresAt: ColumnKey<Intent>;
|
|
21
|
+
readonly attemptLimit: ColumnKey<Intent>;
|
|
22
|
+
readonly failedAttempts: ColumnKey<Intent>;
|
|
23
|
+
readonly consumed: ColumnKey<Intent>;
|
|
24
|
+
readonly encodeInstant: (instant: DateTime.Utc) => unknown;
|
|
25
|
+
/** Consumer fields/defaults; the adapter sets all mapped security columns. */
|
|
26
|
+
readonly encodeInsert: (intent: SessionStepUpIntent, input: {
|
|
27
|
+
readonly subjectId: NativeSubjectId;
|
|
28
|
+
}) => Intent["insert"];
|
|
29
|
+
readonly allocateVersion?: Effect.Effect<SecurityRevision, PersistenceMappingError>;
|
|
30
|
+
readonly allocateVersionSync?: () => SecurityRevision;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface RequiredSessionStepUpConstraints {
|
|
34
|
+
readonly intentDigest: "unique(intent.digest)";
|
|
35
|
+
readonly intentFlow: "unique(intent.flowId)";
|
|
36
|
+
}
|
|
37
|
+
export declare const requiredSessionStepUpConstraints: RequiredSessionStepUpConstraints;
|
|
38
|
+
/** Fixed source discriminator is checked against every stored intent. A minimal
|
|
39
|
+
* pure-signed installation does not provide dummy session or tombstone tables. */
|
|
40
|
+
export type SessionStepUpSourceTables<Claims, Session extends TableModel, Tombstone extends TableModel, NativeSubjectId, NativeSessionId> = {
|
|
41
|
+
readonly kind: "StatelessSigned";
|
|
42
|
+
} | ({
|
|
43
|
+
readonly kind: "StateAssistedSigned";
|
|
44
|
+
readonly constraints: {
|
|
45
|
+
readonly tombstoneOwner: "unique(tombstone.subjectId,tombstone.sessionId)";
|
|
46
|
+
};
|
|
47
|
+
} & SignedSessionValidityTables<Tombstone, NativeSubjectId, NativeSessionId>) | {
|
|
48
|
+
readonly kind: "Stateful";
|
|
49
|
+
readonly constraints: {
|
|
50
|
+
readonly sessionDigest: "unique(session.digest)";
|
|
51
|
+
};
|
|
52
|
+
readonly sessionId: SessionIdCodec<NativeSessionId>;
|
|
53
|
+
/** encodeRotation must replace Claims, provenance, and the queryable security
|
|
54
|
+
* fields together; it must not retain the old proof payload. */
|
|
55
|
+
readonly session: StatefulSessionTables<Claims, Session, TableModel, NativeSubjectId, NativeSessionId>["session"] & {
|
|
56
|
+
readonly credentialVersion: ColumnKey<Session>;
|
|
57
|
+
readonly authenticatedAt: ColumnKey<Session>;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export type SessionStepUpMapping<Claims, Subject extends TableModel, Credential extends TableModel, Intent extends TableModel, Session extends TableModel, Tombstone extends TableModel, NativeSubjectId, NativeSessionId> = SessionAuthorityTables<Subject, Credential, NativeSubjectId> & SessionStepUpIntentTables<Intent, NativeSubjectId> & SessionConstraintClassifier & {
|
|
61
|
+
readonly source: SessionStepUpSourceTables<Claims, Session, Tombstone, NativeSubjectId, NativeSessionId>;
|
|
62
|
+
readonly constraints: RequiredSessionStepUpConstraints;
|
|
63
|
+
};
|
|
64
|
+
export type D1SessionStepUpMapping<Claims, Subject extends TableModel, Credential extends TableModel, Intent extends TableModel, Session extends TableModel, Tombstone extends TableModel, NativeSubjectId, NativeSessionId, Expression extends SqlExpression = SqlExpression> = SessionStepUpMapping<Claims, Subject, Credential, Intent, Session, Tombstone, NativeSubjectId, NativeSessionId> & D1SessionClockMapping<Expression>;
|
|
65
|
+
//#endregion
|
|
66
|
+
//# sourceMappingURL=step-up-model.d.mts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/internal/models/step-up-model.ts
|
|
2
|
+
const requiredSessionStepUpConstraints = {
|
|
3
|
+
intentDigest: "unique(intent.digest)",
|
|
4
|
+
intentFlow: "unique(intent.flowId)"
|
|
5
|
+
};
|
|
6
|
+
//#endregion
|
|
7
|
+
export { requiredSessionStepUpConstraints };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=step-up-model.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-up-model.mjs","names":[],"sources":["../../../src/internal/models/step-up-model.ts"],"sourcesContent":["import type { SecurityRevision, SessionStepUpIntent } from \"@yielded/auth/Sessions\";\nimport type { DateTime, Effect } from \"effect\";\n\nimport type { TableModel as Table, SqlExpression } from \"../query-operations\";\nimport type { PersistenceMappingError } from \"./common\";\nimport type {\n D1SessionClockMapping,\n SessionAuthorityTables,\n SessionConstraintClassifier,\n SessionIdCodec,\n SignedSessionValidityTables,\n StatefulSessionTables,\n} from \"./session-model\";\n\ntype ColumnKey<T extends Table> = T[\"column\"];\n\n/** Distinct from login-pending storage. Snapshot is TEXT encoded by the core's\n * SessionStepUpIntent schema; it contains no Claims and needs no consumer decoder. */\nexport interface SessionStepUpIntentTables<Intent extends Table, NativeSubjectId> {\n readonly intent: {\n readonly table: Intent[\"table\"];\n readonly digest: ColumnKey<Intent>;\n readonly version: ColumnKey<Intent>;\n readonly flowId: ColumnKey<Intent>;\n readonly subjectId: ColumnKey<Intent>;\n readonly bindingDigest: ColumnKey<Intent>;\n readonly snapshot: ColumnKey<Intent>;\n readonly expiresAt: ColumnKey<Intent>;\n readonly attemptLimit: ColumnKey<Intent>;\n readonly failedAttempts: ColumnKey<Intent>;\n readonly consumed: ColumnKey<Intent>;\n readonly encodeInstant: (instant: DateTime.Utc) => unknown;\n /** Consumer fields/defaults; the adapter sets all mapped security columns. */\n readonly encodeInsert: (\n intent: SessionStepUpIntent,\n input: {\n readonly subjectId: NativeSubjectId;\n },\n ) => Intent[\"insert\"];\n readonly allocateVersion?: Effect.Effect<SecurityRevision, PersistenceMappingError>;\n readonly allocateVersionSync?: () => SecurityRevision;\n };\n}\n\nexport interface RequiredSessionStepUpConstraints {\n readonly intentDigest: \"unique(intent.digest)\";\n readonly intentFlow: \"unique(intent.flowId)\";\n}\n\nexport const requiredSessionStepUpConstraints: RequiredSessionStepUpConstraints = {\n intentDigest: \"unique(intent.digest)\",\n intentFlow: \"unique(intent.flowId)\",\n};\n\n/** Fixed source discriminator is checked against every stored intent. A minimal\n * pure-signed installation does not provide dummy session or tombstone tables. */\nexport type SessionStepUpSourceTables<\n Claims,\n Session extends Table,\n Tombstone extends Table,\n NativeSubjectId,\n NativeSessionId,\n> =\n | {\n readonly kind: \"StatelessSigned\";\n }\n | ({\n readonly kind: \"StateAssistedSigned\";\n readonly constraints: {\n readonly tombstoneOwner: \"unique(tombstone.subjectId,tombstone.sessionId)\";\n };\n } & SignedSessionValidityTables<Tombstone, NativeSubjectId, NativeSessionId>)\n | {\n readonly kind: \"Stateful\";\n readonly constraints: {\n readonly sessionDigest: \"unique(session.digest)\";\n };\n readonly sessionId: SessionIdCodec<NativeSessionId>;\n /** encodeRotation must replace Claims, provenance, and the queryable security\n * fields together; it must not retain the old proof payload. */\n readonly session: StatefulSessionTables<\n Claims,\n Session,\n Table,\n NativeSubjectId,\n NativeSessionId\n >[\"session\"] & {\n readonly credentialVersion: ColumnKey<Session>;\n readonly authenticatedAt: ColumnKey<Session>;\n };\n };\n\nexport type SessionStepUpMapping<\n Claims,\n Subject extends Table,\n Credential extends Table,\n Intent extends Table,\n Session extends Table,\n Tombstone extends Table,\n NativeSubjectId,\n NativeSessionId,\n> = SessionAuthorityTables<Subject, Credential, NativeSubjectId> &\n SessionStepUpIntentTables<Intent, NativeSubjectId> &\n SessionConstraintClassifier & {\n readonly source: SessionStepUpSourceTables<\n Claims,\n Session,\n Tombstone,\n NativeSubjectId,\n NativeSessionId\n >;\n readonly constraints: RequiredSessionStepUpConstraints;\n };\n\nexport type D1SessionStepUpMapping<\n Claims,\n Subject extends Table,\n Credential extends Table,\n Intent extends Table,\n Session extends Table,\n Tombstone extends Table,\n NativeSubjectId,\n NativeSessionId,\n Expression extends SqlExpression = SqlExpression,\n> = SessionStepUpMapping<\n Claims,\n Subject,\n Credential,\n Intent,\n Session,\n Tombstone,\n NativeSubjectId,\n NativeSessionId\n> &\n D1SessionClockMapping<Expression>;\n"],"mappings":";AAiDA,MAAa,mCAAqE;CAChF,cAAc;CACd,YAAY;AACd"}
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/unstable/sql";
|
|
3
|
+
import { Persistence, Unavailable } from "@yielded/auth/OAuthServer";
|
|
4
|
+
//#region src/internal/oauth-server.d.ts
|
|
5
|
+
export declare const OAuthServerPersistence: {
|
|
6
|
+
migration: string;
|
|
7
|
+
layer: Layer.Layer<Persistence, Unavailable, SqlClient.SqlClient>;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
//# sourceMappingURL=oauth-server.d.mts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { requireStandalone } from "./standalone.mjs";
|
|
2
|
+
import { Effect, Layer, Schema } from "effect";
|
|
3
|
+
import { SqlClient } from "effect/unstable/sql";
|
|
4
|
+
import { Persistence, Record, Unavailable } from "@yielded/auth/OAuthServer";
|
|
5
|
+
//#region src/internal/oauth-server.ts
|
|
6
|
+
const codec = Schema.fromJsonString(Record);
|
|
7
|
+
const Row = Schema.Struct({
|
|
8
|
+
payload: Schema.String,
|
|
9
|
+
version: Schema.String,
|
|
10
|
+
revoked: Schema.Literals([0, 1])
|
|
11
|
+
});
|
|
12
|
+
const OAuthServerPersistence = {
|
|
13
|
+
migration: `CREATE TABLE yielded_oauth_server (
|
|
14
|
+
namespace TEXT NOT NULL,
|
|
15
|
+
grant_id TEXT NOT NULL,
|
|
16
|
+
version TEXT NOT NULL,
|
|
17
|
+
revoked INTEGER NOT NULL DEFAULT 0,
|
|
18
|
+
expires_at_millis BIGINT NOT NULL,
|
|
19
|
+
payload TEXT NOT NULL,
|
|
20
|
+
PRIMARY KEY (namespace, grant_id)
|
|
21
|
+
)`,
|
|
22
|
+
layer: Layer.effect(Persistence, Effect.gen(function* () {
|
|
23
|
+
const sql = yield* SqlClient.SqlClient;
|
|
24
|
+
if (!sql.onDialectOrElse({
|
|
25
|
+
sqlite: () => true,
|
|
26
|
+
pg: () => true,
|
|
27
|
+
orElse: () => false
|
|
28
|
+
})) return yield* Unavailable.make({});
|
|
29
|
+
const standalone = requireStandalone(() => Unavailable.make({}), sql);
|
|
30
|
+
const failure = (effect) => effect.pipe(Effect.mapError(() => Unavailable.make({})));
|
|
31
|
+
return Persistence.of({
|
|
32
|
+
get: Effect.fn("OAuthServerPersistence.get")(function* (namespace, id) {
|
|
33
|
+
yield* standalone;
|
|
34
|
+
const rows = yield* sql`SELECT payload, version, revoked FROM yielded_oauth_server WHERE namespace = ${namespace} AND grant_id = ${id}`;
|
|
35
|
+
if (rows.length === 0) return void 0;
|
|
36
|
+
if (rows.length !== 1) return yield* Unavailable.make({});
|
|
37
|
+
const row = yield* Schema.decodeUnknownEffect(Row)(rows[0]);
|
|
38
|
+
const record = yield* Schema.decodeEffect(codec)(row.payload);
|
|
39
|
+
if (record.version !== row.version) return yield* Unavailable.make({});
|
|
40
|
+
return row.revoked === 1 ? {
|
|
41
|
+
...record,
|
|
42
|
+
status: "Revoked"
|
|
43
|
+
} : record;
|
|
44
|
+
}, failure),
|
|
45
|
+
insert: Effect.fn("OAuthServerPersistence.insert")(function* (namespace, id, record) {
|
|
46
|
+
yield* standalone;
|
|
47
|
+
const payload = yield* Schema.encodeEffect(codec)(record);
|
|
48
|
+
return (yield* sql`INSERT INTO yielded_oauth_server (namespace, grant_id, version, revoked, expires_at_millis, payload) VALUES (${namespace}, ${id}, ${record.version}, ${record.status === "Revoked" ? 1 : 0}, ${record.expiresAtMillis}, ${payload}) ON CONFLICT (namespace, grant_id) DO NOTHING RETURNING version`).length === 1;
|
|
49
|
+
}, failure),
|
|
50
|
+
compareAndSet: Effect.fn("OAuthServerPersistence.compareAndSet")(function* (namespace, id, version, record) {
|
|
51
|
+
yield* standalone;
|
|
52
|
+
if (version === record.version) return yield* Unavailable.make({});
|
|
53
|
+
const payload = yield* Schema.encodeEffect(codec)(record);
|
|
54
|
+
return (yield* sql`UPDATE yielded_oauth_server SET version = ${record.version}, revoked = ${record.status === "Revoked" ? 1 : 0}, expires_at_millis = ${record.expiresAtMillis}, payload = ${payload} WHERE namespace = ${namespace} AND grant_id = ${id} AND version = ${version} AND revoked = 0 RETURNING version`).length === 1;
|
|
55
|
+
}, failure),
|
|
56
|
+
revoke: Effect.fn("OAuthServerPersistence.revoke")(function* (namespace, id) {
|
|
57
|
+
yield* standalone;
|
|
58
|
+
yield* sql`UPDATE yielded_oauth_server SET revoked = 1 WHERE namespace = ${namespace} AND grant_id = ${id}`;
|
|
59
|
+
}, failure)
|
|
60
|
+
});
|
|
61
|
+
}))
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { OAuthServerPersistence };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=oauth-server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-server.mjs","names":[],"sources":["../../src/internal/oauth-server.ts"],"sourcesContent":["import { Persistence, Record, Unavailable } from \"@yielded/auth/OAuthServer\";\nimport { Effect, Layer, Schema } from \"effect\";\nimport { SqlClient } from \"effect/unstable/sql\";\n\nimport { requireStandalone } from \"./standalone\";\n\nconst codec = Schema.fromJsonString(Record);\n\nconst Row = Schema.Struct({\n payload: Schema.String,\n version: Schema.String,\n revoked: Schema.Literals([0, 1]),\n});\n\n/** SQLite (including D1) and PostgreSQL. Apply once through application migrations.\n * Expired records may be deleted when expires_at_millis <= the current time.\n * Revocation has its own monotonic column so it cannot lose a race with rotation.\n */\nconst migration = `CREATE TABLE yielded_oauth_server (\n namespace TEXT NOT NULL,\n grant_id TEXT NOT NULL,\n version TEXT NOT NULL,\n revoked INTEGER NOT NULL DEFAULT 0,\n expires_at_millis BIGINT NOT NULL,\n payload TEXT NOT NULL,\n PRIMARY KEY (namespace, grant_id)\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* Unavailable.make({});\n const standalone = requireStandalone(() => Unavailable.make({}), sql);\n\n const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n effect.pipe(Effect.mapError(() => Unavailable.make({})));\n\n return Persistence.of({\n get: Effect.fn(\"OAuthServerPersistence.get\")(function* (namespace, id) {\n yield* standalone;\n\n const rows =\n yield* sql`SELECT payload, version, revoked FROM yielded_oauth_server WHERE namespace = ${namespace} AND grant_id = ${id}`;\n\n if (rows.length === 0) return undefined;\n if (rows.length !== 1) return yield* Unavailable.make({});\n const row = yield* Schema.decodeUnknownEffect(Row)(rows[0]);\n const record = yield* Schema.decodeEffect(codec)(row.payload);\n\n if (record.version !== row.version) return yield* Unavailable.make({});\n\n return row.revoked === 1 ? { ...record, status: \"Revoked\" as const } : record;\n }, failure),\n insert: Effect.fn(\"OAuthServerPersistence.insert\")(function* (namespace, id, record) {\n yield* standalone;\n const payload = yield* Schema.encodeEffect(codec)(record);\n\n const rows =\n yield* sql`INSERT INTO yielded_oauth_server (namespace, grant_id, version, revoked, expires_at_millis, payload) VALUES (${namespace}, ${id}, ${record.version}, ${record.status === \"Revoked\" ? 1 : 0}, ${record.expiresAtMillis}, ${payload}) ON CONFLICT (namespace, grant_id) DO NOTHING RETURNING version`;\n\n return rows.length === 1;\n }, failure),\n compareAndSet: Effect.fn(\"OAuthServerPersistence.compareAndSet\")(function* (\n namespace,\n id,\n version,\n record,\n ) {\n yield* standalone;\n if (version === record.version) return yield* Unavailable.make({});\n const payload = yield* Schema.encodeEffect(codec)(record);\n\n const rows =\n yield* sql`UPDATE yielded_oauth_server SET version = ${record.version}, revoked = ${record.status === \"Revoked\" ? 1 : 0}, expires_at_millis = ${record.expiresAtMillis}, payload = ${payload} WHERE namespace = ${namespace} AND grant_id = ${id} AND version = ${version} AND revoked = 0 RETURNING version`;\n\n return rows.length === 1;\n }, failure),\n revoke: Effect.fn(\"OAuthServerPersistence.revoke\")(function* (namespace, id) {\n yield* standalone;\n yield* sql`UPDATE yielded_oauth_server SET revoked = 1 WHERE namespace = ${namespace} AND grant_id = ${id}`;\n }, failure),\n });\n }),\n);\n\nexport const OAuthServerPersistence = { migration, layer };\n"],"mappings":";;;;;AAMA,MAAM,QAAQ,OAAO,eAAe,MAAM;AAE1C,MAAM,MAAM,OAAO,OAAO;CACxB,SAAS,OAAO;CAChB,SAAS,OAAO;CAChB,SAAS,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;AACjC,CAAC;AA4ED,MAAa,yBAAyB;CAAE;;;;;;;;;CAAW,OA5DrC,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,YAAY,KAAK,CAAC,CAAC;EACnC,MAAM,aAAa,wBAAwB,YAAY,KAAK,CAAC,CAAC,GAAG,GAAG;EAEpE,MAAM,WAAoB,WACxB,OAAO,KAAK,OAAO,eAAe,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC;EAEzD,OAAO,YAAY,GAAG;GACpB,KAAK,OAAO,GAAG,4BAA4B,CAAC,CAAC,WAAW,WAAW,IAAI;IACrE,OAAO;IAEP,MAAM,OACJ,OAAO,GAAG,gFAAgF,UAAU,kBAAkB;IAExH,IAAI,KAAK,WAAW,GAAG,OAAO,KAAA;IAC9B,IAAI,KAAK,WAAW,GAAG,OAAO,OAAO,YAAY,KAAK,CAAC,CAAC;IACxD,MAAM,MAAM,OAAO,OAAO,oBAAoB,GAAG,CAAC,CAAC,KAAK,EAAE;IAC1D,MAAM,SAAS,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,IAAI,OAAO;IAE5D,IAAI,OAAO,YAAY,IAAI,SAAS,OAAO,OAAO,YAAY,KAAK,CAAC,CAAC;IAErE,OAAO,IAAI,YAAY,IAAI;KAAE,GAAG;KAAQ,QAAQ;IAAmB,IAAI;GACzE,GAAG,OAAO;GACV,QAAQ,OAAO,GAAG,+BAA+B,CAAC,CAAC,WAAW,WAAW,IAAI,QAAQ;IACnF,OAAO;IACP,MAAM,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,MAAM;IAKxD,QAAO,OAFE,GAAG,gHAAgH,UAAU,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAI,OAAO,WAAW,YAAY,IAAI,EAAE,IAAI,OAAO,gBAAgB,IAAI,QAAQ,kEAAA,CAEnO,WAAW;GACzB,GAAG,OAAO;GACV,eAAe,OAAO,GAAG,sCAAsC,CAAC,CAAC,WAC/D,WACA,IACA,SACA,QACA;IACA,OAAO;IACP,IAAI,YAAY,OAAO,SAAS,OAAO,OAAO,YAAY,KAAK,CAAC,CAAC;IACjE,MAAM,UAAU,OAAO,OAAO,aAAa,KAAK,CAAC,CAAC,MAAM;IAKxD,QAAO,OAFE,GAAG,6CAA6C,OAAO,QAAQ,cAAc,OAAO,WAAW,YAAY,IAAI,EAAE,wBAAwB,OAAO,gBAAgB,cAAc,QAAQ,qBAAqB,UAAU,kBAAkB,GAAG,iBAAiB,QAAQ,oCAAA,CAEhQ,WAAW;GACzB,GAAG,OAAO;GACV,QAAQ,OAAO,GAAG,+BAA+B,CAAC,CAAC,WAAW,WAAW,IAAI;IAC3E,OAAO;IACP,OAAO,GAAG,iEAAiE,UAAU,kBAAkB;GACzG,GAAG,OAAO;EACZ,CAAC;CACH,CAAC,CAGgD;AAAM"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { CurrentPasskeyTransaction } from "./state.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
//#region src/internal/passkey/admission.ts
|
|
4
|
+
const makePasskeyAdmissionKernel = (operations, state, transactions) => {
|
|
5
|
+
const { sql } = operations;
|
|
6
|
+
const { col, equal, existsExact, key, mappedColumns, subjectScope, targetScope } = state;
|
|
7
|
+
const invariant = state.invariant;
|
|
8
|
+
const { both } = transactions;
|
|
9
|
+
const chargeRetentionMillis = 864e5;
|
|
10
|
+
const chargeScopes = (ceremony, subjectId) => {
|
|
11
|
+
const scopes = [{
|
|
12
|
+
kind: "global",
|
|
13
|
+
scope: key("global", [ceremony.moduleId])
|
|
14
|
+
}];
|
|
15
|
+
if (subjectId !== void 0) scopes.push({
|
|
16
|
+
kind: "subject",
|
|
17
|
+
scope: subjectScope(subjectId)
|
|
18
|
+
});
|
|
19
|
+
const target = targetScope(ceremony);
|
|
20
|
+
if (target !== null) scopes.push({
|
|
21
|
+
kind: "target",
|
|
22
|
+
scope: target
|
|
23
|
+
});
|
|
24
|
+
return scopes;
|
|
25
|
+
};
|
|
26
|
+
const lockAdmission = Effect.fn("passkey.lockAdmission")(function* (mapping) {
|
|
27
|
+
const owner = yield* CurrentPasskeyTransaction;
|
|
28
|
+
const table = mapping.admission;
|
|
29
|
+
const identity = {
|
|
30
|
+
[table.authorityScope]: mapping.authorityScope,
|
|
31
|
+
[table.moduleId]: mapping.moduleId
|
|
32
|
+
};
|
|
33
|
+
const found = yield* owner.read(table.table, equal(table.table, identity), {
|
|
34
|
+
limit: 1,
|
|
35
|
+
observe: false,
|
|
36
|
+
columns: [table.authorityScope, table.moduleId]
|
|
37
|
+
});
|
|
38
|
+
invariant(found.rows.length === 1);
|
|
39
|
+
invariant(found.rows[0][table.authorityScope] === mapping.authorityScope && found.rows[0][table.moduleId] === mapping.moduleId);
|
|
40
|
+
const marked = {
|
|
41
|
+
...identity,
|
|
42
|
+
[table.version]: owner.marker,
|
|
43
|
+
[table.ownerMarker]: owner.marker
|
|
44
|
+
};
|
|
45
|
+
yield* owner.updateGuarded(table.table, owner.exact(table.table, identity), {
|
|
46
|
+
[table.version]: owner.marker,
|
|
47
|
+
[table.ownerMarker]: owner.marker,
|
|
48
|
+
[table.admittedAt]: null
|
|
49
|
+
}, {
|
|
50
|
+
rows: 1,
|
|
51
|
+
postcondition: yield* existsExact(table.table, marked)
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
const pendingCount = (mapping, scope) => {
|
|
55
|
+
const table = mapping.flow;
|
|
56
|
+
return sql`(select count(*) from ${table.table} where ${both(equal(table.table, { [table.moduleId]: mapping.moduleId }), sql`${col(table.table, table.state)} in (${table.states.Pending}, ${table.states.Claimed})`, scope === void 0 ? void 0 : equal(table.table, { [table.subjectScope]: scope }))})`;
|
|
57
|
+
};
|
|
58
|
+
const chargedCount = (mapping, kind, scope, window) => {
|
|
59
|
+
const table = mapping.charge;
|
|
60
|
+
const clock = mapping.clock;
|
|
61
|
+
return sql`(select count(*) from ${table.table} where ${both(equal(table.table, {
|
|
62
|
+
[table.moduleId]: mapping.moduleId,
|
|
63
|
+
[table.kind]: kind
|
|
64
|
+
}), kind === "global" ? void 0 : equal(table.table, { [table.scope]: scope }), sql`${col(table.table, table.admittedAt)} is not null`, sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} > ${clock.engineNowMillis} - ${window}`)})`;
|
|
65
|
+
};
|
|
66
|
+
const admissionCondition = (mapping, policies, ceremony, subjectId, added, newFlow, resolvedSubject) => both(...policies.flatMap((policy) => [
|
|
67
|
+
sql`${pendingCount(mapping)} + ${newFlow ? 1 : 0} <= ${policy.maximumPending}`,
|
|
68
|
+
subjectId === void 0 ? void 0 : sql`${pendingCount(mapping, subjectScope(subjectId))} + ${newFlow || resolvedSubject ? 1 : 0} <= ${policy.maximumPendingPerSubject}`,
|
|
69
|
+
...chargeScopes(ceremony, subjectId).map(({ kind, scope }) => sql`${chargedCount(mapping, kind, scope, policy.admission[kind].windowMillis)} + ${added.includes(kind) ? 1 : 0} <= ${policy.admission[kind].limit}`)
|
|
70
|
+
]));
|
|
71
|
+
const guardAdmission = (mapping, policies, ceremony, subjectId) => Effect.map(CurrentPasskeyTransaction, (owner) => {
|
|
72
|
+
owner.postconditions.push(admissionCondition(mapping, policies, ceremony, subjectId, [], false, false));
|
|
73
|
+
});
|
|
74
|
+
const readCharges = Effect.fn("passkey.readCharges")(function* (mapping, ceremony, policy, subjectId) {
|
|
75
|
+
const owner = yield* CurrentPasskeyTransaction;
|
|
76
|
+
const table = mapping.charge;
|
|
77
|
+
const where = equal(table.table, {
|
|
78
|
+
[table.moduleId]: ceremony.moduleId,
|
|
79
|
+
[table.flowId]: ceremony.flowId
|
|
80
|
+
});
|
|
81
|
+
const found = yield* owner.read(table.table, where, {
|
|
82
|
+
limit: 3,
|
|
83
|
+
observe: false,
|
|
84
|
+
columns: mappedColumns(table)
|
|
85
|
+
});
|
|
86
|
+
const scopes = chargeScopes(ceremony, subjectId);
|
|
87
|
+
invariant(found.rows.length === scopes.length);
|
|
88
|
+
for (const { kind, scope } of scopes) {
|
|
89
|
+
const rows = found.rows.filter((row) => row[table.kind] === kind);
|
|
90
|
+
invariant(rows.length === 1);
|
|
91
|
+
const row = rows[0];
|
|
92
|
+
const admitted = mapping.clock.decodeInstant(row[table.admittedAt]);
|
|
93
|
+
const retained = mapping.clock.decodeInstant(row[table.retainUntil]);
|
|
94
|
+
invariant(Number.isSafeInteger(admitted) && admitted >= 0 && retained === admitted + chargeRetentionMillis);
|
|
95
|
+
invariant(row[table.moduleId] === ceremony.moduleId && row[table.flowId] === ceremony.flowId && row[table.purpose] === ceremony.purpose);
|
|
96
|
+
invariant(row[table.scope] === scope && row[table.originalWindowMillis] === policy.admission[kind].windowMillis);
|
|
97
|
+
owner.observations.push({
|
|
98
|
+
table: table.table,
|
|
99
|
+
where: equal(table.table, {
|
|
100
|
+
[table.moduleId]: ceremony.moduleId,
|
|
101
|
+
[table.flowId]: ceremony.flowId,
|
|
102
|
+
[table.kind]: kind
|
|
103
|
+
}),
|
|
104
|
+
rows: [row]
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const guardChargeSet = (mapping, ceremony, subjectId) => Effect.flatMap(CurrentPasskeyTransaction, (owner) => Effect.gen(function* () {
|
|
109
|
+
const table = mapping.charge;
|
|
110
|
+
const scopes = chargeScopes(ceremony, subjectId);
|
|
111
|
+
const where = equal(table.table, {
|
|
112
|
+
[table.moduleId]: ceremony.moduleId,
|
|
113
|
+
[table.flowId]: ceremony.flowId
|
|
114
|
+
});
|
|
115
|
+
owner.postconditions.push(sql`(select count(*) from ${table.table} where ${where}) = ${scopes.length}`);
|
|
116
|
+
for (const { kind, scope } of scopes) owner.postconditions.push(yield* existsExact(table.table, {
|
|
117
|
+
[table.moduleId]: ceremony.moduleId,
|
|
118
|
+
[table.flowId]: ceremony.flowId,
|
|
119
|
+
[table.purpose]: ceremony.purpose,
|
|
120
|
+
[table.kind]: kind,
|
|
121
|
+
[table.scope]: scope
|
|
122
|
+
}));
|
|
123
|
+
}));
|
|
124
|
+
return {
|
|
125
|
+
chargeRetentionMillis,
|
|
126
|
+
chargeScopes,
|
|
127
|
+
lockAdmission,
|
|
128
|
+
admissionCondition,
|
|
129
|
+
guardAdmission,
|
|
130
|
+
readCharges,
|
|
131
|
+
guardChargeSet,
|
|
132
|
+
insertCharges: Effect.fn("passkey.insertCharges")(function* (mapping, ceremony, policy, kinds, subjectId) {
|
|
133
|
+
const owner = yield* CurrentPasskeyTransaction;
|
|
134
|
+
if (kinds.length === 0) return;
|
|
135
|
+
const table = mapping.charge;
|
|
136
|
+
const anchor = mapping.admission;
|
|
137
|
+
const clock = mapping.clock;
|
|
138
|
+
const earliest = yield* owner.now(clock);
|
|
139
|
+
const identities = [];
|
|
140
|
+
for (const { kind, scope } of chargeScopes(ceremony, subjectId).filter((item) => kinds.includes(item.kind))) {
|
|
141
|
+
const identity = {
|
|
142
|
+
[table.moduleId]: ceremony.moduleId,
|
|
143
|
+
[table.flowId]: ceremony.flowId,
|
|
144
|
+
[table.kind]: kind
|
|
145
|
+
};
|
|
146
|
+
const values = {
|
|
147
|
+
...table.encodeInsert({
|
|
148
|
+
moduleId: ceremony.moduleId,
|
|
149
|
+
flowId: ceremony.flowId,
|
|
150
|
+
purpose: ceremony.purpose,
|
|
151
|
+
kind,
|
|
152
|
+
scope,
|
|
153
|
+
originalWindowMillis: policy.admission[kind].windowMillis,
|
|
154
|
+
marker: owner.marker
|
|
155
|
+
}),
|
|
156
|
+
...identity,
|
|
157
|
+
[table.purpose]: ceremony.purpose,
|
|
158
|
+
[table.scope]: scope,
|
|
159
|
+
[table.originalWindowMillis]: policy.admission[kind].windowMillis,
|
|
160
|
+
[table.admittedAt]: null,
|
|
161
|
+
[table.retainUntil]: null,
|
|
162
|
+
[table.version]: owner.marker,
|
|
163
|
+
[table.ownerMarker]: owner.marker
|
|
164
|
+
};
|
|
165
|
+
const observation = yield* owner.insert(table.table, values, identity);
|
|
166
|
+
observation.rows = observation.rows.map((row) => Object.fromEntries(Object.entries(row).filter(([name]) => name !== table.admittedAt && name !== table.retainUntil)));
|
|
167
|
+
identities.push(identity);
|
|
168
|
+
}
|
|
169
|
+
invariant(identities.length === kinds.length);
|
|
170
|
+
const anchorIdentity = {
|
|
171
|
+
[anchor.authorityScope]: mapping.authorityScope,
|
|
172
|
+
[anchor.moduleId]: mapping.moduleId,
|
|
173
|
+
[anchor.version]: owner.marker,
|
|
174
|
+
[anchor.ownerMarker]: owner.marker
|
|
175
|
+
};
|
|
176
|
+
const stamp = sql`(select ${col(anchor.table, anchor.admittedAt)} from ${anchor.table} where ${owner.exact(anchor.table, anchorIdentity)})`;
|
|
177
|
+
const stampMillis = clock.toMillis(stamp);
|
|
178
|
+
const validStamp = both(yield* existsExact(anchor.table, anchorIdentity), sql`${stamp} is not null`, sql`${stampMillis} >= ${earliest}`, sql`${stampMillis} <= ${clock.engineNowMillis}`, sql`${stampMillis} <= ${86399999136e5}`);
|
|
179
|
+
yield* owner.finalUpdate(anchor.table, owner.exact(anchor.table, {
|
|
180
|
+
...anchorIdentity,
|
|
181
|
+
[anchor.admittedAt]: null
|
|
182
|
+
}), { [anchor.admittedAt]: clock.fromMillis(clock.engineNowMillis) }, {
|
|
183
|
+
rows: 1,
|
|
184
|
+
postcondition: validStamp
|
|
185
|
+
});
|
|
186
|
+
for (const identity of identities) {
|
|
187
|
+
const expected = {
|
|
188
|
+
...identity,
|
|
189
|
+
[table.version]: owner.marker,
|
|
190
|
+
[table.ownerMarker]: owner.marker
|
|
191
|
+
};
|
|
192
|
+
const postcondition = yield* existsExact(table.table, expected, both(sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} = ${stampMillis}`, sql`${clock.toMillis(sql`${col(table.table, table.retainUntil)}`)} = ${stampMillis} + ${chargeRetentionMillis}`));
|
|
193
|
+
yield* owner.finalUpdate(table.table, owner.exact(table.table, {
|
|
194
|
+
...expected,
|
|
195
|
+
[table.admittedAt]: null,
|
|
196
|
+
[table.retainUntil]: null
|
|
197
|
+
}), {
|
|
198
|
+
[table.admittedAt]: stamp,
|
|
199
|
+
[table.retainUntil]: clock.fromMillis(sql`${stampMillis} + ${chargeRetentionMillis}`)
|
|
200
|
+
}, {
|
|
201
|
+
rows: 1,
|
|
202
|
+
postcondition
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
owner.postconditions.push(sql`(select count(*) from ${table.table} where ${owner.exact(table.table, {
|
|
206
|
+
[table.moduleId]: ceremony.moduleId,
|
|
207
|
+
[table.flowId]: ceremony.flowId,
|
|
208
|
+
[table.ownerMarker]: owner.marker
|
|
209
|
+
})}) = ${identities.length}`);
|
|
210
|
+
})
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
//#endregion
|
|
214
|
+
export { makePasskeyAdmissionKernel };
|
|
215
|
+
|
|
216
|
+
//# sourceMappingURL=admission.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admission.mjs","names":[],"sources":["../../../src/internal/passkey/admission.ts"],"sourcesContent":["/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */\n\nimport type { PasskeyCeremony, PasskeyMethodPolicy } from \"@yielded/auth/Passkey\";\nimport { Effect } from \"effect\";\n\nimport type { PasskeyChargeKind } from \"../models/passkey-model\";\nimport type { SqlExpression as SQL, QueryOperations } from \"../query-operations\";\nimport type { makeTransactionKernel } from \"../transaction-kernel\";\nimport { CurrentPasskeyTransaction } from \"./state\";\nimport type { makePasskeyStateKernel } from \"./state\";\n\nexport const makePasskeyAdmissionKernel = (\n operations: QueryOperations,\n state: Pick<\n ReturnType<typeof makePasskeyStateKernel>,\n | \"col\"\n | \"equal\"\n | \"existsExact\"\n | \"invariant\"\n | \"key\"\n | \"mappedColumns\"\n | \"subjectScope\"\n | \"targetScope\"\n >,\n transactions: Pick<ReturnType<typeof makeTransactionKernel>, \"both\">,\n) => {\n const { sql } = operations;\n const { col, equal, existsExact, key, mappedColumns, subjectScope, targetScope } = state;\n const invariant: (value: unknown) => asserts value = state.invariant;\n const { both } = transactions;\n\n const chargeRetentionMillis = 86_400_000;\n\n const chargeScopes = (ceremony: PasskeyCeremony, subjectId?: string) => {\n const scopes: { kind: PasskeyChargeKind; scope: string }[] = [\n { kind: \"global\", scope: key(\"global\", [ceremony.moduleId]) },\n ];\n\n if (subjectId !== undefined) scopes.push({ kind: \"subject\", scope: subjectScope(subjectId) });\n const target = targetScope(ceremony);\n\n if (target !== null) scopes.push({ kind: \"target\", scope: target });\n\n return scopes;\n };\n\n const lockAdmission = Effect.fn(\"passkey.lockAdmission\")(function* (mapping: any) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.admission;\n\n const identity = {\n [table.authorityScope]: mapping.authorityScope,\n [table.moduleId]: mapping.moduleId,\n };\n\n // The previous marker/time is telemetry, not an authentication CAS. Actual\n // engine predicates serialize admission, including separately planned D1 batches.\n const found = yield* owner.read(table.table, equal(table.table, identity), {\n limit: 1,\n observe: false,\n columns: [table.authorityScope, table.moduleId],\n });\n\n invariant(found.rows.length === 1);\n invariant(\n found.rows[0]![table.authorityScope] === mapping.authorityScope &&\n found.rows[0]![table.moduleId] === mapping.moduleId,\n );\n\n const marked = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n yield* owner.updateGuarded(\n table.table,\n owner.exact(table.table, identity),\n {\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n [table.admittedAt]: null,\n },\n { rows: 1, postcondition: yield* existsExact(table.table, marked) },\n );\n });\n\n const pendingCount = (mapping: any, scope?: string) => {\n const table = mapping.flow;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId }),\n sql`${col(table.table, table.state)} in (${table.states.Pending}, ${table.states.Claimed})`,\n scope === undefined ? undefined : equal(table.table, { [table.subjectScope]: scope }),\n )})`;\n };\n\n const chargedCount = (mapping: any, kind: PasskeyChargeKind, scope: string, window: number) => {\n const table = mapping.charge;\n const clock = mapping.clock;\n\n return sql`(select count(*) from ${table.table} where ${both(\n equal(table.table, { [table.moduleId]: mapping.moduleId, [table.kind]: kind }),\n kind === \"global\" ? undefined : equal(table.table, { [table.scope]: scope }),\n sql`${col(table.table, table.admittedAt)} is not null`,\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} > ${clock.engineNowMillis} - ${window}`,\n )})`;\n };\n\n const admissionCondition = (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId: string | undefined,\n added: ReadonlyArray<PasskeyChargeKind>,\n newFlow: boolean,\n resolvedSubject: boolean,\n ) =>\n both(\n ...policies.flatMap((policy) => [\n sql`${pendingCount(mapping)} + ${newFlow ? 1 : 0} <= ${policy.maximumPending}`,\n subjectId === undefined\n ? undefined\n : sql`${pendingCount(mapping, subjectScope(subjectId))} + ${newFlow || resolvedSubject ? 1 : 0} <= ${policy.maximumPendingPerSubject}`,\n ...chargeScopes(ceremony, subjectId).map(\n ({ kind, scope }) =>\n sql`${chargedCount(mapping, kind, scope, policy.admission[kind].windowMillis)} + ${added.includes(kind) ? 1 : 0} <= ${policy.admission[kind].limit}`,\n ),\n ]),\n );\n\n const guardAdmission = (\n mapping: any,\n policies: ReadonlyArray<PasskeyMethodPolicy>,\n ceremony: PasskeyCeremony,\n subjectId?: string,\n ) =>\n Effect.map(CurrentPasskeyTransaction, (owner) => {\n owner.postconditions.push(\n admissionCondition(mapping, policies, ceremony, subjectId, [], false, false),\n );\n });\n\n const readCharges = Effect.fn(\"passkey.readCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n const table = mapping.charge;\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n const found = yield* owner.read(table.table, where, {\n limit: 3,\n observe: false,\n columns: mappedColumns(table),\n });\n\n const scopes = chargeScopes(ceremony, subjectId);\n\n invariant(found.rows.length === scopes.length);\n for (const { kind, scope } of scopes) {\n const rows = found.rows.filter((row) => row[table.kind] === kind);\n\n invariant(rows.length === 1);\n const row = rows[0]!;\n const admitted = mapping.clock.decodeInstant(row[table.admittedAt]);\n const retained = mapping.clock.decodeInstant(row[table.retainUntil]);\n\n invariant(\n Number.isSafeInteger(admitted) &&\n admitted >= 0 &&\n retained === admitted + chargeRetentionMillis,\n );\n invariant(\n row[table.moduleId] === ceremony.moduleId &&\n row[table.flowId] === ceremony.flowId &&\n row[table.purpose] === ceremony.purpose,\n );\n invariant(\n row[table.scope] === scope &&\n row[table.originalWindowMillis] === policy.admission[kind].windowMillis,\n );\n owner.observations.push({\n table: table.table,\n where: equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n }),\n rows: [row],\n });\n }\n });\n\n const guardChargeSet = (mapping: any, ceremony: PasskeyCeremony, subjectId?: string) =>\n Effect.flatMap(CurrentPasskeyTransaction, (owner) =>\n Effect.gen(function* () {\n const table = mapping.charge;\n const scopes = chargeScopes(ceremony, subjectId);\n\n const where = equal(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n });\n\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${where}) = ${scopes.length}`,\n );\n for (const { kind, scope } of scopes)\n owner.postconditions.push(\n yield* existsExact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.purpose]: ceremony.purpose,\n [table.kind]: kind,\n [table.scope]: scope,\n }),\n );\n }),\n );\n\n const insertCharges = Effect.fn(\"passkey.insertCharges\")(function* (\n mapping: any,\n ceremony: PasskeyCeremony,\n policy: PasskeyMethodPolicy,\n kinds: ReadonlyArray<PasskeyChargeKind>,\n subjectId?: string,\n ) {\n const owner = yield* CurrentPasskeyTransaction;\n\n if (kinds.length === 0) return;\n const table = mapping.charge;\n const anchor = mapping.admission;\n const clock = mapping.clock;\n const earliest = yield* owner.now(clock);\n const identities: Record<string, unknown>[] = [];\n\n for (const { kind, scope } of chargeScopes(ceremony, subjectId).filter((item) =>\n kinds.includes(item.kind),\n )) {\n const identity = {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.kind]: kind,\n };\n\n const values = {\n ...table.encodeInsert({\n moduleId: ceremony.moduleId,\n flowId: ceremony.flowId,\n purpose: ceremony.purpose,\n kind,\n scope,\n originalWindowMillis: policy.admission[kind].windowMillis,\n marker: owner.marker,\n }),\n ...identity,\n [table.purpose]: ceremony.purpose,\n [table.scope]: scope,\n [table.originalWindowMillis]: policy.admission[kind].windowMillis,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const observation = yield* owner.insert(table.table, values, identity);\n\n observation.rows = observation.rows.map((row) =>\n Object.fromEntries(\n Object.entries(row).filter(\n ([name]) => name !== table.admittedAt && name !== table.retainUntil,\n ),\n ),\n );\n identities.push(identity);\n }\n invariant(identities.length === kinds.length);\n\n const anchorIdentity = {\n [anchor.authorityScope]: mapping.authorityScope,\n [anchor.moduleId]: mapping.moduleId,\n [anchor.version]: owner.marker,\n [anchor.ownerMarker]: owner.marker,\n };\n\n const stamp = sql`(select ${col(anchor.table, anchor.admittedAt)} from ${anchor.table} where ${owner.exact(anchor.table, anchorIdentity)})`;\n const stampMillis = clock.toMillis(stamp);\n\n const validStamp: SQL = both(\n yield* existsExact(anchor.table, anchorIdentity),\n sql`${stamp} is not null`,\n sql`${stampMillis} >= ${earliest}`,\n sql`${stampMillis} <= ${clock.engineNowMillis}`,\n sql`${stampMillis} <= ${8640000000000000 - chargeRetentionMillis}`,\n );\n\n yield* owner.finalUpdate(\n anchor.table,\n owner.exact(anchor.table, { ...anchorIdentity, [anchor.admittedAt]: null }),\n {\n [anchor.admittedAt]: clock.fromMillis(clock.engineNowMillis),\n },\n { rows: 1, postcondition: validStamp },\n );\n for (const identity of identities) {\n const expected = {\n ...identity,\n [table.version]: owner.marker,\n [table.ownerMarker]: owner.marker,\n };\n\n const postcondition = yield* existsExact(\n table.table,\n expected,\n both(\n sql`${clock.toMillis(sql`${col(table.table, table.admittedAt)}`)} = ${stampMillis}`,\n sql`${clock.toMillis(sql`${col(table.table, table.retainUntil)}`)} = ${stampMillis} + ${chargeRetentionMillis}`,\n ),\n );\n\n yield* owner.finalUpdate(\n table.table,\n owner.exact(table.table, {\n ...expected,\n [table.admittedAt]: null,\n [table.retainUntil]: null,\n }),\n {\n [table.admittedAt]: stamp,\n [table.retainUntil]: clock.fromMillis(sql`${stampMillis} + ${chargeRetentionMillis}`),\n },\n { rows: 1, postcondition },\n );\n }\n owner.postconditions.push(\n sql`(select count(*) from ${table.table} where ${owner.exact(table.table, {\n [table.moduleId]: ceremony.moduleId,\n [table.flowId]: ceremony.flowId,\n [table.ownerMarker]: owner.marker,\n })}) = ${identities.length}`,\n );\n });\n\n return {\n chargeRetentionMillis,\n chargeScopes,\n lockAdmission,\n admissionCondition,\n guardAdmission,\n readCharges,\n guardChargeSet,\n insertCharges,\n };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "../query-operations.mjs";
|
|
2
|
+
import "../transaction-kernel.mjs";
|
|
3
|
+
import "./state.mjs";
|
|
4
|
+
import { Effect } from "effect";
|
|
5
|
+
import { PasskeyCredential, PasskeyRevision } from "@yielded/auth/Passkey";
|
|
6
|
+
import { SubjectId } from "@yielded/auth/Schema";
|
|
7
|
+
//#region src/internal/passkey/credentials.d.ts
|
|
8
|
+
export interface SubjectRead {
|
|
9
|
+
readonly nativeId: unknown;
|
|
10
|
+
readonly subjectId: SubjectId;
|
|
11
|
+
readonly securityRevision: typeof PasskeyRevision.Type.securityRevision;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//# sourceMappingURL=credentials.d.mts.map
|