effect-start 0.23.0 → 0.25.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 (356) hide show
  1. package/dist/ChildProcess.js +32 -20
  2. package/dist/Commander.js +377 -293
  3. package/dist/ContentNegotiation.js +424 -318
  4. package/dist/Cookies.js +340 -271
  5. package/dist/Development.js +85 -49
  6. package/dist/Effectify.js +22 -14
  7. package/dist/Entity.js +260 -195
  8. package/dist/Fetch.js +192 -0
  9. package/dist/FilePathPattern.js +88 -77
  10. package/dist/FileRouter.js +192 -136
  11. package/dist/FileRouterCodegen.js +262 -191
  12. package/dist/FileSystem.js +126 -64
  13. package/dist/Http.js +96 -77
  14. package/dist/PathPattern.js +311 -273
  15. package/dist/PlatformError.js +36 -21
  16. package/dist/PlatformRuntime.js +65 -40
  17. package/dist/Route.js +122 -79
  18. package/dist/RouteBody.js +83 -58
  19. package/dist/RouteError.js +46 -25
  20. package/dist/RouteHook.js +58 -34
  21. package/dist/RouteHttp.js +346 -237
  22. package/dist/RouteHttpTracer.js +86 -58
  23. package/dist/RouteMount.js +81 -58
  24. package/dist/RouteSchema.js +253 -170
  25. package/dist/RouteSse.js +87 -72
  26. package/dist/RouteTree.js +99 -73
  27. package/dist/RouteTrie.js +160 -133
  28. package/dist/SchemaExtra.js +87 -62
  29. package/dist/Socket.js +32 -21
  30. package/dist/SqlIntrospect.js +317 -268
  31. package/dist/Start.js +55 -25
  32. package/dist/StartApp.js +3 -21
  33. package/dist/StreamExtra.js +109 -74
  34. package/dist/System.js +37 -21
  35. package/dist/TuplePathPattern.js +64 -58
  36. package/dist/Unique.js +159 -120
  37. package/dist/Values.js +48 -32
  38. package/dist/bun/BunBundle.js +158 -109
  39. package/dist/bun/BunChildProcessSpawner.js +121 -82
  40. package/dist/bun/BunImportTrackerPlugin.js +85 -63
  41. package/dist/bun/BunRoute.js +135 -99
  42. package/dist/bun/BunRuntime.js +39 -29
  43. package/dist/bun/BunServer.js +268 -140
  44. package/dist/bun/BunVirtualFilesPlugin.js +47 -33
  45. package/dist/bun/_BunEnhancedResolve.js +107 -82
  46. package/dist/bun/index.js +5 -5
  47. package/dist/bundler/Bundle.js +82 -38
  48. package/dist/bundler/BundleFiles.js +140 -82
  49. package/dist/bundler/BundleRoute.js +49 -38
  50. package/dist/client/Overlay.js +29 -28
  51. package/dist/client/ScrollState.js +94 -82
  52. package/dist/client/index.js +79 -61
  53. package/dist/console/Console.js +40 -24
  54. package/dist/console/ConsoleErrors.js +189 -178
  55. package/dist/console/ConsoleLogger.js +52 -43
  56. package/dist/console/ConsoleMetrics.js +69 -58
  57. package/dist/console/ConsoleProcess.js +57 -47
  58. package/dist/console/ConsoleStore.js +56 -45
  59. package/dist/console/ConsoleTracer.js +101 -88
  60. package/dist/console/Simulation.js +714 -563
  61. package/dist/console/index.js +3 -3
  62. package/dist/console/routes/tree.js +29 -28
  63. package/dist/datastar/actions/fetch.js +514 -381
  64. package/dist/datastar/actions/peek.js +12 -12
  65. package/dist/datastar/actions/setAll.js +18 -11
  66. package/dist/datastar/actions/toggleAll.js +18 -11
  67. package/dist/datastar/attributes/attr.js +48 -47
  68. package/dist/datastar/attributes/bind.js +186 -167
  69. package/dist/datastar/attributes/class.js +51 -44
  70. package/dist/datastar/attributes/computed.js +23 -24
  71. package/dist/datastar/attributes/effect.js +9 -8
  72. package/dist/datastar/attributes/indicator.js +32 -29
  73. package/dist/datastar/attributes/init.js +26 -25
  74. package/dist/datastar/attributes/jsonSignals.js +32 -29
  75. package/dist/datastar/attributes/on.js +76 -73
  76. package/dist/datastar/attributes/onIntersect.js +51 -51
  77. package/dist/datastar/attributes/onInterval.js +30 -29
  78. package/dist/datastar/attributes/onSignalPatch.js +49 -40
  79. package/dist/datastar/attributes/ref.js +10 -9
  80. package/dist/datastar/attributes/show.js +31 -30
  81. package/dist/datastar/attributes/signals.js +17 -16
  82. package/dist/datastar/attributes/style.js +56 -49
  83. package/dist/datastar/attributes/text.js +28 -25
  84. package/dist/datastar/engine.js +1079 -933
  85. package/dist/datastar/index.js +25 -24
  86. package/dist/datastar/utils.js +203 -155
  87. package/dist/datastar/watchers/patchElements.js +459 -372
  88. package/dist/datastar/watchers/patchSignals.js +13 -13
  89. package/dist/experimental/EncryptedCookies.js +305 -189
  90. package/dist/experimental/index.js +1 -1
  91. package/dist/hyper/Hyper.js +22 -17
  92. package/dist/hyper/HyperHtml.js +138 -123
  93. package/dist/hyper/HyperNode.js +11 -9
  94. package/dist/hyper/HyperRoute.js +41 -28
  95. package/dist/hyper/html.js +27 -27
  96. package/dist/hyper/index.js +5 -5
  97. package/dist/hyper/jsx-runtime.js +11 -5
  98. package/dist/index.js +8 -8
  99. package/dist/node/NodeFileSystem.js +606 -341
  100. package/dist/node/NodeUtils.js +21 -18
  101. package/dist/sql/Sql.js +8 -0
  102. package/dist/sql/bun/index.js +134 -67
  103. package/dist/sql/index.js +1 -0
  104. package/dist/sql/libsql/index.js +156 -0
  105. package/dist/sql/mssql/docker.js +103 -60
  106. package/dist/sql/mssql/index.js +182 -101
  107. package/dist/testing/TestLogger.js +39 -29
  108. package/dist/testing/index.js +2 -2
  109. package/dist/testing/utils.js +45 -23
  110. package/dist/x/cloudflare/CloudflareTunnel.js +61 -28
  111. package/dist/x/cloudflare/index.js +1 -1
  112. package/dist/x/tailscale/TailscaleTunnel.js +86 -60
  113. package/dist/x/tailscale/index.js +1 -1
  114. package/dist/x/tailwind/TailwindPlugin.js +280 -205
  115. package/dist/x/tailwind/compile.js +185 -129
  116. package/dist/x/tailwind/plugin.js +13 -11
  117. package/package.json +1 -1
  118. package/src/Development.ts +3 -1
  119. package/src/Entity.ts +17 -0
  120. package/src/Fetch.ts +271 -0
  121. package/src/SqlIntrospect.ts +64 -70
  122. package/src/Start.ts +24 -22
  123. package/src/StartApp.ts +11 -0
  124. package/src/bun/BunServer.ts +89 -16
  125. package/src/hyper/HyperHtml.ts +0 -1
  126. package/src/sql/bun/index.ts +1 -1
  127. package/src/sql/index.ts +1 -0
  128. package/src/sql/libsql/index.ts +173 -0
  129. package/src/sql/libsql/libsql.d.ts +39 -0
  130. package/src/sql/mssql/index.ts +1 -1
  131. package/src/x/tailscale/TailscaleTunnel.ts +7 -5
  132. package/dist/BlobStore.d.ts +0 -80
  133. package/dist/BlobStore.js +0 -19
  134. package/dist/ChildProcess.d.ts +0 -60
  135. package/dist/Commander.d.ts +0 -100
  136. package/dist/ContentNegotiation.d.ts +0 -12
  137. package/dist/Cookies.d.ts +0 -47
  138. package/dist/Development.d.ts +0 -39
  139. package/dist/Effectify.d.ts +0 -209
  140. package/dist/Entity.d.ts +0 -47
  141. package/dist/FilePathPattern.d.ts +0 -29
  142. package/dist/FileRouter.d.ts +0 -56
  143. package/dist/FileRouterCodegen.d.ts +0 -18
  144. package/dist/FileRouterPattern.d.ts +0 -9
  145. package/dist/FileRouterPattern.js +0 -35
  146. package/dist/FileSystem.d.ts +0 -158
  147. package/dist/Http.d.ts +0 -37
  148. package/dist/HttpAppExtra.d.ts +0 -7
  149. package/dist/HttpAppExtra.js +0 -320
  150. package/dist/HttpUtils.d.ts +0 -3
  151. package/dist/HttpUtils.js +0 -11
  152. package/dist/PathPattern.d.ts +0 -134
  153. package/dist/PlatformError.d.ts +0 -38
  154. package/dist/PlatformRuntime.d.ts +0 -27
  155. package/dist/Route.d.ts +0 -97
  156. package/dist/RouteBody.d.ts +0 -47
  157. package/dist/RouteError.d.ts +0 -98
  158. package/dist/RouteHook.d.ts +0 -12
  159. package/dist/RouteHttp.d.ts +0 -21
  160. package/dist/RouteHttpTracer.d.ts +0 -10
  161. package/dist/RouteMount.d.ts +0 -86
  162. package/dist/RouteSchema.d.ts +0 -86
  163. package/dist/RouteSse.d.ts +0 -21
  164. package/dist/RouteTree.d.ts +0 -57
  165. package/dist/RouteTrie.d.ts +0 -20
  166. package/dist/RouterPattern.d.ts +0 -118
  167. package/dist/RouterPattern.js +0 -269
  168. package/dist/SchemaExtra.d.ts +0 -7
  169. package/dist/Socket.d.ts +0 -27
  170. package/dist/Sql.d.ts +0 -34
  171. package/dist/Sql.js +0 -5
  172. package/dist/SqlIntrospect.d.ts +0 -91
  173. package/dist/Start.d.ts +0 -44
  174. package/dist/StartApp.d.ts +0 -19
  175. package/dist/StreamExtra.d.ts +0 -28
  176. package/dist/System.d.ts +0 -7
  177. package/dist/TuplePathPattern.d.ts +0 -9
  178. package/dist/Unique.d.ts +0 -50
  179. package/dist/Values.d.ts +0 -27
  180. package/dist/bun/BunBlobStoreDisk.d.ts +0 -6
  181. package/dist/bun/BunBlobStoreDisk.js +0 -116
  182. package/dist/bun/BunBlobStoreS3.d.ts +0 -11
  183. package/dist/bun/BunBlobStoreS3.js +0 -89
  184. package/dist/bun/BunBlobWatcherDisk.d.ts +0 -6
  185. package/dist/bun/BunBlobWatcherDisk.js +0 -60
  186. package/dist/bun/BunBlobWatcherQueue.d.ts +0 -6
  187. package/dist/bun/BunBlobWatcherQueue.js +0 -17
  188. package/dist/bun/BunBundle.d.ts +0 -11
  189. package/dist/bun/BunChildProcessSpawner.d.ts +0 -3
  190. package/dist/bun/BunHttpServer.d.ts +0 -44
  191. package/dist/bun/BunHttpServer.js +0 -186
  192. package/dist/bun/BunHttpServer_web.d.ts +0 -60
  193. package/dist/bun/BunHttpServer_web.js +0 -252
  194. package/dist/bun/BunImportTrackerPlugin.d.ts +0 -13
  195. package/dist/bun/BunPlatformHttpServer.d.ts +0 -10
  196. package/dist/bun/BunPlatformHttpServer.js +0 -53
  197. package/dist/bun/BunRoute.d.ts +0 -48
  198. package/dist/bun/BunRuntime.d.ts +0 -2
  199. package/dist/bun/BunServer.d.ts +0 -40
  200. package/dist/bun/BunServerRequest.d.ts +0 -60
  201. package/dist/bun/BunServerRequest.js +0 -252
  202. package/dist/bun/BunSql.d.ts +0 -4
  203. package/dist/bun/BunSql.js +0 -81
  204. package/dist/bun/BunVirtualFilesPlugin.d.ts +0 -4
  205. package/dist/bun/_BunEnhancedResolve.d.ts +0 -45
  206. package/dist/bun/index.d.ts +0 -5
  207. package/dist/bundler/Bundle.d.ts +0 -61
  208. package/dist/bundler/BundleFiles.d.ts +0 -13
  209. package/dist/bundler/BundleHttp.d.ts +0 -45
  210. package/dist/bundler/BundleHttp.js +0 -176
  211. package/dist/bundler/BundleRoute.d.ts +0 -27
  212. package/dist/client/Overlay.d.ts +0 -2
  213. package/dist/client/ScrollState.d.ts +0 -6
  214. package/dist/client/index.d.ts +0 -6
  215. package/dist/console/Console.d.ts +0 -6
  216. package/dist/console/ConsoleErrors.d.ts +0 -3
  217. package/dist/console/ConsoleLogger.d.ts +0 -3
  218. package/dist/console/ConsoleMetrics.d.ts +0 -3
  219. package/dist/console/ConsoleProcess.d.ts +0 -3
  220. package/dist/console/ConsoleStore.d.ts +0 -144
  221. package/dist/console/ConsoleTracer.d.ts +0 -3
  222. package/dist/console/Simulation.d.ts +0 -2
  223. package/dist/console/index.d.ts +0 -3
  224. package/dist/console/routes/errors/route.d.ts +0 -10
  225. package/dist/console/routes/errors/route.js +0 -47
  226. package/dist/console/routes/fiberDetail.d.ts +0 -16
  227. package/dist/console/routes/fiberDetail.js +0 -38
  228. package/dist/console/routes/fibers/route.d.ts +0 -10
  229. package/dist/console/routes/fibers/route.js +0 -19
  230. package/dist/console/routes/git/route.d.ts +0 -11
  231. package/dist/console/routes/git/route.js +0 -33
  232. package/dist/console/routes/layout.d.ts +0 -9
  233. package/dist/console/routes/layout.js +0 -3
  234. package/dist/console/routes/logs/route.d.ts +0 -10
  235. package/dist/console/routes/logs/route.js +0 -32
  236. package/dist/console/routes/metrics/route.d.ts +0 -10
  237. package/dist/console/routes/metrics/route.js +0 -17
  238. package/dist/console/routes/route.d.ts +0 -6
  239. package/dist/console/routes/route.js +0 -5
  240. package/dist/console/routes/routes/route.d.ts +0 -6
  241. package/dist/console/routes/routes/route.js +0 -20
  242. package/dist/console/routes/services/route.d.ts +0 -6
  243. package/dist/console/routes/services/route.js +0 -12
  244. package/dist/console/routes/system/route.d.ts +0 -10
  245. package/dist/console/routes/system/route.js +0 -18
  246. package/dist/console/routes/traceDetail.d.ts +0 -16
  247. package/dist/console/routes/traceDetail.js +0 -14
  248. package/dist/console/routes/traces/route.d.ts +0 -10
  249. package/dist/console/routes/traces/route.js +0 -39
  250. package/dist/console/routes/tree.d.ts +0 -153
  251. package/dist/console/ui/Errors.d.ts +0 -4
  252. package/dist/console/ui/Errors.js +0 -15
  253. package/dist/console/ui/Fibers.d.ts +0 -24
  254. package/dist/console/ui/Fibers.js +0 -121
  255. package/dist/console/ui/Git.d.ts +0 -20
  256. package/dist/console/ui/Git.js +0 -95
  257. package/dist/console/ui/Logs.d.ts +0 -4
  258. package/dist/console/ui/Logs.js +0 -25
  259. package/dist/console/ui/Metrics.d.ts +0 -4
  260. package/dist/console/ui/Metrics.js +0 -26
  261. package/dist/console/ui/Routes.d.ts +0 -8
  262. package/dist/console/ui/Routes.js +0 -70
  263. package/dist/console/ui/Services.d.ts +0 -10
  264. package/dist/console/ui/Services.js +0 -246
  265. package/dist/console/ui/Shell.d.ts +0 -10
  266. package/dist/console/ui/Shell.js +0 -7
  267. package/dist/console/ui/System.d.ts +0 -4
  268. package/dist/console/ui/System.js +0 -35
  269. package/dist/console/ui/Traces.d.ts +0 -12
  270. package/dist/console/ui/Traces.js +0 -179
  271. package/dist/datastar/actions/fetch.d.ts +0 -30
  272. package/dist/datastar/actions/peek.d.ts +0 -1
  273. package/dist/datastar/actions/setAll.d.ts +0 -1
  274. package/dist/datastar/actions/toggleAll.d.ts +0 -1
  275. package/dist/datastar/attributes/attr.d.ts +0 -1
  276. package/dist/datastar/attributes/bind.d.ts +0 -1
  277. package/dist/datastar/attributes/class.d.ts +0 -1
  278. package/dist/datastar/attributes/computed.d.ts +0 -1
  279. package/dist/datastar/attributes/effect.d.ts +0 -1
  280. package/dist/datastar/attributes/indicator.d.ts +0 -1
  281. package/dist/datastar/attributes/init.d.ts +0 -1
  282. package/dist/datastar/attributes/jsonSignals.d.ts +0 -1
  283. package/dist/datastar/attributes/on.d.ts +0 -1
  284. package/dist/datastar/attributes/onIntersect.d.ts +0 -1
  285. package/dist/datastar/attributes/onInterval.d.ts +0 -1
  286. package/dist/datastar/attributes/onSignalPatch.d.ts +0 -1
  287. package/dist/datastar/attributes/ref.d.ts +0 -1
  288. package/dist/datastar/attributes/show.d.ts +0 -1
  289. package/dist/datastar/attributes/signals.d.ts +0 -1
  290. package/dist/datastar/attributes/style.d.ts +0 -1
  291. package/dist/datastar/attributes/text.d.ts +0 -1
  292. package/dist/datastar/engine.d.ts +0 -162
  293. package/dist/datastar/happydom.d.ts +0 -1
  294. package/dist/datastar/happydom.js +0 -8
  295. package/dist/datastar/index.d.ts +0 -24
  296. package/dist/datastar/load.d.ts +0 -24
  297. package/dist/datastar/load.js +0 -24
  298. package/dist/datastar/utils.d.ts +0 -51
  299. package/dist/datastar/watchers/patchElements.d.ts +0 -1
  300. package/dist/datastar/watchers/patchSignals.d.ts +0 -1
  301. package/dist/experimental/EncryptedCookies.d.ts +0 -48
  302. package/dist/experimental/SseHttpResponse.d.ts +0 -7
  303. package/dist/experimental/SseHttpResponse.js +0 -28
  304. package/dist/experimental/index.d.ts +0 -1
  305. package/dist/hyper/Hyper.d.ts +0 -25
  306. package/dist/hyper/HyperHtml.d.ts +0 -23
  307. package/dist/hyper/HyperHtml.test.d.ts +0 -1
  308. package/dist/hyper/HyperHtml.test.js +0 -197
  309. package/dist/hyper/HyperNode.d.ts +0 -14
  310. package/dist/hyper/HyperRoute.d.ts +0 -8
  311. package/dist/hyper/HyperRoute.test.d.ts +0 -1
  312. package/dist/hyper/HyperRoute.test.js +0 -83
  313. package/dist/hyper/html.d.ts +0 -11
  314. package/dist/hyper/index.d.ts +0 -6
  315. package/dist/hyper/jsx-runtime.d.ts +0 -7
  316. package/dist/index.d.ts +0 -8
  317. package/dist/inference_check.d.ts +0 -1
  318. package/dist/inference_check.js +0 -15
  319. package/dist/lint/plugin.d.ts +0 -86
  320. package/dist/lint/plugin.js +0 -341
  321. package/dist/middlewares/BasicAuthMiddleware.d.ts +0 -8
  322. package/dist/middlewares/BasicAuthMiddleware.js +0 -22
  323. package/dist/middlewares/index.d.ts +0 -1
  324. package/dist/middlewares/index.js +0 -1
  325. package/dist/node/Effectify.d.ts +0 -209
  326. package/dist/node/Effectify.js +0 -19
  327. package/dist/node/FileSystem.d.ts +0 -7
  328. package/dist/node/FileSystem.js +0 -420
  329. package/dist/node/NodeFileSystem.d.ts +0 -7
  330. package/dist/node/NodeUtils.d.ts +0 -2
  331. package/dist/node/PlatformError.d.ts +0 -46
  332. package/dist/node/PlatformError.js +0 -43
  333. package/dist/node/Utils.d.ts +0 -1
  334. package/dist/node/Utils.js +0 -19
  335. package/dist/repro_fail.d.ts +0 -1
  336. package/dist/repro_fail.js +0 -14
  337. package/dist/sql/bun/index.d.ts +0 -3
  338. package/dist/sql/mssql/docker.d.ts +0 -2
  339. package/dist/sql/mssql/index.d.ts +0 -21
  340. package/dist/testing/TestHttpClient.d.ts +0 -13
  341. package/dist/testing/TestHttpClient.js +0 -68
  342. package/dist/testing/TestLogger.d.ts +0 -13
  343. package/dist/testing/index.d.ts +0 -2
  344. package/dist/testing/utils.d.ts +0 -9
  345. package/dist/x/cloudflare/CloudflareTunnel.d.ts +0 -10
  346. package/dist/x/cloudflare/index.d.ts +0 -1
  347. package/dist/x/datastar/Datastar.d.ts +0 -6
  348. package/dist/x/datastar/Datastar.js +0 -47
  349. package/dist/x/datastar/index.d.ts +0 -1
  350. package/dist/x/datastar/index.js +0 -1
  351. package/dist/x/tailscale/TailscaleTunnel.d.ts +0 -15
  352. package/dist/x/tailscale/index.d.ts +0 -1
  353. package/dist/x/tailwind/TailwindPlugin.d.ts +0 -23
  354. package/dist/x/tailwind/compile.d.ts +0 -19
  355. package/dist/x/tailwind/plugin.d.ts +0 -2
  356. /package/src/{Sql.ts → sql/Sql.ts} +0 -0
package/src/Fetch.ts ADDED
@@ -0,0 +1,271 @@
1
+ import * as Data from "effect/Data"
2
+ import * as Effect from "effect/Effect"
3
+ import * as Schedule from "effect/Schedule"
4
+ import * as Entity from "./Entity.ts"
5
+
6
+ const TypeId: unique symbol = Symbol.for("effect-start/FetchClient")
7
+ type TypeId = typeof TypeId
8
+
9
+ export type FetchEntity = Entity.Entity<Effect.Effect<Uint8Array, FetchError, never>, FetchError>
10
+
11
+ export class FetchError extends Data.TaggedError("FetchError")<{
12
+ readonly reason: "Network" | "Status"
13
+ readonly cause?: unknown
14
+ readonly request?: Request
15
+ readonly response?: FetchEntity
16
+ }> {}
17
+
18
+ type Next = (request: Request) => Effect.Effect<FetchEntity, FetchError, never>
19
+
20
+ export type Middleware<E = never, R = never> = (
21
+ request: Request,
22
+ next: Next,
23
+ ) => Effect.Effect<FetchEntity, FetchError | E, R>
24
+
25
+ const tryFetch: Middleware = (request) =>
26
+ Effect.map(
27
+ Effect.tryPromise({
28
+ try: () => globalThis.fetch(request),
29
+ catch: (e) => new FetchError({ reason: "Network", cause: e, request }),
30
+ }),
31
+ (response) => Entity.fromResponse(response, request) as FetchEntity,
32
+ )
33
+
34
+ const defaultMiddleware: ReadonlyArray<Middleware> = [tryFetch]
35
+ const noopNext: Next = () => Effect.die("no middleware")
36
+
37
+ function withTrace<E, R>(
38
+ request: Request,
39
+ effect: Effect.Effect<FetchEntity, E, R>,
40
+ ): Effect.Effect<FetchEntity, E, R> {
41
+ const url = new URL(request.url)
42
+
43
+ return Effect.useSpan(
44
+ `http.client ${request.method}`,
45
+ { kind: "client", captureStackTrace: false },
46
+ (span) => {
47
+ span.attribute("http.request.method", request.method)
48
+ span.attribute("url.full", url.toString())
49
+ span.attribute("url.path", url.pathname)
50
+ const query = url.search.slice(1)
51
+ if (query !== "") {
52
+ span.attribute("url.query", query)
53
+ }
54
+ span.attribute("url.scheme", url.protocol.slice(0, -1))
55
+
56
+ return Effect.flatMap(Effect.exit(Effect.withParentSpan(effect, span)), (exit) => {
57
+ if (exit._tag === "Success") {
58
+ span.attribute("http.response.status_code", exit.value.status ?? 0)
59
+ }
60
+ return exit
61
+ })
62
+ },
63
+ )
64
+ }
65
+
66
+ export interface FetchClient<E = never, R = never> {
67
+ readonly [TypeId]: TypeId
68
+ readonly middleware: ReadonlyArray<Middleware<any, any>>
69
+ readonly fetch: (
70
+ input: string | URL | Request,
71
+ init?: RequestInit,
72
+ ) => Effect.Effect<FetchEntity, FetchError | E, R>
73
+ readonly get: (
74
+ input: string | URL | Request,
75
+ init?: Omit<RequestInit, "method">,
76
+ ) => Effect.Effect<FetchEntity, FetchError | E, R>
77
+ readonly post: (
78
+ input: string | URL | Request,
79
+ init?: Omit<RequestInit, "method">,
80
+ ) => Effect.Effect<FetchEntity, FetchError | E, R>
81
+ readonly use: <E2, R2>(
82
+ ...middleware: ReadonlyArray<Middleware<E2, R2>>
83
+ ) => FetchClient<E | E2, R | R2>
84
+ }
85
+
86
+ export function fetch(
87
+ input: string | URL | Request,
88
+ init?: RequestInit,
89
+ ): Effect.Effect<FetchEntity, FetchError>
90
+ export function fetch<E, R>(
91
+ this: FetchClient<E, R>,
92
+ input: string | URL | Request,
93
+ init?: RequestInit,
94
+ ): Effect.Effect<FetchEntity, FetchError | E, R>
95
+ export function fetch(
96
+ this: void | FetchClient<any, any>,
97
+ input: string | URL | Request,
98
+ init?: RequestInit,
99
+ ): Effect.Effect<FetchEntity, any, any> {
100
+ const middleware: ReadonlyArray<Middleware<any, any>> =
101
+ (this as any)?.middleware ?? defaultMiddleware
102
+ return Effect.gen(function* () {
103
+ const request = new Request(input, init)
104
+
105
+ let handler: Next = noopNext as Next
106
+ for (let i = middleware.length - 1; i >= 0; i--) {
107
+ const nextHandler = handler
108
+ handler = ((req: Request) => middleware[i](req, nextHandler)) as Next
109
+ }
110
+
111
+ return yield* withTrace(request, handler(request))
112
+ }) as any
113
+ }
114
+
115
+ export function get(
116
+ input: string | URL | Request,
117
+ init?: Omit<RequestInit, "method">,
118
+ ): Effect.Effect<FetchEntity, FetchError>
119
+ export function get<E, R>(
120
+ this: FetchClient<E, R>,
121
+ input: string | URL | Request,
122
+ init?: Omit<RequestInit, "method">,
123
+ ): Effect.Effect<FetchEntity, FetchError | E, R>
124
+ export function get(
125
+ this: void | FetchClient<any, any>,
126
+ input: string | URL | Request,
127
+ init?: Omit<RequestInit, "method">,
128
+ ): Effect.Effect<FetchEntity, any, any> {
129
+ return fetch.call(this as any, input, { ...init, method: "GET" }) as any
130
+ }
131
+
132
+ export function post(
133
+ input: string | URL | Request,
134
+ init?: Omit<RequestInit, "method">,
135
+ ): Effect.Effect<FetchEntity, FetchError>
136
+ export function post<E, R>(
137
+ this: FetchClient<E, R>,
138
+ input: string | URL | Request,
139
+ init?: Omit<RequestInit, "method">,
140
+ ): Effect.Effect<FetchEntity, FetchError | E, R>
141
+ export function post(
142
+ this: void | FetchClient<any, any>,
143
+ input: string | URL | Request,
144
+ init?: Omit<RequestInit, "method">,
145
+ ): Effect.Effect<FetchEntity, any, any> {
146
+ return fetch.call(this as any, input, { ...init, method: "POST" }) as any
147
+ }
148
+
149
+ export function use<E2, R2>(...middleware: ReadonlyArray<Middleware<E2, R2>>): FetchClient<E2, R2>
150
+ export function use<E, R, E2, R2>(
151
+ this: FetchClient<E, R>,
152
+ ...middleware: ReadonlyArray<Middleware<E2, R2>>
153
+ ): FetchClient<E | E2, R | R2>
154
+ export function use(
155
+ this: void | FetchClient<any, any>,
156
+ ...middleware: ReadonlyArray<Middleware<any, any>>
157
+ ): FetchClient<any, any> {
158
+ const base = (this as any)?.middleware ?? defaultMiddleware
159
+ const transport = base[base.length - 1]
160
+ const existing = base.slice(0, -1)
161
+ return Object.create(ClientProto, {
162
+ middleware: { value: [...existing, ...middleware, transport] },
163
+ }) as FetchClient<any, any>
164
+ }
165
+
166
+ const ClientProto: any = {
167
+ [TypeId]: TypeId,
168
+ middleware: defaultMiddleware,
169
+ fetch,
170
+ get,
171
+ post,
172
+ use,
173
+ }
174
+
175
+ export function filterStatus(
176
+ predicate: (status: number) => boolean,
177
+ options?: {
178
+ readonly orElse?: (entity: FetchEntity) => Effect.Effect<FetchEntity, any, any>
179
+ },
180
+ ): Middleware<FetchError> {
181
+ return ((_request: Request, next: Next) =>
182
+ Effect.gen(function* () {
183
+ const entity = yield* next(_request)
184
+ const status = entity.status ?? 0
185
+ if (predicate(status)) {
186
+ return entity
187
+ }
188
+ if (options?.orElse) {
189
+ return yield* options.orElse(entity)
190
+ }
191
+ return yield* Effect.fail(new FetchError({ reason: "Status", response: entity }))
192
+ })) as Middleware<FetchError>
193
+ }
194
+
195
+ export function filterStatusOk(options?: {
196
+ readonly orElse?: (entity: FetchEntity) => Effect.Effect<FetchEntity, any, any>
197
+ }): Middleware<FetchError> {
198
+ return filterStatus((status) => status >= 200 && status < 300, options)
199
+ }
200
+
201
+ export function followRedirects(options?: { readonly maxRedirects?: number }): Middleware {
202
+ const maxRedirects = options?.maxRedirects ?? 10
203
+
204
+ return (request: Request, next: Next) =>
205
+ Effect.gen(function* () {
206
+ let currentRequest = new Request(request.url, {
207
+ ...request,
208
+ redirect: "manual",
209
+ })
210
+ let redirectCount = 0
211
+
212
+ while (true) {
213
+ const entity = yield* next(currentRequest)
214
+ const status = entity.status ?? 0
215
+
216
+ if (status >= 300 && status < 400) {
217
+ if (redirectCount >= maxRedirects) {
218
+ return entity
219
+ }
220
+
221
+ const location = entity.headers["location"]
222
+ if (!location) {
223
+ return entity
224
+ }
225
+
226
+ redirectCount++
227
+ const nextUrl = new URL(location, currentRequest.url)
228
+
229
+ if (status === 303) {
230
+ currentRequest = new Request(nextUrl.toString(), {
231
+ method: "GET",
232
+ headers: currentRequest.headers,
233
+ redirect: "manual",
234
+ })
235
+ } else {
236
+ currentRequest = new Request(nextUrl.toString(), {
237
+ method: currentRequest.method,
238
+ headers: currentRequest.headers,
239
+ body: currentRequest.body,
240
+ redirect: "manual",
241
+ })
242
+ }
243
+ } else {
244
+ return entity
245
+ }
246
+ }
247
+ })
248
+ }
249
+
250
+ export function retry(options: {
251
+ readonly times?: number
252
+ readonly delay?: number
253
+ readonly schedule?: Schedule.Schedule<any, any, any>
254
+ }): Middleware {
255
+ return ((request: Request, next: Next) => {
256
+ if (options.schedule) {
257
+ return Effect.retry(
258
+ Effect.suspend(() => next(request)),
259
+ options.schedule,
260
+ )
261
+ }
262
+
263
+ const times = options.times ?? 3
264
+ const delay = options.delay ?? 1000
265
+
266
+ return Effect.retry(
267
+ Effect.suspend(() => next(request)),
268
+ Schedule.intersect(Schedule.recurs(times), Schedule.exponential(delay)),
269
+ )
270
+ }) as Middleware
271
+ }
@@ -1,10 +1,6 @@
1
1
  import * as Effect from "effect/Effect"
2
2
  import * as Schema from "effect/Schema"
3
- import * as Sql from "./Sql.ts"
4
-
5
- // ---------------------------------------------------------------------------
6
- // Types
7
- // ---------------------------------------------------------------------------
3
+ import * as Sql from "./sql/Sql.ts"
8
4
 
9
5
  export interface Column {
10
6
  readonly tableSchema: string
@@ -58,10 +54,6 @@ export interface IntrospectOptions {
58
54
  readonly indexes?: boolean
59
55
  }
60
56
 
61
- // ---------------------------------------------------------------------------
62
- // SQLite
63
- // ---------------------------------------------------------------------------
64
-
65
57
  const sqliteColumns = `
66
58
  SELECT
67
59
  '' as tableSchema,
@@ -115,10 +107,6 @@ const sqliteIndexes = `
115
107
  ORDER BY m.name, il.name, ii.seqno
116
108
  `
117
109
 
118
- // ---------------------------------------------------------------------------
119
- // PostgreSQL
120
- // ---------------------------------------------------------------------------
121
-
122
110
  const postgresColumns = `
123
111
  SELECT
124
112
  c.table_schema as "tableSchema",
@@ -191,10 +179,6 @@ const postgresIndexes = `
191
179
  ORDER BY n.nspname, t.relname, i.relname, array_position(ix.indkey, a.attnum)
192
180
  `
193
181
 
194
- // ---------------------------------------------------------------------------
195
- // SQL Server (MSSQL)
196
- // ---------------------------------------------------------------------------
197
-
198
182
  const mssqlColumns = `
199
183
  SELECT
200
184
  s.name as tableSchema,
@@ -265,10 +249,6 @@ const mssqlIndexes = `
265
249
  ORDER BY s.name, t.name, i.name, ic.key_ordinal
266
250
  `
267
251
 
268
- // ---------------------------------------------------------------------------
269
- // Dialect resolution
270
- // ---------------------------------------------------------------------------
271
-
272
252
  export type Dialect = "sqlite" | "postgres" | "mssql"
273
253
 
274
254
  const dialectQueries: Record<Dialect, { columns: string; foreignKeys: string; indexes: string }> = {
@@ -281,10 +261,6 @@ const dialectQueries: Record<Dialect, { columns: string; foreignKeys: string; in
281
261
  mssql: { columns: mssqlColumns, foreignKeys: mssqlForeignKeys, indexes: mssqlIndexes },
282
262
  }
283
263
 
284
- // ---------------------------------------------------------------------------
285
- // Introspect
286
- // ---------------------------------------------------------------------------
287
-
288
264
  const singleColumnIndexes = (indexes: ReadonlyArray<Index>): Set<string> => {
289
265
  const countByIndex = new Map<string, { count: number; columnName: string }>()
290
266
  for (const idx of indexes) {
@@ -377,10 +353,6 @@ export const introspect = (
377
353
  return { tables: groupByTable(columns, foreignKeys, indexes) }
378
354
  })
379
355
 
380
- // ---------------------------------------------------------------------------
381
- // Column → Schema mapping
382
- // ---------------------------------------------------------------------------
383
-
384
356
  const dataTypeToSchema = (dataType: string): Schema.Schema.Any | null => {
385
357
  const t = dataType.toLowerCase()
386
358
  if (
@@ -452,10 +424,6 @@ const columnToSchema = (col: Column): Schema.Schema.Any | Schema.PropertySignatu
452
424
  return base
453
425
  }
454
426
 
455
- // ---------------------------------------------------------------------------
456
- // Table → Schema.Struct
457
- // ---------------------------------------------------------------------------
458
-
459
427
  export interface TableSchema {
460
428
  readonly tableName: string
461
429
  readonly tableSchema: string
@@ -487,10 +455,6 @@ export const toSchemas = (db: DatabaseSchema): ReadonlyArray<TableSchema> =>
487
455
  return s ? [s] : []
488
456
  })
489
457
 
490
- // ---------------------------------------------------------------------------
491
- // Read-only table access
492
- // ---------------------------------------------------------------------------
493
-
494
458
  export interface SortOrder {
495
459
  readonly column: string
496
460
  readonly reverse?: boolean
@@ -531,28 +495,51 @@ export interface DatabaseReader {
531
495
 
532
496
  const escapeIdentifier = (id: string): string => `"${id.replace(/"/g, '""')}"`
533
497
 
534
- const buildWhereClause = (
498
+ const concatSql = (
499
+ sql: Sql.SqlQuery,
500
+ fragments: Array<{ strings: ReadonlyArray<string>; values: Array<unknown> }>,
501
+ ): Effect.Effect<ReadonlyArray<unknown>, Sql.SqlError> => {
502
+ const strings: Array<string> = []
503
+ const values: Array<unknown> = []
504
+ for (let i = 0; i < fragments.length; i++) {
505
+ const frag = fragments[i]
506
+ for (let j = 0; j < frag.strings.length; j++) {
507
+ if (j === 0 && strings.length > 0) {
508
+ strings[strings.length - 1] += frag.strings[j]
509
+ } else {
510
+ strings.push(frag.strings[j])
511
+ }
512
+ if (j < frag.values.length) values.push(frag.values[j])
513
+ }
514
+ }
515
+ const tsa = Object.assign([...strings], { raw: strings }) as unknown as TemplateStringsArray
516
+ return sql(tsa, ...values)
517
+ }
518
+
519
+ const literal = (text: string) => ({ strings: [text], values: [] as Array<unknown> })
520
+
521
+ const param = (value: unknown) => ({ strings: ["", ""], values: [value] })
522
+
523
+ const buildWhereFragments = (
535
524
  filters: ReadonlyArray<Filter>,
536
525
  columnSet: Set<string>,
537
- paramOffset: number,
538
- ): { clause: string; values: Array<unknown> } => {
539
- const conditions: Array<string> = []
540
- const values: Array<unknown> = []
541
- for (const f of filters) {
542
- if (!columnSet.has(f.column)) continue
526
+ ): Array<{ strings: ReadonlyArray<string>; values: Array<unknown> }> => {
527
+ const parts: Array<{ strings: ReadonlyArray<string>; values: Array<unknown> }> = []
528
+ const valid = filters.filter((f) => columnSet.has(f.column))
529
+ if (valid.length === 0) return parts
530
+ parts.push(literal(" WHERE "))
531
+ for (let i = 0; i < valid.length; i++) {
532
+ if (i > 0) parts.push(literal(" AND "))
533
+ const f = valid[i]
543
534
  const col = escapeIdentifier(f.column)
544
- const paramIndex = paramOffset + values.length + 1
545
535
  if (f.value === null) {
546
- conditions.push(f.op === "eq" ? `${col} IS NULL` : `${col} IS NOT NULL`)
536
+ parts.push(literal(f.op === "eq" ? `${col} IS NULL` : `${col} IS NOT NULL`))
547
537
  } else {
548
- conditions.push(`${col} ${f.op === "eq" ? "=" : "!="} $${paramIndex}`)
549
- values.push(f.value)
538
+ parts.push(literal(`${col} ${f.op === "eq" ? "=" : "!="} `))
539
+ parts.push(param(f.value))
550
540
  }
551
541
  }
552
- return {
553
- clause: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
554
- values,
555
- }
542
+ return parts
556
543
  }
557
544
 
558
545
  const makeTableReader = (ts: TableSchema): TableReader => {
@@ -573,39 +560,46 @@ const makeTableReader = (ts: TableSchema): TableReader => {
573
560
  findAll: (options) =>
574
561
  Effect.gen(function* () {
575
562
  const sql = yield* Sql.SqlClient
576
- let query = `SELECT ${selectCols} FROM ${qualifiedName}`
577
- const where = options?.filters
578
- ? buildWhereClause(options.filters, columnSet, 0)
579
- : { clause: "", values: [] }
580
- query += where.clause
563
+ const fragments: Array<{ strings: ReadonlyArray<string>; values: Array<unknown> }> = [
564
+ literal(`SELECT ${selectCols} FROM ${qualifiedName}`),
565
+ ]
566
+ if (options?.filters) {
567
+ fragments.push(...buildWhereFragments(options.filters, columnSet))
568
+ }
581
569
  if (options?.sort && options.sort.length > 0) {
582
570
  const sortClauses = options.sort
583
571
  .filter((s) => sortableSet.has(s.column))
584
572
  .map((s) => `${escapeIdentifier(s.column)} ${s.reverse ? "DESC" : "ASC"}`)
585
- if (sortClauses.length > 0) query += ` ORDER BY ${sortClauses.join(", ")}`
573
+ if (sortClauses.length > 0) fragments.push(literal(` ORDER BY ${sortClauses.join(", ")}`))
586
574
  }
587
- if (options?.limit !== undefined) query += ` LIMIT ${Number(options.limit)}`
588
- if (options?.offset !== undefined) query += ` OFFSET ${Number(options.offset)}`
589
- return yield* sql.unsafe(query, where.values)
575
+ if (options?.limit !== undefined)
576
+ fragments.push(literal(` LIMIT ${Math.trunc(Number(options.limit))}`))
577
+ if (options?.offset !== undefined)
578
+ fragments.push(literal(` OFFSET ${Math.trunc(Number(options.offset))}`))
579
+ return yield* concatSql(sql, fragments)
590
580
  }),
591
581
  findById: (id) =>
592
582
  Effect.gen(function* () {
593
583
  if (!primaryKey) return null
594
584
  const sql = yield* Sql.SqlClient
595
- const query = `SELECT ${selectCols} FROM ${qualifiedName} WHERE ${escapeIdentifier(primaryKey.columnName)} = $1`
596
- const rows = yield* sql.unsafe(query, [id])
585
+ const pkCol = escapeIdentifier(primaryKey.columnName)
586
+ const rows = yield* concatSql(sql, [
587
+ literal(`SELECT ${selectCols} FROM ${qualifiedName} WHERE ${pkCol} = `),
588
+ param(id),
589
+ ])
597
590
  return rows[0] ?? null
598
591
  }),
599
592
  count: (options) =>
600
593
  Effect.gen(function* () {
601
594
  const sql = yield* Sql.SqlClient
602
- let query = `SELECT COUNT(*) as count FROM ${qualifiedName}`
603
- const where = options?.filters
604
- ? buildWhereClause(options.filters, columnSet, 0)
605
- : { clause: "", values: [] }
606
- query += where.clause
607
- const rows = yield* sql.unsafe<{ count: number }>(query, where.values)
608
- return Number(rows[0].count)
595
+ const fragments: Array<{ strings: ReadonlyArray<string>; values: Array<unknown> }> = [
596
+ literal(`SELECT COUNT(*) as count FROM ${qualifiedName}`),
597
+ ]
598
+ if (options?.filters) {
599
+ fragments.push(...buildWhereFragments(options.filters, columnSet))
600
+ }
601
+ const rows = yield* concatSql(sql, fragments)
602
+ return Number((rows[0] as any).count)
609
603
  }),
610
604
  }
611
605
  }
package/src/Start.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import type * as FileSystem from "./FileSystem.ts"
2
2
  import * as Context from "effect/Context"
3
+ import * as Deferred from "effect/Deferred"
3
4
  import * as Effect from "effect/Effect"
4
5
  import * as Function from "effect/Function"
5
6
  import * as Layer from "effect/Layer"
6
- import * as Option from "effect/Option"
7
7
  import type * as ChildProcess from "./ChildProcess.ts"
8
8
  import * as BunRuntime from "./bun/BunRuntime.ts"
9
9
  import * as BunServer from "./bun/BunServer.ts"
10
10
  import * as NodeFileSystem from "./node/NodeFileSystem.ts"
11
11
  import * as BunChildProcessSpawner from "./bun/BunChildProcessSpawner.ts"
12
+ import * as StartApp from "./StartApp.ts"
12
13
 
13
14
  export function layer<
14
15
  Layers extends [Layer.Layer<never, any, any>, ...Array<Layer.Layer<never, any, any>>],
@@ -63,11 +64,26 @@ export function pack<const Layers extends readonly [Layer.Layer.Any, ...Array<La
63
64
  return result as AnyLayer
64
65
  }
65
66
 
66
- export function serve<
67
- ROut,
68
- E,
69
- RIn extends BunServer.BunServer | FileSystem.FileSystem | ChildProcess.ChildProcessSpawner,
70
- >(
67
+ export type PlatformServices =
68
+ | BunServer.BunServer
69
+ | FileSystem.FileSystem
70
+ | ChildProcess.ChildProcessSpawner
71
+ | StartApp.StartApp
72
+
73
+ export const Live: Layer.Layer<
74
+ Exclude<PlatformServices, BunServer.BunServer>,
75
+ never,
76
+ never
77
+ > = Layer.mergeAll(
78
+ NodeFileSystem.layer,
79
+ BunChildProcessSpawner.layer,
80
+ Layer.effect(
81
+ StartApp.StartApp,
82
+ Deferred.make<BunServer.BunServer>().pipe(Effect.map((server) => ({ server }))),
83
+ ),
84
+ )
85
+
86
+ export function serve<ROut, E, RIn extends PlatformServices>(
71
87
  load: () => Promise<{
72
88
  default: Layer.Layer<ROut, E, RIn>
73
89
  }>,
@@ -79,24 +95,10 @@ export function serve<
79
95
  Layer.unwrapEffect,
80
96
  )
81
97
 
82
- const serverLayer = Layer.scoped(
83
- BunServer.BunServer,
84
- Effect.gen(function* () {
85
- const existing = yield* Effect.serviceOption(BunServer.BunServer)
86
- if (Option.isSome(existing)) return existing.value
87
- return yield* BunServer.make({})
88
- }),
89
- )
90
-
91
- const appLayerResolved = Function.pipe(
92
- appLayer,
93
- Layer.provide(serverLayer),
94
- Layer.provide(NodeFileSystem.layer),
95
- Layer.provide(BunChildProcessSpawner.layer),
96
- )
98
+ const appLayerResolved = Function.pipe(appLayer, Layer.provideMerge(Live))
97
99
 
98
100
  const composed = Function.pipe(
99
- serverLayer,
101
+ BunServer.layerStart(),
100
102
  BunServer.withLogAddress,
101
103
  Layer.provide(appLayerResolved),
102
104
  ) as Layer.Layer<BunServer.BunServer, never, never>
@@ -0,0 +1,11 @@
1
+ import * as Context from "effect/Context"
2
+ import type * as Deferred from "effect/Deferred"
3
+ import type * as BunServer from "./bun/BunServer.ts"
4
+
5
+ export namespace StartApp {
6
+ export interface Service {
7
+ readonly server: Deferred.Deferred<BunServer.BunServer>
8
+ }
9
+ }
10
+
11
+ export class StartApp extends Context.Tag("effect-start/StartApp")<StartApp, StartApp.Service>() {}