@effect-app/infra 4.0.0-beta.33 → 4.0.0-beta.330

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 (490) hide show
  1. package/CHANGELOG.md +2555 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +29 -10
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +36 -15
  6. package/dist/ClusterCosmos.d.ts +64 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +576 -0
  9. package/dist/ClusterServiceBus.d.ts +67 -0
  10. package/dist/ClusterServiceBus.d.ts.map +1 -0
  11. package/dist/ClusterServiceBus.js +84 -0
  12. package/dist/ContextProvider.d.ts +34 -0
  13. package/dist/ContextProvider.d.ts.map +1 -0
  14. package/dist/ContextProvider.js +40 -0
  15. package/dist/Emailer/Sendgrid.d.ts +110 -146
  16. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  17. package/dist/Emailer/Sendgrid.js +24 -19
  18. package/dist/Emailer/fake.d.ts +1 -1
  19. package/dist/Emailer/fake.d.ts.map +1 -1
  20. package/dist/Emailer/fake.js +4 -4
  21. package/dist/MainFiberSet.d.ts +12 -8
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +23 -7
  24. package/dist/QueueMaker/errors.d.ts +4 -2
  25. package/dist/QueueMaker/errors.d.ts.map +1 -1
  26. package/dist/QueueMaker/errors.js +4 -2
  27. package/dist/{adapters/SQL → SQL}/Model.d.ts +31 -42
  28. package/dist/SQL/Model.d.ts.map +1 -0
  29. package/dist/SQL/Model.js +469 -0
  30. package/dist/SQL.d.ts.map +1 -0
  31. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  32. package/dist/Store/Cosmos/query.d.ts +16 -4
  33. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  34. package/dist/Store/Cosmos/query.js +238 -42
  35. package/dist/Store/Cosmos.d.ts +2 -2
  36. package/dist/Store/Cosmos.d.ts.map +1 -1
  37. package/dist/Store/Cosmos.js +395 -256
  38. package/dist/Store/Disk.d.ts +4 -4
  39. package/dist/Store/Disk.d.ts.map +1 -1
  40. package/dist/Store/Disk.js +93 -42
  41. package/dist/Store/Memory.d.ts +7 -10
  42. package/dist/Store/Memory.d.ts.map +1 -1
  43. package/dist/Store/Memory.js +349 -79
  44. package/dist/Store/SQL/Pg.d.ts +4 -0
  45. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  46. package/dist/Store/SQL/Pg.js +261 -0
  47. package/dist/Store/SQL/query.d.ts +53 -0
  48. package/dist/Store/SQL/query.d.ts.map +1 -0
  49. package/dist/Store/SQL/query.js +656 -0
  50. package/dist/Store/SQL.d.ts +28 -0
  51. package/dist/Store/SQL.d.ts.map +1 -0
  52. package/dist/Store/SQL.js +483 -0
  53. package/dist/Store/codeFilter.d.ts +4 -4
  54. package/dist/Store/codeFilter.d.ts.map +1 -1
  55. package/dist/Store/codeFilter.js +71 -22
  56. package/dist/Store/index.d.ts +6 -4
  57. package/dist/Store/index.d.ts.map +1 -1
  58. package/dist/Store/index.js +18 -5
  59. package/dist/Store/jsonDocument.d.ts +67 -0
  60. package/dist/Store/jsonDocument.d.ts.map +1 -0
  61. package/dist/Store/jsonDocument.js +107 -0
  62. package/dist/Store/utils.d.ts +21 -2
  63. package/dist/Store/utils.d.ts.map +1 -1
  64. package/dist/Store/utils.js +253 -10
  65. package/dist/WorkflowEngineCosmos.d.ts +30 -0
  66. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  67. package/dist/WorkflowEngineCosmos.js +542 -0
  68. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  69. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  70. package/dist/WorkflowEngineSqlite.js +550 -0
  71. package/dist/arbs.d.ts +6 -3
  72. package/dist/arbs.d.ts.map +1 -1
  73. package/dist/arbs.js +20 -4
  74. package/dist/codec.d.ts.map +1 -0
  75. package/dist/codec.js +5 -0
  76. package/dist/cosmos-client.d.ts +40 -0
  77. package/dist/cosmos-client.d.ts.map +1 -0
  78. package/dist/cosmos-client.js +24 -0
  79. package/dist/errorReporter.d.ts +6 -4
  80. package/dist/errorReporter.d.ts.map +1 -1
  81. package/dist/errorReporter.js +23 -27
  82. package/dist/fileUtil.d.ts +1 -1
  83. package/dist/fileUtil.d.ts.map +1 -1
  84. package/dist/fileUtil.js +2 -2
  85. package/dist/index.d.ts +2 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +3 -2
  88. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  89. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  90. package/dist/internal/RequestContextMiddleware.js +45 -0
  91. package/dist/internal/auth.d.ts +53 -0
  92. package/dist/internal/auth.d.ts.map +1 -0
  93. package/dist/internal/auth.js +180 -0
  94. package/dist/internal/events.d.ts +11 -0
  95. package/dist/internal/events.d.ts.map +1 -0
  96. package/dist/internal/events.js +49 -0
  97. package/dist/internal/health.d.ts.map +1 -0
  98. package/dist/internal/health.js +5 -0
  99. package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +14 -6
  100. package/dist/layerUtils.d.ts.map +1 -0
  101. package/dist/layerUtils.js +17 -0
  102. package/dist/logger/jsonLogger.d.ts +1 -1
  103. package/dist/logger/jsonLogger.d.ts.map +1 -1
  104. package/dist/logger/jsonLogger.js +5 -3
  105. package/dist/logger/logFmtLogger.d.ts +1 -1
  106. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  107. package/dist/logger/logFmtLogger.js +3 -3
  108. package/dist/logger/shared.d.ts +2 -2
  109. package/dist/logger/shared.d.ts.map +1 -1
  110. package/dist/logger/shared.js +6 -6
  111. package/dist/logger.d.ts +4 -4
  112. package/dist/logger.d.ts.map +1 -1
  113. package/dist/middlewares.d.ts.map +1 -0
  114. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  115. package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +3 -2
  116. package/dist/mongo-client.d.ts.map +1 -0
  117. package/dist/mongo-client.js +15 -0
  118. package/dist/otel.d.ts +75 -0
  119. package/dist/otel.d.ts.map +1 -0
  120. package/dist/otel.js +84 -0
  121. package/dist/rateLimit.d.ts +11 -3
  122. package/dist/rateLimit.d.ts.map +1 -1
  123. package/dist/rateLimit.js +7 -12
  124. package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +5 -2
  125. package/dist/redis-client.d.ts.map +1 -0
  126. package/dist/redis-client.js +98 -0
  127. package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
  128. package/dist/reportError.d.ts.map +1 -0
  129. package/dist/reportError.js +28 -0
  130. package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +4 -3
  131. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  132. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  133. package/dist/routing/middleware/middleware.d.ts +48 -0
  134. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  135. package/dist/routing/middleware/middleware.js +152 -0
  136. package/dist/{api/routing → routing}/middleware.d.ts +0 -1
  137. package/dist/routing/middleware.d.ts.map +1 -0
  138. package/dist/{api/routing → routing}/middleware.js +1 -2
  139. package/dist/routing/schema/jwt.d.ts.map +1 -0
  140. package/dist/routing/schema/jwt.js +12 -0
  141. package/dist/routing/tsort.d.ts.map +1 -0
  142. package/dist/routing/tsort.js +51 -0
  143. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  144. package/dist/routing/utils.d.ts.map +1 -0
  145. package/dist/routing/utils.js +45 -0
  146. package/dist/routing.d.ts +182 -0
  147. package/dist/routing.d.ts.map +1 -0
  148. package/dist/routing.js +256 -0
  149. package/dist/test.d.ts +2 -2
  150. package/dist/test.d.ts.map +1 -1
  151. package/dist/test.js +5 -5
  152. package/dist/util.d.ts.map +1 -0
  153. package/dist/util.js +14 -0
  154. package/docs/cluster-storage.md +26 -0
  155. package/docs/workflow-engine.md +262 -0
  156. package/examples/query.ts +25 -21
  157. package/package.json +38 -352
  158. package/src/CUPS.ts +54 -15
  159. package/src/ClusterCosmos.ts +1150 -0
  160. package/src/ClusterServiceBus.ts +244 -0
  161. package/src/{api/ContextProvider.ts → ContextProvider.ts} +20 -17
  162. package/src/Emailer/Sendgrid.ts +83 -60
  163. package/src/Emailer/fake.ts +4 -4
  164. package/src/MainFiberSet.ts +32 -13
  165. package/src/QueueMaker/errors.ts +3 -1
  166. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  167. package/src/SQL.ts +1 -0
  168. package/src/Store/Cosmos/query.ts +275 -55
  169. package/src/Store/Cosmos.ts +547 -366
  170. package/src/Store/Disk.ts +146 -73
  171. package/src/Store/Memory.ts +420 -115
  172. package/src/Store/SQL/Pg.ts +412 -0
  173. package/src/Store/SQL/query.ts +748 -0
  174. package/src/Store/SQL.ts +806 -0
  175. package/src/Store/codeFilter.ts +78 -28
  176. package/src/Store/index.ts +25 -10
  177. package/src/Store/jsonDocument.ts +143 -0
  178. package/src/Store/utils.ts +307 -30
  179. package/src/WorkflowEngineCosmos.ts +749 -0
  180. package/src/WorkflowEngineSqlite.ts +813 -0
  181. package/src/arbs.ts +25 -5
  182. package/src/cosmos-client.ts +56 -0
  183. package/src/errorReporter.ts +68 -78
  184. package/src/fileUtil.ts +1 -1
  185. package/src/index.ts +2 -1
  186. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  187. package/src/internal/auth.ts +272 -0
  188. package/src/{api/internal → internal}/events.ts +22 -13
  189. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  190. package/src/logger/jsonLogger.ts +5 -3
  191. package/src/logger/logFmtLogger.ts +3 -3
  192. package/src/logger/shared.ts +6 -5
  193. package/src/middlewares.ts +10 -0
  194. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  195. package/src/otel.ts +177 -0
  196. package/src/rateLimit.ts +35 -24
  197. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  198. package/src/{api/reportError.ts → reportError.ts} +3 -2
  199. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  200. package/src/routing/middleware/middleware.ts +196 -0
  201. package/src/routing/middleware.ts +4 -0
  202. package/src/{api/routing → routing}/schema/jwt.ts +2 -2
  203. package/src/{api/routing → routing}/utils.ts +2 -1
  204. package/src/routing.ts +823 -0
  205. package/src/test.ts +6 -6
  206. package/src/vitest.ts +1 -1
  207. package/test/arbs-magnitude.test.ts +33 -0
  208. package/test/arbs.test.ts +41 -0
  209. package/test/auth.test.ts +101 -0
  210. package/test/cluster-cosmos.test.ts +612 -0
  211. package/test/cluster-servicebus.test.ts +180 -0
  212. package/test/cluster-sqlite.test.ts +207 -0
  213. package/test/contextProvider.test.ts +15 -12
  214. package/test/controller.test.ts +27 -31
  215. package/test/cosmos-container-throughput.test.ts +61 -0
  216. package/test/cosmos-query.test.ts +368 -0
  217. package/test/dist/arbs-magnitude.test.d.ts.map +1 -0
  218. package/test/dist/arbs.test.d.ts.map +1 -0
  219. package/test/dist/auth.test.d.ts.map +1 -0
  220. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  221. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  222. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  223. package/test/dist/contextProvider.test.d.ts.map +1 -1
  224. package/test/dist/controller.test.d.ts.map +1 -1
  225. package/test/dist/cosmos-container-throughput.test.d.ts.map +1 -0
  226. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  227. package/test/dist/fixtures.d.ts +29 -11
  228. package/test/dist/fixtures.d.ts.map +1 -1
  229. package/test/dist/fixtures.js +17 -10
  230. package/test/dist/json-lower.test.d.ts.map +1 -0
  231. package/test/dist/query.test.d.ts.map +1 -1
  232. package/test/dist/rawQuery.test.d.ts.map +1 -1
  233. package/test/dist/repository-ext.test.d.ts.map +1 -0
  234. package/test/dist/repository-legacy-document.test.d.ts.map +1 -0
  235. package/test/dist/requires.test.d.ts.map +1 -1
  236. package/test/dist/router-generator.test.d.ts.map +1 -0
  237. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  238. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  239. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  240. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  241. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  242. package/test/dist/sql-store.test.d.ts.map +1 -0
  243. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  244. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  245. package/test/fixtures.ts +16 -9
  246. package/test/json-lower.test.ts +129 -0
  247. package/test/layerUtils.test.ts +2 -2
  248. package/test/query.test.ts +1382 -44
  249. package/test/rawQuery.test.ts +433 -8
  250. package/test/repository-ext.test.ts +456 -0
  251. package/test/repository-legacy-document.test.ts +394 -0
  252. package/test/requires.test.ts +10 -5
  253. package/test/router-generator.test.ts +187 -0
  254. package/test/routing-interruptibility.test.ts +66 -0
  255. package/test/rpc-context-map-streaming.test.ts +265 -0
  256. package/test/rpc-e2e-invalidation.test.ts +421 -0
  257. package/test/rpc-multi-middleware.test.ts +85 -10
  258. package/test/rpc-stream-fullstack.test.ts +307 -0
  259. package/test/sql-store.test.ts +1931 -0
  260. package/test/validateSample.test.ts +71 -43
  261. package/test/workflow-engine-cosmos.test.ts +345 -0
  262. package/test/workflow-engine-sqlite.test.ts +297 -0
  263. package/tsconfig.examples.json +1 -1
  264. package/tsconfig.json +9 -2
  265. package/tsconfig.src.json +35 -35
  266. package/tsconfig.test.json +2 -2
  267. package/dist/Emailer/service.d.ts +0 -55
  268. package/dist/Emailer/service.d.ts.map +0 -1
  269. package/dist/Emailer/service.js +0 -6
  270. package/dist/Emailer.d.ts +0 -2
  271. package/dist/Emailer.d.ts.map +0 -1
  272. package/dist/Emailer.js +0 -2
  273. package/dist/Model/Repository/ext.d.ts +0 -41
  274. package/dist/Model/Repository/ext.d.ts.map +0 -1
  275. package/dist/Model/Repository/ext.js +0 -65
  276. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  277. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  278. package/dist/Model/Repository/internal/internal.js +0 -316
  279. package/dist/Model/Repository/legacy.d.ts +0 -19
  280. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  281. package/dist/Model/Repository/legacy.js +0 -2
  282. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  283. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  284. package/dist/Model/Repository/makeRepo.js +0 -24
  285. package/dist/Model/Repository/service.d.ts +0 -89
  286. package/dist/Model/Repository/service.d.ts.map +0 -1
  287. package/dist/Model/Repository/service.js +0 -2
  288. package/dist/Model/Repository/validation.d.ts +0 -42
  289. package/dist/Model/Repository/validation.d.ts.map +0 -1
  290. package/dist/Model/Repository/validation.js +0 -32
  291. package/dist/Model/Repository.d.ts +0 -6
  292. package/dist/Model/Repository.d.ts.map +0 -1
  293. package/dist/Model/Repository.js +0 -6
  294. package/dist/Model/dsl.d.ts +0 -32
  295. package/dist/Model/dsl.d.ts.map +0 -1
  296. package/dist/Model/dsl.js +0 -44
  297. package/dist/Model/filter/filterApi.d.ts +0 -30
  298. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  299. package/dist/Model/filter/filterApi.js +0 -2
  300. package/dist/Model/filter/types/errors.d.ts +0 -29
  301. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  302. package/dist/Model/filter/types/errors.js +0 -2
  303. package/dist/Model/filter/types/fields.d.ts +0 -15
  304. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  305. package/dist/Model/filter/types/fields.js +0 -2
  306. package/dist/Model/filter/types/path/common.d.ts +0 -316
  307. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  308. package/dist/Model/filter/types/path/common.js +0 -2
  309. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  310. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  311. package/dist/Model/filter/types/path/eager.js +0 -31
  312. package/dist/Model/filter/types/path/index.d.ts +0 -4
  313. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  314. package/dist/Model/filter/types/path/index.js +0 -3
  315. package/dist/Model/filter/types/utils.d.ts +0 -79
  316. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  317. package/dist/Model/filter/types/utils.js +0 -2
  318. package/dist/Model/filter/types/validator.d.ts +0 -30
  319. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  320. package/dist/Model/filter/types/validator.js +0 -2
  321. package/dist/Model/filter/types.d.ts +0 -5
  322. package/dist/Model/filter/types.d.ts.map +0 -1
  323. package/dist/Model/filter/types.js +0 -7
  324. package/dist/Model/query/dsl.d.ts +0 -248
  325. package/dist/Model/query/dsl.d.ts.map +0 -1
  326. package/dist/Model/query/dsl.js +0 -104
  327. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  328. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  329. package/dist/Model/query/new-kid-interpreter.js +0 -165
  330. package/dist/Model/query.d.ts +0 -15
  331. package/dist/Model/query.d.ts.map +0 -1
  332. package/dist/Model/query.js +0 -3
  333. package/dist/Model.d.ts +0 -4
  334. package/dist/Model.d.ts.map +0 -1
  335. package/dist/Model.js +0 -4
  336. package/dist/Operations.d.ts +0 -55
  337. package/dist/Operations.d.ts.map +0 -1
  338. package/dist/Operations.js +0 -102
  339. package/dist/OperationsRepo.d.ts +0 -41
  340. package/dist/OperationsRepo.d.ts.map +0 -1
  341. package/dist/OperationsRepo.js +0 -14
  342. package/dist/QueueMaker/SQLQueue.d.ts +0 -16
  343. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  344. package/dist/QueueMaker/SQLQueue.js +0 -130
  345. package/dist/QueueMaker/memQueue.d.ts +0 -11
  346. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  347. package/dist/QueueMaker/memQueue.js +0 -76
  348. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  349. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  350. package/dist/QueueMaker/sbqueue.js +0 -91
  351. package/dist/QueueMaker/service.d.ts +0 -11
  352. package/dist/QueueMaker/service.d.ts.map +0 -1
  353. package/dist/QueueMaker/service.js +0 -4
  354. package/dist/RequestContext.d.ts +0 -63
  355. package/dist/RequestContext.d.ts.map +0 -1
  356. package/dist/RequestContext.js +0 -49
  357. package/dist/RequestFiberSet.d.ts +0 -36
  358. package/dist/RequestFiberSet.d.ts.map +0 -1
  359. package/dist/RequestFiberSet.js +0 -78
  360. package/dist/Store/ContextMapContainer.d.ts +0 -14
  361. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  362. package/dist/Store/ContextMapContainer.js +0 -16
  363. package/dist/Store/service.d.ts +0 -108
  364. package/dist/Store/service.d.ts.map +0 -1
  365. package/dist/Store/service.js +0 -71
  366. package/dist/Store.d.ts +0 -2
  367. package/dist/Store.d.ts.map +0 -1
  368. package/dist/Store.js +0 -2
  369. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  370. package/dist/adapters/SQL/Model.js +0 -478
  371. package/dist/adapters/SQL.d.ts.map +0 -1
  372. package/dist/adapters/ServiceBus.d.ts +0 -58
  373. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  374. package/dist/adapters/ServiceBus.js +0 -99
  375. package/dist/adapters/cosmos-client.d.ts +0 -14
  376. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  377. package/dist/adapters/cosmos-client.js +0 -9
  378. package/dist/adapters/index.d.ts +0 -2
  379. package/dist/adapters/index.d.ts.map +0 -1
  380. package/dist/adapters/index.js +0 -2
  381. package/dist/adapters/logger.d.ts +0 -9
  382. package/dist/adapters/logger.d.ts.map +0 -1
  383. package/dist/adapters/logger.js +0 -3
  384. package/dist/adapters/memQueue.d.ts +0 -13
  385. package/dist/adapters/memQueue.d.ts.map +0 -1
  386. package/dist/adapters/memQueue.js +0 -20
  387. package/dist/adapters/mongo-client.d.ts.map +0 -1
  388. package/dist/adapters/mongo-client.js +0 -13
  389. package/dist/adapters/redis-client.d.ts.map +0 -1
  390. package/dist/adapters/redis-client.js +0 -94
  391. package/dist/api/ContextProvider.d.ts +0 -31
  392. package/dist/api/ContextProvider.d.ts.map +0 -1
  393. package/dist/api/ContextProvider.js +0 -38
  394. package/dist/api/codec.d.ts.map +0 -1
  395. package/dist/api/codec.js +0 -5
  396. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  397. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  398. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  399. package/dist/api/internal/auth.d.ts +0 -15
  400. package/dist/api/internal/auth.d.ts.map +0 -1
  401. package/dist/api/internal/auth.js +0 -47
  402. package/dist/api/internal/events.d.ts +0 -9
  403. package/dist/api/internal/events.d.ts.map +0 -1
  404. package/dist/api/internal/events.js +0 -42
  405. package/dist/api/internal/health.d.ts.map +0 -1
  406. package/dist/api/internal/health.js +0 -5
  407. package/dist/api/layerUtils.d.ts.map +0 -1
  408. package/dist/api/layerUtils.js +0 -16
  409. package/dist/api/middlewares.d.ts.map +0 -1
  410. package/dist/api/reportError.d.ts.map +0 -1
  411. package/dist/api/reportError.js +0 -27
  412. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  413. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  414. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  415. package/dist/api/routing/middleware/middleware.js +0 -92
  416. package/dist/api/routing/middleware.d.ts.map +0 -1
  417. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  418. package/dist/api/routing/schema/jwt.js +0 -12
  419. package/dist/api/routing/tsort.d.ts.map +0 -1
  420. package/dist/api/routing/tsort.js +0 -51
  421. package/dist/api/routing/utils.d.ts.map +0 -1
  422. package/dist/api/routing/utils.js +0 -44
  423. package/dist/api/routing.d.ts +0 -138
  424. package/dist/api/routing.d.ts.map +0 -1
  425. package/dist/api/routing.js +0 -166
  426. package/dist/api/setupRequest.d.ts +0 -12
  427. package/dist/api/setupRequest.d.ts.map +0 -1
  428. package/dist/api/setupRequest.js +0 -44
  429. package/dist/api/util.d.ts.map +0 -1
  430. package/dist/api/util.js +0 -14
  431. package/eslint.config.mjs +0 -24
  432. package/src/Emailer/service.ts +0 -52
  433. package/src/Emailer.ts +0 -1
  434. package/src/Model/Repository/ext.ts +0 -283
  435. package/src/Model/Repository/internal/internal.ts +0 -577
  436. package/src/Model/Repository/legacy.ts +0 -27
  437. package/src/Model/Repository/makeRepo.ts +0 -139
  438. package/src/Model/Repository/service.ts +0 -627
  439. package/src/Model/Repository/validation.ts +0 -31
  440. package/src/Model/Repository.ts +0 -5
  441. package/src/Model/dsl.ts +0 -128
  442. package/src/Model/filter/filterApi.ts +0 -60
  443. package/src/Model/filter/types/errors.ts +0 -47
  444. package/src/Model/filter/types/fields.ts +0 -50
  445. package/src/Model/filter/types/path/common.ts +0 -404
  446. package/src/Model/filter/types/path/eager.ts +0 -298
  447. package/src/Model/filter/types/path/index.ts +0 -4
  448. package/src/Model/filter/types/utils.ts +0 -128
  449. package/src/Model/filter/types/validator.ts +0 -46
  450. package/src/Model/filter/types.ts +0 -6
  451. package/src/Model/query/dsl.ts +0 -2110
  452. package/src/Model/query/new-kid-interpreter.ts +0 -210
  453. package/src/Model/query.ts +0 -13
  454. package/src/Model.ts +0 -3
  455. package/src/Operations.ts +0 -235
  456. package/src/OperationsRepo.ts +0 -16
  457. package/src/QueueMaker/SQLQueue.ts +0 -189
  458. package/src/QueueMaker/memQueue.ts +0 -130
  459. package/src/QueueMaker/sbqueue.ts +0 -137
  460. package/src/QueueMaker/service.ts +0 -17
  461. package/src/RequestContext.ts +0 -63
  462. package/src/RequestFiberSet.ts +0 -105
  463. package/src/Store/ContextMapContainer.ts +0 -20
  464. package/src/Store/index.test.ts.bak +0 -112
  465. package/src/Store/service.ts +0 -184
  466. package/src/Store.ts +0 -1
  467. package/src/adapters/SQL.ts +0 -1
  468. package/src/adapters/ServiceBus.ts +0 -209
  469. package/src/adapters/cosmos-client.ts +0 -16
  470. package/src/adapters/index.ts +0 -0
  471. package/src/adapters/logger.ts +0 -3
  472. package/src/adapters/memQueue.ts +0 -21
  473. package/src/api/internal/auth.ts +0 -68
  474. package/src/api/middlewares.ts +0 -10
  475. package/src/api/routing/middleware/middleware.ts +0 -128
  476. package/src/api/routing/middleware.ts +0 -6
  477. package/src/api/routing.ts +0 -598
  478. package/src/api/setupRequest.ts +0 -84
  479. package/tsconfig.json.bak +0 -12
  480. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  481. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  482. /package/dist/{api/internal → internal}/health.d.ts +0 -0
  483. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  484. /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
  485. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  486. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  487. /package/src/{api/codec.ts → codec.ts} +0 -0
  488. /package/src/{api/internal → internal}/health.ts +0 -0
  489. /package/src/{api/routing → routing}/tsort.ts +0 -0
  490. /package/src/{api/util.ts → util.ts} +0 -0
package/src/arbs.ts CHANGED
@@ -1,13 +1,17 @@
1
1
  // Do not import to frontend
2
2
 
3
3
  import { faker } from "@faker-js/faker"
4
- import { type S } from "effect-app"
5
4
  import { setFaker } from "effect-app/faker"
6
- import * as FastCheck from "effect/testing/FastCheck"
5
+ import type * as S from "effect-app/Schema"
6
+ import * as Effect from "effect/Effect"
7
+ import * as Arbitrary from "effect/unstable/arbitrary/Arbitrary"
8
+ import * as FastCheck from "fast-check"
7
9
  import { Random } from "fast-check"
8
- import * as rand from "pure-rand"
10
+ import { congruential32 } from "pure-rand/generator/congruential32"
9
11
 
10
- const rnd = new Random(rand.congruential32(5))
12
+ const seed = 5
13
+ const rng = congruential32(seed)
14
+ const rnd = new Random(rng)
11
15
 
12
16
  setFaker(faker)
13
17
 
@@ -15,6 +19,22 @@ export function generate<T>(arb: FastCheck.Arbitrary<T>) {
15
19
  return arb.generate(rnd, undefined)
16
20
  }
17
21
 
18
- export function generateFromArbitrary<T>(arb: S.LazyArbitrary<T>) {
22
+ export function generateFromArbitrary<T>(arb: S.Arbitrary<T>) {
19
23
  return generate(arb(FastCheck))
20
24
  }
25
+
26
+ // Native `sampleEffect({ count: 1 })` always uses attempt 0 of the master seed (edge-biased).
27
+ // Integer `seed + n` stayed in that bucket and collided for StringId. Mix a per-call suffix
28
+ // so each call is a jump-before-toss, like successive `generate(rnd)` draws.
29
+ let schemaSamples = 0
30
+
31
+ export function generateFromSchema<S extends S.Constraint>(schema: S) {
32
+ const samples = Effect.runSync(
33
+ Arbitrary.sampleEffect(Arbitrary.schema(schema), { count: 1, seed: `${seed}:${schemaSamples++}` })
34
+ )
35
+ const value = samples[0]
36
+ if (value === undefined) {
37
+ throw new Error("failed to sample schema")
38
+ }
39
+ return { value }
40
+ }
@@ -0,0 +1,56 @@
1
+ import { type ContainerRequest, CosmosClient as ComosClient_ } from "@azure/cosmos"
2
+ import * as Context from "effect-app/Context"
3
+ import * as Effect from "effect-app/Effect"
4
+ import * as Layer from "effect-app/Layer"
5
+
6
+ const withClient = (url: string) => Effect.sync(() => new ComosClient_(url))
7
+
8
+ export const makeCosmosClient = (url: string, dbName: string) =>
9
+ Effect.map(withClient(url), (x) => ({ db: x.database(dbName) }))
10
+
11
+ export class CosmosClient extends Context.Service<CosmosClient, {
12
+ readonly db: ReturnType<InstanceType<typeof ComosClient_>["database"]>
13
+ }>()("@services/CosmosClient") {}
14
+
15
+ export const db = CosmosClient.pipe(Effect.map((_) => _.db))
16
+
17
+ export const CosmosClientLayer = (cosmosUrl: string, dbName: string) =>
18
+ Layer.effect(CosmosClient, makeCosmosClient(cosmosUrl, dbName))
19
+
20
+ export interface CosmosContainerThroughput {
21
+ /**
22
+ * Autoscale max RU/s for containers created on demand (min 1000; Cosmos bills at
23
+ * least 10% of it). Only applied to containers that do not exist yet, in databases
24
+ * without shared throughput — those containers share the database pool instead.
25
+ * Unset keeps the Cosmos default: manual 400 RU/s per container, billed 24/7.
26
+ */
27
+ readonly autoscaleMaxThroughput?: number | undefined
28
+ }
29
+
30
+ /** The slice of a Cosmos `Database` needed to create containers. */
31
+ export interface ContainerDb {
32
+ readonly container: (id: string) => { readonly read: () => Promise<unknown> }
33
+ readonly containers: { readonly createIfNotExists: (body: ContainerRequest) => Promise<unknown> }
34
+ readonly readOffer: () => Promise<{ readonly resource?: unknown }>
35
+ }
36
+
37
+ const isNotFound = (e: unknown) => typeof e === "object" && e !== null && "code" in e && e.code === 404
38
+
39
+ export const createContainerIfNotExists = async (
40
+ db: ContainerDb,
41
+ body: ContainerRequest & { id: string },
42
+ throughput?: CosmosContainerThroughput
43
+ ) => {
44
+ const maxThroughput = throughput?.autoscaleMaxThroughput
45
+ if (maxThroughput === undefined) {
46
+ await db.containers.createIfNotExists(body)
47
+ return
48
+ }
49
+ const exists = await db.container(body.id).read().then(
50
+ () => true,
51
+ (e) => isNotFound(e) ? false : Promise.reject(e)
52
+ )
53
+ if (exists) return
54
+ const { resource: sharedOffer } = await db.readOffer()
55
+ await db.containers.createIfNotExists(sharedOffer ? body : { ...body, maxThroughput })
56
+ }
@@ -1,9 +1,11 @@
1
1
  import * as Sentry from "@sentry/node"
2
- import { Cause, Effect, type LogLevel } from "effect-app"
2
+ import * as Effect from "effect-app/Effect"
3
+ import { getRC } from "effect-app/setupRequest"
3
4
  import { dropUndefined, LogLevelToSentry } from "effect-app/utils"
4
- import { getRC } from "./api/setupRequest.js"
5
- import { CauseException, tryToJson, tryToReport } from "./errors.js"
6
- import { InfraLogger } from "./logger.js"
5
+ import * as Cause from "effect/Cause"
6
+ import type * as LogLevel from "effect/LogLevel"
7
+ import { CauseException, tryToJson, tryToReport } from "./errors.ts"
8
+ import { InfraLogger } from "./logger.ts"
7
9
 
8
10
  const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseException<E> => {
9
11
  try {
@@ -13,47 +15,41 @@ const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseExcepti
13
15
  }
14
16
  }
15
17
 
16
- export function reportError(
17
- name: string
18
- ) {
19
- return (
20
- cause: Cause.Cause<unknown>,
21
- extras?: Record<string, unknown>,
22
- level: LogLevel.Severity = "Error"
23
- ) =>
24
- Effect
25
- .gen(function*() {
26
- if (Cause.hasInterruptsOnly(cause)) {
27
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
28
- return
29
- }
30
- const error = tryCauseException(cause, name)
31
-
32
- yield* reportSentry(error, extras, LogLevelToSentry(level))
33
- yield* InfraLogger
34
- .logWithLevel(level, "Reporting error", cause)
35
- .pipe(
36
- Effect.annotateLogs(dropUndefined({
37
- extras,
38
- error: tryToReport(error),
39
- cause: tryToJson(cause),
40
- __error_name__: name
41
- }))
42
- )
43
- .pipe(
44
- Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
45
- Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
46
- )
18
+ export function reportError(name: string) {
19
+ return Effect.fnUntraced(
20
+ function*(
21
+ cause: Cause.Cause<unknown>,
22
+ extras?: Record<string, unknown>,
23
+ level: LogLevel.Severity = "Error"
24
+ ) {
25
+ if (Cause.hasInterruptsOnly(cause)) {
26
+ yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
27
+ return
28
+ }
29
+ const error = tryCauseException(cause, name)
47
30
 
48
- return error
49
- })
50
- .pipe(
51
- Effect.tapCause((cause) =>
52
- InfraLogger.logError("Failed to report error", cause).pipe(
53
- Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
54
- )
31
+ yield* reportSentry(error, extras, LogLevelToSentry(level))
32
+ yield* InfraLogger
33
+ .logWithLevel(level, "Reporting error", cause)
34
+ .pipe(
35
+ Effect.annotateLogs(dropUndefined({
36
+ extras,
37
+ error: tryToReport(error),
38
+ cause: tryToJson(cause),
39
+ __error_name__: name
40
+ })),
41
+ Effect.catchCause((cause) => InfraLogger.logWarning("Failed to log error", cause)),
42
+ Effect.catchCause(() => InfraLogger.logFatal("Failed to log error cause"))
55
43
  )
56
- )
44
+
45
+ return error
46
+ },
47
+ (effect) =>
48
+ Effect.tapCause(effect, (cause) =>
49
+ InfraLogger.logError("Failed to report error", cause).pipe(
50
+ Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
51
+ ))
52
+ )
57
53
  }
58
54
 
59
55
  function reportSentry(
@@ -66,45 +62,39 @@ function reportSentry(
66
62
  scope.setLevel(level)
67
63
  if (context) scope.setContext("context", { ...context })
68
64
  if (extras) scope.setContext("extras", extras)
69
- scope.setContext("error", { data: tryToReport(error) })
70
- scope.setContext("cause", { data: tryToJson(error.originalCause) })
65
+ const squashed = Cause.squash(error.originalCause)
66
+ scope.setContext("mainError", tryToJson(squashed))
67
+ scope.setContext("error", tryToReport(error))
68
+ scope.setContext("cause", tryToJson(error.originalCause))
71
69
  Sentry.captureException(error, scope)
72
70
  }))
73
71
  }
74
72
 
75
- export function logError<E>(
76
- name: string
77
- ) {
78
- return (cause: Cause.Cause<E>, extras?: Record<string, unknown>) =>
79
- Effect
80
- .gen(function*() {
81
- if (Cause.hasInterruptsOnly(cause)) {
82
- yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
83
- return
84
- }
85
- yield* InfraLogger
86
- .logWarning("Logging error", cause)
87
- .pipe(
88
- Effect.annotateLogs(dropUndefined({
89
- extras,
90
- cause: tryToJson(cause),
91
- __error_name__: name
92
- }))
93
- )
94
- })
95
- .pipe(
96
- Effect.tapCause(() => InfraLogger.logFatal("Failed to log error cause"))
97
- )
73
+ export function logError<E>(name: string) {
74
+ return Effect.fnUntraced(
75
+ function*(cause: Cause.Cause<E>, extras?: Record<string, unknown>) {
76
+ if (Cause.hasInterruptsOnly(cause)) {
77
+ yield* InfraLogger.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
78
+ return
79
+ }
80
+ yield* InfraLogger
81
+ .logWarning("Logging error", cause)
82
+ .pipe(Effect.annotateLogs(dropUndefined({
83
+ extras,
84
+ cause: tryToJson(cause),
85
+ __error_name__: name
86
+ })))
87
+ },
88
+ (effect) => Effect.tapCause(effect, () => InfraLogger.logFatal("Failed to log error cause"))
89
+ )
98
90
  }
99
91
 
100
- export function reportMessage(message: string, extras?: Record<string, unknown>) {
101
- return Effect.gen(function*() {
102
- const context = yield* getRC
103
- const scope = new Sentry.Scope()
104
- if (context) scope.setContext("context", { ...context })
105
- if (extras) scope.setContext("extras", extras)
106
- Sentry.captureMessage(message, scope)
92
+ export const reportMessage = Effect.fnUntraced(function*(message: string, extras?: Record<string, unknown>) {
93
+ const context = yield* getRC
94
+ const scope = new Sentry.Scope()
95
+ if (context) scope.setContext("context", { ...context })
96
+ if (extras) scope.setContext("extras", extras)
97
+ Sentry.captureMessage(message, scope)
107
98
 
108
- console.warn(message, extras)
109
- })
110
- }
99
+ console.warn(message, extras)
100
+ })
package/src/fileUtil.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import crypto from "crypto"
2
- import { Effect } from "effect-app"
2
+ import * as Effect from "effect-app/Effect"
3
3
  import type { Abortable } from "events"
4
4
  import type { Mode, ObjectEncodingOptions, OpenMode } from "fs"
5
5
  import fs from "fs/promises"
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
- export * as Model from "./Model.js"
1
+ export * from "./logger.ts"
2
+ export * from "./Store/index.ts"
@@ -1,8 +1,10 @@
1
- import { Effect } from "effect-app"
1
+ import * as Effect from "effect-app/Effect"
2
2
  import { HttpMiddleware, HttpServerRequest, HttpServerResponse } from "effect-app/http"
3
+ import * as Layer from "effect-app/Layer"
4
+ import { Locale, LocaleRef, RequestContext, spanAttributes } from "effect-app/RequestContext"
3
5
  import { NonEmptyString255 } from "effect-app/Schema"
4
- import { Locale, RequestContext } from "../../RequestContext.js"
5
- import { setupRequestContext } from "../setupRequest.js"
6
+ import { provideOnRequestScope, requestStateLayer } from "effect-app/setupRequest"
7
+ import { storeId } from "effect-app/Store"
6
8
 
7
9
  export const RequestContextMiddleware = (defaultLocale: Locale = "en") =>
8
10
  HttpMiddleware.make((app) =>
@@ -21,18 +23,33 @@ export const RequestContextMiddleware = (defaultLocale: Locale = "en") =>
21
23
  const namespace = NonEmptyString255((ns && (Array.isArray(ns) ? ns[0] : ns)) || "primary")
22
24
  const deviceId = req.headers["x-fe-device-id"]
23
25
 
24
- const requestContext = new RequestContext({
26
+ const requestContext = RequestContext.make({
25
27
  span: {
26
28
  traceId: currentSpan.traceId,
27
29
  spanId: currentSpan.spanId,
28
30
  sampled: currentSpan.sampled
29
31
  },
30
- name: NonEmptyString255(req.originalUrl), // set more detailed elsewhere
32
+ name: NonEmptyString255(`HTTP ${req.method} ${req.originalUrl.split("?")[0]}`), // set more detailed elsewhere
31
33
  locale,
32
34
  namespace,
33
35
  sourceId: deviceId ? NonEmptyString255(deviceId) : undefined
34
36
  })
35
- const res = yield* setupRequestContext(app, requestContext)
37
+ yield* Effect.annotateCurrentSpan(spanAttributes(requestContext))
38
+ const layer = Layer.mergeAll(
39
+ requestStateLayer,
40
+ Layer.succeed(LocaleRef, requestContext.locale),
41
+ Layer.succeed(storeId, requestContext.namespace)
42
+ )
43
+ // Bind layer to the request scope so ContextMap's finalizer (clear()) runs only
44
+ // after the response body is fully drained — not when `app` returns its
45
+ // HttpServerResponse value. Streaming RPC responses keep producing chunks (and
46
+ // using ContextMap-cached etags) after `app` returns; a sub-scope from
47
+ // `Effect.provide(layer)` would close too early and wipe etags mid-stream,
48
+ // causing spurious OptimisticConcurrencyException on later writes.
49
+ const res = yield* app.pipe(
50
+ Effect.withLogSpan(requestContext.name),
51
+ provideOnRequestScope(layer)
52
+ )
36
53
 
37
54
  // TODO: how to set also on errors?
38
55
  return HttpServerResponse.setHeaders(res, {
@@ -0,0 +1,272 @@
1
+ import * as Effect from "effect-app/Effect"
2
+ import { HttpHeaders, HttpMiddleware, HttpServerRequest, HttpServerResponse } from "effect-app/http"
3
+ import * as Option from "effect-app/Option"
4
+ import * as Data from "effect/Data"
5
+ import { createRemoteJWKSet, jwtVerify } from "jose"
6
+
7
+ const getHeaders = (error: string, description: string, scopes?: ReadonlyArray<string>) => ({
8
+ "WWW-Authenticate": `Bearer realm="api", error="${error}", error_description="${description.replace(/"/g, "'")}"${
9
+ scopes ? `, scope="${scopes.join(" ")}"` : ""
10
+ }`
11
+ })
12
+
13
+ export class UnauthorizedError extends Error {
14
+ readonly status: number = 401
15
+ readonly statusCode: number = 401
16
+ headers = { "WWW-Authenticate": "Bearer realm=\"api\"" }
17
+
18
+ constructor(message = "Unauthorized") {
19
+ super(message)
20
+ this.name = this.constructor.name
21
+ }
22
+ }
23
+
24
+ export class InvalidRequestError extends UnauthorizedError {
25
+ readonly code: string
26
+ override readonly status = 400
27
+ override readonly statusCode = 400
28
+
29
+ constructor(message = "Invalid Request", useErrorCode = true) {
30
+ super(message)
31
+ this.code = useErrorCode ? "invalid_request" : ""
32
+ if (useErrorCode) {
33
+ this.headers = getHeaders(this.code, this.message)
34
+ }
35
+ }
36
+ }
37
+
38
+ export class InvalidTokenError extends UnauthorizedError {
39
+ readonly code = "invalid_token"
40
+
41
+ constructor(message = "Invalid Token") {
42
+ super(message)
43
+ this.headers = getHeaders(this.code, this.message)
44
+ }
45
+ }
46
+
47
+ export class InsufficientScopeError extends UnauthorizedError {
48
+ readonly code = "insufficient_scope"
49
+ override readonly status = 403
50
+ override readonly statusCode = 403
51
+
52
+ constructor(scopes?: ReadonlyArray<string>, message = "Insufficient Scope") {
53
+ super(message)
54
+ this.headers = getHeaders(this.code, this.message, scopes)
55
+ }
56
+ }
57
+
58
+ export interface JwtVerifierOptions {
59
+ readonly audience?: string | Array<string> | ReadonlyArray<string>
60
+ readonly clockTolerance?: number
61
+ readonly issuer?: string
62
+ readonly issuerBaseURL?: string
63
+ readonly jwksUri?: string
64
+ readonly maxTokenAge?: number
65
+ readonly secret?: string
66
+ readonly strict?: boolean
67
+ readonly tokenSigningAlg?: string
68
+ }
69
+
70
+ export interface AuthOptions extends JwtVerifierOptions {
71
+ readonly authRequired?: boolean
72
+ }
73
+
74
+ type Config = AuthOptions
75
+
76
+ type JwtError = InsufficientScopeError | InvalidRequestError | InvalidTokenError | UnauthorizedError
77
+
78
+ type ResolvedConfigBase = {
79
+ readonly audience: string | Array<string> | undefined
80
+ readonly clockTolerance: number
81
+ readonly issuer: string | undefined
82
+ readonly maxTokenAge: number | undefined
83
+ readonly strict: boolean
84
+ readonly tokenSigningAlg: string | undefined
85
+ }
86
+
87
+ type ResolvedConfig =
88
+ & ResolvedConfigBase
89
+ & (
90
+ | {
91
+ readonly key: ReturnType<typeof createRemoteJWKSet>
92
+ readonly keyType: "jwks"
93
+ }
94
+ | {
95
+ readonly key: Uint8Array
96
+ readonly keyType: "secret"
97
+ }
98
+ )
99
+
100
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null
101
+
102
+ const getErrorMessage = (error: unknown) => error instanceof Error ? error.message : String(error)
103
+
104
+ const normalizeAudience = (audience: Config["audience"]): string | Array<string> | undefined =>
105
+ Array.isArray(audience) ? Array.from(audience) : audience as string | undefined
106
+
107
+ const buildDiscoveryUrl = (issuerBaseURL: string) => {
108
+ const url = new URL(issuerBaseURL)
109
+ if (!url.pathname.includes("/.well-known/")) {
110
+ url.pathname = url.pathname.endsWith("/")
111
+ ? `${url.pathname}.well-known/openid-configuration`
112
+ : `${url.pathname}/.well-known/openid-configuration`
113
+ }
114
+ url.search = ""
115
+ url.hash = ""
116
+ return url
117
+ }
118
+
119
+ const fetchDiscoveryDocumentPromise = async (issuerBaseURL: string) => {
120
+ const response = await fetch(buildDiscoveryUrl(issuerBaseURL))
121
+ if (!response.ok) {
122
+ throw new Error(`Failed to fetch authorization server metadata: ${response.status}`)
123
+ }
124
+ const json = await response.json()
125
+ if (!isRecord(json) || typeof json["issuer"] !== "string" || typeof json["jwks_uri"] !== "string") {
126
+ throw new Error("Invalid authorization server metadata")
127
+ }
128
+ return { issuer: json["issuer"], jwksUri: json["jwks_uri"] }
129
+ }
130
+
131
+ const getAuthorizationToken = (headers: HttpHeaders.Headers, authRequired: boolean) => {
132
+ const authorization = HttpHeaders.get(headers, "authorization")
133
+ if (Option.isNone(authorization)) {
134
+ return authRequired ? Effect.fail(new UnauthorizedError()) : Effect.succeed(Option.none<string>())
135
+ }
136
+
137
+ const [scheme, token] = authorization.value.split(" ")
138
+ if (!scheme || !token || scheme.toLowerCase() !== "bearer") {
139
+ return Effect.fail(new InvalidRequestError("", false))
140
+ }
141
+
142
+ return Effect.succeed(Option.some(token))
143
+ }
144
+
145
+ const makeResolveConfig = (config: Config) => {
146
+ let cached: Promise<ResolvedConfig> | undefined
147
+
148
+ return Effect.tryPromise({
149
+ try: () => {
150
+ if (!cached) {
151
+ cached = (async (): Promise<ResolvedConfig> => {
152
+ const discovery = config.issuerBaseURL
153
+ ? await fetchDiscoveryDocumentPromise(config.issuerBaseURL)
154
+ : undefined
155
+
156
+ const issuer = config.issuer ?? discovery?.issuer
157
+ const jwksUri = config.jwksUri ?? discovery?.jwksUri
158
+ const secret = config.secret
159
+ const base = {
160
+ audience: normalizeAudience(config.audience),
161
+ clockTolerance: config.clockTolerance ?? 5,
162
+ issuer,
163
+ maxTokenAge: config.maxTokenAge,
164
+ strict: config.strict ?? false,
165
+ tokenSigningAlg: config.tokenSigningAlg
166
+ } satisfies ResolvedConfigBase
167
+
168
+ if (!issuer && !secret) {
169
+ throw new InvalidRequestError("JWT config requires 'issuer', 'issuerBaseURL', or 'secret'")
170
+ }
171
+
172
+ if (!secret) {
173
+ if (!jwksUri) {
174
+ throw new InvalidRequestError("JWT config requires 'jwksUri', 'issuerBaseURL', or 'secret'")
175
+ }
176
+
177
+ return {
178
+ ...base,
179
+ key: createRemoteJWKSet(new URL(jwksUri)),
180
+ keyType: "jwks"
181
+ }
182
+ }
183
+
184
+ return {
185
+ ...base,
186
+ key: new TextEncoder().encode(secret),
187
+ keyType: "secret"
188
+ }
189
+ })()
190
+ }
191
+
192
+ return cached
193
+ },
194
+ catch: (error) =>
195
+ error instanceof InvalidRequestError || error instanceof InvalidTokenError
196
+ ? error
197
+ : new InvalidTokenError(getErrorMessage(error))
198
+ })
199
+ }
200
+
201
+ const verifyToken =
202
+ (resolveConfig: Effect.Effect<ResolvedConfig, InvalidRequestError | InvalidTokenError>) => (token: string) =>
203
+ resolveConfig.pipe(
204
+ Effect.flatMap((config) => {
205
+ const options = {
206
+ clockTolerance: config.clockTolerance,
207
+ ...(config.tokenSigningAlg ? { algorithms: [config.tokenSigningAlg] } : {}),
208
+ ...(config.audience !== undefined ? { audience: config.audience } : {}),
209
+ ...(config.issuer !== undefined ? { issuer: config.issuer } : {}),
210
+ ...(config.maxTokenAge !== undefined ? { maxTokenAge: config.maxTokenAge } : {})
211
+ }
212
+ const verified = config.keyType === "jwks"
213
+ ? Effect.tryPromise({
214
+ try: () => jwtVerify(token, config.key, options).then(({ protectedHeader }) => ({ protectedHeader })),
215
+ catch: (error) => new InvalidTokenError(getErrorMessage(error))
216
+ })
217
+ : Effect.tryPromise({
218
+ try: () => jwtVerify(token, config.key, options).then(({ protectedHeader }) => ({ protectedHeader })),
219
+ catch: (error) => new InvalidTokenError(getErrorMessage(error))
220
+ })
221
+
222
+ return verified.pipe(
223
+ Effect.flatMap(({ protectedHeader }) => {
224
+ const typ = protectedHeader.typ?.toLowerCase().replace(/^application\//, "")
225
+ return config.strict && typ !== "at+jwt"
226
+ ? Effect.fail(new InvalidTokenError("Unexpected 'typ' value"))
227
+ : Effect.void
228
+ })
229
+ )
230
+ })
231
+ )
232
+
233
+ export const checkJWTI = (config: Config) => {
234
+ const resolveConfig = makeResolveConfig(config)
235
+ const verify = verifyToken(resolveConfig)
236
+
237
+ return Effect.fnUntraced(function*(headers: HttpHeaders.Headers) {
238
+ const token = yield* getAuthorizationToken(headers, config.authRequired !== false)
239
+ if (Option.isNone(token)) {
240
+ return
241
+ }
242
+
243
+ yield* verify(token.value)
244
+ })
245
+ }
246
+
247
+ export const checkJwt = (config: Config) => {
248
+ const check = checkJWTI(config)
249
+ return HttpMiddleware.make((app) =>
250
+ Effect.gen(function*() {
251
+ const req = yield* HttpServerRequest.HttpServerRequest
252
+ const response = yield* check(req.headers).pipe(
253
+ Effect.catch((error: JwtError) =>
254
+ HttpServerResponse.json({ message: error.message }, {
255
+ status: error.status,
256
+ headers: HttpHeaders.fromInput(error.headers)
257
+ })
258
+ )
259
+ )
260
+
261
+ if (response) {
262
+ return response
263
+ }
264
+
265
+ return yield* app
266
+ })
267
+ )
268
+ }
269
+
270
+ export class JWTError extends Data.TaggedClass("JWTError")<{
271
+ error: JwtError
272
+ }> {}