@effect-app/infra 4.0.0-beta.99 → 4.0.0

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 (486) hide show
  1. package/CHANGELOG.md +2884 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +28 -9
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +35 -14
  6. package/dist/ClusterCosmos.d.ts +67 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +588 -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/{api/ContextProvider.d.ts → ContextProvider.d.ts} +7 -4
  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 +11 -7
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +22 -6
  24. package/dist/QueueMaker/errors.d.ts +3 -1
  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 +34 -45
  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 +394 -319
  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 +91 -39
  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 +348 -78
  44. package/dist/Store/SQL/Pg.d.ts +2 -2
  45. package/dist/Store/SQL/Pg.d.ts.map +1 -1
  46. package/dist/Store/SQL/Pg.js +229 -154
  47. package/dist/Store/SQL/query.d.ts +19 -3
  48. package/dist/Store/SQL/query.d.ts.map +1 -1
  49. package/dist/Store/SQL/query.js +306 -12
  50. package/dist/Store/SQL.d.ts +22 -5
  51. package/dist/Store/SQL.d.ts.map +1 -1
  52. package/dist/Store/SQL.js +369 -98
  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 +9 -6
  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 +41 -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/{api/internal → internal}/health.d.ts +1 -1
  98. package/dist/internal/health.d.ts.map +1 -0
  99. package/dist/internal/health.js +5 -0
  100. package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +10 -2
  101. package/dist/layerUtils.d.ts.map +1 -0
  102. package/dist/layerUtils.js +17 -0
  103. package/dist/logger/jsonLogger.d.ts +1 -1
  104. package/dist/logger/jsonLogger.d.ts.map +1 -1
  105. package/dist/logger/jsonLogger.js +5 -3
  106. package/dist/logger/logFmtLogger.d.ts +1 -1
  107. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  108. package/dist/logger/logFmtLogger.js +3 -3
  109. package/dist/logger/shared.d.ts +2 -2
  110. package/dist/logger/shared.d.ts.map +1 -1
  111. package/dist/logger/shared.js +6 -6
  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} +2 -1
  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} +4 -1
  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 +2 -1
  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/{api/routing → routing}/middleware/middleware.d.ts +11 -6
  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 +23 -19
  157. package/package.json +36 -364
  158. package/src/CUPS.ts +53 -14
  159. package/src/ClusterCosmos.ts +1170 -0
  160. package/src/ClusterServiceBus.ts +244 -0
  161. package/src/{api/ContextProvider.ts → ContextProvider.ts} +10 -7
  162. package/src/Emailer/Sendgrid.ts +83 -60
  163. package/src/Emailer/fake.ts +4 -4
  164. package/src/MainFiberSet.ts +31 -12
  165. package/src/QueueMaker/errors.ts +3 -1
  166. package/src/{adapters/SQL → SQL}/Model.ts +48 -47
  167. package/src/SQL.ts +1 -0
  168. package/src/Store/Cosmos/query.ts +275 -55
  169. package/src/Store/Cosmos.ts +597 -511
  170. package/src/Store/Disk.ts +145 -71
  171. package/src/Store/Memory.ts +420 -115
  172. package/src/Store/SQL/Pg.ts +352 -268
  173. package/src/Store/SQL/query.ts +366 -20
  174. package/src/Store/SQL.ts +742 -293
  175. package/src/Store/codeFilter.ts +78 -28
  176. package/src/Store/index.ts +18 -13
  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 +72 -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 +18 -9
  189. package/src/{api/layerUtils.ts → layerUtils.ts} +7 -3
  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} +3 -1
  195. package/src/otel.ts +177 -0
  196. package/src/rateLimit.ts +35 -24
  197. package/src/{adapters/redis-client.ts → redis-client.ts} +6 -2
  198. package/src/{api/reportError.ts → reportError.ts} +3 -2
  199. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +2 -1
  200. package/src/{api/routing → routing}/middleware/middleware.ts +75 -50
  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 +634 -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 +11 -8
  214. package/test/controller.test.ts +23 -28
  215. package/test/cosmos-container-throughput.test.ts +80 -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 +17 -9
  228. package/test/dist/fixtures.d.ts.map +1 -1
  229. package/test/dist/fixtures.js +8 -3
  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/workflow-engine-cosmos.test.d.ts.map +1 -0
  243. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  244. package/test/fixtures.ts +7 -2
  245. package/test/json-lower.test.ts +129 -0
  246. package/test/layerUtils.test.ts +2 -2
  247. package/test/query.test.ts +1232 -42
  248. package/test/rawQuery.test.ts +432 -7
  249. package/test/repository-ext.test.ts +456 -0
  250. package/test/repository-legacy-document.test.ts +394 -0
  251. package/test/requires.test.ts +7 -3
  252. package/test/router-generator.test.ts +187 -0
  253. package/test/routing-interruptibility.test.ts +66 -0
  254. package/test/rpc-context-map-streaming.test.ts +265 -0
  255. package/test/rpc-e2e-invalidation.test.ts +421 -0
  256. package/test/rpc-multi-middleware.test.ts +17 -11
  257. package/test/rpc-stream-fullstack.test.ts +307 -0
  258. package/test/sql-store.test.ts +1202 -47
  259. package/test/validateSample.test.ts +70 -42
  260. package/test/workflow-engine-cosmos.test.ts +345 -0
  261. package/test/workflow-engine-sqlite.test.ts +297 -0
  262. package/tsconfig.examples.json +1 -1
  263. package/tsconfig.json +9 -1
  264. package/tsconfig.src.json +35 -35
  265. package/tsconfig.test.json +2 -2
  266. package/dist/Emailer/service.d.ts +0 -55
  267. package/dist/Emailer/service.d.ts.map +0 -1
  268. package/dist/Emailer/service.js +0 -6
  269. package/dist/Emailer.d.ts +0 -2
  270. package/dist/Emailer.d.ts.map +0 -1
  271. package/dist/Emailer.js +0 -2
  272. package/dist/Model/Repository/ext.d.ts +0 -41
  273. package/dist/Model/Repository/ext.d.ts.map +0 -1
  274. package/dist/Model/Repository/ext.js +0 -65
  275. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  276. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  277. package/dist/Model/Repository/internal/internal.js +0 -320
  278. package/dist/Model/Repository/legacy.d.ts +0 -19
  279. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  280. package/dist/Model/Repository/legacy.js +0 -2
  281. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  282. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  283. package/dist/Model/Repository/makeRepo.js +0 -24
  284. package/dist/Model/Repository/service.d.ts +0 -89
  285. package/dist/Model/Repository/service.d.ts.map +0 -1
  286. package/dist/Model/Repository/service.js +0 -2
  287. package/dist/Model/Repository/validation.d.ts +0 -43
  288. package/dist/Model/Repository/validation.d.ts.map +0 -1
  289. package/dist/Model/Repository/validation.js +0 -32
  290. package/dist/Model/Repository.d.ts +0 -6
  291. package/dist/Model/Repository.d.ts.map +0 -1
  292. package/dist/Model/Repository.js +0 -6
  293. package/dist/Model/dsl.d.ts +0 -32
  294. package/dist/Model/dsl.d.ts.map +0 -1
  295. package/dist/Model/dsl.js +0 -44
  296. package/dist/Model/filter/filterApi.d.ts +0 -30
  297. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  298. package/dist/Model/filter/filterApi.js +0 -2
  299. package/dist/Model/filter/types/errors.d.ts +0 -29
  300. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  301. package/dist/Model/filter/types/errors.js +0 -2
  302. package/dist/Model/filter/types/fields.d.ts +0 -15
  303. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  304. package/dist/Model/filter/types/fields.js +0 -2
  305. package/dist/Model/filter/types/path/common.d.ts +0 -316
  306. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  307. package/dist/Model/filter/types/path/common.js +0 -2
  308. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  309. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  310. package/dist/Model/filter/types/path/eager.js +0 -31
  311. package/dist/Model/filter/types/path/index.d.ts +0 -4
  312. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  313. package/dist/Model/filter/types/path/index.js +0 -3
  314. package/dist/Model/filter/types/utils.d.ts +0 -79
  315. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  316. package/dist/Model/filter/types/utils.js +0 -2
  317. package/dist/Model/filter/types/validator.d.ts +0 -30
  318. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  319. package/dist/Model/filter/types/validator.js +0 -2
  320. package/dist/Model/filter/types.d.ts +0 -5
  321. package/dist/Model/filter/types.d.ts.map +0 -1
  322. package/dist/Model/filter/types.js +0 -7
  323. package/dist/Model/query/dsl.d.ts +0 -248
  324. package/dist/Model/query/dsl.d.ts.map +0 -1
  325. package/dist/Model/query/dsl.js +0 -104
  326. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  327. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  328. package/dist/Model/query/new-kid-interpreter.js +0 -165
  329. package/dist/Model/query.d.ts +0 -15
  330. package/dist/Model/query.d.ts.map +0 -1
  331. package/dist/Model/query.js +0 -3
  332. package/dist/Model.d.ts +0 -4
  333. package/dist/Model.d.ts.map +0 -1
  334. package/dist/Model.js +0 -4
  335. package/dist/Operations.d.ts +0 -55
  336. package/dist/Operations.d.ts.map +0 -1
  337. package/dist/Operations.js +0 -102
  338. package/dist/OperationsRepo.d.ts +0 -41
  339. package/dist/OperationsRepo.d.ts.map +0 -1
  340. package/dist/OperationsRepo.js +0 -14
  341. package/dist/QueueMaker/SQLQueue.d.ts +0 -14
  342. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  343. package/dist/QueueMaker/SQLQueue.js +0 -132
  344. package/dist/QueueMaker/memQueue.d.ts +0 -11
  345. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  346. package/dist/QueueMaker/memQueue.js +0 -76
  347. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  348. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  349. package/dist/QueueMaker/sbqueue.js +0 -91
  350. package/dist/QueueMaker/service.d.ts +0 -11
  351. package/dist/QueueMaker/service.d.ts.map +0 -1
  352. package/dist/QueueMaker/service.js +0 -4
  353. package/dist/RequestContext.d.ts +0 -68
  354. package/dist/RequestContext.d.ts.map +0 -1
  355. package/dist/RequestContext.js +0 -49
  356. package/dist/RequestFiberSet.d.ts +0 -36
  357. package/dist/RequestFiberSet.d.ts.map +0 -1
  358. package/dist/RequestFiberSet.js +0 -78
  359. package/dist/Store/ContextMapContainer.d.ts +0 -25
  360. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  361. package/dist/Store/ContextMapContainer.js +0 -77
  362. package/dist/Store/service.d.ts +0 -111
  363. package/dist/Store/service.d.ts.map +0 -1
  364. package/dist/Store/service.js +0 -79
  365. package/dist/Store.d.ts +0 -2
  366. package/dist/Store.d.ts.map +0 -1
  367. package/dist/Store.js +0 -2
  368. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  369. package/dist/adapters/SQL/Model.js +0 -478
  370. package/dist/adapters/SQL.d.ts.map +0 -1
  371. package/dist/adapters/ServiceBus.d.ts +0 -58
  372. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  373. package/dist/adapters/ServiceBus.js +0 -99
  374. package/dist/adapters/cosmos-client.d.ts +0 -14
  375. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  376. package/dist/adapters/cosmos-client.js +0 -9
  377. package/dist/adapters/index.d.ts +0 -2
  378. package/dist/adapters/index.d.ts.map +0 -1
  379. package/dist/adapters/index.js +0 -2
  380. package/dist/adapters/logger.d.ts +0 -9
  381. package/dist/adapters/logger.d.ts.map +0 -1
  382. package/dist/adapters/logger.js +0 -3
  383. package/dist/adapters/memQueue.d.ts +0 -13
  384. package/dist/adapters/memQueue.d.ts.map +0 -1
  385. package/dist/adapters/memQueue.js +0 -20
  386. package/dist/adapters/mongo-client.d.ts.map +0 -1
  387. package/dist/adapters/mongo-client.js +0 -13
  388. package/dist/adapters/redis-client.d.ts.map +0 -1
  389. package/dist/adapters/redis-client.js +0 -94
  390. package/dist/api/ContextProvider.d.ts.map +0 -1
  391. package/dist/api/ContextProvider.js +0 -38
  392. package/dist/api/codec.d.ts.map +0 -1
  393. package/dist/api/codec.js +0 -5
  394. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  395. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  396. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  397. package/dist/api/internal/auth.d.ts +0 -15
  398. package/dist/api/internal/auth.d.ts.map +0 -1
  399. package/dist/api/internal/auth.js +0 -47
  400. package/dist/api/internal/events.d.ts +0 -9
  401. package/dist/api/internal/events.d.ts.map +0 -1
  402. package/dist/api/internal/events.js +0 -42
  403. package/dist/api/internal/health.d.ts.map +0 -1
  404. package/dist/api/internal/health.js +0 -5
  405. package/dist/api/layerUtils.d.ts.map +0 -1
  406. package/dist/api/layerUtils.js +0 -16
  407. package/dist/api/middlewares.d.ts.map +0 -1
  408. package/dist/api/reportError.d.ts.map +0 -1
  409. package/dist/api/reportError.js +0 -27
  410. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  411. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  412. package/dist/api/routing/middleware/middleware.js +0 -130
  413. package/dist/api/routing/middleware.d.ts.map +0 -1
  414. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  415. package/dist/api/routing/schema/jwt.js +0 -12
  416. package/dist/api/routing/tsort.d.ts.map +0 -1
  417. package/dist/api/routing/tsort.js +0 -51
  418. package/dist/api/routing/utils.d.ts.map +0 -1
  419. package/dist/api/routing/utils.js +0 -44
  420. package/dist/api/routing.d.ts +0 -134
  421. package/dist/api/routing.d.ts.map +0 -1
  422. package/dist/api/routing.js +0 -167
  423. package/dist/api/setupRequest.d.ts +0 -15
  424. package/dist/api/setupRequest.d.ts.map +0 -1
  425. package/dist/api/setupRequest.js +0 -49
  426. package/dist/api/util.d.ts.map +0 -1
  427. package/dist/api/util.js +0 -14
  428. package/eslint.config.mjs +0 -24
  429. package/src/Emailer/service.ts +0 -52
  430. package/src/Emailer.ts +0 -1
  431. package/src/Model/Repository/ext.ts +0 -283
  432. package/src/Model/Repository/internal/internal.ts +0 -580
  433. package/src/Model/Repository/legacy.ts +0 -27
  434. package/src/Model/Repository/makeRepo.ts +0 -139
  435. package/src/Model/Repository/service.ts +0 -627
  436. package/src/Model/Repository/validation.ts +0 -31
  437. package/src/Model/Repository.ts +0 -5
  438. package/src/Model/dsl.ts +0 -128
  439. package/src/Model/filter/filterApi.ts +0 -60
  440. package/src/Model/filter/types/errors.ts +0 -47
  441. package/src/Model/filter/types/fields.ts +0 -50
  442. package/src/Model/filter/types/path/common.ts +0 -404
  443. package/src/Model/filter/types/path/eager.ts +0 -298
  444. package/src/Model/filter/types/path/index.ts +0 -4
  445. package/src/Model/filter/types/utils.ts +0 -128
  446. package/src/Model/filter/types/validator.ts +0 -46
  447. package/src/Model/filter/types.ts +0 -6
  448. package/src/Model/query/dsl.ts +0 -2110
  449. package/src/Model/query/new-kid-interpreter.ts +0 -210
  450. package/src/Model/query.ts +0 -13
  451. package/src/Model.ts +0 -3
  452. package/src/Operations.ts +0 -235
  453. package/src/OperationsRepo.ts +0 -16
  454. package/src/QueueMaker/SQLQueue.ts +0 -190
  455. package/src/QueueMaker/memQueue.ts +0 -130
  456. package/src/QueueMaker/sbqueue.ts +0 -137
  457. package/src/QueueMaker/service.ts +0 -17
  458. package/src/RequestContext.ts +0 -63
  459. package/src/RequestFiberSet.ts +0 -105
  460. package/src/Store/ContextMapContainer.ts +0 -116
  461. package/src/Store/index.test.ts.bak +0 -112
  462. package/src/Store/service.ts +0 -193
  463. package/src/Store.ts +0 -1
  464. package/src/adapters/SQL.ts +0 -1
  465. package/src/adapters/ServiceBus.ts +0 -209
  466. package/src/adapters/cosmos-client.ts +0 -16
  467. package/src/adapters/index.ts +0 -0
  468. package/src/adapters/logger.ts +0 -3
  469. package/src/adapters/memQueue.ts +0 -21
  470. package/src/api/internal/auth.ts +0 -68
  471. package/src/api/middlewares.ts +0 -10
  472. package/src/api/routing/middleware.ts +0 -6
  473. package/src/api/routing.ts +0 -598
  474. package/src/api/setupRequest.ts +0 -104
  475. package/test/dist/date-query.test.d.ts.map +0 -1
  476. package/tsconfig.json.bak +0 -12
  477. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  478. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  479. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  480. /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
  481. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  482. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  483. /package/src/{api/codec.ts → codec.ts} +0 -0
  484. /package/src/{api/internal → internal}/health.ts +0 -0
  485. /package/src/{api/routing → routing}/tsort.ts +0 -0
  486. /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 Arbitrary from "effect/Arbitrary"
7
+ import * as Effect from "effect/Effect"
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,72 @@
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
+ const isServerlessOfferError = (error: unknown): boolean => {
40
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined
41
+ const message = error instanceof Error
42
+ ? error.message
43
+ : typeof error === "object" && error !== null && "message" in error
44
+ ? String(error.message)
45
+ : String(error)
46
+ const badRequest = code === 400 || code === "BadRequest" || code === "400"
47
+ return badRequest && message.toLowerCase().includes("not supported for serverless")
48
+ }
49
+
50
+ export const createContainerIfNotExists = async (
51
+ db: ContainerDb,
52
+ body: ContainerRequest & { id: string },
53
+ throughput?: CosmosContainerThroughput
54
+ ) => {
55
+ const maxThroughput = throughput?.autoscaleMaxThroughput
56
+ if (maxThroughput === undefined) {
57
+ await db.containers.createIfNotExists(body)
58
+ return
59
+ }
60
+ const exists = await db.container(body.id).read().then(
61
+ () => true,
62
+ (e) => isNotFound(e) ? false : Promise.reject(e)
63
+ )
64
+ if (exists) return
65
+ try {
66
+ const { resource: sharedOffer } = await db.readOffer()
67
+ await db.containers.createIfNotExists(sharedOffer ? body : { ...body, maxThroughput })
68
+ } catch (error) {
69
+ if (!isServerlessOfferError(error)) throw error
70
+ await db.containers.createIfNotExists(body)
71
+ }
72
+ }
@@ -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
+ }> {}