effect-start 0.22.1 → 0.23.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 (313) hide show
  1. package/dist/BlobStore.d.ts +80 -0
  2. package/dist/BlobStore.js +19 -0
  3. package/dist/ChildProcess.d.ts +60 -0
  4. package/dist/ChildProcess.js +30 -0
  5. package/dist/Commander.d.ts +3 -6
  6. package/dist/Commander.js +6 -13
  7. package/dist/ContentNegotiation.d.ts +8 -9
  8. package/dist/ContentNegotiation.js +32 -37
  9. package/dist/Cookies.d.ts +47 -0
  10. package/dist/Cookies.js +273 -363
  11. package/dist/Development.d.ts +2 -2
  12. package/dist/Development.js +3 -4
  13. package/dist/Effectify.d.ts +1 -4
  14. package/dist/FilePathPattern.d.ts +3 -3
  15. package/dist/FileRouter.d.ts +5 -8
  16. package/dist/FileRouter.js +9 -10
  17. package/dist/FileRouterCodegen.d.ts +1 -1
  18. package/dist/FileRouterCodegen.js +33 -13
  19. package/dist/FileSystem.d.ts +158 -0
  20. package/dist/FileSystem.js +64 -125
  21. package/dist/Http.js +2 -6
  22. package/dist/PathPattern.d.ts +7 -7
  23. package/dist/PathPattern.js +1 -3
  24. package/dist/PlatformError.d.ts +24 -32
  25. package/dist/PlatformError.js +3 -21
  26. package/dist/PlatformRuntime.js +5 -10
  27. package/dist/Route.d.ts +14 -19
  28. package/dist/Route.js +8 -11
  29. package/dist/RouteBody.d.ts +6 -12
  30. package/dist/RouteBody.js +2 -2
  31. package/dist/RouteError.d.ts +98 -0
  32. package/dist/RouteError.js +55 -0
  33. package/dist/RouteHook.js +6 -11
  34. package/dist/RouteHttp.d.ts +3 -3
  35. package/dist/RouteHttp.js +27 -22
  36. package/dist/RouteMount.d.ts +16 -50
  37. package/dist/RouteMount.js +6 -20
  38. package/dist/RouteSchema.d.ts +22 -1
  39. package/dist/RouteSchema.js +33 -0
  40. package/dist/RouteSse.js +4 -10
  41. package/dist/RouteTree.d.ts +2 -1
  42. package/dist/RouteTree.js +17 -15
  43. package/dist/RouteTrie.d.ts +2 -2
  44. package/dist/RouteTrie.js +4 -9
  45. package/dist/SchemaExtra.d.ts +1 -1
  46. package/dist/Socket.d.ts +27 -0
  47. package/dist/Socket.js +20 -28
  48. package/dist/Sql.d.ts +34 -0
  49. package/dist/Sql.js +5 -0
  50. package/dist/SqlIntrospect.d.ts +91 -0
  51. package/dist/SqlIntrospect.js +466 -0
  52. package/dist/Start.d.ts +4 -6
  53. package/dist/Start.js +10 -2
  54. package/dist/StreamExtra.d.ts +1 -1
  55. package/dist/StreamExtra.js +9 -9
  56. package/dist/System.d.ts +7 -0
  57. package/dist/System.js +22 -0
  58. package/dist/TuplePathPattern.js +55 -50
  59. package/dist/Unique.js +7 -7
  60. package/dist/Values.d.ts +2 -1
  61. package/dist/Values.js +19 -13
  62. package/dist/bun/BunBlobStoreDisk.d.ts +6 -0
  63. package/dist/bun/BunBlobStoreDisk.js +116 -0
  64. package/dist/bun/BunBlobStoreS3.d.ts +11 -0
  65. package/dist/bun/BunBlobStoreS3.js +89 -0
  66. package/dist/bun/BunBlobWatcherDisk.d.ts +6 -0
  67. package/dist/bun/BunBlobWatcherDisk.js +60 -0
  68. package/dist/bun/BunBlobWatcherQueue.d.ts +6 -0
  69. package/dist/bun/BunBlobWatcherQueue.js +17 -0
  70. package/dist/bun/BunBundle.d.ts +5 -6
  71. package/dist/bun/BunBundle.js +7 -15
  72. package/dist/bun/BunChildProcessSpawner.d.ts +3 -0
  73. package/dist/bun/BunChildProcessSpawner.js +103 -0
  74. package/dist/bun/BunImportTrackerPlugin.d.ts +1 -1
  75. package/dist/bun/BunImportTrackerPlugin.js +3 -5
  76. package/dist/bun/BunRoute.d.ts +3 -2
  77. package/dist/bun/BunRoute.js +5 -7
  78. package/dist/bun/BunRuntime.js +1 -1
  79. package/dist/bun/BunServer.d.ts +11 -4
  80. package/dist/bun/BunServer.js +35 -11
  81. package/dist/bun/BunSql.d.ts +4 -0
  82. package/dist/bun/BunSql.js +81 -0
  83. package/dist/bun/_BunEnhancedResolve.d.ts +3 -3
  84. package/dist/bun/_BunEnhancedResolve.js +2 -4
  85. package/dist/bun/index.d.ts +1 -0
  86. package/dist/bun/index.js +1 -0
  87. package/dist/bundler/Bundle.d.ts +2 -1
  88. package/dist/bundler/Bundle.js +1 -1
  89. package/dist/bundler/BundleFiles.d.ts +5 -5
  90. package/dist/bundler/BundleFiles.js +10 -8
  91. package/dist/bundler/BundleRoute.d.ts +27 -0
  92. package/dist/bundler/BundleRoute.js +51 -0
  93. package/dist/client/ScrollState.js +2 -6
  94. package/dist/client/index.js +6 -8
  95. package/dist/console/Console.d.ts +6 -0
  96. package/dist/console/Console.js +26 -0
  97. package/dist/console/ConsoleErrors.d.ts +3 -0
  98. package/dist/console/ConsoleErrors.js +200 -0
  99. package/dist/console/ConsoleLogger.d.ts +3 -0
  100. package/dist/console/ConsoleLogger.js +47 -0
  101. package/dist/console/ConsoleMetrics.d.ts +3 -0
  102. package/dist/console/ConsoleMetrics.js +61 -0
  103. package/dist/console/ConsoleProcess.d.ts +3 -0
  104. package/dist/console/ConsoleProcess.js +49 -0
  105. package/dist/console/ConsoleStore.d.ts +144 -0
  106. package/dist/console/ConsoleStore.js +61 -0
  107. package/dist/console/ConsoleTracer.d.ts +3 -0
  108. package/dist/console/ConsoleTracer.js +94 -0
  109. package/dist/console/Simulation.d.ts +2 -0
  110. package/dist/console/Simulation.js +633 -0
  111. package/dist/console/index.d.ts +3 -0
  112. package/dist/console/index.js +3 -0
  113. package/dist/console/routes/errors/route.d.ts +10 -0
  114. package/dist/console/routes/errors/route.js +47 -0
  115. package/dist/console/routes/fiberDetail.d.ts +16 -0
  116. package/dist/console/routes/fiberDetail.js +38 -0
  117. package/dist/console/routes/fibers/route.d.ts +10 -0
  118. package/dist/console/routes/fibers/route.js +19 -0
  119. package/dist/console/routes/git/route.d.ts +11 -0
  120. package/dist/console/routes/git/route.js +33 -0
  121. package/dist/console/routes/layout.d.ts +9 -0
  122. package/dist/console/routes/layout.js +3 -0
  123. package/dist/console/routes/logs/route.d.ts +10 -0
  124. package/dist/console/routes/logs/route.js +32 -0
  125. package/dist/console/routes/metrics/route.d.ts +10 -0
  126. package/dist/console/routes/metrics/route.js +17 -0
  127. package/dist/console/routes/route.d.ts +6 -0
  128. package/dist/console/routes/route.js +5 -0
  129. package/dist/console/routes/routes/route.d.ts +6 -0
  130. package/dist/console/routes/routes/route.js +20 -0
  131. package/dist/console/routes/services/route.d.ts +6 -0
  132. package/dist/console/routes/services/route.js +12 -0
  133. package/dist/console/routes/system/route.d.ts +10 -0
  134. package/dist/console/routes/system/route.js +18 -0
  135. package/dist/console/routes/traceDetail.d.ts +16 -0
  136. package/dist/console/routes/traceDetail.js +14 -0
  137. package/dist/console/routes/traces/route.d.ts +10 -0
  138. package/dist/console/routes/traces/route.js +39 -0
  139. package/dist/console/routes/tree.d.ts +153 -0
  140. package/dist/console/routes/tree.js +29 -0
  141. package/dist/console/ui/Errors.d.ts +4 -0
  142. package/dist/console/ui/Errors.js +15 -0
  143. package/dist/console/ui/Fibers.d.ts +24 -0
  144. package/dist/console/ui/Fibers.js +121 -0
  145. package/dist/console/ui/Git.d.ts +20 -0
  146. package/dist/console/ui/Git.js +95 -0
  147. package/dist/console/ui/Logs.d.ts +4 -0
  148. package/dist/console/ui/Logs.js +25 -0
  149. package/dist/console/ui/Metrics.d.ts +4 -0
  150. package/dist/console/ui/Metrics.js +26 -0
  151. package/dist/console/ui/Routes.d.ts +8 -0
  152. package/dist/console/ui/Routes.js +70 -0
  153. package/dist/console/ui/Services.d.ts +10 -0
  154. package/dist/console/ui/Services.js +246 -0
  155. package/dist/console/ui/Shell.d.ts +10 -0
  156. package/dist/console/ui/Shell.js +7 -0
  157. package/dist/console/ui/System.d.ts +4 -0
  158. package/dist/console/ui/System.js +35 -0
  159. package/dist/console/ui/Traces.d.ts +12 -0
  160. package/dist/console/ui/Traces.js +179 -0
  161. package/dist/datastar/actions/fetch.d.ts +1 -1
  162. package/dist/datastar/actions/fetch.js +10 -18
  163. package/dist/datastar/actions/peek.js +1 -2
  164. package/dist/datastar/actions/setAll.js +1 -2
  165. package/dist/datastar/actions/toggleAll.js +1 -2
  166. package/dist/datastar/attributes/attr.js +1 -2
  167. package/dist/datastar/attributes/bind.js +10 -18
  168. package/dist/datastar/attributes/class.js +2 -5
  169. package/dist/datastar/attributes/computed.js +2 -3
  170. package/dist/datastar/attributes/effect.js +1 -2
  171. package/dist/datastar/attributes/indicator.js +2 -4
  172. package/dist/datastar/attributes/init.js +2 -3
  173. package/dist/datastar/attributes/jsonSignals.js +1 -2
  174. package/dist/datastar/attributes/on.js +41 -22
  175. package/dist/datastar/attributes/onIntersect.js +2 -3
  176. package/dist/datastar/attributes/onInterval.js +2 -3
  177. package/dist/datastar/attributes/onSignalPatch.js +2 -4
  178. package/dist/datastar/attributes/ref.js +1 -2
  179. package/dist/datastar/attributes/show.js +1 -2
  180. package/dist/datastar/attributes/signals.js +1 -2
  181. package/dist/datastar/attributes/style.js +6 -12
  182. package/dist/datastar/attributes/text.js +1 -2
  183. package/dist/datastar/engine.d.ts +13 -7
  184. package/dist/datastar/engine.js +76 -48
  185. package/dist/datastar/happydom.d.ts +1 -0
  186. package/dist/datastar/happydom.js +8 -0
  187. package/dist/datastar/index.d.ts +1 -1
  188. package/dist/datastar/index.js +1 -1
  189. package/dist/datastar/utils.js +4 -7
  190. package/dist/datastar/watchers/patchElements.js +24 -45
  191. package/dist/datastar/watchers/patchSignals.js +1 -2
  192. package/dist/experimental/EncryptedCookies.d.ts +2 -5
  193. package/dist/experimental/EncryptedCookies.js +17 -48
  194. package/dist/experimental/index.d.ts +0 -1
  195. package/dist/experimental/index.js +0 -1
  196. package/dist/hyper/Hyper.d.ts +2 -9
  197. package/dist/hyper/Hyper.js +1 -12
  198. package/dist/hyper/HyperHtml.d.ts +1 -1
  199. package/dist/hyper/HyperHtml.js +18 -12
  200. package/dist/hyper/HyperHtml.test.d.ts +1 -0
  201. package/dist/hyper/HyperHtml.test.js +197 -0
  202. package/dist/hyper/HyperRoute.test.js +14 -3
  203. package/dist/hyper/html.d.ts +11 -0
  204. package/dist/hyper/html.js +30 -0
  205. package/dist/hyper/index.d.ts +2 -0
  206. package/dist/hyper/index.js +1 -0
  207. package/dist/hyper/jsx-runtime.d.ts +1 -1
  208. package/dist/hyper/jsx-runtime.js +1 -1
  209. package/dist/index.d.ts +1 -0
  210. package/dist/index.js +1 -0
  211. package/dist/lint/plugin.d.ts +86 -0
  212. package/dist/lint/plugin.js +341 -0
  213. package/dist/node/NodeFileSystem.d.ts +2 -2
  214. package/dist/node/NodeFileSystem.js +4 -14
  215. package/dist/sql/bun/index.d.ts +3 -0
  216. package/dist/sql/bun/index.js +75 -0
  217. package/dist/sql/mssql/docker.d.ts +2 -0
  218. package/dist/sql/mssql/docker.js +67 -0
  219. package/dist/sql/mssql/index.d.ts +21 -0
  220. package/dist/sql/mssql/index.js +113 -0
  221. package/dist/testing/TestLogger.js +4 -1
  222. package/dist/testing/index.d.ts +0 -1
  223. package/dist/testing/index.js +0 -1
  224. package/dist/testing/utils.d.ts +3 -3
  225. package/dist/testing/utils.js +4 -4
  226. package/dist/x/cloudflare/CloudflareTunnel.d.ts +2 -5
  227. package/dist/x/cloudflare/CloudflareTunnel.js +14 -27
  228. package/dist/x/datastar/Datastar.d.ts +1 -1
  229. package/dist/x/datastar/Datastar.js +13 -12
  230. package/dist/x/datastar/index.d.ts +1 -2
  231. package/dist/x/datastar/index.js +1 -2
  232. package/dist/x/tailscale/TailscaleTunnel.d.ts +15 -0
  233. package/dist/x/tailscale/TailscaleTunnel.js +68 -0
  234. package/dist/x/tailscale/index.d.ts +1 -0
  235. package/dist/x/tailscale/index.js +1 -0
  236. package/dist/x/tailwind/TailwindPlugin.js +19 -19
  237. package/dist/x/tailwind/compile.d.ts +2 -2
  238. package/dist/x/tailwind/compile.js +2 -4
  239. package/package.json +22 -9
  240. package/src/ChildProcess.ts +145 -0
  241. package/src/PlatformError.ts +27 -50
  242. package/src/Route.ts +2 -2
  243. package/src/RouteError.ts +76 -0
  244. package/src/RouteHttp.ts +13 -5
  245. package/src/RouteSchema.ts +96 -1
  246. package/src/RouteTree.ts +12 -0
  247. package/src/Sql.ts +51 -0
  248. package/src/SqlIntrospect.ts +620 -0
  249. package/src/Start.ts +15 -3
  250. package/src/System.ts +43 -0
  251. package/src/Values.ts +7 -0
  252. package/src/bun/BunChildProcessSpawner.ts +143 -0
  253. package/src/bun/BunRoute.ts +5 -2
  254. package/src/bun/BunServer.ts +22 -1
  255. package/src/bun/index.ts +1 -0
  256. package/src/bundler/BundleRoute.ts +66 -0
  257. package/src/console/Console.ts +42 -0
  258. package/src/console/ConsoleErrors.ts +213 -0
  259. package/src/console/ConsoleLogger.ts +56 -0
  260. package/src/console/ConsoleMetrics.ts +72 -0
  261. package/src/console/ConsoleProcess.ts +59 -0
  262. package/src/console/ConsoleStore.ts +187 -0
  263. package/src/console/ConsoleTracer.ts +107 -0
  264. package/src/console/Simulation.ts +814 -0
  265. package/src/console/console.html +340 -0
  266. package/src/console/index.ts +3 -0
  267. package/src/console/routes/errors/route.tsx +97 -0
  268. package/src/console/routes/fiberDetail.tsx +54 -0
  269. package/src/console/routes/fibers/route.tsx +45 -0
  270. package/src/console/routes/git/route.tsx +64 -0
  271. package/src/console/routes/layout.tsx +4 -0
  272. package/src/console/routes/logs/route.tsx +77 -0
  273. package/src/console/routes/metrics/route.tsx +36 -0
  274. package/src/console/routes/route.tsx +8 -0
  275. package/src/console/routes/routes/route.tsx +30 -0
  276. package/src/console/routes/services/route.tsx +21 -0
  277. package/src/console/routes/system/route.tsx +43 -0
  278. package/src/console/routes/traceDetail.tsx +22 -0
  279. package/src/console/routes/traces/route.tsx +81 -0
  280. package/src/console/routes/tree.ts +30 -0
  281. package/src/console/ui/Errors.tsx +76 -0
  282. package/src/console/ui/Fibers.tsx +321 -0
  283. package/src/console/ui/Git.tsx +182 -0
  284. package/src/console/ui/Logs.tsx +46 -0
  285. package/src/console/ui/Metrics.tsx +78 -0
  286. package/src/console/ui/Routes.tsx +125 -0
  287. package/src/console/ui/Services.tsx +273 -0
  288. package/src/console/ui/Shell.tsx +62 -0
  289. package/src/console/ui/System.tsx +131 -0
  290. package/src/console/ui/Traces.tsx +426 -0
  291. package/src/datastar/README.md +6 -1
  292. package/src/datastar/actions/fetch.ts +0 -1
  293. package/src/datastar/attributes/on.ts +40 -20
  294. package/src/datastar/engine.ts +51 -0
  295. package/src/datastar/jsx.d.ts +79 -0
  296. package/src/hyper/Hyper.ts +1 -16
  297. package/src/hyper/HyperHtml.ts +6 -4
  298. package/src/hyper/HyperRoute.ts +2 -1
  299. package/src/hyper/html.ts +47 -0
  300. package/src/hyper/index.ts +2 -0
  301. package/src/hyper/jsx.d.ts +5 -3
  302. package/src/index.ts +1 -0
  303. package/src/lint/plugin.js +129 -0
  304. package/src/sql/bun/index.ts +147 -0
  305. package/src/sql/mssql/docker.ts +117 -0
  306. package/src/sql/mssql/index.ts +223 -0
  307. package/src/sql/mssql/mssql.d.ts +41 -0
  308. package/src/x/cloudflare/CloudflareTunnel.ts +8 -36
  309. package/src/x/tailscale/TailscaleTunnel.ts +113 -0
  310. package/src/x/tailscale/index.ts +1 -0
  311. package/src/x/datastar/Datastar.ts +0 -61
  312. package/src/x/datastar/index.ts +0 -2
  313. package/src/x/datastar/jsx-datastar.d.ts +0 -60
@@ -0,0 +1,103 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Sink from "effect/Sink";
4
+ import * as Stream from "effect/Stream";
5
+ import * as ChildProcess from "../ChildProcess.js";
6
+ import * as PlatformError from "../PlatformError.js";
7
+ const toCmd = (command) => [
8
+ command.command,
9
+ ...command.args,
10
+ ];
11
+ export const layer = Layer.succeed(ChildProcess.ChildProcessSpawner, {
12
+ spawn: (command) => Effect.gen(function* () {
13
+ const cmd = toCmd(command);
14
+ const proc = yield* Effect.try({
15
+ try: () => Bun.spawn(cmd, {
16
+ cwd: command.cwd,
17
+ env: command.env,
18
+ stdin: command.stdin ?? "ignore",
19
+ stdout: command.stdout ?? "pipe",
20
+ stderr: command.stderr ?? "pipe",
21
+ detached: command.detached,
22
+ }),
23
+ catch: (err) => new PlatformError.SystemError({
24
+ reason: "Unknown",
25
+ module: "ChildProcess",
26
+ method: "spawn",
27
+ description: err instanceof Error ? err.message : "Failed to spawn process",
28
+ cause: err,
29
+ }),
30
+ });
31
+ yield* Effect.addFinalizer(() => Effect.sync(() => {
32
+ if (!proc.killed) {
33
+ proc.kill();
34
+ }
35
+ }));
36
+ const handle = {
37
+ pid: proc.pid,
38
+ exitCode: Effect.tryPromise({
39
+ try: () => proc.exited,
40
+ catch: (err) => new PlatformError.SystemError({
41
+ reason: "Unknown",
42
+ module: "ChildProcess",
43
+ method: "exitCode",
44
+ description: "Process exited unexpectedly",
45
+ cause: err,
46
+ }),
47
+ }),
48
+ isRunning: Effect.try({
49
+ try: () => !proc.killed && proc.exitCode === null,
50
+ catch: (err) => new PlatformError.SystemError({
51
+ reason: "BadResource",
52
+ module: "ChildProcess",
53
+ method: "isRunning",
54
+ description: err instanceof Error ? err.message : "Failed to check process status",
55
+ cause: err,
56
+ }),
57
+ }),
58
+ kill: (options) => Effect.try({
59
+ try: () => {
60
+ proc.kill(options?.killSignal);
61
+ },
62
+ catch: (err) => new PlatformError.SystemError({
63
+ reason: "BadResource",
64
+ module: "ChildProcess",
65
+ method: "kill",
66
+ description: err instanceof Error ? err.message : "Failed to kill process",
67
+ cause: err,
68
+ }),
69
+ }),
70
+ stdin: Sink.forEach((chunk) => Effect.try({
71
+ try: () => {
72
+ const sink = proc.stdin;
73
+ sink.write(chunk);
74
+ },
75
+ catch: (err) => new PlatformError.SystemError({
76
+ reason: "Unknown",
77
+ module: "ChildProcess",
78
+ method: "fromWritable(stdin)",
79
+ description: "Failed to write to stdin",
80
+ cause: err,
81
+ }),
82
+ })).pipe(Sink.ensuring(Effect.promise(async () => {
83
+ const sink = proc.stdin;
84
+ await sink.end();
85
+ }))),
86
+ stdout: Stream.fromReadableStream(() => proc.stdout, (err) => new PlatformError.SystemError({
87
+ reason: "Unknown",
88
+ module: "ChildProcess",
89
+ method: "fromReadable(stdout)",
90
+ description: "Failed to read stdout stream",
91
+ cause: err,
92
+ })),
93
+ stderr: Stream.fromReadableStream(() => proc.stderr, (err) => new PlatformError.SystemError({
94
+ reason: "Unknown",
95
+ module: "ChildProcess",
96
+ method: "fromReadable(stderr)",
97
+ description: "Failed to read stderr stream",
98
+ cause: err,
99
+ })),
100
+ };
101
+ return handle;
102
+ }),
103
+ });
@@ -1,5 +1,5 @@
1
1
  import { type BunPlugin, type Import } from "bun";
2
- export type ImportMap = ReadonlyMap<string, Import[]>;
2
+ export type ImportMap = ReadonlyMap<string, Array<Import>>;
3
3
  /**
4
4
  * Tracks all imported modules.
5
5
  * State can be accessed via 'virtual:import-tracker' module within a bundle
@@ -18,14 +18,13 @@ export const make = (opts = {}) => {
18
18
  build.onLoad({
19
19
  filter: /\.(ts|js)x?$/,
20
20
  }, async (args) => {
21
- if (!opts.includeNodeModules
22
- && args.path.includes("/node_modules/")) {
21
+ if (!opts.includeNodeModules && args.path.includes("/node_modules/")) {
23
22
  return undefined;
24
23
  }
25
24
  const contents = await Bun.file(args.path).arrayBuffer();
26
25
  try {
27
26
  const fileImport = transpiler.scanImports(contents);
28
- const resolvedImports = fileImport.map(imp => {
27
+ const resolvedImports = fileImport.map((imp) => {
29
28
  const absoluteImportPath = NPath.resolve(NPath.dirname(args.path),
30
29
  // 'file' is a default namespace, trim it
31
30
  imp.path.replace(/^file:/, ""));
@@ -39,8 +38,7 @@ export const make = (opts = {}) => {
39
38
  });
40
39
  foundImports.set(NPath.relative(baseDir, args.path), resolvedImports);
41
40
  }
42
- catch (e) {
43
- }
41
+ catch (e) { }
44
42
  return undefined;
45
43
  });
46
44
  build.onResolve({
@@ -16,9 +16,10 @@ export type BunDescriptors = {
16
16
  bunLoad: () => Promise<Bun.HTMLBundle>;
17
17
  };
18
18
  export declare function descriptors(route: Route.Route.Route<any, any, any, any, any>): BunDescriptors | undefined;
19
- export declare function htmlBundle(load: () => Promise<Bun.HTMLBundle | {
19
+ type HTMLBundleModule = Bun.HTMLBundle | {
20
20
  default: Bun.HTMLBundle;
21
- }>): <D extends Route.RouteDescriptor.Any, B extends {}, I extends Route.Route.Tuple>(self: Route.RouteSet.RouteSet<D, B, I>) => Route.RouteSet.RouteSet<D, B, [...I, Route.Route.Route<BunDescriptors & {
21
+ };
22
+ export declare function htmlBundle(load: () => HTMLBundleModule | Promise<HTMLBundleModule>): <D extends Route.RouteDescriptor.Any, B extends {}, I extends Route.Route.Tuple>(self: Route.RouteSet.RouteSet<D, B, I>) => Route.RouteSet.RouteSet<D, B, [...I, Route.Route.Route<BunDescriptors & {
22
23
  format: "html";
23
24
  }, {
24
25
  request: Request;
@@ -14,15 +14,14 @@ export class BunRouteError extends Data.TaggedError("BunRouteError") {
14
14
  }
15
15
  export function descriptors(route) {
16
16
  const descriptor = Route.descriptor(route);
17
- if (typeof descriptor.bunPrefix === "string"
18
- && typeof descriptor.bunLoad === "function") {
17
+ if (typeof descriptor.bunPrefix === "string" && typeof descriptor.bunLoad === "function") {
19
18
  return descriptor;
20
19
  }
21
20
  return undefined;
22
21
  }
23
22
  export function htmlBundle(load) {
24
23
  const bunPrefix = `/.BunRoute-${Unique.token(10)}`;
25
- const bunLoad = () => load().then(mod => "default" in mod ? mod.default : mod);
24
+ const bunLoad = () => Promise.resolve(load()).then((mod) => ("default" in mod ? mod.default : mod));
26
25
  const descriptors = { bunPrefix, bunLoad, format: "html" };
27
26
  return function (self) {
28
27
  const handler = (context, next) => Effect.gen(function* () {
@@ -116,8 +115,7 @@ export function validateBunPattern(pattern) {
116
115
  return Option.none();
117
116
  }
118
117
  export const isHtmlBundle = (handle) => {
119
- return (typeof handle === "object"
120
- && handle !== null
121
- && (handle.toString() === "[object HTMLBundle]"
122
- || typeof handle.index === "string"));
118
+ return (typeof handle === "object" &&
119
+ handle !== null &&
120
+ (handle.toString() === "[object HTMLBundle]" || typeof handle.index === "string"));
123
121
  };
@@ -2,7 +2,7 @@ import * as GlobalValue from "effect/GlobalValue";
2
2
  import * as MutableRef from "effect/MutableRef";
3
3
  import * as PlatformRuntime from "../PlatformRuntime.js";
4
4
  const mainFiber = GlobalValue.globalValue(Symbol.for("effect-start/BunRuntime/existingFiber"), () => MutableRef.make(undefined));
5
- export const runMain = PlatformRuntime.makeRunMain(({ fiber, teardown, }) => {
5
+ export const runMain = PlatformRuntime.makeRunMain(({ fiber, teardown }) => {
6
6
  const prevFiber = MutableRef.get(mainFiber);
7
7
  MutableRef.set(mainFiber, fiber);
8
8
  let receivedSignal = false;
@@ -1,10 +1,17 @@
1
- import * as Bun from "bun";
1
+ import * as Socket from "../Socket.ts";
2
+ import type * as Bun from "bun";
2
3
  import * as Context from "effect/Context";
4
+ import * as Deferred from "effect/Deferred";
3
5
  import * as Effect from "effect/Effect";
4
6
  import * as Layer from "effect/Layer";
5
7
  import type * as Scope from "effect/Scope";
6
- import * as BunServerRequest from "./BunServerRequest.ts";
7
- type FetchHandler = (request: Request, server: Bun.Server<BunServerRequest.WebSocketContext>) => Response | Promise<Response>;
8
+ export interface WebSocketContext {
9
+ readonly deferred: Deferred.Deferred<Bun.ServerWebSocket<WebSocketContext>>;
10
+ readonly closeDeferred: Deferred.Deferred<void, Socket.SocketError>;
11
+ readonly buffer: Array<Uint8Array | string>;
12
+ run: (_: Uint8Array | string) => void;
13
+ }
14
+ type FetchHandler = (request: Request, server: Bun.Server<WebSocketContext>) => Response | Promise<Response>;
8
15
  /**
9
16
  * Basically `Omit<Bun.Serve.Options, "fetch" | "error" | "websocket">`
10
17
  * TypeScript 5.9 cannot verify discriminated union types used in
@@ -19,7 +26,7 @@ interface BunServeOptions {
19
26
  readonly development?: boolean;
20
27
  }
21
28
  export type BunServer = {
22
- readonly server: Bun.Server<BunServerRequest.WebSocketContext>;
29
+ readonly server: Bun.Server<WebSocketContext>;
23
30
  readonly pushHandler: (fetch: FetchHandler) => void;
24
31
  readonly popHandler: () => void;
25
32
  };
@@ -1,5 +1,4 @@
1
- import * as Socket from "@effect/platform/Socket";
2
- import * as Bun from "bun";
1
+ import * as Socket from "../Socket.js";
3
2
  import * as Config from "effect/Config";
4
3
  import * as Context from "effect/Context";
5
4
  import * as Deferred from "effect/Deferred";
@@ -8,6 +7,8 @@ import * as Exit from "effect/Exit";
8
7
  import * as Layer from "effect/Layer";
9
8
  import * as Option from "effect/Option";
10
9
  import * as Runtime from "effect/Runtime";
10
+ import * as NOs from "node:os";
11
+ import * as NPath from "node:path";
11
12
  import * as PathPattern from "../PathPattern.js";
12
13
  import * as PlataformRuntime from "../PlatformRuntime.js";
13
14
  import * as Route from "../Route.js";
@@ -22,7 +23,8 @@ export const make = (options) => Effect.gen(function* () {
22
23
  ? Effect.succeed(0) // random port
23
24
  : Effect.succeed(3000);
24
25
  }));
25
- const hostname = yield* Config.string("HOSTNAME").pipe(Effect.catchTag("ConfigError", () => Effect.succeed(undefined)));
26
+ const hostFlag = process.argv.includes("--host");
27
+ const hostname = yield* Config.string("HOSTNAME").pipe(Effect.catchTag("ConfigError", () => Effect.succeed(hostFlag ? "0.0.0.0" : undefined)));
26
28
  const handlerStack = [
27
29
  function (_request, _server) {
28
30
  return new Response("not found", { status: 404 });
@@ -46,9 +48,7 @@ export const make = (options) => Effect.gen(function* () {
46
48
  },
47
49
  });
48
50
  const runtime = yield* Effect.runtime().pipe(Effect.andThen(Runtime.provideService(BunServer, service)));
49
- let currentRoutes = routes
50
- ? yield* walkBunRoutes(runtime, routes)
51
- : {};
51
+ let currentRoutes = routes ? yield* walkBunRoutes(runtime, routes) : {};
52
52
  const websocket = {
53
53
  open(ws) {
54
54
  Deferred.unsafeDone(ws.data.deferred, Exit.succeed(ws));
@@ -66,7 +66,7 @@ export const make = (options) => Effect.gen(function* () {
66
66
  : Exit.void);
67
67
  },
68
68
  };
69
- const server = Bun.serve({
69
+ const server = Bun?.serve({
70
70
  port,
71
71
  hostname,
72
72
  ...options,
@@ -103,9 +103,11 @@ export const make = (options) => Effect.gen(function* () {
103
103
  * Provides HttpServer using BunServer under the hood.
104
104
  */
105
105
  export const layer = (options) => Layer.scoped(BunServer, make(options ?? {}));
106
- export const withLogAddress = (layer) => Layer
107
- .effectDiscard(BunServer.pipe(Effect.andThen(server => Effect.log(`Listening on ${server.server.hostname}:${server.server.port}`))))
108
- .pipe(Layer.provideMerge(layer));
106
+ export const withLogAddress = (layer) => Layer.effectDiscard(BunServer.pipe(Effect.andThen((server) => {
107
+ const { hostname, port } = server.server;
108
+ const addr = hostname === "0.0.0.0" ? getLocalIp() : "localhost";
109
+ return Effect.log(`Listening on http://${addr}:${port}`);
110
+ }))).pipe(Layer.provideMerge(layer));
109
111
  function walkBunRoutes(runtime, tree) {
110
112
  return Effect.gen(function* () {
111
113
  const bunRoutes = {};
@@ -115,7 +117,12 @@ function walkBunRoutes(runtime, tree) {
115
117
  const bunDescriptors = BunRoute.descriptors(route);
116
118
  if (bunDescriptors) {
117
119
  const htmlBundle = yield* Effect.promise(bunDescriptors.bunLoad);
118
- bunRoutes[`${bunDescriptors.bunPrefix}/*`] = htmlBundle;
120
+ if (htmlBundle.files) {
121
+ registerPrebuiltBundle(bunDescriptors.bunPrefix, htmlBundle, bunRoutes);
122
+ }
123
+ else {
124
+ bunRoutes[`${bunDescriptors.bunPrefix}/*`] = htmlBundle;
125
+ }
119
126
  }
120
127
  const path = Route.descriptor(route).path;
121
128
  const group = pathGroups.get(path) ?? [];
@@ -131,3 +138,20 @@ function walkBunRoutes(runtime, tree) {
131
138
  return bunRoutes;
132
139
  });
133
140
  }
141
+ function registerPrebuiltBundle(prefix, bundle, bunRoutes) {
142
+ const mainDir = NPath.dirname(Bun.main);
143
+ const indexPath = NPath.resolve(mainDir, bundle.index);
144
+ bunRoutes[`${prefix}/*`] = () => new Response(Bun.file(indexPath), { headers: { "content-type": "text/html;charset=utf-8" } });
145
+ for (const file of bundle.files ?? []) {
146
+ if (file.loader === "html")
147
+ continue;
148
+ const absPath = NPath.resolve(mainDir, file.path);
149
+ const basename = NPath.basename(file.path);
150
+ bunRoutes[`/${basename}`] = () => new Response(Bun.file(absPath), { headers: file.headers ?? {} });
151
+ }
152
+ }
153
+ function getLocalIp() {
154
+ return Object.values(NOs.networkInterfaces())
155
+ .flatMap((addresses) => addresses ?? [])
156
+ .find((addr) => addr.family === "IPv4" && !addr.internal)?.address;
157
+ }
@@ -0,0 +1,4 @@
1
+ import * as Layer from "effect/Layer";
2
+ import * as Sql from "../Sql.ts";
3
+ export declare const layer: (config: Sql.SqlClientConfig) => Layer.Layer<Sql.SqlClient, Sql.SqlError>;
4
+ export declare const layerMemory: Layer.Layer<Sql.SqlClient, Sql.SqlError>;
@@ -0,0 +1,81 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Exit from "effect/Exit";
3
+ import * as FiberRef from "effect/FiberRef";
4
+ import * as GlobalValue from "effect/GlobalValue";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Option from "effect/Option";
7
+ import * as Sql from "../Sql.js";
8
+ const wrapError = (error) => new Sql.SqlError({
9
+ code: error?.code ?? "UNKNOWN",
10
+ message: error instanceof Error ? error.message : String(error),
11
+ cause: error,
12
+ });
13
+ const wrapQuery = (promise) => Effect.tryPromise({ try: () => promise, catch: wrapError });
14
+ const makeValues = (obj, ...columns) => {
15
+ const items = Array.isArray(obj) ? obj : [obj];
16
+ const cols = columns.length > 0 ? columns : Object.keys(items[0]);
17
+ return { value: items, columns: cols };
18
+ };
19
+ const currentTransaction = GlobalValue.globalValue(Symbol.for("effect-start/BunSql/currentTransaction"), () => FiberRef.unsafeMake(Option.none()));
20
+ const runQuery = (bunSql, strings, values) => Effect.flatMap(FiberRef.get(currentTransaction), (txOpt) => {
21
+ if (Option.isSome(txOpt)) {
22
+ return wrapQuery(txOpt.value.conn(strings, ...values));
23
+ }
24
+ return wrapQuery(bunSql(strings, ...values));
25
+ });
26
+ const runUnsafe = (bunSql, query, values) => Effect.flatMap(FiberRef.get(currentTransaction), (txOpt) => {
27
+ if (Option.isSome(txOpt)) {
28
+ return wrapQuery(txOpt.value.conn.unsafe(query, values));
29
+ }
30
+ return wrapQuery(bunSql.unsafe(query, values));
31
+ });
32
+ const makeWithTransaction = (bunSql) => (self) => Effect.uninterruptibleMask((restore) => Effect.flatMap(FiberRef.get(currentTransaction), (txOpt) => {
33
+ if (Option.isSome(txOpt)) {
34
+ const { conn, depth } = txOpt.value;
35
+ const name = `sp_${depth}`;
36
+ return Effect.gen(function* () {
37
+ yield* wrapQuery(conn.unsafe(`SAVEPOINT ${name}`));
38
+ const exit = yield* Effect.exit(restore(Effect.locally(self, currentTransaction, Option.some({ conn, depth: depth + 1 }))));
39
+ if (Exit.isSuccess(exit)) {
40
+ yield* wrapQuery(conn.unsafe(`RELEASE SAVEPOINT ${name}`));
41
+ return exit.value;
42
+ }
43
+ yield* wrapQuery(conn.unsafe(`ROLLBACK TO SAVEPOINT ${name}`)).pipe(Effect.orDie);
44
+ return yield* exit;
45
+ });
46
+ }
47
+ const runTx = (conn) => Effect.gen(function* () {
48
+ yield* wrapQuery(conn.unsafe("BEGIN"));
49
+ const exit = yield* Effect.exit(restore(Effect.locally(self, currentTransaction, Option.some({ conn, depth: 1 }))));
50
+ if (Exit.isSuccess(exit)) {
51
+ yield* wrapQuery(conn.unsafe("COMMIT"));
52
+ return exit.value;
53
+ }
54
+ yield* wrapQuery(conn.unsafe("ROLLBACK")).pipe(Effect.orDie);
55
+ return yield* exit;
56
+ });
57
+ return Effect.matchEffect(Effect.tryPromise({ try: () => bunSql.reserve(), catch: wrapError }), {
58
+ onFailure: () => runTx(bunSql),
59
+ onSuccess: (reserved) => Effect.ensuring(runTx(reserved), Effect.sync(() => reserved.release())),
60
+ });
61
+ }));
62
+ export const layer = (config) => Layer.scoped(Sql.SqlClient, Effect.acquireRelease(Effect.try({
63
+ try: () => {
64
+ const bunSql = new Bun.SQL(config);
65
+ return Object.assign((strings, ...values) => runQuery(bunSql, strings, values), {
66
+ unsafe: (query, values) => runUnsafe(bunSql, query, values),
67
+ values: makeValues,
68
+ withTransaction: makeWithTransaction(bunSql),
69
+ reserve: Effect.acquireRelease(Effect.tryPromise({ try: () => bunSql.reserve(), catch: wrapError }), (reserved) => Effect.sync(() => reserved.release())).pipe(Effect.map((reserved) => Object.assign((strings, ...values) => wrapQuery(reserved(strings, ...values)), {
70
+ unsafe: (query, values) => wrapQuery(reserved.unsafe(query, values)),
71
+ values: makeValues,
72
+ }))),
73
+ close: (options) => wrapQuery(bunSql.close(options)),
74
+ });
75
+ },
76
+ catch: wrapError,
77
+ }), (client) => client.close().pipe(Effect.orDie)));
78
+ export const layerMemory = layer({
79
+ adapter: "sqlite",
80
+ filename: ":memory:",
81
+ });
@@ -26,9 +26,9 @@ interface ResolveContext {
26
26
  yield?: (request: ResolveRequest) => void;
27
27
  }
28
28
  interface ResolveOptions {
29
- extensions?: string[];
30
- mainFields?: (string | string[])[];
31
- conditionNames?: string[];
29
+ extensions?: Array<string>;
30
+ mainFields?: Array<string | string[]>;
31
+ conditionNames?: Array<string>;
32
32
  fileSystem?: unknown;
33
33
  useSyncFileSystemCalls?: boolean;
34
34
  modules?: string | string[];
@@ -13,7 +13,7 @@ export class CachedInputFileSystem {
13
13
  export const ResolverFactory = {
14
14
  createResolver(options) {
15
15
  const extensions = options.extensions ?? [];
16
- const mainFields = (options.mainFields ?? []).flatMap((f) => Array.isArray(f) ? f : [f]);
16
+ const mainFields = (options.mainFields ?? []).flatMap((f) => (Array.isArray(f) ? f : [f]));
17
17
  const conditionNames = options.conditionNames ?? [];
18
18
  return {
19
19
  resolve(_context, basePath, id, _resolveContext, callback) {
@@ -70,9 +70,7 @@ function resolveSync(id, base, options) {
70
70
  }
71
71
  function resolvePackagePath(id, base) {
72
72
  const parts = id.split("/");
73
- const packageName = id.startsWith("@")
74
- ? parts.slice(0, 2).join("/")
75
- : parts[0];
73
+ const packageName = id.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
76
74
  let dir = base;
77
75
  while (dir !== path.dirname(dir)) {
78
76
  const candidate = path.join(dir, "node_modules", packageName);
@@ -1,3 +1,4 @@
1
+ export * as BunChildProcessSpawner from "./BunChildProcessSpawner.ts";
1
2
  export * as BunBundle from "./BunBundle.ts";
2
3
  export * as BunImportTrackerPlugin from "./BunImportTrackerPlugin.ts";
3
4
  export * as BunRoute from "./BunRoute.ts";
package/dist/bun/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * as BunChildProcessSpawner from "./BunChildProcessSpawner.js";
1
2
  export * as BunBundle from "./BunBundle.js";
2
3
  export * as BunImportTrackerPlugin from "./BunImportTrackerPlugin.js";
3
4
  export * as BunRoute from "./BunRoute.js";
@@ -1,4 +1,5 @@
1
- import { Context, Effect, PubSub } from "effect";
1
+ import type { PubSub } from "effect";
2
+ import { Context, Effect } from "effect";
2
3
  import * as Schema from "effect/Schema";
3
4
  export declare const BundleEntrypointMetaKey: unique symbol;
4
5
  export type BundleOutputMetaValue = {};
@@ -1,4 +1,4 @@
1
- import { Context, Data, Effect, pipe, } from "effect";
1
+ import { Context, Data, Effect, pipe } from "effect";
2
2
  import * as Schema from "effect/Schema";
3
3
  export const BundleEntrypointMetaKey = Symbol.for("effect-start/BundleEntrypointMetaKey");
4
4
  /**
@@ -1,13 +1,13 @@
1
- import * as FileSystem from "@effect/platform/FileSystem";
1
+ import * as FileSystem from "../FileSystem.ts";
2
2
  import * as Effect from "effect/Effect";
3
- import { type BundleContext, BundleError } from "./Bundle.ts";
3
+ import * as Bundle from "./Bundle.ts";
4
4
  /**
5
5
  * Exports a bundle to a file system under specified directory.
6
6
  */
7
- export declare const toFiles: (context: BundleContext, outDir: string) => Effect.Effect<undefined, import("@effect/platform/Error").PlatformError | BundleError, FileSystem.FileSystem>;
7
+ export declare const toFiles: (context: Bundle.BundleContext, outDir: string) => Effect.Effect<undefined, import("../PlatformError.ts").PlatformError | Bundle.BundleError, FileSystem.FileSystem>;
8
8
  /**
9
- * Loads a bundle from a directory and returns a BundleContext.
9
+ * Loads a bundle from a directory and returns a Bundle.BundleContext.
10
10
  * Expects the directory to contain a manifest.json file and all the artifacts
11
11
  * referenced in the manifest.
12
12
  */
13
- export declare const fromFiles: (directory: string) => Effect.Effect<BundleContext, BundleError, FileSystem.FileSystem>;
13
+ export declare const fromFiles: (directory: string) => Effect.Effect<Bundle.BundleContext, Bundle.BundleError, FileSystem.FileSystem>;
@@ -1,11 +1,11 @@
1
- import * as FileSystem from "@effect/platform/FileSystem";
1
+ import * as FileSystem from "../FileSystem.js";
2
2
  import * as Array from "effect/Array";
3
3
  import * as Effect from "effect/Effect";
4
4
  import * as Function from "effect/Function";
5
5
  import * as Iterable from "effect/Iterable";
6
6
  import * as Record from "effect/Record";
7
7
  import * as S from "effect/Schema";
8
- import { BundleError, BundleManifestSchema, } from "./Bundle.js";
8
+ import * as Bundle from "./Bundle.js";
9
9
  /**
10
10
  * Exports a bundle to a file system under specified directory.
11
11
  */
@@ -27,7 +27,9 @@ export const toFiles = (context, outDir) => {
27
27
  ...bundleArtifacts,
28
28
  ...extraArtifacts,
29
29
  };
30
- const existingOutDirFiles = yield* fs.readDirectory(normalizedOutDir).pipe(Effect.catchAll(() => Effect.succeed(null)));
30
+ const existingOutDirFiles = yield* fs
31
+ .readDirectory(normalizedOutDir)
32
+ .pipe(Effect.catchAll(() => Effect.succeed(null)));
31
33
  // check if the output directory is empty. if it contains previous build,
32
34
  // remove it. Otherwise fail.
33
35
  if (existingOutDirFiles && existingOutDirFiles.length > 0) {
@@ -38,7 +40,7 @@ export const toFiles = (context, outDir) => {
38
40
  });
39
41
  }
40
42
  else {
41
- return yield* Effect.fail(new BundleError({
43
+ return yield* Effect.fail(new Bundle.BundleError({
42
44
  message: "Output directory is not empty",
43
45
  }));
44
46
  }
@@ -49,7 +51,7 @@ export const toFiles = (context, outDir) => {
49
51
  // write all artifacts to files
50
52
  yield* Effect.all(Function.pipe(allArtifacts, Record.toEntries, Array.map(([p, b]) => Function.pipe(Effect.tryPromise({
51
53
  try: () => b.arrayBuffer(),
52
- catch: (e) => new BundleError({
54
+ catch: (e) => new Bundle.BundleError({
53
55
  message: "Failed to read an artifact as a buffer",
54
56
  cause: e,
55
57
  }),
@@ -57,7 +59,7 @@ export const toFiles = (context, outDir) => {
57
59
  });
58
60
  };
59
61
  /**
60
- * Loads a bundle from a directory and returns a BundleContext.
62
+ * Loads a bundle from a directory and returns a Bundle.BundleContext.
61
63
  * Expects the directory to contain a manifest.json file and all the artifacts
62
64
  * referenced in the manifest.
63
65
  */
@@ -65,12 +67,12 @@ export const fromFiles = (directory) => {
65
67
  return Effect.gen(function* () {
66
68
  const fs = yield* FileSystem.FileSystem;
67
69
  const normalizedDir = directory.replace(/\/$/, "");
68
- const manifest = yield* Function.pipe(fs.readFileString(`${normalizedDir}/manifest.json`), Effect.andThen((v) => JSON.parse(v)), Effect.andThen(S.decodeUnknownSync(BundleManifestSchema)), Effect.catchAll((e) => Effect.fail(new BundleError({
70
+ const manifest = yield* Function.pipe(fs.readFileString(`${normalizedDir}/manifest.json`), Effect.andThen((v) => JSON.parse(v)), Effect.andThen(S.decodeUnknownSync(Bundle.BundleManifestSchema)), Effect.catchAll((e) => Effect.fail(new Bundle.BundleError({
69
71
  message: `Failed to read manifest.json from ${normalizedDir}`,
70
72
  cause: e,
71
73
  }))));
72
74
  const artifactPaths = Array.map(manifest.artifacts, (a) => a.path);
73
- const artifactBlobs = yield* Function.pipe(artifactPaths, Iterable.map((path) => fs.readFile(`${normalizedDir}/${path}`)), Effect.all, Effect.catchAll((e) => new BundleError({
75
+ const artifactBlobs = yield* Function.pipe(artifactPaths, Iterable.map((path) => fs.readFile(`${normalizedDir}/${path}`)), Effect.all, Effect.catchAll((e) => new Bundle.BundleError({
74
76
  message: `Failed to read an artifact from ${normalizedDir}`,
75
77
  cause: e,
76
78
  })), Effect.andThen(Iterable.map((v, i) => new Blob([v.slice(0)], {
@@ -0,0 +1,27 @@
1
+ import type * as Context from "effect/Context";
2
+ import * as Layer from "effect/Layer";
3
+ import * as PathPattern from "../PathPattern.ts";
4
+ import * as Route from "../Route.ts";
5
+ import * as Bundle from "./Bundle.ts";
6
+ /**
7
+ * Creates a GET route that serves bundle artifacts.
8
+ * Mount at a path with a wildcard param (any name works).
9
+ *
10
+ * ```ts
11
+ * RouteTree.make({
12
+ * "/_bundle/:path+": BundleRoute.make(Bundle.ClientBundle),
13
+ * })
14
+ * ```
15
+ */
16
+ export declare const make: <Tag extends Context.Tag<any, Bundle.BundleContext>>(tag: Tag) => import("../RouteMount.ts").RouteMount.Builder<{}, [Route.Route.Route<{
17
+ method: "GET";
18
+ format: "*";
19
+ }, {}, Uint8Array<ArrayBuffer>, never, any>]>;
20
+ export declare const client: () => import("../RouteMount.ts").RouteMount.Builder<{}, [Route.Route.Route<{
21
+ method: "GET";
22
+ format: "*";
23
+ }, {}, Uint8Array<ArrayBuffer>, never, any>]>;
24
+ export declare const layer: (options?: {
25
+ bundle?: Context.Tag<any, Bundle.BundleContext>;
26
+ path?: PathPattern.PathPattern;
27
+ }) => Layer.Layer<Route.Routes, never, never>;
@@ -0,0 +1,51 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Layer from "effect/Layer";
3
+ import * as Option from "effect/Option";
4
+ import * as Entity from "../Entity.js";
5
+ import * as PathPattern from "../PathPattern.js";
6
+ import * as Route from "../Route.js";
7
+ import * as RouteTree from "../RouteTree.js";
8
+ import * as Values from "../Values.js";
9
+ import * as Bundle from "./Bundle.js";
10
+ /**
11
+ * Creates a GET route that serves bundle artifacts.
12
+ * Mount at a path with a wildcard param (any name works).
13
+ *
14
+ * ```ts
15
+ * RouteTree.make({
16
+ * "/_bundle/:path+": BundleRoute.make(Bundle.ClientBundle),
17
+ * })
18
+ * ```
19
+ */
20
+ export const make = (tag) => Route.get(Route.render(function* (ctx) {
21
+ const bundle = yield* tag;
22
+ const url = new URL(ctx.request.url);
23
+ const mountPath = ctx.path ?? "/";
24
+ const params = PathPattern.match(mountPath, url.pathname);
25
+ const artifactPath = params ? Values.firstValue(params) : undefined;
26
+ if (!artifactPath) {
27
+ return Entity.make(new Uint8Array(0), { status: 404 });
28
+ }
29
+ const blob = bundle.getArtifact(artifactPath);
30
+ if (!blob) {
31
+ return Entity.make(new Uint8Array(0), { status: 404 });
32
+ }
33
+ const bytes = new Uint8Array(yield* Effect.promise(() => blob.arrayBuffer()));
34
+ return Entity.make(bytes, {
35
+ headers: {
36
+ "content-type": blob.type || "application/octet-stream",
37
+ "cache-control": "public, max-age=31536000, immutable",
38
+ },
39
+ });
40
+ }));
41
+ export const client = () => make(Bundle.ClientBundle);
42
+ export const layer = (options) => Layer.effect(Route.Routes, Effect.gen(function* () {
43
+ const existing = yield* Effect.serviceOption(Route.Routes).pipe(Effect.andThen(Option.getOrUndefined));
44
+ const path = options?.path ?? "/_bundle/:path+";
45
+ const bundleTree = Route.tree({
46
+ [path]: make(options?.bundle ?? Bundle.ClientBundle),
47
+ });
48
+ if (!existing)
49
+ return bundleTree;
50
+ return RouteTree.merge(existing, bundleTree);
51
+ }));