@yielded/auth-persistence 0.1.0-beta.30 → 0.1.0-beta.32

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 (130) hide show
  1. package/README.md +11 -7
  2. package/dist/Adapter.d.mts +1 -1
  3. package/dist/Adapter.mjs +8 -8
  4. package/dist/OAuthPersistence.mjs +10 -5
  5. package/dist/OAuthPersistence.mjs.map +1 -1
  6. package/dist/internal/configuration.d.mts +49 -13
  7. package/dist/internal/configuration.mjs.map +1 -1
  8. package/dist/internal/email-native.mjs +10 -2
  9. package/dist/internal/email-native.mjs.map +1 -1
  10. package/dist/internal/email-policy.mjs +3 -2
  11. package/dist/internal/email-policy.mjs.map +1 -1
  12. package/dist/internal/models/oauth-model.d.mts +4 -2
  13. package/dist/internal/models/oauth-model.mjs.map +1 -1
  14. package/dist/internal/models/password-model.d.mts +2 -1
  15. package/dist/internal/models/password-model.mjs.map +1 -1
  16. package/dist/internal/oauth/native-accounts.d.mts +1 -1
  17. package/dist/internal/oauth/native-accounts.mjs +6 -6
  18. package/dist/internal/oauth/native-accounts.mjs.map +1 -1
  19. package/dist/internal/oauth/native-connected.d.mts +1 -1
  20. package/dist/internal/oauth/native-connected.mjs +1 -1
  21. package/dist/internal/oauth/native-registration.mjs +19 -8
  22. package/dist/internal/oauth/native-registration.mjs.map +1 -1
  23. package/dist/internal/oauth/native-sign-in.d.mts +1 -1
  24. package/dist/internal/oauth/native-sign-in.mjs +1 -1
  25. package/dist/internal/oauth/native-state.mjs +21 -11
  26. package/dist/internal/oauth/native-state.mjs.map +1 -1
  27. package/dist/internal/oauth/state.d.mts +1 -1
  28. package/dist/internal/oauth/state.mjs +1 -1
  29. package/dist/internal/oauth-managed.mjs +182 -0
  30. package/dist/internal/oauth-managed.mjs.map +1 -0
  31. package/dist/internal/passkey-managed.mjs +5 -3
  32. package/dist/internal/passkey-managed.mjs.map +1 -1
  33. package/dist/internal/passkey-native-registration.mjs +1 -1
  34. package/dist/internal/passkey-native-state.mjs +33 -23
  35. package/dist/internal/passkey-native-state.mjs.map +1 -1
  36. package/dist/internal/passkey-native-write.mjs +3 -3
  37. package/dist/internal/passkey-native-write.mjs.map +1 -1
  38. package/dist/internal/passkey-native.d.mts +15 -7
  39. package/dist/internal/passkey-native.mjs +2 -1
  40. package/dist/internal/passkey-native.mjs.map +1 -1
  41. package/dist/internal/passkey-policy.mjs +9 -7
  42. package/dist/internal/passkey-policy.mjs.map +1 -1
  43. package/dist/internal/password-native.mjs +22 -5
  44. package/dist/internal/password-native.mjs.map +1 -1
  45. package/dist/internal/password-policy.mjs +7 -6
  46. package/dist/internal/password-policy.mjs.map +1 -1
  47. package/dist/internal/password-registration-native.d.mts +1 -1
  48. package/dist/internal/persistence.d.mts +3 -0
  49. package/dist/internal/persistence.mjs +99 -42
  50. package/dist/internal/persistence.mjs.map +1 -1
  51. package/dist/internal/phone-native.mjs +1 -1
  52. package/dist/internal/phone-native.mjs.map +1 -1
  53. package/dist/internal/registration.mjs +17 -8
  54. package/dist/internal/registration.mjs.map +1 -1
  55. package/dist/internal/session-native-authority.mjs +8 -6
  56. package/dist/internal/session-native-authority.mjs.map +1 -1
  57. package/dist/internal/session-native-login.d.mts +2 -2
  58. package/dist/internal/session-native-login.mjs +4 -2
  59. package/dist/internal/session-native-login.mjs.map +1 -1
  60. package/dist/internal/session-native-pending-read.mjs +26 -20
  61. package/dist/internal/session-native-pending-read.mjs.map +1 -1
  62. package/dist/internal/session-native-record.mjs +6 -4
  63. package/dist/internal/session-native-record.mjs.map +1 -1
  64. package/dist/internal/session-native-state.mjs +24 -8
  65. package/dist/internal/session-native-state.mjs.map +1 -1
  66. package/dist/internal/session-native-stateful.d.mts +2 -2
  67. package/dist/internal/session-native-stateful.mjs +67 -38
  68. package/dist/internal/session-native-stateful.mjs.map +1 -1
  69. package/dist/internal/session-native-step-up.d.mts +2 -2
  70. package/dist/internal/session-native-step-up.mjs +17 -19
  71. package/dist/internal/session-native-step-up.mjs.map +1 -1
  72. package/dist/internal/session-native-validity.mjs +3 -1
  73. package/dist/internal/session-native-validity.mjs.map +1 -1
  74. package/dist/internal/sql-authority.mjs +5 -1
  75. package/dist/internal/sql-authority.mjs.map +1 -1
  76. package/dist/internal/sql-storage-validation.mjs +2 -1
  77. package/dist/internal/sql-storage-validation.mjs.map +1 -1
  78. package/dist/internal/sql-table.d.mts +1 -0
  79. package/dist/internal/sql-table.mjs.map +1 -1
  80. package/dist/internal/sqlite-policy-guard.mjs +23 -0
  81. package/dist/internal/sqlite-policy-guard.mjs.map +1 -0
  82. package/dist/internal/step-up-state.mjs +19 -6
  83. package/dist/internal/step-up-state.mjs.map +1 -1
  84. package/dist/internal/storage-mapping.d.mts +9 -5
  85. package/dist/internal/storage-mapping.mjs +17 -5
  86. package/dist/internal/storage-mapping.mjs.map +1 -1
  87. package/dist/internal/storage-tables.d.mts +237 -0
  88. package/dist/internal/storage-tables.mjs +68 -0
  89. package/dist/internal/storage-tables.mjs.map +1 -1
  90. package/dist/internal/storage-validation.d.mts +3 -1
  91. package/dist/internal/storage-validation.mjs +70 -17
  92. package/dist/internal/storage-validation.mjs.map +1 -1
  93. package/dist/internal/totp-native.mjs +1 -1
  94. package/package.json +1 -1
  95. package/src/OAuthPersistence.ts +7 -2
  96. package/src/internal/configuration.ts +72 -15
  97. package/src/internal/email-native.ts +16 -6
  98. package/src/internal/email-policy.ts +12 -3
  99. package/src/internal/models/oauth-model.ts +7 -1
  100. package/src/internal/models/password-model.ts +2 -1
  101. package/src/internal/oauth/native-accounts.ts +3 -3
  102. package/src/internal/oauth/native-registration.ts +17 -6
  103. package/src/internal/oauth/native-state.ts +38 -18
  104. package/src/internal/oauth-managed.ts +277 -0
  105. package/src/internal/passkey-managed.ts +8 -2
  106. package/src/internal/passkey-native-state.ts +59 -52
  107. package/src/internal/passkey-native-write.ts +2 -2
  108. package/src/internal/passkey-native.ts +4 -2
  109. package/src/internal/passkey-policy.ts +13 -9
  110. package/src/internal/password-native.ts +45 -11
  111. package/src/internal/password-policy.ts +16 -8
  112. package/src/internal/persistence.ts +360 -204
  113. package/src/internal/phone-native.ts +1 -1
  114. package/src/internal/registration.ts +53 -40
  115. package/src/internal/session-native-authority.ts +26 -8
  116. package/src/internal/session-native-login.ts +9 -4
  117. package/src/internal/session-native-pending-read.ts +46 -33
  118. package/src/internal/session-native-record.ts +6 -4
  119. package/src/internal/session-native-state.ts +32 -10
  120. package/src/internal/session-native-stateful.ts +147 -52
  121. package/src/internal/session-native-step-up.ts +31 -24
  122. package/src/internal/session-native-validity.ts +3 -1
  123. package/src/internal/sql-authority.ts +5 -1
  124. package/src/internal/sql-storage-validation.ts +53 -46
  125. package/src/internal/sql-table.ts +1 -0
  126. package/src/internal/sqlite-policy-guard.ts +34 -0
  127. package/src/internal/step-up-state.ts +43 -6
  128. package/src/internal/storage-mapping.ts +40 -4
  129. package/src/internal/storage-tables.ts +76 -0
  130. package/src/internal/storage-validation.ts +127 -27
@@ -1,6 +1,7 @@
1
1
  //#region src/internal/sql-table.d.ts
2
2
  export interface ColumnOptions {
3
3
  readonly name: string;
4
+ /** Driver value representation, not the database column's physical SQL type. */
4
5
  readonly type: "text" | "integer" | "boolean";
5
6
  readonly nullable?: boolean;
6
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { Option, Schema, SchemaGetter } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nconst SqlInteger = Schema.Union([\n Schema.Int,\n Schema.BigInt,\n Schema.String.check(Schema.isPattern(/^-?\\d+$/)),\n]).pipe(\n Schema.decodeTo(Schema.Int, {\n decode: SchemaGetter.transform(Number),\n encode: SchemaGetter.passthrough(),\n }),\n);\n\nconst decodeInteger = Schema.decodeUnknownOption(SqlInteger);\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport interface ColumnOptions {\n readonly name: string;\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {}\n\n decode(value: unknown): unknown {\n if (value === null) return value;\n if (this.options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (this.options.type === \"integer\") {\n const number = decodeInteger(value);\n\n if (Option.isSome(number)) return number.value;\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"SQL integer is outside the safe range\",\n });\n }\n\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table {\n readonly columns: Readonly<Record<string, Column>>;\n\n constructor(\n readonly name: string,\n columns: Readonly<Record<string, ColumnOptions>>,\n readonly unique: ReadonlyArray<ReadonlyArray<string>>,\n readonly schema?: string,\n ) {\n this.columns = Object.fromEntries(\n Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]),\n );\n }\n}\n"],"mappings":";;;AAIA,MAAM,aAAa,OAAO,MAAM;CAC9B,OAAO;CACP,OAAO;CACP,OAAO,OAAO,MAAM,OAAO,UAAU,SAAS,CAAC;AACjD,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,OAAO,KAAK;CAC1B,QAAQ,aAAa,UAAU,MAAM;CACrC,QAAQ,aAAa,YAAY;AACnC,CAAC,CACH;AAEA,MAAM,gBAAgB,OAAO,oBAAoB,UAAU;AAK3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;AAQ/E,IAAa,SAAb,MAAoB;CAEP;CACA;CACA;CAHX,YACE,WACA,SACA,QACA;EAHS,KAAA,YAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;CACR;CAEH,OAAO,OAAyB;EAC9B,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,IAAI,UAAU,QAAQ,UAAU,GAAG,OAAO;GAC1C,IAAI,UAAU,SAAS,UAAU,GAAG,OAAO;GAC3C,MAAM,wBAAwB,KAAK;IAAE,WAAW;IAAU,OAAO;GAAsB,CAAC;EAC1F;EACA,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,MAAM,SAAS,cAAc,KAAK;GAElC,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,OAAO;GACzC,MAAM,wBAAwB,KAAK;IACjC,WAAW;IACX,OAAO;GACT,CAAC;EACH;EAEA,OAAO;CACT;AACF;;AAGA,IAAa,QAAb,MAAmB;CAIN;CAEA;CACA;CANX;CAEA,YACE,MACA,SACA,QACA,QACA;EAJS,KAAA,OAAA;EAEA,KAAA,SAAA;EACA,KAAA,SAAA;EAET,KAAK,UAAU,OAAO,YACpB,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,KAAK,IAAI,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,CAC1F;CACF;AACF"}
1
+ {"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { Option, Schema, SchemaGetter } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nconst SqlInteger = Schema.Union([\n Schema.Int,\n Schema.BigInt,\n Schema.String.check(Schema.isPattern(/^-?\\d+$/)),\n]).pipe(\n Schema.decodeTo(Schema.Int, {\n decode: SchemaGetter.transform(Number),\n encode: SchemaGetter.passthrough(),\n }),\n);\n\nconst decodeInteger = Schema.decodeUnknownOption(SqlInteger);\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport interface ColumnOptions {\n readonly name: string;\n /** Driver value representation, not the database column's physical SQL type. */\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {}\n\n decode(value: unknown): unknown {\n if (value === null) return value;\n if (this.options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (this.options.type === \"integer\") {\n const number = decodeInteger(value);\n\n if (Option.isSome(number)) return number.value;\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"SQL integer is outside the safe range\",\n });\n }\n\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table {\n readonly columns: Readonly<Record<string, Column>>;\n\n constructor(\n readonly name: string,\n columns: Readonly<Record<string, ColumnOptions>>,\n readonly unique: ReadonlyArray<ReadonlyArray<string>>,\n readonly schema?: string,\n ) {\n this.columns = Object.fromEntries(\n Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]),\n );\n }\n}\n"],"mappings":";;;AAIA,MAAM,aAAa,OAAO,MAAM;CAC9B,OAAO;CACP,OAAO;CACP,OAAO,OAAO,MAAM,OAAO,UAAU,SAAS,CAAC;AACjD,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,OAAO,KAAK;CAC1B,QAAQ,aAAa,UAAU,MAAM;CACrC,QAAQ,aAAa,YAAY;AACnC,CAAC,CACH;AAEA,MAAM,gBAAgB,OAAO,oBAAoB,UAAU;AAK3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;AAS/E,IAAa,SAAb,MAAoB;CAEP;CACA;CACA;CAHX,YACE,WACA,SACA,QACA;EAHS,KAAA,YAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;CACR;CAEH,OAAO,OAAyB;EAC9B,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,IAAI,UAAU,QAAQ,UAAU,GAAG,OAAO;GAC1C,IAAI,UAAU,SAAS,UAAU,GAAG,OAAO;GAC3C,MAAM,wBAAwB,KAAK;IAAE,WAAW;IAAU,OAAO;GAAsB,CAAC;EAC1F;EACA,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,MAAM,SAAS,cAAc,KAAK;GAElC,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,OAAO;GACzC,MAAM,wBAAwB,KAAK;IACjC,WAAW;IACX,OAAO;GACT,CAAC;EACH;EAEA,OAAO;CACT;AACF;;AAGA,IAAa,QAAb,MAAmB;CAIN;CAEA;CACA;CANX;CAEA,YACE,MACA,SACA,QACA,QACA;EAJS,KAAA,OAAA;EAEA,KAAA,SAAA;EACA,KAAA,SAAA;EAET,KAAK,UAAU,OAAO,YACpB,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,KAAK,IAAI,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,CAC1F;CACF;AACF"}
@@ -0,0 +1,23 @@
1
+ import { appendSqlBatchStatement, registerSqlBatchPostcondition } from "./sql-commit.mjs";
2
+ import { Array, Effect } from "effect";
3
+ import { SqlClient } from "effect/sql/SqlClient";
4
+ //#region src/internal/sqlite-policy-guard.ts
5
+ /** Check original values before writes and at the end of the same atomic batch.
6
+ * Small groups bound parameters and expression depth without expanding retained
7
+ * values into hex/JSON. BINARY preserves text bytes; IS also compares NULLs. */
8
+ const guardSqlitePolicy = Effect.fnUntraced(function* (input) {
9
+ const sql = (yield* SqlClient).withoutTransforms();
10
+ for (const values of Array.chunksOf(input.values, 32)) {
11
+ const matches = sql.and(values.map(({ column, value }) => sql`${column} collate binary is ${value}`));
12
+ const statement = sql`select case when ${sql`exists(select 1 from ${input.table} where ${input.owner} and ${matches})`} then 1 else json_extract('[]', '$[auth-batch-precondition]') end as valid`;
13
+ yield* appendSqlBatchStatement(statement);
14
+ yield* registerSqlBatchPostcondition({
15
+ name: input.name,
16
+ statement
17
+ });
18
+ }
19
+ });
20
+ //#endregion
21
+ export { guardSqlitePolicy };
22
+
23
+ //# sourceMappingURL=sqlite-policy-guard.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-policy-guard.mjs","names":[],"sources":["../../src/internal/sqlite-policy-guard.ts"],"sourcesContent":["import { Array, Effect } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { appendSqlBatchStatement, registerSqlBatchPostcondition } from \"./sql-commit\";\n\nexport interface SqlitePolicyValue {\n readonly column: Fragment;\n readonly value: Fragment;\n}\n\n/** Check original values before writes and at the end of the same atomic batch.\n * Small groups bound parameters and expression depth without expanding retained\n * values into hex/JSON. BINARY preserves text bytes; IS also compares NULLs. */\nexport const guardSqlitePolicy = Effect.fnUntraced(function* (input: {\n readonly name: string;\n readonly table: Fragment;\n readonly owner: Fragment;\n readonly values: ReadonlyArray<SqlitePolicyValue>;\n}) {\n const sql = (yield* SqlClient).withoutTransforms();\n\n for (const values of Array.chunksOf(input.values, 32)) {\n const matches = sql.and(\n values.map(({ column, value }) => sql`${column} collate binary is ${value}`),\n );\n\n const condition = sql`exists(select 1 from ${input.table} where ${input.owner} and ${matches})`;\n const statement = sql`select case when ${condition} then 1 else json_extract('[]', '$[auth-batch-precondition]') end as valid`;\n\n yield* appendSqlBatchStatement(statement);\n yield* registerSqlBatchPostcondition({ name: input.name, statement });\n }\n});\n"],"mappings":";;;;;;;AAcA,MAAa,oBAAoB,OAAO,WAAW,WAAW,OAK3D;CACD,MAAM,OAAO,OAAO,UAAA,CAAW,kBAAkB;CAEjD,KAAK,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ,EAAE,GAAG;EACrD,MAAM,UAAU,IAAI,IAClB,OAAO,KAAK,EAAE,QAAQ,YAAY,GAAG,GAAG,OAAO,qBAAqB,OAAO,CAC7E;EAGA,MAAM,YAAY,GAAG,oBAAoB,GADpB,wBAAwB,MAAM,MAAM,SAAS,MAAM,MAAM,OAAO,QAAQ,GAC1C;EAEnD,OAAO,wBAAwB,SAAS;EACxC,OAAO,8BAA8B;GAAE,MAAM,MAAM;GAAM;EAAU,CAAC;CACtE;AACF,CAAC"}
@@ -1,27 +1,40 @@
1
- import { sameSessionRevision } from "./session-native-state.mjs";
1
+ import { assessSessionAt, sameSessionRevision } from "./session-native-state.mjs";
2
2
  import { DateTime, Effect, Schema } from "effect";
3
- import { AuthenticationEvidence, SessionMetadata, SessionStepUpIntent, SessionStepUpInvalid, SessionStepUpRequirement, SessionUnavailable } from "@yielded/auth/Sessions";
3
+ import { AuthenticationEvidence, AuthenticationProof, SessionMetadata, SessionStepUpIntent, SessionStepUpInvalid, SessionStepUpRequirement, SessionUnavailable } from "@yielded/auth/Sessions";
4
+ import { AuthenticationAssurance, AuthenticationClock } from "@yielded/auth/Operations";
4
5
  //#region src/internal/step-up-state.ts
5
6
  const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);
6
7
  const BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));
7
8
  const encodeStepUpIntent = (intent) => Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)), Effect.mapError(() => SessionUnavailable.make({})));
8
9
  const decodeStepUpIntent = (snapshot) => Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)), Effect.mapError(() => SessionUnavailable.make({})));
9
- const stepUpIntentLive = (intent, kind, now) => {
10
+ const stepUpIntentLive = Effect.fnUntraced(function* (intent, kind, now) {
11
+ const { futureToleranceMillis } = yield* AuthenticationClock;
10
12
  const n = DateTime.toEpochMillis(now), authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt), idle = DateTime.toEpochMillis(intent.sourceExpiresAt), absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt), expires = DateTime.toEpochMillis(intent.expiresAt);
11
- return intent.sourceKind === kind && authenticated <= n && n < expires && expires <= idle && idle <= absolute;
12
- };
13
+ return intent.sourceKind === kind && authenticated - n <= futureToleranceMillis && authenticated < idle && n < expires && expires <= idle && idle <= absolute;
14
+ });
13
15
  const evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));
14
16
  const requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));
15
17
  Schema.encodeSync(Schema.fromJsonString(SessionMetadata));
18
+ const proofEncoding = Schema.encodeSync(Schema.fromJsonString(Schema.Array(AuthenticationProof)));
19
+ const assuranceEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata.fields.assurance));
16
20
  /** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */
17
21
  const validateStepUpPlan = Effect.fn("DrizzleStepUp.validatePlan")(function* (plan) {
18
22
  const { intent, source, replacement, evidence } = plan;
19
23
  const original = source.inspection;
24
+ const issuedAt = DateTime.toEpochMillis(replacement.inspection.session.issuedAt);
25
+ if (issuedAt < DateTime.toEpochMillis(original.session.issuedAt) || evidence.proofs.some((proof) => DateTime.toEpochMillis(proof.verifiedAt) > issuedAt) || issuedAt >= DateTime.toEpochMillis(replacement.inspection.session.expiresAt)) return yield* SessionStepUpInvalid.make({});
20
26
  if (requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) || replacement.inspection.session.subjectId !== evidence.revision.subjectId || replacement.inspection.session.securityRevision !== evidence.revision.securityRevision || intent.revision.credentials.some((item) => !evidence.revision.credentials.some((actual) => actual.credentialId === item.credentialId && actual.revision === item.revision))) return yield* SessionStepUpInvalid.make({});
21
- if (intent.sourceKind !== source.guard._tag || intent.sourceKind !== replacement._tag || intent.sourceSessionId !== original.session.sessionId || intent.sourceCredentialVersion !== original.credentialVersion || !sameSessionRevision(intent.revision, original.provenance.evidence.revision) || intent.revision.subjectId !== evidence.revision.subjectId || intent.revision.securityRevision !== evidence.revision.securityRevision || intent.flowId !== evidence.flowId || intent.bindingDigest !== evidence.bindingDigest || DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !== DateTime.toEpochMillis(original.session.assurance.authenticatedAt) || DateTime.toEpochMillis(intent.sourceExpiresAt) !== DateTime.toEpochMillis(original.session.expiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(original.session.absoluteExpiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) || evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence)) return yield* SessionStepUpInvalid.make({});
27
+ if (intent.sourceKind !== source.guard._tag || intent.sourceKind !== replacement._tag || intent.sourceSessionId !== original.session.sessionId || intent.sourceCredentialVersion !== original.credentialVersion || !sameSessionRevision(intent.revision, original.provenance.evidence.revision) || intent.revision.subjectId !== evidence.revision.subjectId || intent.revision.securityRevision !== evidence.revision.securityRevision || intent.flowId !== evidence.flowId || intent.bindingDigest !== evidence.bindingDigest || DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !== DateTime.toEpochMillis(original.session.assurance.authenticatedAt) || DateTime.toEpochMillis(intent.sourceExpiresAt) !== DateTime.toEpochMillis(original.session.expiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(original.session.absoluteExpiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) || evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence) || proofEncoding(evidence.proofs.slice(0, original.provenance.evidence.proofs.length)) !== proofEncoding(original.provenance.evidence.proofs)) return yield* SessionStepUpInvalid.make({});
22
28
  if (replacement._tag === "Stateful" && (source.guard._tag !== "Stateful" || source.guard.digest !== replacement.expectedDigest || replacement.inspection.session.sessionId !== intent.sourceSessionId)) return yield* SessionStepUpInvalid.make({});
23
29
  if (replacement._tag === "StateAssistedSigned" && (replacement.tombstoneSessionId !== intent.sourceSessionId || DateTime.toEpochMillis(replacement.tombstoneUntil) !== DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))) return yield* SessionStepUpInvalid.make({});
24
30
  if (replacement._tag !== "Stateful" && replacement.inspection.session.sessionId === intent.sourceSessionId) return yield* SessionStepUpInvalid.make({});
31
+ const at = DateTime.toEpochMillis(plan.now);
32
+ const base = yield* assessSessionAt(evidence, plan.baseRequirement, at);
33
+ const profile = yield* assessSessionAt(evidence, plan.profileRequirement, at);
34
+ if (!base.satisfied || !profile.satisfied || assuranceEncoding(AuthenticationAssurance.make({
35
+ ...base.assurance,
36
+ authenticatedAt: profile.assurance.authenticatedAt
37
+ })) !== assuranceEncoding(replacement.inspection.session.assurance)) return yield* SessionStepUpInvalid.make({});
25
38
  });
26
39
  //#endregion
27
40
  export { decodeStepUpIntent, encodeStepUpIntent, stepUpIntentCodec, stepUpIntentLive, validateStepUpPlan };
@@ -1 +1 @@
1
- {"version":3,"file":"step-up-state.mjs","names":[],"sources":["../../src/internal/step-up-state.ts"],"sourcesContent":["import {\n SessionStepUpInvalid,\n SessionUnavailable,\n AuthenticationEvidence,\n SessionMetadata,\n SessionStepUpIntent,\n SessionStepUpRequirement,\n type SessionStepUpCompletionPlan,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\n\nimport { sameSessionRevision } from \"./session-native-state\";\n\nexport const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);\nconst BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));\n\nexport const encodeStepUpIntent = (intent: SessionStepUpIntent) =>\n Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(\n Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const decodeStepUpIntent = (snapshot: string) =>\n Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(\n Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const stepUpIntentLive = (\n intent: Omit<SessionStepUpIntent, \"version\">,\n kind: SessionStepUpIntent[\"sourceKind\"],\n now: DateTime.Utc,\n) => {\n const n = DateTime.toEpochMillis(now),\n authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt),\n idle = DateTime.toEpochMillis(intent.sourceExpiresAt),\n absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt),\n expires = DateTime.toEpochMillis(intent.expiresAt);\n\n return (\n intent.sourceKind === kind &&\n authenticated <= n &&\n n < expires &&\n expires <= idle &&\n idle <= absolute\n );\n};\n\nconst evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));\nconst requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));\nconst metadataEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\n\n/** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */\nexport const validateStepUpPlan = Effect.fn(\"DrizzleStepUp.validatePlan\")(function* <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n) {\n const { intent, source, replacement, evidence } = plan;\n const original = source.inspection;\n\n if (\n requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) ||\n replacement.inspection.session.subjectId !== evidence.revision.subjectId ||\n replacement.inspection.session.securityRevision !== evidence.revision.securityRevision ||\n intent.revision.credentials.some(\n (item) =>\n !evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === item.credentialId && actual.revision === item.revision,\n ),\n )\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n intent.sourceKind !== source.guard._tag ||\n intent.sourceKind !== replacement._tag ||\n intent.sourceSessionId !== original.session.sessionId ||\n intent.sourceCredentialVersion !== original.credentialVersion ||\n !sameSessionRevision(intent.revision, original.provenance.evidence.revision) ||\n intent.revision.subjectId !== evidence.revision.subjectId ||\n intent.revision.securityRevision !== evidence.revision.securityRevision ||\n intent.flowId !== evidence.flowId ||\n intent.bindingDigest !== evidence.bindingDigest ||\n DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !==\n DateTime.toEpochMillis(original.session.assurance.authenticatedAt) ||\n DateTime.toEpochMillis(intent.sourceExpiresAt) !==\n DateTime.toEpochMillis(original.session.expiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(original.session.absoluteExpiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) ||\n evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"Stateful\" &&\n (source.guard._tag !== \"Stateful\" ||\n source.guard.digest !== replacement.expectedDigest ||\n replacement.inspection.session.sessionId !== intent.sourceSessionId)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"StateAssistedSigned\" &&\n (replacement.tombstoneSessionId !== intent.sourceSessionId ||\n DateTime.toEpochMillis(replacement.tombstoneUntil) !==\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag !== \"Stateful\" &&\n replacement.inspection.session.sessionId === intent.sourceSessionId\n )\n return yield* SessionStepUpInvalid.make({});\n});\n\n/** Compare a consumer-decoded encoded update/readback with the intended rotation.\n * Claims are consumer-owned; their lossless round trip remains the codec contract. */\nexport const stepUpRotationMatches = <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n record: StatefulSessionRecord<Claims>,\n) =>\n plan.replacement._tag === \"Stateful\" &&\n record.digest === plan.replacement.nextDigest &&\n record.credentialVersion === plan.replacement.inspection.credentialVersion &&\n metadataEncoding(record) === metadataEncoding(plan.replacement.inspection.session) &&\n evidenceEncoding(record.provenance.evidence) === evidenceEncoding(plan.evidence);\n"],"mappings":"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"}
1
+ {"version":3,"file":"step-up-state.mjs","names":[],"sources":["../../src/internal/step-up-state.ts"],"sourcesContent":["import { AuthenticationAssurance, AuthenticationClock } from \"@yielded/auth/Operations\";\nimport {\n SessionStepUpInvalid,\n SessionUnavailable,\n AuthenticationEvidence,\n AuthenticationProof,\n SessionMetadata,\n SessionStepUpIntent,\n SessionStepUpRequirement,\n type SessionStepUpCompletionPlan,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\n\nimport { assessSessionAt, sameSessionRevision } from \"./session-native-state\";\n\nexport const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);\nconst BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));\n\nexport const encodeStepUpIntent = (intent: SessionStepUpIntent) =>\n Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(\n Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const decodeStepUpIntent = (snapshot: string) =>\n Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(\n Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const stepUpIntentLive = Effect.fnUntraced(function* (\n intent: Omit<SessionStepUpIntent, \"version\">,\n kind: SessionStepUpIntent[\"sourceKind\"],\n now: DateTime.Utc,\n): Effect.fn.Return<boolean> {\n const { futureToleranceMillis } = yield* AuthenticationClock;\n\n const n = DateTime.toEpochMillis(now),\n authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt),\n idle = DateTime.toEpochMillis(intent.sourceExpiresAt),\n absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt),\n expires = DateTime.toEpochMillis(intent.expiresAt);\n\n return (\n intent.sourceKind === kind &&\n authenticated - n <= futureToleranceMillis &&\n authenticated < idle &&\n n < expires &&\n expires <= idle &&\n idle <= absolute\n );\n});\n\nconst evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));\nconst requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));\nconst metadataEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\nconst proofEncoding = Schema.encodeSync(Schema.fromJsonString(Schema.Array(AuthenticationProof)));\n\nconst assuranceEncoding = Schema.encodeSync(\n Schema.fromJsonString(SessionMetadata.fields.assurance),\n);\n\n/** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */\nexport const validateStepUpPlan = Effect.fn(\"DrizzleStepUp.validatePlan\")(function* <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n) {\n const { intent, source, replacement, evidence } = plan;\n const original = source.inspection;\n const issuedAt = DateTime.toEpochMillis(replacement.inspection.session.issuedAt);\n\n if (\n issuedAt < DateTime.toEpochMillis(original.session.issuedAt) ||\n evidence.proofs.some((proof) => DateTime.toEpochMillis(proof.verifiedAt) > issuedAt) ||\n issuedAt >= DateTime.toEpochMillis(replacement.inspection.session.expiresAt)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) ||\n replacement.inspection.session.subjectId !== evidence.revision.subjectId ||\n replacement.inspection.session.securityRevision !== evidence.revision.securityRevision ||\n intent.revision.credentials.some(\n (item) =>\n !evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === item.credentialId && actual.revision === item.revision,\n ),\n )\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n intent.sourceKind !== source.guard._tag ||\n intent.sourceKind !== replacement._tag ||\n intent.sourceSessionId !== original.session.sessionId ||\n intent.sourceCredentialVersion !== original.credentialVersion ||\n !sameSessionRevision(intent.revision, original.provenance.evidence.revision) ||\n intent.revision.subjectId !== evidence.revision.subjectId ||\n intent.revision.securityRevision !== evidence.revision.securityRevision ||\n intent.flowId !== evidence.flowId ||\n intent.bindingDigest !== evidence.bindingDigest ||\n DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !==\n DateTime.toEpochMillis(original.session.assurance.authenticatedAt) ||\n DateTime.toEpochMillis(intent.sourceExpiresAt) !==\n DateTime.toEpochMillis(original.session.expiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(original.session.absoluteExpiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) ||\n evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence) ||\n proofEncoding(evidence.proofs.slice(0, original.provenance.evidence.proofs.length)) !==\n proofEncoding(original.provenance.evidence.proofs)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"Stateful\" &&\n (source.guard._tag !== \"Stateful\" ||\n source.guard.digest !== replacement.expectedDigest ||\n replacement.inspection.session.sessionId !== intent.sourceSessionId)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"StateAssistedSigned\" &&\n (replacement.tombstoneSessionId !== intent.sourceSessionId ||\n DateTime.toEpochMillis(replacement.tombstoneUntil) !==\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag !== \"Stateful\" &&\n replacement.inspection.session.sessionId === intent.sourceSessionId\n )\n return yield* SessionStepUpInvalid.make({});\n\n const at = DateTime.toEpochMillis(plan.now);\n const base = yield* assessSessionAt(evidence, plan.baseRequirement, at);\n\n const profile = yield* assessSessionAt(evidence, plan.profileRequirement, at);\n\n // Freshness can change metadata at commit; validate the exact preparation snapshot here.\n if (\n !base.satisfied ||\n !profile.satisfied ||\n assuranceEncoding(\n AuthenticationAssurance.make({\n ...base.assurance,\n authenticatedAt: profile.assurance.authenticatedAt,\n }),\n ) !== assuranceEncoding(replacement.inspection.session.assurance)\n )\n return yield* SessionStepUpInvalid.make({});\n});\n\n/** Compare a consumer-decoded encoded update/readback with the intended rotation.\n * Claims are consumer-owned; their lossless round trip remains the codec contract. */\nexport const stepUpRotationMatches = <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n record: StatefulSessionRecord<Claims>,\n) =>\n plan.replacement._tag === \"Stateful\" &&\n record.digest === plan.replacement.nextDigest &&\n record.credentialVersion === plan.replacement.inspection.credentialVersion &&\n metadataEncoding(record) === metadataEncoding(plan.replacement.inspection.session) &&\n evidenceEncoding(record.provenance.evidence) === evidenceEncoding(plan.evidence);\n"],"mappings":"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"}
@@ -6,17 +6,19 @@ import { AnyPasswordPersistenceMapping } from "./models/password-model.mjs";
6
6
  import { PendingAuthenticationMapping, StatefulSessionMapping } from "./models/session-model.mjs";
7
7
  import { StorageRole } from "./storage-tables.mjs";
8
8
  import { MappingInput } from "./configuration.mjs";
9
+ import { NativeSqlTables } from "./native-sql-table.mjs";
9
10
  import { Crypto, Effect, Schema } from "effect";
11
+ import { SqlClient } from "effect/sql";
10
12
  import { SecurityRevision } from "@yielded/auth/Sessions";
11
13
  //#region src/internal/storage-mapping.d.ts
12
14
  declare const string: (value: unknown) => string;
13
15
  /** Derive shared row mappings from a managed or custom storage layout.
14
16
  * Acquire Effect Crypto once; the returned allocators retain that implementation.
15
- * Each factory requires its role tables to exist. Adapter authors supply typed
16
- * table handles and dialect-specific clocks/commit predicates, and may refine
17
- * authority policy for explicit services. This does not alter composed defaults.
17
+ * Each factory requires its role tables to exist. Supplying native tables derives
18
+ * batch commit predicates and subject policy columns. Adapter authors may refine
19
+ * clocks and authority policy for explicit services.
18
20
  */
19
- export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
21
+ export declare const makeMappings: (input: MappingInput, tables?: NativeSqlTables | undefined) => Effect.Effect<{
20
22
  table: (role: StorageRole) => object;
21
23
  authority: () => {
22
24
  subjectId: {
@@ -29,6 +31,7 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
29
31
  id: string;
30
32
  status: string;
31
33
  securityRevision: string;
34
+ requirementColumns?: string[];
32
35
  isActiveStatus: (value: unknown) => boolean;
33
36
  d1ActiveStatusValue: unknown;
34
37
  activeStatusValue: unknown;
@@ -43,6 +46,7 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
43
46
  revision: string;
44
47
  status: string;
45
48
  activeStatusValue: boolean;
49
+ d1ActiveStatusValue: boolean;
46
50
  isActiveStatus: (value: unknown) => value is true;
47
51
  encodeInsert: (row: object) => {
48
52
  active: boolean;
@@ -58,6 +62,6 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
58
62
  sessions: <C extends Schema.Codec<unknown, unknown, never, never>>(claims: C, moduleId: string) => StatefulSessionMapping<C["Type"], TableModel, TableModel, TableModel, TableModel, unknown, unknown>;
59
63
  pending: <C extends Schema.Codec<unknown, unknown, never, never>>(claims: C, moduleId: string) => PendingAuthenticationMapping<C["Type"], TableModel, TableModel, TableModel, unknown>;
60
64
  string: typeof string;
61
- }, PersistenceMappingError, Crypto.Crypto | import("effect/sql/SqlClient").SqlClient>;
65
+ }, PersistenceMappingError, Crypto.Crypto | SqlClient.SqlClient>;
62
66
  //#endregion
63
67
  //# sourceMappingURL=storage-mapping.d.mts.map
@@ -1,11 +1,13 @@
1
1
  import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { exactSqlText } from "./sql-change.mjs";
2
3
  import { randomId } from "./crypto.mjs";
4
+ import { makeStorageClock } from "./storage-clock.mjs";
3
5
  import { storageTables } from "./storage-tables.mjs";
4
6
  import { requiredEmailAddressConstraints } from "./models/email-model.mjs";
5
7
  import { requiredPasswordConstraints } from "./models/password-model.mjs";
6
8
  import { requiredProofConstraints } from "./models/proof-model.mjs";
7
- import { makeStorageClock } from "./storage-clock.mjs";
8
9
  import { Crypto, DateTime, Effect, Redacted, Schema, Struct } from "effect";
10
+ import { SqlClient } from "effect/sql";
9
11
  import { EmailCredentialSnapshot } from "@yielded/auth/Email";
10
12
  import { EncodedPasswordHash, PasswordCredentialSnapshot } from "@yielded/auth/Password";
11
13
  import { AuthenticationEvidence, SecurityRevision, SessionAuthenticationProvenance, SessionCredentialVersion, SessionId, SessionMetadata } from "@yielded/auth/Sessions";
@@ -20,12 +22,13 @@ const decode = (schema, value) => Schema.decodeUnknownEffect(schema)(value).pipe
20
22
  const string = (value) => Schema.decodeUnknownSync(Schema.String)(value);
21
23
  /** Derive shared row mappings from a managed or custom storage layout.
22
24
  * Acquire Effect Crypto once; the returned allocators retain that implementation.
23
- * Each factory requires its role tables to exist. Adapter authors supply typed
24
- * table handles and dialect-specific clocks/commit predicates, and may refine
25
- * authority policy for explicit services. This does not alter composed defaults.
25
+ * Each factory requires its role tables to exist. Supplying native tables derives
26
+ * batch commit predicates and subject policy columns. Adapter authors may refine
27
+ * clocks and authority policy for explicit services.
26
28
  */
27
- const makeMappings = Effect.fnUntraced(function* (input) {
29
+ const makeMappings = Effect.fnUntraced(function* (input, tables) {
28
30
  const crypto = yield* Crypto.Crypto;
31
+ const sql = (yield* SqlClient.SqlClient).withoutTransforms();
29
32
  const allocate = randomId.pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError(failure));
30
33
  const table = (role) => {
31
34
  const found = input.tables[role];
@@ -42,11 +45,14 @@ const makeMappings = Effect.fnUntraced(function* (input) {
42
45
  toSubject: s.toSubject,
43
46
  equals: Object.is
44
47
  };
48
+ const identifierTable = tables?.(table("identifiers"));
49
+ const activeIdentifier = identifierTable === void 0 ? void 0 : sql`${identifierTable.column("active")} = ${identifierTable.value("active", true)}`;
45
50
  const subject = {
46
51
  table: s.table,
47
52
  id: s.id,
48
53
  status: s.status,
49
54
  securityRevision: s.securityRevision,
55
+ ...tables === void 0 ? {} : { requirementColumns: tables(s.table).keys.filter((key) => key !== s.id && key !== s.status && key !== s.securityRevision) },
50
56
  isActiveStatus: (value) => Object.is(value, s.activeValue),
51
57
  d1ActiveStatusValue: s.activeValue,
52
58
  activeStatusValue: s.activeValue,
@@ -61,6 +67,7 @@ const makeMappings = Effect.fnUntraced(function* (input) {
61
67
  revision: "revision",
62
68
  status: "active",
63
69
  activeStatusValue: true,
70
+ d1ActiveStatusValue: true,
64
71
  isActiveStatus: (value) => value === true,
65
72
  encodeInsert: (row) => ({
66
73
  ...row,
@@ -120,6 +127,7 @@ const makeMappings = Effect.fnUntraced(function* (input) {
120
127
  verifiedAt: "verifiedAt",
121
128
  bindingRevision: "revision",
122
129
  isCurrent: (row) => row.active === true,
130
+ ...activeIdentifier === void 0 ? {} : { d1CurrentCondition: () => activeIdentifier },
123
131
  encodeInitialInsert: (identifier, subjectId, revision) => ({
124
132
  ...identifier,
125
133
  subjectId,
@@ -216,6 +224,10 @@ const makeMappings = Effect.fnUntraced(function* (input) {
216
224
  bindingRevision: "revision",
217
225
  isCurrent: (row) => row.active === true && row.verifiedAt !== null,
218
226
  isMutableTarget: (row) => row.active === true && row.verifiedAt === null,
227
+ ...identifierTable === void 0 || activeIdentifier === void 0 ? {} : {
228
+ d1CurrentCondition: ({ bindingRevision }) => sql`${activeIdentifier} and ${identifierTable.column("verifiedAt")} is not null and ${exactSqlText(sql, identifierTable.column("revision"), identifierTable.value("revision", bindingRevision))}`,
229
+ d1MutableTargetCondition: () => sql`${activeIdentifier} and ${identifierTable.column("verifiedAt")} is null`
230
+ },
219
231
  encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({
220
232
  ...identifier,
221
233
  subjectId,
@@ -1 +1 @@
1
- {"version":3,"file":"storage-mapping.mjs","names":[],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n AuthenticationEvidence,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Redacted, Schema, Struct } from \"effect\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping, PendingAuthenticationMapping } from \"./models/session-model\";\nimport { makeStorageClock } from \"./storage-clock\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\nimport type { TableModel, SqlExpression } from \"./table-model\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Acquire Effect Crypto once; the returned allocators retain that implementation.\n * Each factory requires its role tables to exist. Adapter authors supply typed\n * table handles and dialect-specific clocks/commit predicates, and may refine\n * authority policy for explicit services. This does not alter composed defaults.\n */\nexport const makeMappings = Effect.fnUntraced(function* (input: MappingInput) {\n const crypto = yield* Crypto.Crypto;\n\n const allocate = randomId.pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError(failure),\n );\n\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n activeStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevision: () => allocate.pipe(Effect.map(SecurityRevision.make)),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n activeStatusValue: true,\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const clock = yield* makeStorageClock(input);\n\n const proofs = (): AnyProofPersistenceMapping<SqlExpression> => ({\n constraints: requiredProofConstraints,\n subjectId,\n subject: { table: s.table, id: s.id },\n clock,\n proof: {\n ...mapped(\"proofs\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n proofId: \"proofId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n failedAttempts: \"failedAttempts\",\n encodeInsert: () => ({}),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlExpression> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n clock,\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlExpression> => ({\n subjectId,\n subject: { ...subject, activeStatusValue: s.activeValue },\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n clock,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n activeStatusValue: true,\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(\n Schema.Struct(Struct.omit(EmailCredentialSnapshot.fields, [\"requirement\"])),\n {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n },\n );\n }),\n },\n });\n\n const pending = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): PendingAuthenticationMapping<C[\"Type\"], TableModel, TableModel, TableModel, unknown> => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n digest: TokenDigest,\n version: SecurityRevision,\n evidence: AuthenticationEvidence,\n expiresAt: Schema.DateTimeUtcFromMillis,\n attemptLimit: Schema.Int.check(Schema.isGreaterThan(0)),\n claims: Schema.Unknown,\n }),\n );\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n constraints: { pendingDigest: \"unique(pending.digest)\" },\n pending: {\n table: table(\"pending\"),\n moduleId: \"moduleId\",\n kind: \"kind\",\n digest: \"digest\",\n version: \"version\",\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n bindingDigest: \"bindingDigest\",\n snapshot: \"snapshot\",\n expiresAt: \"expiresAt\",\n attemptLimit: \"attemptLimit\",\n failedAttempts: \"failedAttempts\",\n consumed: \"consumed\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateVersion: allocate.pipe(Effect.map(SecurityRevision.make)),\n encodeInsert: (value) => ({\n ...value,\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n failedAttempts: 0,\n consumed: false,\n }),\n },\n login: {\n encode: (value) =>\n Schema.encodeEffect(claims)(value.claims).pipe(\n Effect.flatMap((saved) => Schema.encodeEffect(record)({ ...value, claims: saved })),\n Effect.mapError(failure),\n ),\n decode: (value) =>\n decode(record, value).pipe(\n Effect.flatMap((saved) =>\n decode(claims, saved.claims).pipe(\n Effect.map((decoded) => ({ ...saved, claims: decoded })),\n ),\n ),\n ),\n },\n };\n };\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n unknown\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n const login = pending(claims, moduleId);\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n pending: { pending: login.pending, login: login.login },\n constraints: {\n sessionDigest: \"unique(session.digest)\",\n pendingDigest: \"unique(pending.digest)\",\n },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateId: allocate,\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record),\n decodedClaims = yield* decode(claims, saved.claims);\n\n return {\n ...saved,\n issuedAt: DateTime.makeUnsafe(yield* readInstant(row.issuedAt)),\n claims: decodedClaims,\n };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, pending, string };\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"storage-mapping.mjs","names":[],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n AuthenticationEvidence,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Redacted, Schema, Struct } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping, PendingAuthenticationMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { exactSqlText } from \"./sql-change\";\nimport { makeStorageClock } from \"./storage-clock\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\nimport type { TableModel, SqlExpression } from \"./table-model\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Acquire Effect Crypto once; the returned allocators retain that implementation.\n * Each factory requires its role tables to exist. Supplying native tables derives\n * batch commit predicates and subject policy columns. Adapter authors may refine\n * clocks and authority policy for explicit services.\n */\nexport const makeMappings = Effect.fnUntraced(function* (\n input: MappingInput,\n tables?: NativeSqlTables,\n) {\n const crypto = yield* Crypto.Crypto;\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const allocate = randomId.pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError(failure),\n );\n\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n const identifierTable = tables?.(table(\"identifiers\"));\n\n const activeIdentifier =\n identifierTable === undefined\n ? undefined\n : sql`${identifierTable.column(\"active\")} = ${identifierTable.value(\"active\", true)}`;\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n // Policy callbacks may read any application column. Identity, status and\n // security revision have their own final checks and can be intentionally revised.\n ...(tables === undefined\n ? {}\n : {\n requirementColumns: tables(s.table).keys.filter(\n (key) => key !== s.id && key !== s.status && key !== s.securityRevision,\n ),\n }),\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n activeStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevision: () => allocate.pipe(Effect.map(SecurityRevision.make)),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n activeStatusValue: true,\n d1ActiveStatusValue: true,\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const clock = yield* makeStorageClock(input);\n\n const proofs = (): AnyProofPersistenceMapping<SqlExpression> => ({\n constraints: requiredProofConstraints,\n subjectId,\n subject: { table: s.table, id: s.id },\n clock,\n proof: {\n ...mapped(\"proofs\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n proofId: \"proofId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n failedAttempts: \"failedAttempts\",\n encodeInsert: () => ({}),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlExpression> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n ...(activeIdentifier === undefined\n ? {}\n : {\n d1CurrentCondition: () => activeIdentifier,\n }),\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n clock,\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlExpression> => ({\n subjectId,\n subject: { ...subject, activeStatusValue: s.activeValue },\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n clock,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n ...(identifierTable === undefined || activeIdentifier === undefined\n ? {}\n : {\n d1CurrentCondition: ({ bindingRevision }) =>\n sql`${activeIdentifier} and ${identifierTable.column(\"verifiedAt\")} is not null and ${exactSqlText(sql, identifierTable.column(\"revision\"), identifierTable.value(\"revision\", bindingRevision))}`,\n d1MutableTargetCondition: () =>\n sql`${activeIdentifier} and ${identifierTable.column(\"verifiedAt\")} is null`,\n }),\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n activeStatusValue: true,\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(\n Schema.Struct(Struct.omit(EmailCredentialSnapshot.fields, [\"requirement\"])),\n {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n },\n );\n }),\n },\n });\n\n const pending = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): PendingAuthenticationMapping<C[\"Type\"], TableModel, TableModel, TableModel, unknown> => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n digest: TokenDigest,\n version: SecurityRevision,\n evidence: AuthenticationEvidence,\n expiresAt: Schema.DateTimeUtcFromMillis,\n attemptLimit: Schema.Int.check(Schema.isGreaterThan(0)),\n claims: Schema.Unknown,\n }),\n );\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n constraints: { pendingDigest: \"unique(pending.digest)\" },\n pending: {\n table: table(\"pending\"),\n moduleId: \"moduleId\",\n kind: \"kind\",\n digest: \"digest\",\n version: \"version\",\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n bindingDigest: \"bindingDigest\",\n snapshot: \"snapshot\",\n expiresAt: \"expiresAt\",\n attemptLimit: \"attemptLimit\",\n failedAttempts: \"failedAttempts\",\n consumed: \"consumed\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateVersion: allocate.pipe(Effect.map(SecurityRevision.make)),\n encodeInsert: (value) => ({\n ...value,\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n failedAttempts: 0,\n consumed: false,\n }),\n },\n login: {\n encode: (value) =>\n Schema.encodeEffect(claims)(value.claims).pipe(\n Effect.flatMap((saved) => Schema.encodeEffect(record)({ ...value, claims: saved })),\n Effect.mapError(failure),\n ),\n decode: (value) =>\n decode(record, value).pipe(\n Effect.flatMap((saved) =>\n decode(claims, saved.claims).pipe(\n Effect.map((decoded) => ({ ...saved, claims: decoded })),\n ),\n ),\n ),\n },\n };\n };\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n unknown\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n const login = pending(claims, moduleId);\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n pending: { pending: login.pending, login: login.login },\n constraints: {\n sessionDigest: \"unique(session.digest)\",\n pendingDigest: \"unique(pending.digest)\",\n },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateId: allocate,\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record),\n decodedClaims = yield* decode(claims, saved.claims);\n\n return {\n ...saved,\n issuedAt: DateTime.makeUnsafe(yield* readInstant(row.issuedAt)),\n claims: decodedClaims,\n };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, pending, string };\n});\n"],"mappings":"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"}