@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.
Files changed (182) hide show
  1. package/README.md +27 -0
  2. package/dist/Adapter.d.mts +30 -0
  3. package/dist/Adapter.mjs +25 -0
  4. package/dist/index.d.mts +7 -0
  5. package/dist/index.mjs +7 -0
  6. package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
  7. package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
  8. package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
  9. package/dist/internal/configuration.d.mts +174 -0
  10. package/dist/internal/configuration.mjs +7 -0
  11. package/dist/internal/configuration.mjs.map +1 -0
  12. package/dist/internal/email-kernel.d.mts +275 -0
  13. package/dist/internal/email-kernel.mjs +487 -0
  14. package/dist/internal/email-kernel.mjs.map +1 -0
  15. package/dist/internal/mapping-error.d.mts +10 -0
  16. package/dist/internal/mapping-error.mjs +30 -0
  17. package/dist/internal/mapping-error.mjs.map +1 -0
  18. package/dist/internal/models/common.d.mts +12 -0
  19. package/dist/internal/models/email-model.d.mts +353 -0
  20. package/dist/internal/models/email-model.mjs +21 -0
  21. package/dist/internal/models/email-model.mjs.map +1 -0
  22. package/dist/internal/models/passkey-model.d.mts +266 -0
  23. package/dist/internal/models/passkey-model.mjs +34 -0
  24. package/dist/internal/models/passkey-model.mjs.map +1 -0
  25. package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
  26. package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
  27. package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
  28. package/dist/internal/models/passkey-write-model.d.mts +189 -0
  29. package/dist/internal/models/passkey-write-model.mjs +13 -0
  30. package/dist/internal/models/passkey-write-model.mjs.map +1 -0
  31. package/dist/internal/models/password-model.d.mts +328 -0
  32. package/dist/internal/models/password-model.mjs +23 -0
  33. package/dist/internal/models/password-model.mjs.map +1 -0
  34. package/dist/internal/models/proof-model.d.mts +285 -0
  35. package/dist/internal/models/proof-model.mjs +17 -0
  36. package/dist/internal/models/proof-model.mjs.map +1 -0
  37. package/dist/internal/models/session-model.d.mts +199 -0
  38. package/dist/internal/models/session-model.mjs +20 -0
  39. package/dist/internal/models/session-model.mjs.map +1 -0
  40. package/dist/internal/models/step-up-model.d.mts +66 -0
  41. package/dist/internal/models/step-up-model.mjs +9 -0
  42. package/dist/internal/models/step-up-model.mjs.map +1 -0
  43. package/dist/internal/oauth-app.d.mts +11 -0
  44. package/dist/internal/oauth-app.mjs +57 -0
  45. package/dist/internal/oauth-app.mjs.map +1 -0
  46. package/dist/internal/oauth-server.d.mts +10 -0
  47. package/dist/internal/oauth-server.mjs +66 -0
  48. package/dist/internal/oauth-server.mjs.map +1 -0
  49. package/dist/internal/passkey/admission.d.mts +6 -0
  50. package/dist/internal/passkey/admission.mjs +216 -0
  51. package/dist/internal/passkey/admission.mjs.map +1 -0
  52. package/dist/internal/passkey/credentials.d.mts +14 -0
  53. package/dist/internal/passkey/credentials.mjs +249 -0
  54. package/dist/internal/passkey/credentials.mjs.map +1 -0
  55. package/dist/internal/passkey/enrollment.mjs +138 -0
  56. package/dist/internal/passkey/enrollment.mjs.map +1 -0
  57. package/dist/internal/passkey/flow.d.mts +21 -0
  58. package/dist/internal/passkey/flow.mjs +452 -0
  59. package/dist/internal/passkey/flow.mjs.map +1 -0
  60. package/dist/internal/passkey/management.mjs +231 -0
  61. package/dist/internal/passkey/management.mjs.map +1 -0
  62. package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
  63. package/dist/internal/passkey/registration-ceremony.mjs +129 -0
  64. package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
  65. package/dist/internal/passkey/registration-custody.d.mts +2 -0
  66. package/dist/internal/passkey/registration-custody.mjs +52 -0
  67. package/dist/internal/passkey/registration-custody.mjs.map +1 -0
  68. package/dist/internal/passkey/registration-write.mjs +211 -0
  69. package/dist/internal/passkey/registration-write.mjs.map +1 -0
  70. package/dist/internal/passkey/state.d.mts +10 -0
  71. package/dist/internal/passkey/state.mjs +234 -0
  72. package/dist/internal/passkey/state.mjs.map +1 -0
  73. package/dist/internal/passkey/target.d.mts +19 -0
  74. package/dist/internal/passkey/target.mjs +190 -0
  75. package/dist/internal/passkey/target.mjs.map +1 -0
  76. package/dist/internal/passkey/write-state.d.mts +17 -0
  77. package/dist/internal/passkey/write-state.mjs +345 -0
  78. package/dist/internal/passkey/write-state.mjs.map +1 -0
  79. package/dist/internal/passkey/write-target.mjs +192 -0
  80. package/dist/internal/passkey/write-target.mjs.map +1 -0
  81. package/dist/internal/passkey-kernel.d.mts +5592 -0
  82. package/dist/internal/passkey-kernel.mjs +49 -0
  83. package/dist/internal/passkey-kernel.mjs.map +1 -0
  84. package/dist/internal/passkeys.d.mts +7 -0
  85. package/dist/internal/passkeys.mjs +368 -0
  86. package/dist/internal/passkeys.mjs.map +1 -0
  87. package/dist/internal/password-kernel.d.mts +290 -0
  88. package/dist/internal/password-kernel.mjs +704 -0
  89. package/dist/internal/password-kernel.mjs.map +1 -0
  90. package/dist/internal/persistence.d.mts +25 -0
  91. package/dist/internal/persistence.mjs +291 -0
  92. package/dist/internal/persistence.mjs.map +1 -0
  93. package/dist/internal/phone-kernel.d.mts +162 -0
  94. package/dist/internal/phone-kernel.mjs +434 -0
  95. package/dist/internal/phone-kernel.mjs.map +1 -0
  96. package/dist/internal/proof-kernel.d.mts +93 -0
  97. package/dist/internal/proof-kernel.mjs +619 -0
  98. package/dist/internal/proof-kernel.mjs.map +1 -0
  99. package/dist/internal/query-operations.d.mts +61 -0
  100. package/dist/internal/registration-contract.d.mts +16 -0
  101. package/dist/internal/registration.mjs +66 -0
  102. package/dist/internal/registration.mjs.map +1 -0
  103. package/dist/internal/session-kernel.d.mts +48 -0
  104. package/dist/internal/session-kernel.mjs +737 -0
  105. package/dist/internal/session-kernel.mjs.map +1 -0
  106. package/dist/internal/sql-persistence.d.mts +10 -0
  107. package/dist/internal/sql-persistence.mjs +37 -0
  108. package/dist/internal/sql-persistence.mjs.map +1 -0
  109. package/dist/internal/sql-query.mjs +207 -0
  110. package/dist/internal/sql-query.mjs.map +1 -0
  111. package/dist/internal/sql-table.d.mts +36 -0
  112. package/dist/internal/sql-table.mjs +81 -0
  113. package/dist/internal/sql-table.mjs.map +1 -0
  114. package/dist/internal/standalone.d.mts +9 -0
  115. package/dist/internal/standalone.mjs +13 -0
  116. package/dist/internal/standalone.mjs.map +1 -0
  117. package/dist/internal/step-up-state.d.mts +49 -0
  118. package/dist/internal/step-up-state.mjs +32 -0
  119. package/dist/internal/step-up-state.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +686 -0
  121. package/dist/internal/storage-mapping.mjs.map +1 -0
  122. package/dist/internal/storage-tables.d.mts +913 -0
  123. package/dist/internal/storage-tables.mjs +387 -0
  124. package/dist/internal/storage-tables.mjs.map +1 -0
  125. package/dist/internal/storage-validation.mjs +58 -0
  126. package/dist/internal/storage-validation.mjs.map +1 -0
  127. package/dist/internal/transaction-execution-kernel.d.mts +38 -0
  128. package/dist/internal/transaction-execution-kernel.mjs +116 -0
  129. package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
  130. package/dist/internal/transaction-kernel.d.mts +77 -0
  131. package/dist/internal/transaction-kernel.mjs +260 -0
  132. package/dist/internal/transaction-kernel.mjs.map +1 -0
  133. package/package.json +1 -0
  134. package/src/Adapter.ts +291 -0
  135. package/src/index.ts +6 -0
  136. package/src/internal/PasswordPreparedPostconditions.ts +30 -0
  137. package/src/internal/configuration.ts +282 -0
  138. package/src/internal/email-kernel.ts +1535 -0
  139. package/src/internal/mapping-error.ts +36 -0
  140. package/src/internal/models/common.ts +11 -0
  141. package/src/internal/models/email-model.ts +559 -0
  142. package/src/internal/models/passkey-model.ts +390 -0
  143. package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
  144. package/src/internal/models/passkey-write-model.ts +297 -0
  145. package/src/internal/models/password-model.ts +513 -0
  146. package/src/internal/models/proof-model.ts +424 -0
  147. package/src/internal/models/session-model.ts +375 -0
  148. package/src/internal/models/step-up-model.ts +135 -0
  149. package/src/internal/oauth-app.ts +82 -0
  150. package/src/internal/oauth-server.ts +89 -0
  151. package/src/internal/passkey/admission.ts +361 -0
  152. package/src/internal/passkey/credentials.ts +511 -0
  153. package/src/internal/passkey/enrollment.ts +326 -0
  154. package/src/internal/passkey/flow.ts +1062 -0
  155. package/src/internal/passkey/management.ts +421 -0
  156. package/src/internal/passkey/registration-ceremony.ts +283 -0
  157. package/src/internal/passkey/registration-custody.ts +91 -0
  158. package/src/internal/passkey/registration-write.ts +426 -0
  159. package/src/internal/passkey/state.ts +353 -0
  160. package/src/internal/passkey/target.ts +586 -0
  161. package/src/internal/passkey/write-state.ts +681 -0
  162. package/src/internal/passkey/write-target.ts +452 -0
  163. package/src/internal/passkey-kernel.ts +104 -0
  164. package/src/internal/passkeys.ts +606 -0
  165. package/src/internal/password-kernel.ts +2124 -0
  166. package/src/internal/persistence.ts +588 -0
  167. package/src/internal/phone-kernel.ts +846 -0
  168. package/src/internal/proof-kernel.ts +1954 -0
  169. package/src/internal/query-operations.ts +81 -0
  170. package/src/internal/registration-contract.ts +22 -0
  171. package/src/internal/registration.ts +161 -0
  172. package/src/internal/session-kernel.ts +2217 -0
  173. package/src/internal/sql-persistence.ts +45 -0
  174. package/src/internal/sql-query.ts +298 -0
  175. package/src/internal/sql-table.ts +104 -0
  176. package/src/internal/standalone.ts +30 -0
  177. package/src/internal/step-up-state.ts +139 -0
  178. package/src/internal/storage-mapping.ts +708 -0
  179. package/src/internal/storage-tables.ts +408 -0
  180. package/src/internal/storage-validation.ts +117 -0
  181. package/src/internal/transaction-execution-kernel.ts +313 -0
  182. package/src/internal/transaction-kernel.ts +593 -0
@@ -0,0 +1,36 @@
1
+ //#region src/internal/sql-table.d.ts
2
+ export type Dialect = "pg" | "sqlite";
3
+ export interface Compiler {
4
+ readonly dialect: Dialect;
5
+ readonly parameters: unknown[];
6
+ }
7
+ export declare class Fragment {
8
+ readonly render: (compiler: Compiler) => string;
9
+ readonly decode: (value: unknown) => unknown;
10
+ constructor(render: (compiler: Compiler) => string, decode?: (value: unknown) => unknown);
11
+ mapWith(decode: (value: unknown) => unknown): Fragment;
12
+ as(_name: string): Fragment;
13
+ getSQL(): Fragment;
14
+ }
15
+ export interface ColumnOptions {
16
+ readonly name: string;
17
+ readonly type: "text" | "integer" | "boolean";
18
+ readonly nullable?: boolean;
19
+ }
20
+ export declare class Column extends Fragment {
21
+ readonly tableName: string;
22
+ readonly options: ColumnOptions;
23
+ readonly schema?: string | undefined;
24
+ constructor(tableName: string, options: ColumnOptions, schema?: string | undefined);
25
+ mapToDriverValue(value: unknown): unknown;
26
+ }
27
+ /** Plain SQL identifiers and column representations, independent of an ORM. */
28
+ export declare class Table extends Fragment {
29
+ readonly name: string;
30
+ readonly unique: ReadonlyArray<ReadonlyArray<string>>;
31
+ readonly schema?: string | undefined;
32
+ readonly columns: Readonly<Record<string, Column>>;
33
+ constructor(name: string, columns: Readonly<Record<string, ColumnOptions>>, unique: ReadonlyArray<ReadonlyArray<string>>, schema?: string | undefined);
34
+ }
35
+ //#endregion
36
+ //# sourceMappingURL=sql-table.d.mts.map
@@ -0,0 +1,81 @@
1
+ import { PersistenceMappingError } from "./mapping-error.mjs";
2
+ import { Option, Schema, SchemaGetter } from "effect";
3
+ //#region src/internal/sql-table.ts
4
+ const SqlInteger = Schema.Union([
5
+ Schema.Int,
6
+ Schema.BigInt,
7
+ Schema.String.check(Schema.isPattern(/^-?\d+$/))
8
+ ]).pipe(Schema.decodeTo(Schema.Int, {
9
+ decode: SchemaGetter.transform(Number),
10
+ encode: SchemaGetter.passthrough()
11
+ }));
12
+ const decodeInteger = Schema.decodeUnknownOption(SqlInteger);
13
+ const identifier = (name) => "\"" + name.replaceAll("\"", "\"\"") + "\"";
14
+ var Fragment = class Fragment {
15
+ render;
16
+ decode;
17
+ constructor(render, decode = (value) => value) {
18
+ this.render = render;
19
+ this.decode = decode;
20
+ }
21
+ mapWith(decode) {
22
+ return new Fragment(this.render, decode);
23
+ }
24
+ as(_name) {
25
+ return this;
26
+ }
27
+ getSQL() {
28
+ return this;
29
+ }
30
+ };
31
+ var Column = class extends Fragment {
32
+ tableName;
33
+ options;
34
+ schema;
35
+ constructor(tableName, options, schema) {
36
+ super(() => `${schema === void 0 ? "" : identifier(schema) + "."}${identifier(tableName)}.${identifier(options.name)}`, (value) => {
37
+ if (value === null) return value;
38
+ if (options.type === "boolean") {
39
+ if (value === true || value === 1) return true;
40
+ if (value === false || value === 0) return false;
41
+ throw PersistenceMappingError.make({
42
+ operation: "decode",
43
+ cause: "Invalid SQL boolean"
44
+ });
45
+ }
46
+ if (options.type === "integer") {
47
+ const number = decodeInteger(value);
48
+ if (Option.isSome(number)) return number.value;
49
+ throw PersistenceMappingError.make({
50
+ operation: "decode",
51
+ cause: "SQL integer is outside the safe range"
52
+ });
53
+ }
54
+ return value;
55
+ });
56
+ this.tableName = tableName;
57
+ this.options = options;
58
+ this.schema = schema;
59
+ }
60
+ mapToDriverValue(value) {
61
+ return value;
62
+ }
63
+ };
64
+ /** Plain SQL identifiers and column representations, independent of an ORM. */
65
+ var Table = class extends Fragment {
66
+ name;
67
+ unique;
68
+ schema;
69
+ columns;
70
+ constructor(name, columns, unique, schema) {
71
+ super(() => (schema === void 0 ? "" : identifier(schema) + ".") + identifier(name));
72
+ this.name = name;
73
+ this.unique = unique;
74
+ this.schema = schema;
75
+ this.columns = Object.fromEntries(Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]));
76
+ }
77
+ };
78
+ //#endregion
79
+ export { Column, Fragment, Table, identifier };
80
+
81
+ //# sourceMappingURL=sql-table.mjs.map
@@ -0,0 +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 interface Compiler {\n readonly dialect: Dialect;\n readonly parameters: unknown[];\n}\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport class Fragment {\n constructor(\n readonly render: (compiler: Compiler) => string,\n readonly decode: (value: unknown) => unknown = (value) => value,\n ) {}\n\n mapWith(decode: (value: unknown) => unknown): Fragment {\n return new Fragment(this.render, decode);\n }\n\n as(_name: string): Fragment {\n return this;\n }\n\n getSQL(): Fragment {\n return this;\n }\n}\n\nexport interface ColumnOptions {\n readonly name: string;\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column extends Fragment {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {\n super(\n () =>\n `${schema === undefined ? \"\" : identifier(schema) + \".\"}${identifier(tableName)}.${identifier(options.name)}`,\n (value) => {\n if (value === null) return value;\n if (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 (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\n mapToDriverValue(value: unknown): unknown {\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table extends Fragment {\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 super(() => (schema === undefined ? \"\" : identifier(schema) + \".\") + identifier(name));\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;AAU3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;AAE/E,IAAa,WAAb,MAAa,SAAS;CAET;CACA;CAFX,YACE,QACA,UAAgD,UAAU,OAC1D;EAFS,KAAA,SAAA;EACA,KAAA,SAAA;CACR;CAEH,QAAQ,QAA+C;EACrD,OAAO,IAAI,SAAS,KAAK,QAAQ,MAAM;CACzC;CAEA,GAAG,OAAyB;EAC1B,OAAO;CACT;CAEA,SAAmB;EACjB,OAAO;CACT;AACF;AAQA,IAAa,SAAb,cAA4B,SAAS;CAExB;CACA;CACA;CAHX,YACE,WACA,SACA,QACA;EACA,YAEI,GAAG,WAAW,KAAA,IAAY,KAAK,WAAW,MAAM,IAAI,MAAM,WAAW,SAAS,EAAE,GAAG,WAAW,QAAQ,IAAI,MAC3G,UAAU;GACT,IAAI,UAAU,MAAM,OAAO;GAC3B,IAAI,QAAQ,SAAS,WAAW;IAC9B,IAAI,UAAU,QAAQ,UAAU,GAAG,OAAO;IAC1C,IAAI,UAAU,SAAS,UAAU,GAAG,OAAO;IAC3C,MAAM,wBAAwB,KAAK;KAAE,WAAW;KAAU,OAAO;IAAsB,CAAC;GAC1F;GACA,IAAI,QAAQ,SAAS,WAAW;IAC9B,MAAM,SAAS,cAAc,KAAK;IAElC,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,OAAO;IACzC,MAAM,wBAAwB,KAAK;KACjC,WAAW;KACX,OAAO;IACT,CAAC;GACH;GAEA,OAAO;EACT,CACF;EA1BS,KAAA,YAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;CAyBX;CAEA,iBAAiB,OAAyB;EACxC,OAAO;CACT;AACF;;AAGA,IAAa,QAAb,cAA2B,SAAS;CAIvB;CAEA;CACA;CANX;CAEA,YACE,MACA,SACA,QACA,QACA;EACA,aAAa,WAAW,KAAA,IAAY,KAAK,WAAW,MAAM,IAAI,OAAO,WAAW,IAAI,CAAC;EAL5E,KAAA,OAAA;EAEA,KAAA,SAAA;EACA,KAAA,SAAA;EAGT,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,9 @@
1
+ import { Context, Effect } from "effect";
2
+ import * as SqlClient from "effect/unstable/sql/SqlClient";
3
+ //#region src/internal/standalone.d.ts
4
+ /** Standalone operations must observe their own durable commit. */
5
+ export declare const requireStandalone: <Failure>(unavailable: () => Failure, client?: {
6
+ readonly transactionService: Context.Key<SqlClient.TransactionConnection, SqlClient.TransactionConnection.Service>;
7
+ }) => Effect.Effect<void, Failure>;
8
+ //#endregion
9
+ //# sourceMappingURL=standalone.d.mts.map
@@ -0,0 +1,13 @@
1
+ import { Effect } from "effect";
2
+ //#region src/internal/standalone.ts
3
+ /** Standalone operations must observe their own durable commit. */
4
+ const requireStandalone = (unavailable, client) => Effect.withFiber((fiber) => {
5
+ const services = fiber.context.mapUnsafe;
6
+ if (client !== void 0 && services.has(client.transactionService.key)) return Effect.fail(unavailable());
7
+ for (const key of services.keys()) if (key.startsWith("@effect/sql-libsql/LibsqlClient/LibsqlTransaction/")) return Effect.fail(unavailable());
8
+ return Effect.void;
9
+ });
10
+ //#endregion
11
+ export { requireStandalone };
12
+
13
+ //# sourceMappingURL=standalone.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standalone.mjs","names":[],"sources":["../../src/internal/standalone.ts"],"sourcesContent":["import { type Context, Effect } from \"effect\";\nimport type * as SqlClient from \"effect/unstable/sql/SqlClient\";\n\n/** Standalone operations must observe their own durable commit. */\nexport const requireStandalone = <Failure>(\n unavailable: () => Failure,\n client?: {\n readonly transactionService: Context.Key<\n SqlClient.TransactionConnection,\n SqlClient.TransactionConnection.Service\n >;\n },\n): Effect.Effect<void, Failure> =>\n Effect.withFiber((fiber) => {\n const services = fiber.context.mapUnsafe;\n\n if (client !== undefined && services.has(client.transactionService.key))\n return Effect.fail(unavailable());\n\n // rc.117 libSQL uses a private per-client marker that is not exposed through\n // SqlClient.transactionService. Conservatively reject every ambient libSQL\n // transaction, including another client's, before issuing standalone writes.\n // Recheck this upstream detail whenever the Effect driver is upgraded.\n for (const key of services.keys()) {\n if (key.startsWith(\"@effect/sql-libsql/LibsqlClient/LibsqlTransaction/\"))\n return Effect.fail(unavailable());\n }\n\n return Effect.void;\n });\n"],"mappings":";;;AAIA,MAAa,qBACX,aACA,WAOA,OAAO,WAAW,UAAU;CAC1B,MAAM,WAAW,MAAM,QAAQ;CAE/B,IAAI,WAAW,KAAA,KAAa,SAAS,IAAI,OAAO,mBAAmB,GAAG,GACpE,OAAO,OAAO,KAAK,YAAY,CAAC;CAMlC,KAAK,MAAM,OAAO,SAAS,KAAK,GAC9B,IAAI,IAAI,WAAW,oDAAoD,GACrE,OAAO,OAAO,KAAK,YAAY,CAAC;CAGpC,OAAO,OAAO;AAChB,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { DateTime, Effect, Schema } from "effect";
2
+ import { AuthenticationRevision, SessionStepUpCompletionPlan, SessionStepUpIntent, SessionStepUpInvalid, SessionUnavailable, StatefulSessionRecord } from "@yielded/auth/Sessions";
3
+ //#region src/internal/step-up-state.d.ts
4
+ export declare const encodeStepUpIntent: (intent: SessionStepUpIntent) => Effect.Effect<string, SessionUnavailable, never>;
5
+ export declare const decodeStepUpIntent: (snapshot: string) => Effect.Effect<{
6
+ readonly _tag: "SessionStepUpIntent";
7
+ readonly digest: string & import("effect/Brand").Brand<"effect-auth/TokenDigest">;
8
+ readonly version: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
9
+ readonly flowId: string & import("effect/Brand").Brand<"effect-auth/AuthenticationFlowId">;
10
+ readonly bindingDigest: string & import("effect/Brand").Brand<"effect-auth/TokenDigest">;
11
+ readonly revision: {
12
+ readonly subjectId: string & import("effect/Brand").Brand<"effect-auth/SubjectId">;
13
+ readonly securityRevision: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
14
+ readonly credentials: readonly {
15
+ readonly credentialId: string;
16
+ readonly revision: string & import("effect/Brand").Brand<"effect-auth/SecurityRevision">;
17
+ }[];
18
+ };
19
+ readonly sourceKind: "StateAssistedSigned" | "Stateful" | "StatelessSigned";
20
+ readonly sourceSessionId: string & import("effect/Brand").Brand<"effect-auth/SessionId">;
21
+ readonly sourceCredentialVersion: string & import("effect/Brand").Brand<"effect-auth/SessionCredentialVersion">;
22
+ readonly sourceAuthenticatedAt: DateTime.Utc;
23
+ readonly sourceExpiresAt: DateTime.Utc;
24
+ readonly sourceAbsoluteExpiresAt: DateTime.Utc;
25
+ readonly profileId: string & import("effect/Brand").Brand<"effect-auth/SessionStepUpProfileId">;
26
+ readonly profileGeneration: number;
27
+ readonly profileDigest: string & import("effect/Brand").Brand<"effect-auth/TokenDigest">;
28
+ readonly requirement: {
29
+ readonly maximumAgeMillis: number;
30
+ readonly alternatives: readonly [{
31
+ readonly userVerified: boolean;
32
+ readonly phishingResistant: boolean;
33
+ readonly factors: readonly ("inherence" | "knowledge" | "possession")[];
34
+ readonly minimumCredentials: number;
35
+ }, ...{
36
+ readonly userVerified: boolean;
37
+ readonly phishingResistant: boolean;
38
+ readonly factors: readonly ("inherence" | "knowledge" | "possession")[];
39
+ readonly minimumCredentials: number;
40
+ }[]];
41
+ };
42
+ readonly expiresAt: DateTime.Utc;
43
+ readonly attemptLimit: number;
44
+ }, SessionUnavailable, never>;
45
+ export declare const stepUpIntentLive: (intent: SessionStepUpIntent, kind: SessionStepUpIntent["sourceKind"], now: DateTime.Utc) => boolean;
46
+ /** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */
47
+ export declare const validateStepUpPlan: <Claims>(plan: SessionStepUpCompletionPlan<Claims>) => Effect.Effect<undefined, SessionStepUpInvalid, never>;
48
+ //#endregion
49
+ //# sourceMappingURL=step-up-state.d.mts.map
@@ -0,0 +1,32 @@
1
+ import { DateTime, Effect, Schema } from "effect";
2
+ import { AuthenticationEvidence, SessionMetadata, SessionStepUpIntent, SessionStepUpInvalid, SessionStepUpRequirement, SessionUnavailable } from "@yielded/auth/Sessions";
3
+ //#region src/internal/step-up-state.ts
4
+ const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);
5
+ const BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));
6
+ const encodeStepUpIntent = (intent) => Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)), Effect.mapError(() => SessionUnavailable.make({})));
7
+ const decodeStepUpIntent = (snapshot) => Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)), Effect.mapError(() => SessionUnavailable.make({})));
8
+ const sameStepUpRevision = (left, right) => left.subjectId === right.subjectId && left.securityRevision === right.securityRevision && left.credentials.length === right.credentials.length && new Set(left.credentials.map((c) => c.credentialId)).size === left.credentials.length && left.credentials.every((item) => right.credentials.some((other) => item.credentialId === other.credentialId && item.revision === other.revision));
9
+ const stepUpIntentLive = (intent, kind, now) => {
10
+ 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
+ const evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));
14
+ const requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));
15
+ const metadataEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));
16
+ /** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */
17
+ const validateStepUpPlan = Effect.fn("DrizzleStepUp.validatePlan")(function* (plan) {
18
+ const { intent, source, replacement, evidence } = plan;
19
+ const original = source.inspection;
20
+ 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 || !sameStepUpRevision(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.sourceAuthenticatedAt) !== DateTime.toEpochMillis(replacement.inspection.session.assurance.authenticatedAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) || evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence)) return yield* SessionStepUpInvalid.make({});
22
+ if (replacement._tag === "Stateful" && (source.guard._tag !== "Stateful" || source.guard.digest !== replacement.expectedDigest || source.guard.rowVersion !== replacement.expectedRowVersion || replacement.inspection.session.sessionId !== intent.sourceSessionId)) return yield* SessionStepUpInvalid.make({});
23
+ if (replacement._tag === "StateAssistedSigned" && (replacement.tombstoneSessionId !== intent.sourceSessionId || DateTime.toEpochMillis(replacement.tombstoneUntil) !== DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))) return yield* SessionStepUpInvalid.make({});
24
+ if (replacement._tag !== "Stateful" && replacement.inspection.session.sessionId === intent.sourceSessionId) return yield* SessionStepUpInvalid.make({});
25
+ });
26
+ /** Compare a consumer-decoded encoded update/readback with the intended rotation.
27
+ * Claims are consumer-owned; their lossless round trip remains the codec contract. */
28
+ const stepUpRotationMatches = (plan, record) => plan.replacement._tag === "Stateful" && record.digest === plan.replacement.nextDigest && record.credentialVersion === plan.replacement.inspection.credentialVersion && metadataEncoding(record) === metadataEncoding(plan.replacement.inspection.session) && evidenceEncoding(record.provenance.evidence) === evidenceEncoding(plan.evidence);
29
+ //#endregion
30
+ export { decodeStepUpIntent, encodeStepUpIntent, sameStepUpRevision, stepUpIntentCodec, stepUpIntentLive, stepUpRotationMatches, validateStepUpPlan };
31
+
32
+ //# sourceMappingURL=step-up-state.mjs.map
@@ -0,0 +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 type AuthenticationRevision,\n SessionMetadata,\n SessionStepUpIntent,\n SessionStepUpRequirement,\n type SessionStepUpCompletionPlan,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\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 sameStepUpRevision = (left: AuthenticationRevision, right: AuthenticationRevision) =>\n left.subjectId === right.subjectId &&\n left.securityRevision === right.securityRevision &&\n left.credentials.length === right.credentials.length &&\n new Set(left.credentials.map((c) => c.credentialId)).size === left.credentials.length &&\n left.credentials.every((item) =>\n right.credentials.some(\n (other) => item.credentialId === other.credentialId && item.revision === other.revision,\n ),\n );\n\nexport const stepUpIntentLive = (\n intent: SessionStepUpIntent,\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 !sameStepUpRevision(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.sourceAuthenticatedAt) !==\n DateTime.toEpochMillis(replacement.inspection.session.assurance.authenticatedAt) ||\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 source.guard.rowVersion !== replacement.expectedRowVersion ||\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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"}