@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24

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 +11 -0
  2. package/dist/Adapter.d.mts +13 -5
  3. package/dist/Adapter.mjs +9 -2
  4. package/dist/OAuthPersistence.d.mts +87 -0
  5. package/dist/OAuthPersistence.mjs +125 -0
  6. package/dist/OAuthPersistence.mjs.map +1 -0
  7. package/dist/_virtual/_rolldown/runtime.mjs +13 -0
  8. package/dist/index.d.mts +3 -1
  9. package/dist/index.mjs +3 -1
  10. package/dist/internal/authority-snapshot.mjs +53 -0
  11. package/dist/internal/authority-snapshot.mjs.map +1 -0
  12. package/dist/internal/configuration.d.mts +1 -1
  13. package/dist/internal/configuration.mjs.map +1 -1
  14. package/dist/internal/models/oauth-connected-model.d.mts +294 -0
  15. package/dist/internal/models/oauth-connected-model.mjs +22 -0
  16. package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
  17. package/dist/internal/models/oauth-model.d.mts +467 -0
  18. package/dist/internal/models/oauth-model.mjs +49 -0
  19. package/dist/internal/models/oauth-model.mjs.map +1 -0
  20. package/dist/internal/models/password-model.d.mts +4 -97
  21. package/dist/internal/models/password-model.mjs +0 -3
  22. package/dist/internal/models/password-model.mjs.map +1 -1
  23. package/dist/internal/native-sql-table.d.mts +34 -0
  24. package/dist/internal/native-sql-table.mjs +111 -0
  25. package/dist/internal/native-sql-table.mjs.map +1 -0
  26. package/dist/internal/oauth/accounts.d.mts +8 -0
  27. package/dist/internal/oauth/accounts.mjs +379 -0
  28. package/dist/internal/oauth/accounts.mjs.map +1 -0
  29. package/dist/internal/oauth/connected-access.d.mts +9 -0
  30. package/dist/internal/oauth/connected-access.mjs +229 -0
  31. package/dist/internal/oauth/connected-access.mjs.map +1 -0
  32. package/dist/internal/oauth/connected-collection.d.mts +7 -0
  33. package/dist/internal/oauth/connected-collection.mjs +214 -0
  34. package/dist/internal/oauth/connected-collection.mjs.map +1 -0
  35. package/dist/internal/oauth/connected-custody.d.mts +6 -0
  36. package/dist/internal/oauth/connected-custody.mjs +165 -0
  37. package/dist/internal/oauth/connected-custody.mjs.map +1 -0
  38. package/dist/internal/oauth/connected-flow.d.mts +7 -0
  39. package/dist/internal/oauth/connected-flow.mjs +324 -0
  40. package/dist/internal/oauth/connected-flow.mjs.map +1 -0
  41. package/dist/internal/oauth/connected-input.mjs +122 -0
  42. package/dist/internal/oauth/connected-input.mjs.map +1 -0
  43. package/dist/internal/oauth/connected-keys.d.mts +2 -0
  44. package/dist/internal/oauth/connected-keys.mjs +22 -0
  45. package/dist/internal/oauth/connected-keys.mjs.map +1 -0
  46. package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
  47. package/dist/internal/oauth/connected-maintenance.mjs +226 -0
  48. package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
  49. package/dist/internal/oauth/connected-management.d.mts +9 -0
  50. package/dist/internal/oauth/connected-management.mjs +243 -0
  51. package/dist/internal/oauth/connected-management.mjs.map +1 -0
  52. package/dist/internal/oauth/connected-reference.mjs +63 -0
  53. package/dist/internal/oauth/connected-reference.mjs.map +1 -0
  54. package/dist/internal/oauth/connected-settlement.d.mts +8 -0
  55. package/dist/internal/oauth/connected-settlement.mjs +141 -0
  56. package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
  57. package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
  58. package/dist/internal/oauth/connected-sign-in.mjs +281 -0
  59. package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
  60. package/dist/internal/oauth/connected-state.d.mts +14 -0
  61. package/dist/internal/oauth/connected-state.mjs +469 -0
  62. package/dist/internal/oauth/connected-state.mjs.map +1 -0
  63. package/dist/internal/oauth/connected-target.d.mts +21 -0
  64. package/dist/internal/oauth/connected-target.mjs +117 -0
  65. package/dist/internal/oauth/connected-target.mjs.map +1 -0
  66. package/dist/internal/oauth/flow.d.mts +5 -0
  67. package/dist/internal/oauth/flow.mjs +279 -0
  68. package/dist/internal/oauth/flow.mjs.map +1 -0
  69. package/dist/internal/oauth/input.mjs +127 -0
  70. package/dist/internal/oauth/input.mjs.map +1 -0
  71. package/dist/internal/oauth/owner.d.mts +10 -0
  72. package/dist/internal/oauth/owner.mjs +10 -0
  73. package/dist/internal/oauth/owner.mjs.map +1 -0
  74. package/dist/internal/oauth/query-compiler.d.mts +7 -0
  75. package/dist/internal/oauth/query-compiler.mjs +9 -0
  76. package/dist/internal/oauth/query-compiler.mjs.map +1 -0
  77. package/dist/internal/oauth/registration.d.mts +7 -0
  78. package/dist/internal/oauth/registration.mjs +393 -0
  79. package/dist/internal/oauth/registration.mjs.map +1 -0
  80. package/dist/internal/oauth/state.mjs +107 -0
  81. package/dist/internal/oauth/state.mjs.map +1 -0
  82. package/dist/internal/oauth/target.d.mts +31 -0
  83. package/dist/internal/oauth/target.mjs +273 -0
  84. package/dist/internal/oauth/target.mjs.map +1 -0
  85. package/dist/internal/oauth-kernel.d.mts +1423 -0
  86. package/dist/internal/oauth-kernel.mjs +187 -0
  87. package/dist/internal/oauth-kernel.mjs.map +1 -0
  88. package/dist/internal/oauth-proxy.d.mts +31 -0
  89. package/dist/internal/oauth-proxy.mjs +129 -0
  90. package/dist/internal/oauth-proxy.mjs.map +1 -0
  91. package/dist/internal/passkey/target.d.mts +1 -1
  92. package/dist/internal/passkey-kernel.d.mts +5 -5
  93. package/dist/internal/passkeys.mjs +1 -1
  94. package/dist/internal/password-credentials.d.mts +11 -0
  95. package/dist/internal/password-credentials.mjs +121 -0
  96. package/dist/internal/password-credentials.mjs.map +1 -0
  97. package/dist/internal/password-kernel.d.mts +15 -28
  98. package/dist/internal/password-kernel.mjs +35 -236
  99. package/dist/internal/password-kernel.mjs.map +1 -1
  100. package/dist/internal/persistence.d.mts +2 -0
  101. package/dist/internal/persistence.mjs +5 -5
  102. package/dist/internal/persistence.mjs.map +1 -1
  103. package/dist/internal/query-operations.d.mts +2 -0
  104. package/dist/internal/session-kernel.d.mts +5 -3
  105. package/dist/internal/session-kernel.mjs +95 -29
  106. package/dist/internal/session-kernel.mjs.map +1 -1
  107. package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
  108. package/dist/internal/sql-oauth-connected-model.mjs +7 -0
  109. package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
  110. package/dist/internal/sql-oauth-model.d.mts +60 -0
  111. package/dist/internal/sql-oauth-model.mjs +10 -0
  112. package/dist/internal/sql-oauth-model.mjs.map +1 -0
  113. package/dist/internal/sql-persistence.mjs +3 -1
  114. package/dist/internal/sql-persistence.mjs.map +1 -1
  115. package/dist/internal/sql-query.d.mts +12 -0
  116. package/dist/internal/sql-query.mjs +15 -18
  117. package/dist/internal/sql-query.mjs.map +1 -1
  118. package/dist/internal/sql-storage-validation.mjs +54 -0
  119. package/dist/internal/sql-storage-validation.mjs.map +1 -0
  120. package/dist/internal/storage-mapping.mjs +2 -67
  121. package/dist/internal/storage-mapping.mjs.map +1 -1
  122. package/dist/internal/storage-plans.d.mts +7 -0
  123. package/dist/internal/storage-plans.mjs +189 -0
  124. package/dist/internal/storage-plans.mjs.map +1 -0
  125. package/dist/internal/storage-tables.d.mts +0 -99
  126. package/dist/internal/storage-tables.mjs +0 -43
  127. package/dist/internal/storage-tables.mjs.map +1 -1
  128. package/dist/internal/storage-validation.d.mts +6 -1
  129. package/dist/internal/storage-validation.mjs +42 -1
  130. package/dist/internal/storage-validation.mjs.map +1 -1
  131. package/dist/internal/transaction-execution-kernel.d.mts +1 -1
  132. package/dist/internal/transaction-kernel.d.mts +1 -1
  133. package/dist/internal/transaction-kernel.mjs +3 -2
  134. package/dist/internal/transaction-kernel.mjs.map +1 -1
  135. package/package.json +1 -1
  136. package/src/Adapter.ts +97 -8
  137. package/src/OAuthPersistence.ts +427 -0
  138. package/src/index.ts +3 -0
  139. package/src/internal/authority-snapshot.ts +91 -0
  140. package/src/internal/configuration.ts +1 -8
  141. package/src/internal/models/oauth-connected-model.ts +396 -0
  142. package/src/internal/models/oauth-model.ts +708 -0
  143. package/src/internal/models/password-model.ts +0 -120
  144. package/src/internal/native-sql-table.ts +178 -0
  145. package/src/internal/oauth/accounts.ts +838 -0
  146. package/src/internal/oauth/connected-access.ts +388 -0
  147. package/src/internal/oauth/connected-collection.ts +345 -0
  148. package/src/internal/oauth/connected-custody.ts +308 -0
  149. package/src/internal/oauth/connected-flow.ts +562 -0
  150. package/src/internal/oauth/connected-input.ts +147 -0
  151. package/src/internal/oauth/connected-keys.ts +31 -0
  152. package/src/internal/oauth/connected-maintenance.ts +379 -0
  153. package/src/internal/oauth/connected-management.ts +391 -0
  154. package/src/internal/oauth/connected-reference.ts +135 -0
  155. package/src/internal/oauth/connected-settlement.ts +278 -0
  156. package/src/internal/oauth/connected-sign-in.ts +472 -0
  157. package/src/internal/oauth/connected-state.ts +924 -0
  158. package/src/internal/oauth/connected-target.ts +307 -0
  159. package/src/internal/oauth/flow.ts +505 -0
  160. package/src/internal/oauth/input.ts +201 -0
  161. package/src/internal/oauth/owner.ts +22 -0
  162. package/src/internal/oauth/query-compiler.ts +15 -0
  163. package/src/internal/oauth/registration.ts +676 -0
  164. package/src/internal/oauth/state.ts +218 -0
  165. package/src/internal/oauth/target.ts +803 -0
  166. package/src/internal/oauth-kernel.ts +294 -0
  167. package/src/internal/oauth-proxy.ts +176 -0
  168. package/src/internal/password-credentials.ts +245 -0
  169. package/src/internal/password-kernel.ts +98 -712
  170. package/src/internal/persistence.ts +5 -10
  171. package/src/internal/query-operations.ts +2 -0
  172. package/src/internal/session-kernel.ts +231 -81
  173. package/src/internal/sql-oauth-connected-model.ts +145 -0
  174. package/src/internal/sql-oauth-model.ts +263 -0
  175. package/src/internal/sql-persistence.ts +2 -0
  176. package/src/internal/sql-query.ts +16 -13
  177. package/src/internal/sql-storage-validation.ts +64 -0
  178. package/src/internal/storage-mapping.ts +0 -66
  179. package/src/internal/storage-plans.ts +218 -0
  180. package/src/internal/storage-tables.ts +0 -35
  181. package/src/internal/storage-validation.ts +58 -0
  182. package/src/internal/transaction-kernel.ts +6 -2
@@ -0,0 +1,294 @@
1
+ import { Effect, Layer } from "effect";
2
+ import type { SqlClient } from "effect/sql/SqlClient";
3
+
4
+ import { OAuthAccounts } from "./oauth/accounts";
5
+ import { OAuthConnectedAccess } from "./oauth/connected-access";
6
+ import { OAuthConnectedCollection } from "./oauth/connected-collection";
7
+ import { OAuthConnectedCustody } from "./oauth/connected-custody";
8
+ import { OAuthConnectedFlow } from "./oauth/connected-flow";
9
+ import { OAuthConnectedMaintenance } from "./oauth/connected-maintenance";
10
+ import { OAuthConnectedManagement } from "./oauth/connected-management";
11
+ import { makeOAuthConnectedReferenceKernel } from "./oauth/connected-reference";
12
+ import { OAuthConnectedSettlement } from "./oauth/connected-settlement";
13
+ import { OAuthConnectedSignIn } from "./oauth/connected-sign-in";
14
+ import { OAuthConnectedState } from "./oauth/connected-state";
15
+ import {
16
+ OAuthConnectedTarget,
17
+ oauthConnectedPersistenceLayer,
18
+ oauthConnectedRevocationsLayer,
19
+ } from "./oauth/connected-target";
20
+ import { OAuthFlow } from "./oauth/flow";
21
+ import { OAuthTransactionExecution } from "./oauth/owner";
22
+ import { OAuthQueryCompiler } from "./oauth/query-compiler";
23
+ import { OAuthRegistration } from "./oauth/registration";
24
+ import { captureOAuthMapping, unavailable } from "./oauth/state";
25
+ import {
26
+ OAuthTarget,
27
+ oauthAccountsPersistenceLayer,
28
+ oauthSignInPersistenceLayer,
29
+ oauthRegistrationIntentsLayer,
30
+ oauthRegistrationAuthorityLayer,
31
+ } from "./oauth/target";
32
+ import type { QueryOperations } from "./query-operations";
33
+ import { requireStandalone } from "./standalone";
34
+ import { makeTransactionExecutionKernel } from "./transaction-execution-kernel";
35
+ import { makeTransactionKernel } from "./transaction-kernel";
36
+
37
+ export type OAuthKernel = ReturnType<typeof makeOAuthKernel>;
38
+
39
+ /** Adapter composition root. Shared operation Layers acquire their dependencies;
40
+ * database and transaction services are still resolved by each operation. */
41
+ export const makeOAuthKernel = (
42
+ operations: QueryOperations,
43
+ dialect: (table: object) => "pg" | "sqlite" | "mysql",
44
+ ) => {
45
+ const transactions = makeTransactionKernel(operations);
46
+ const connectedReference = makeOAuthConnectedReferenceKernel(operations, dialect);
47
+
48
+ const compilerLive = Layer.succeed(OAuthQueryCompiler, {
49
+ ...operations,
50
+ ...transactions.makeTransactionRows(unavailable),
51
+ both: transactions.both,
52
+ connectedReferenceCondition: connectedReference.connectedReferenceCondition,
53
+ });
54
+
55
+ const executionLive = Layer.succeed(
56
+ OAuthTransactionExecution,
57
+ makeTransactionExecutionKernel(transactions),
58
+ );
59
+
60
+ const flowLive = OAuthFlow.layer.pipe(Layer.provide([compilerLive]));
61
+ const registrationLive = OAuthRegistration.layer.pipe(Layer.provide([compilerLive, flowLive]));
62
+
63
+ const accountsLive = OAuthAccounts.layer.pipe(
64
+ Layer.provide([compilerLive, flowLive, registrationLive]),
65
+ );
66
+
67
+ const targetLive = OAuthTarget.layer.pipe(
68
+ Layer.provide([compilerLive, accountsLive, flowLive, registrationLive, executionLive]),
69
+ );
70
+
71
+ const connectedStateLive = OAuthConnectedState.layer.pipe(
72
+ Layer.provide([compilerLive, flowLive]),
73
+ );
74
+
75
+ const connectedFlowLive = OAuthConnectedFlow.layer.pipe(
76
+ Layer.provide([compilerLive, connectedStateLive, registrationLive]),
77
+ );
78
+
79
+ const connectedCustodyLive = OAuthConnectedCustody.layer.pipe(
80
+ Layer.provide([compilerLive, connectedStateLive]),
81
+ );
82
+
83
+ const connectedCollectionLive = OAuthConnectedCollection.layer.pipe(
84
+ Layer.provide([compilerLive, connectedCustodyLive, connectedFlowLive, connectedStateLive]),
85
+ );
86
+
87
+ const connectedSettlementLive = OAuthConnectedSettlement.layer.pipe(
88
+ Layer.provide([compilerLive, connectedCustodyLive, connectedFlowLive, connectedStateLive]),
89
+ );
90
+
91
+ const connectedAccessLive = OAuthConnectedAccess.layer.pipe(
92
+ Layer.provide([
93
+ compilerLive,
94
+ connectedCustodyLive,
95
+ connectedFlowLive,
96
+ connectedSettlementLive,
97
+ connectedStateLive,
98
+ ]),
99
+ );
100
+
101
+ const connectedManagementLive = OAuthConnectedManagement.layer.pipe(
102
+ Layer.provide([
103
+ compilerLive,
104
+ connectedAccessLive,
105
+ connectedCustodyLive,
106
+ connectedFlowLive,
107
+ connectedStateLive,
108
+ ]),
109
+ );
110
+
111
+ const connectedMaintenanceLive = OAuthConnectedMaintenance.layer.pipe(
112
+ Layer.provide([
113
+ compilerLive,
114
+ connectedCollectionLive,
115
+ connectedCustodyLive,
116
+ connectedFlowLive,
117
+ connectedStateLive,
118
+ ]),
119
+ );
120
+
121
+ const connectedSignInLive = OAuthConnectedSignIn.layer.pipe(
122
+ Layer.provide([
123
+ compilerLive,
124
+ connectedCustodyLive,
125
+ connectedFlowLive,
126
+ connectedSettlementLive,
127
+ connectedStateLive,
128
+ flowLive,
129
+ ]),
130
+ );
131
+
132
+ const connectedTargetLive = OAuthConnectedTarget.layer.pipe(
133
+ Layer.provide([
134
+ connectedAccessLive,
135
+ connectedFlowLive,
136
+ connectedMaintenanceLive,
137
+ connectedManagementLive,
138
+ connectedSettlementLive,
139
+ connectedSignInLive,
140
+ connectedStateLive,
141
+ targetLive,
142
+ ]),
143
+ );
144
+
145
+ // Keep shared operation Layers local to this adapter compiler, even inside another
146
+ // coordinator. Preserve constructor-time snapshots before deferred acquisition.
147
+ const makeTargetOAuthSignInServices: OAuthTarget["Service"]["makeTargetOAuthSignInServices"] = (
148
+ mapping,
149
+ configuration,
150
+ ) => {
151
+ const captured = captureOAuthMapping(mapping);
152
+
153
+ return Effect.flatMap(OAuthTarget, (service) =>
154
+ service.makeTargetOAuthSignInServices(captured, configuration),
155
+ ).pipe(Effect.provide(targetLive, { local: true }));
156
+ };
157
+
158
+ const makeTargetOAuthRegistrationIntentServices: OAuthTarget["Service"]["makeTargetOAuthRegistrationIntentServices"] =
159
+ (mapping, configuration) => {
160
+ const captured = captureOAuthMapping(mapping);
161
+
162
+ return Effect.flatMap(OAuthTarget, (service) =>
163
+ service.makeTargetOAuthRegistrationIntentServices(captured, configuration),
164
+ ).pipe(Effect.provide(targetLive, { local: true }));
165
+ };
166
+
167
+ const makeTargetOAuthRegistrationServices: OAuthTarget["Service"]["makeTargetOAuthRegistrationServices"] =
168
+ (mapping, configuration) => {
169
+ const captured = captureOAuthMapping(mapping);
170
+
171
+ return Effect.flatMap(OAuthTarget, (service) =>
172
+ service.makeTargetOAuthRegistrationServices(captured, configuration),
173
+ ).pipe(Effect.provide(targetLive, { local: true }));
174
+ };
175
+
176
+ const makeTargetOAuthAccountsServices: OAuthTarget["Service"]["makeTargetOAuthAccountsServices"] =
177
+ (mapping, configuration) => {
178
+ const captured = captureOAuthMapping(mapping);
179
+
180
+ return Effect.flatMap(OAuthTarget, (service) =>
181
+ service.makeTargetOAuthAccountsServices(captured, configuration),
182
+ ).pipe(Effect.provide(targetLive, { local: true }));
183
+ };
184
+
185
+ const coordinateTargetOAuthRegistration: OAuthTarget["Service"]["coordinateTargetOAuthRegistration"] =
186
+ (database, mapping, configuration, owner) => {
187
+ const captured = captureOAuthMapping(mapping);
188
+
189
+ return Effect.flatMap(OAuthTarget, (service) =>
190
+ service.coordinateTargetOAuthRegistration(database, captured, configuration, owner),
191
+ ).pipe(Effect.provide(targetLive, { local: true }));
192
+ };
193
+
194
+ const coordinateTargetOAuthSignIn: OAuthTarget["Service"]["coordinateTargetOAuthSignIn"] = (
195
+ database,
196
+ mapping,
197
+ configuration,
198
+ owner,
199
+ ) => {
200
+ const captured = captureOAuthMapping(mapping);
201
+
202
+ return Effect.flatMap(OAuthTarget, (service) =>
203
+ service.coordinateTargetOAuthSignIn(database, captured, configuration, owner),
204
+ ).pipe(Effect.provide(targetLive, { local: true }));
205
+ };
206
+
207
+ const coordinateTargetOAuthRegistrationIntents: OAuthTarget["Service"]["coordinateTargetOAuthRegistrationIntents"] =
208
+ (database, mapping, configuration, owner) => {
209
+ const captured = captureOAuthMapping(mapping);
210
+
211
+ return Effect.flatMap(OAuthTarget, (service) =>
212
+ service.coordinateTargetOAuthRegistrationIntents(database, captured, configuration, owner),
213
+ ).pipe(Effect.provide(targetLive, { local: true }));
214
+ };
215
+
216
+ const coordinateTargetOAuthAccounts: OAuthTarget["Service"]["coordinateTargetOAuthAccounts"] = (
217
+ database,
218
+ mapping,
219
+ configuration,
220
+ owner,
221
+ ) => {
222
+ const captured = captureOAuthMapping(mapping);
223
+
224
+ return Effect.flatMap(OAuthTarget, (service) =>
225
+ service.coordinateTargetOAuthAccounts(database, captured, configuration, owner),
226
+ ).pipe(Effect.provide(targetLive, { local: true }));
227
+ };
228
+
229
+ const makeTargetOAuthConnectedServices: OAuthConnectedTarget["Service"]["makeTargetOAuthConnectedServices"] =
230
+ (mapping, configuration) => {
231
+ const captured = captureOAuthMapping(mapping);
232
+
233
+ return Effect.flatMap(OAuthConnectedTarget, (service) =>
234
+ service.makeTargetOAuthConnectedServices(captured, configuration),
235
+ ).pipe(Effect.provide(connectedTargetLive, { local: true }));
236
+ };
237
+
238
+ const makeTargetOAuthConnectedRevocationServices: OAuthConnectedTarget["Service"]["makeTargetOAuthConnectedRevocationServices"] =
239
+ (mapping, configuration) => {
240
+ const captured = captureOAuthMapping(mapping);
241
+
242
+ return Effect.flatMap(OAuthConnectedTarget, (service) =>
243
+ service.makeTargetOAuthConnectedRevocationServices(captured, configuration),
244
+ ).pipe(Effect.provide(connectedTargetLive, { local: true }));
245
+ };
246
+
247
+ const coordinateTargetOAuthConnected: OAuthConnectedTarget["Service"]["coordinateTargetOAuthConnected"] =
248
+ (database, mapping, configuration, owner) => {
249
+ const captured = captureOAuthMapping(mapping);
250
+
251
+ return Effect.flatMap(OAuthConnectedTarget, (service) =>
252
+ service.coordinateTargetOAuthConnected(database, captured, configuration, owner),
253
+ ).pipe(Effect.provide(connectedTargetLive, { local: true }));
254
+ };
255
+
256
+ const coordinateTargetOAuthConnectedRevocations: OAuthConnectedTarget["Service"]["coordinateTargetOAuthConnectedRevocations"] =
257
+ (database, mapping, configuration, owner) => {
258
+ const captured = captureOAuthMapping(mapping);
259
+
260
+ return Effect.flatMap(OAuthConnectedTarget, (service) =>
261
+ service.coordinateTargetOAuthConnectedRevocations(database, captured, configuration, owner),
262
+ ).pipe(Effect.provide(connectedTargetLive, { local: true }));
263
+ };
264
+
265
+ const sqlClientOAuthStandaloneGuard = (service: SqlClient["transactionService"] | undefined) =>
266
+ requireStandalone(unavailable, service);
267
+
268
+ return {
269
+ target: {
270
+ makeTargetOAuthSignInServices,
271
+ makeTargetOAuthRegistrationIntentServices,
272
+ makeTargetOAuthRegistrationServices,
273
+ makeTargetOAuthAccountsServices,
274
+ coordinateTargetOAuthRegistration,
275
+ coordinateTargetOAuthSignIn,
276
+ coordinateTargetOAuthRegistrationIntents,
277
+ coordinateTargetOAuthAccounts,
278
+ sqlClientOAuthStandaloneGuard,
279
+ oauthAccountsPersistenceLayer,
280
+ oauthSignInPersistenceLayer,
281
+ oauthRegistrationIntentsLayer,
282
+ oauthRegistrationAuthorityLayer,
283
+ },
284
+ connectedTarget: {
285
+ makeTargetOAuthConnectedServices,
286
+ makeTargetOAuthConnectedRevocationServices,
287
+ coordinateTargetOAuthConnected,
288
+ coordinateTargetOAuthConnectedRevocations,
289
+ oauthConnectedPersistenceLayer,
290
+ oauthConnectedRevocationsLayer,
291
+ },
292
+ connectedReference,
293
+ };
294
+ };
@@ -0,0 +1,176 @@
1
+ import { FlowContext, Persistence, Record, Unavailable } from "@yielded/auth/OAuthProxy";
2
+ import { Effect, Layer, Schema } from "effect";
3
+ import { SqlClient } from "effect/sql";
4
+
5
+ import { identifier } from "./sql-table";
6
+ import { requireStandalone } from "./standalone";
7
+
8
+ export const oauthProxyColumns = {
9
+ namespace: "namespace",
10
+ flowId: "flow_id",
11
+ version: "version",
12
+ stage: "stage",
13
+ context: "context",
14
+ expiresAtMillis: "expires_at_millis",
15
+ handoffExpiresAtMillis: "handoff_expires_at_millis",
16
+ payload: "payload",
17
+ } as const;
18
+
19
+ /** Plain text and integer-millisecond storage; adapters supply physical names. */
20
+ export interface OAuthProxySqlTable {
21
+ readonly name: string;
22
+ readonly schema?: string;
23
+ readonly columns: { readonly [K in keyof typeof oauthProxyColumns]: string };
24
+ }
25
+
26
+ const codec = Schema.fromJsonString(Record);
27
+ const contextCodec = Schema.fromJsonString(FlowContext);
28
+
29
+ const Row = Schema.Struct({
30
+ payload: Schema.String,
31
+ version: Schema.String,
32
+ stage: Schema.String,
33
+ context: Schema.String,
34
+ expiry: Schema.String,
35
+ deadline: Schema.NullOr(Schema.String),
36
+ });
37
+
38
+ /** SQLite/D1 and PostgreSQL; apply once through application migrations.
39
+ * Retain terminal records until expires_at_millis, then expired rows may be deleted.
40
+ * Context and stage columns guard each standalone transition without a prior read.
41
+ */
42
+ const migration = `CREATE TABLE yielded_oauth_proxy (
43
+ namespace TEXT NOT NULL,
44
+ flow_id TEXT NOT NULL,
45
+ version TEXT NOT NULL,
46
+ stage TEXT NOT NULL CHECK (stage IN ('Pending', 'Exchanging', 'Ready', 'Consumed')),
47
+ context TEXT NOT NULL,
48
+ expires_at_millis BIGINT NOT NULL,
49
+ handoff_expires_at_millis BIGINT,
50
+ payload TEXT NOT NULL,
51
+ PRIMARY KEY (namespace, flow_id),
52
+ CHECK ((stage = 'Ready' AND handoff_expires_at_millis IS NOT NULL)
53
+ OR (stage <> 'Ready' AND handoff_expires_at_millis IS NULL))
54
+ )`;
55
+
56
+ export const makeOAuthProxyPersistence = Effect.fnUntraced(function* (table: OAuthProxySqlTable) {
57
+ const sql = (yield* SqlClient.SqlClient).withoutTransforms();
58
+
59
+ if (!sql.onDialectOrElse({ sqlite: () => true, pg: () => true, orElse: () => false }))
60
+ return yield* Unavailable.make({});
61
+
62
+ const names = Object.values(table.columns);
63
+
64
+ if (
65
+ [table.name, table.schema ?? "main", ...names].some((name) => !name || name.includes("\0")) ||
66
+ new Set(names).size !== names.length
67
+ )
68
+ return yield* Unavailable.make({});
69
+
70
+ const name = sql.literal(
71
+ (table.schema === undefined ? "" : `${identifier(table.schema)}.`) + identifier(table.name),
72
+ );
73
+
74
+ const column = (key: keyof typeof oauthProxyColumns) =>
75
+ sql.literal(identifier(table.columns[key]));
76
+
77
+ const standalone = requireStandalone(() => Unavailable.make({}), sql.transactionService);
78
+
79
+ const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
80
+ effect.pipe(Effect.mapError(() => Unavailable.make({})));
81
+
82
+ const clock = sql.onDialectOrElse({
83
+ sqlite: () => sql.literal("CAST((julianday('now') - 2440587.5) * 86400000 AS INTEGER)"),
84
+ orElse: () => sql.literal("CAST(EXTRACT(EPOCH FROM clock_timestamp()) * 1000 AS BIGINT)"),
85
+ });
86
+
87
+ return Persistence.of({
88
+ get: Effect.fnUntraced(function* (namespace, id) {
89
+ yield* standalone;
90
+
91
+ const rows = yield* sql`SELECT ${column("payload")} AS payload,
92
+ ${column("version")} AS version, ${column("stage")} AS stage, ${column("context")} AS context,
93
+ CAST(${column("expiresAtMillis")} AS TEXT) AS expiry,
94
+ CAST(${column("handoffExpiresAtMillis")} AS TEXT) AS deadline
95
+ FROM ${name} WHERE ${column("namespace")} = ${namespace} AND ${column("flowId")} = ${id}`;
96
+
97
+ if (rows.length === 0) return undefined;
98
+ if (rows.length !== 1) return yield* Unavailable.make({});
99
+ const row = yield* Schema.decodeUnknownEffect(Row)(rows[0]);
100
+ const record = yield* Schema.decodeEffect(codec)(row.payload);
101
+ const context = yield* Schema.encodeEffect(contextCodec)(record.context);
102
+
103
+ if (
104
+ record.context.id !== id ||
105
+ record.version !== row.version ||
106
+ record._tag !== row.stage ||
107
+ context !== row.context ||
108
+ String(record.context.expiresAtMillis) !== row.expiry ||
109
+ (record._tag === "Ready" ? String(record.handoffExpiresAtMillis) : null) !== row.deadline
110
+ )
111
+ return yield* Unavailable.make({});
112
+
113
+ return record;
114
+ }, failure),
115
+ insert: Effect.fnUntraced(function* (namespace, record) {
116
+ yield* standalone;
117
+ if (record._tag !== "Pending") return yield* Unavailable.make({});
118
+ const payload = yield* Schema.encodeEffect(codec)(record);
119
+ const context = yield* Schema.encodeEffect(contextCodec)(record.context);
120
+
121
+ const rows = yield* sql`INSERT INTO ${name}
122
+ (${column("namespace")}, ${column("flowId")}, ${column("version")}, ${column("stage")},
123
+ ${column("context")}, ${column("expiresAtMillis")}, ${column("payload")})
124
+ SELECT ${namespace}, ${record.context.id}, ${record.version}, ${record._tag},
125
+ ${context}, ${record.context.expiresAtMillis}, ${payload}
126
+ WHERE ${record.context.expiresAtMillis} > ${clock}
127
+ ON CONFLICT (${column("namespace")}, ${column("flowId")}) DO NOTHING
128
+ RETURNING ${column("version")}`;
129
+
130
+ return rows.length === 1;
131
+ }, failure),
132
+ compareAndSet: Effect.fnUntraced(function* (namespace, version, record) {
133
+ yield* standalone;
134
+ if (version === record.version || record._tag === "Pending")
135
+ return yield* Unavailable.make({});
136
+
137
+ // Explicitly omit private envelopes from both irreversible marker stages.
138
+ const payload = yield* Schema.encodeEffect(codec)(
139
+ record._tag === "Ready"
140
+ ? record
141
+ : { _tag: record._tag, version: record.version, context: record.context },
142
+ );
143
+
144
+ const context = yield* Schema.encodeEffect(contextCodec)(record.context);
145
+
146
+ const previous =
147
+ record._tag === "Exchanging" ? "Pending" : record._tag === "Ready" ? "Exchanging" : "Ready";
148
+
149
+ const deadline = record._tag === "Ready" ? record.handoffExpiresAtMillis : null;
150
+
151
+ const consume =
152
+ record._tag === "Consumed"
153
+ ? sql`AND ${column("handoffExpiresAtMillis")} > ${clock}`
154
+ : sql.literal("");
155
+
156
+ const rows = yield* sql`UPDATE ${name}
157
+ SET ${column("version")} = ${record.version}, ${column("stage")} = ${record._tag},
158
+ ${column("handoffExpiresAtMillis")} = ${deadline}, ${column("payload")} = ${payload}
159
+ WHERE ${column("namespace")} = ${namespace} AND ${column("flowId")} = ${record.context.id}
160
+ AND ${column("version")} = ${version} AND ${column("stage")} = ${previous}
161
+ AND ${column("context")} = ${context}
162
+ AND ${column("expiresAtMillis")} = ${record.context.expiresAtMillis}
163
+ AND ${column("expiresAtMillis")} > ${clock} ${consume}
164
+ RETURNING ${column("version")}`;
165
+
166
+ return rows.length === 1;
167
+ }, failure),
168
+ });
169
+ });
170
+
171
+ const layer = Layer.effect(
172
+ Persistence,
173
+ makeOAuthProxyPersistence({ name: "yielded_oauth_proxy", columns: oauthProxyColumns }),
174
+ );
175
+
176
+ export const OAuthProxyPersistence = { migration, layer };