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
package/src/Values.ts CHANGED
@@ -66,6 +66,13 @@ export type Simplify<T> = {
66
66
  ? { [K in keyof U]: U[K] }
67
67
  : never
68
68
 
69
+ export const firstValue = <T>(record: Record<string, T>): T | undefined => {
70
+ for (const key in record) {
71
+ return record[key]
72
+ }
73
+ return undefined
74
+ }
75
+
69
76
  export const concatBytes = (a: Uint8Array, b: Uint8Array): Uint8Array => {
70
77
  const result = new Uint8Array(a.byteLength + b.byteLength)
71
78
  result.set(a)
@@ -0,0 +1,143 @@
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 type * as BunTypes from "bun"
6
+ import * as ChildProcess from "../ChildProcess.ts"
7
+ import * as PlatformError from "../PlatformError.ts"
8
+
9
+ const toCmd = (command: ChildProcess.Command): ReadonlyArray<string> => [
10
+ command.command,
11
+ ...command.args,
12
+ ]
13
+
14
+ export const layer: Layer.Layer<ChildProcess.ChildProcessSpawner> = Layer.succeed(
15
+ ChildProcess.ChildProcessSpawner,
16
+ {
17
+ spawn: (command) =>
18
+ Effect.gen(function* () {
19
+ const cmd = toCmd(command)
20
+
21
+ const proc = yield* Effect.try({
22
+ try: () =>
23
+ Bun.spawn(cmd as Array<string>, {
24
+ cwd: command.cwd,
25
+ env: command.env,
26
+ stdin: command.stdin ?? "ignore",
27
+ stdout: command.stdout ?? "pipe",
28
+ stderr: command.stderr ?? "pipe",
29
+ detached: command.detached,
30
+ }),
31
+ catch: (err) =>
32
+ new PlatformError.SystemError({
33
+ reason: "Unknown",
34
+ module: "ChildProcess",
35
+ method: "spawn",
36
+ description: err instanceof Error ? err.message : "Failed to spawn process",
37
+ cause: err,
38
+ }),
39
+ })
40
+
41
+ yield* Effect.addFinalizer(() =>
42
+ Effect.sync(() => {
43
+ if (!proc.killed) {
44
+ proc.kill()
45
+ }
46
+ }),
47
+ )
48
+
49
+ const handle: ChildProcess.ChildProcessHandle = {
50
+ pid: proc.pid,
51
+
52
+ exitCode: Effect.tryPromise({
53
+ try: () => proc.exited,
54
+ catch: (err) =>
55
+ new PlatformError.SystemError({
56
+ reason: "Unknown",
57
+ module: "ChildProcess",
58
+ method: "exitCode",
59
+ description: "Process exited unexpectedly",
60
+ cause: err,
61
+ }),
62
+ }),
63
+
64
+ isRunning: Effect.try({
65
+ try: () => !proc.killed && proc.exitCode === null,
66
+ catch: (err) =>
67
+ new PlatformError.SystemError({
68
+ reason: "BadResource",
69
+ module: "ChildProcess",
70
+ method: "isRunning",
71
+ description: err instanceof Error ? err.message : "Failed to check process status",
72
+ cause: err,
73
+ }),
74
+ }),
75
+
76
+ kill: (options) =>
77
+ Effect.try({
78
+ try: () => {
79
+ proc.kill(options?.killSignal)
80
+ },
81
+ catch: (err) =>
82
+ new PlatformError.SystemError({
83
+ reason: "BadResource",
84
+ module: "ChildProcess",
85
+ method: "kill",
86
+ description: err instanceof Error ? err.message : "Failed to kill process",
87
+ cause: err,
88
+ }),
89
+ }),
90
+
91
+ stdin: Sink.forEach((chunk: Uint8Array) =>
92
+ Effect.try({
93
+ try: () => {
94
+ const sink = proc.stdin as unknown as BunTypes.FileSink
95
+ sink.write(chunk)
96
+ },
97
+ catch: (err) =>
98
+ new PlatformError.SystemError({
99
+ reason: "Unknown",
100
+ module: "ChildProcess",
101
+ method: "fromWritable(stdin)",
102
+ description: "Failed to write to stdin",
103
+ cause: err,
104
+ }),
105
+ }),
106
+ ).pipe(
107
+ Sink.ensuring(
108
+ Effect.promise(async () => {
109
+ const sink = proc.stdin as unknown as BunTypes.FileSink
110
+ await sink.end()
111
+ }),
112
+ ),
113
+ ),
114
+
115
+ stdout: Stream.fromReadableStream(
116
+ () => proc.stdout as ReadableStream<Uint8Array>,
117
+ (err) =>
118
+ new PlatformError.SystemError({
119
+ reason: "Unknown",
120
+ module: "ChildProcess",
121
+ method: "fromReadable(stdout)",
122
+ description: "Failed to read stdout stream",
123
+ cause: err,
124
+ }),
125
+ ),
126
+
127
+ stderr: Stream.fromReadableStream(
128
+ () => proc.stderr as ReadableStream<Uint8Array>,
129
+ (err) =>
130
+ new PlatformError.SystemError({
131
+ reason: "Unknown",
132
+ module: "ChildProcess",
133
+ method: "fromReadable(stderr)",
134
+ description: "Failed to read stderr stream",
135
+ cause: err,
136
+ }),
137
+ ),
138
+ }
139
+
140
+ return handle
141
+ }),
142
+ },
143
+ )
@@ -34,9 +34,12 @@ export function descriptors(
34
34
  return undefined
35
35
  }
36
36
 
37
- export function htmlBundle(load: () => Promise<Bun.HTMLBundle | { default: Bun.HTMLBundle }>) {
37
+ type HTMLBundleModule = Bun.HTMLBundle | { default: Bun.HTMLBundle }
38
+
39
+ export function htmlBundle(load: () => HTMLBundleModule | Promise<HTMLBundleModule>) {
38
40
  const bunPrefix = `/.BunRoute-${Unique.token(10)}`
39
- const bunLoad = () => load().then((mod) => ("default" in mod ? mod.default : mod))
41
+ const bunLoad = () =>
42
+ Promise.resolve(load()).then((mod) => ("default" in mod ? mod.default : mod))
40
43
  const descriptors = { bunPrefix, bunLoad, format: "html" as const }
41
44
 
42
45
  return function <D extends Route.RouteDescriptor.Any, B extends {}, I extends Route.Route.Tuple>(
@@ -10,6 +10,7 @@ import * as Option from "effect/Option"
10
10
  import * as Runtime from "effect/Runtime"
11
11
  import type * as Scope from "effect/Scope"
12
12
  import * as NOs from "node:os"
13
+ import * as NPath from "node:path"
13
14
  import * as PathPattern from "../PathPattern.ts"
14
15
  import * as PlataformRuntime from "../PlatformRuntime.ts"
15
16
  import * as Route from "../Route.ts"
@@ -191,7 +192,11 @@ function walkBunRoutes(runtime: Runtime.Runtime<BunServer>, tree: RouteTree.Rout
191
192
  const bunDescriptors = BunRoute.descriptors(route)
192
193
  if (bunDescriptors) {
193
194
  const htmlBundle = yield* Effect.promise(bunDescriptors.bunLoad)
194
- bunRoutes[`${bunDescriptors.bunPrefix}/*`] = htmlBundle
195
+ if (htmlBundle.files) {
196
+ registerPrebuiltBundle(bunDescriptors.bunPrefix, htmlBundle, bunRoutes)
197
+ } else {
198
+ bunRoutes[`${bunDescriptors.bunPrefix}/*`] = htmlBundle
199
+ }
195
200
  }
196
201
 
197
202
  const path = Route.descriptor(route).path
@@ -211,6 +216,22 @@ function walkBunRoutes(runtime: Runtime.Runtime<BunServer>, tree: RouteTree.Rout
211
216
  })
212
217
  }
213
218
 
219
+ function registerPrebuiltBundle(prefix: string, bundle: any, bunRoutes: BunRoute.BunRoutes) {
220
+ const mainDir = NPath.dirname(Bun.main)
221
+ const indexPath = NPath.resolve(mainDir, bundle.index)
222
+
223
+ bunRoutes[`${prefix}/*`] = () =>
224
+ new Response(Bun.file(indexPath), { headers: { "content-type": "text/html;charset=utf-8" } })
225
+
226
+ for (const file of bundle.files ?? []) {
227
+ if (file.loader === "html") continue
228
+ const absPath = NPath.resolve(mainDir, file.path)
229
+ const basename = NPath.basename(file.path)
230
+ bunRoutes[`/${basename}`] = () =>
231
+ new Response(Bun.file(absPath), { headers: file.headers ?? {} })
232
+ }
233
+ }
234
+
214
235
  function getLocalIp(): string | undefined {
215
236
  return Object.values(NOs.networkInterfaces())
216
237
  .flatMap((addresses) => addresses ?? [])
package/src/bun/index.ts CHANGED
@@ -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"
@@ -0,0 +1,66 @@
1
+ import type * as Context from "effect/Context"
2
+ import * as Effect from "effect/Effect"
3
+ import * as Layer from "effect/Layer"
4
+ import * as Option from "effect/Option"
5
+ import * as Entity from "../Entity.ts"
6
+ import * as PathPattern from "../PathPattern.ts"
7
+ import * as Route from "../Route.ts"
8
+ import * as RouteTree from "../RouteTree.ts"
9
+ import * as Values from "../Values.ts"
10
+ import * as Bundle from "./Bundle.ts"
11
+
12
+ /**
13
+ * Creates a GET route that serves bundle artifacts.
14
+ * Mount at a path with a wildcard param (any name works).
15
+ *
16
+ * ```ts
17
+ * RouteTree.make({
18
+ * "/_bundle/:path+": BundleRoute.make(Bundle.ClientBundle),
19
+ * })
20
+ * ```
21
+ */
22
+ export const make = <Tag extends Context.Tag<any, Bundle.BundleContext>>(tag: Tag) =>
23
+ Route.get(
24
+ Route.render(function* (ctx) {
25
+ const bundle = yield* tag
26
+ const url = new URL(ctx.request.url)
27
+ const mountPath = (ctx as unknown as { path?: string }).path ?? "/"
28
+ const params = PathPattern.match(mountPath, url.pathname)
29
+ const artifactPath = params ? Values.firstValue(params) : undefined
30
+ if (!artifactPath) {
31
+ return Entity.make(new Uint8Array(0), { status: 404 })
32
+ }
33
+ const blob = bundle.getArtifact(artifactPath)
34
+ if (!blob) {
35
+ return Entity.make(new Uint8Array(0), { status: 404 })
36
+ }
37
+ const bytes = new Uint8Array(yield* Effect.promise(() => blob.arrayBuffer()))
38
+ return Entity.make(bytes, {
39
+ headers: {
40
+ "content-type": blob.type || "application/octet-stream",
41
+ "cache-control": "public, max-age=31536000, immutable",
42
+ },
43
+ })
44
+ }),
45
+ )
46
+
47
+ export const client = () => make(Bundle.ClientBundle)
48
+
49
+ export const layer = (options?: {
50
+ bundle?: Context.Tag<any, Bundle.BundleContext>
51
+ path?: PathPattern.PathPattern
52
+ }) =>
53
+ Layer.effect(
54
+ Route.Routes,
55
+ Effect.gen(function* () {
56
+ const existing = yield* Effect.serviceOption(Route.Routes).pipe(
57
+ Effect.andThen(Option.getOrUndefined),
58
+ )
59
+ const path = options?.path ?? "/_bundle/:path+"
60
+ const bundleTree = Route.tree({
61
+ [path]: make(options?.bundle ?? Bundle.ClientBundle),
62
+ })
63
+ if (!existing) return bundleTree
64
+ return RouteTree.merge(existing, bundleTree)
65
+ }),
66
+ )
@@ -0,0 +1,42 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Layer from "effect/Layer"
3
+ import * as Route from "../Route.ts"
4
+ import * as RouteTree from "../RouteTree.ts"
5
+ import * as ConsoleErrors from "./ConsoleErrors.ts"
6
+ import * as ConsoleLogger from "./ConsoleLogger.ts"
7
+ import * as ConsoleMetrics from "./ConsoleMetrics.ts"
8
+ import * as ConsoleProcess from "./ConsoleProcess.ts"
9
+ import * as ConsoleStore from "./ConsoleStore.ts"
10
+ import * as ConsoleTracer from "./ConsoleTracer.ts"
11
+ import consoleRoutes from "./routes/tree.ts"
12
+
13
+ export { ConsoleStore } from "./ConsoleStore.ts"
14
+
15
+ export function layer(
16
+ options?: ConsoleStore.ConsoleStoreOptions,
17
+ ): Layer.Layer<ConsoleStore.ConsoleStore> {
18
+ const store = ConsoleStore.layer(options)
19
+ return Layer.mergeAll(
20
+ ConsoleTracer.layer,
21
+ ConsoleLogger.layer,
22
+ ConsoleMetrics.layer,
23
+ ConsoleErrors.layer,
24
+ ConsoleProcess.layer,
25
+ ).pipe(Layer.provideMerge(store))
26
+ }
27
+
28
+ export function routeLayer(
29
+ prefix: string,
30
+ ): Layer.Layer<Route.Routes, never, Route.Routes | ConsoleStore.ConsoleStore> {
31
+ return Layer.effect(
32
+ Route.Routes,
33
+ Effect.gen(function* () {
34
+ const existing = yield* Route.Routes
35
+ ConsoleStore.store.prefix = prefix
36
+ const tree = Route.tree({
37
+ [prefix as "/"]: consoleRoutes,
38
+ })
39
+ return RouteTree.merge(existing, tree)
40
+ }),
41
+ )
42
+ }
@@ -0,0 +1,213 @@
1
+ import * as Cause from "effect/Cause"
2
+ import * as Chunk from "effect/Chunk"
3
+ import * as Effect from "effect/Effect"
4
+ import * as Exit from "effect/Exit"
5
+ import * as FiberId from "effect/FiberId"
6
+ import * as FiberRef from "effect/FiberRef"
7
+ import * as HashMap from "effect/HashMap"
8
+ import * as Layer from "effect/Layer"
9
+ import * as Option from "effect/Option"
10
+ import * as PubSub from "effect/PubSub"
11
+ import type * as Context from "effect/Context"
12
+ import type * as Fiber from "effect/Fiber"
13
+ import * as Supervisor from "effect/Supervisor"
14
+ import * as ConsoleStore from "./ConsoleStore.ts"
15
+
16
+ let errorId = 0
17
+
18
+ function safeSerialize(value: unknown, depth = 0): unknown {
19
+ if (depth > 4) return "<deep>"
20
+ if (value === null || value === undefined) return value
21
+ if (typeof value === "bigint") return `${value}n`
22
+ if (typeof value === "function") return undefined
23
+ if (typeof value === "symbol") return value.toString()
24
+ if (typeof value !== "object") return value
25
+ if (value instanceof Date) return value.toISOString()
26
+ if (value instanceof Error) return value.message
27
+ if (Array.isArray(value)) return value.slice(0, 20).map((v) => safeSerialize(v, depth + 1))
28
+ const proto = Object.getPrototypeOf(value)
29
+ if (proto !== null && proto !== Object.prototype) {
30
+ if (typeof (value as any)._tag === "string") {
31
+ return serializeTaggedObject(value as Record<string, unknown>, depth)
32
+ }
33
+ return `<${proto.constructor?.name ?? "object"}>`
34
+ }
35
+ return serializePlainObject(value as Record<string, unknown>, depth)
36
+ }
37
+
38
+ function serializePlainObject(
39
+ obj: Record<string, unknown>,
40
+ depth: number,
41
+ ): Record<string, unknown> {
42
+ const out: Record<string, unknown> = {}
43
+ let count = 0
44
+ for (const [k, v] of Object.entries(obj)) {
45
+ if (count >= 20) break
46
+ if (typeof v === "function") continue
47
+ const serialized = safeSerialize(v, depth + 1)
48
+ if (serialized !== undefined) {
49
+ out[k] = serialized
50
+ count++
51
+ }
52
+ }
53
+ return out
54
+ }
55
+
56
+ function serializeTaggedObject(
57
+ obj: Record<string, unknown>,
58
+ depth: number,
59
+ ): Record<string, unknown> {
60
+ const out: Record<string, unknown> = { _tag: obj._tag }
61
+ let count = 0
62
+ for (const [k, v] of Object.entries(obj)) {
63
+ if (count >= 20) break
64
+ if (k === "_tag") continue
65
+ if (typeof v === "function") continue
66
+ if (k === "stack" || k === "name") continue
67
+ const serialized = safeSerialize(v, depth + 1)
68
+ if (serialized !== undefined) {
69
+ out[k] = serialized
70
+ count++
71
+ }
72
+ }
73
+ return out
74
+ }
75
+
76
+ function extractTag(error: unknown): string | undefined {
77
+ if (
78
+ error !== null &&
79
+ typeof error === "object" &&
80
+ "_tag" in error &&
81
+ typeof (error as any)._tag === "string"
82
+ ) {
83
+ return (error as any)._tag
84
+ }
85
+ return undefined
86
+ }
87
+
88
+ function extractMessage(error: unknown): string {
89
+ if (error instanceof Error) return error.message
90
+ if (typeof error === "string") return error
91
+ const tag = extractTag(error)
92
+ if (tag) return tag
93
+ try {
94
+ return String(error)
95
+ } catch {
96
+ return "<unknown>"
97
+ }
98
+ }
99
+
100
+ function extractProperties(error: unknown): Record<string, unknown> {
101
+ if (error === null || error === undefined || typeof error !== "object") return {}
102
+ const out: Record<string, unknown> = {}
103
+ let count = 0
104
+ for (const [k, v] of Object.entries(error)) {
105
+ if (count >= 20) break
106
+ if (k === "_tag" || k === "stack" || k === "name") continue
107
+ if (typeof v === "function") continue
108
+ const serialized = safeSerialize(v, 0)
109
+ if (serialized !== undefined) {
110
+ out[k] = serialized
111
+ count++
112
+ }
113
+ }
114
+ return out
115
+ }
116
+
117
+ const spanSymbol = Symbol.for("effect/SpanAnnotation")
118
+
119
+ function extractSpanName(error: unknown): string | undefined {
120
+ if (error !== null && typeof error === "object" && spanSymbol in error) {
121
+ const span = (error as any)[spanSymbol]
122
+ return typeof span?.name === "string" ? span.name : undefined
123
+ }
124
+ return undefined
125
+ }
126
+
127
+ function extractDetails(cause: Cause.Cause<unknown>): Array<ConsoleStore.ConsoleErrorDetail> {
128
+ const details: Array<ConsoleStore.ConsoleErrorDetail> = []
129
+
130
+ const failures = Chunk.toArray(Cause.failures(cause))
131
+ for (const error of failures) {
132
+ details.push({
133
+ kind: "fail",
134
+ tag: extractTag(error),
135
+ message: extractMessage(error),
136
+ properties: extractProperties(error),
137
+ span: extractSpanName(error),
138
+ })
139
+ }
140
+
141
+ const defects = Chunk.toArray(Cause.defects(cause))
142
+ for (const defect of defects) {
143
+ details.push({
144
+ kind: "die",
145
+ tag: extractTag(defect),
146
+ message: extractMessage(defect),
147
+ properties: extractProperties(defect),
148
+ span: extractSpanName(defect),
149
+ })
150
+ }
151
+
152
+ return details
153
+ }
154
+
155
+ function make(store: ConsoleStore.ConsoleStoreShape): Supervisor.Supervisor<void> {
156
+ return new (class extends Supervisor.AbstractSupervisor<void> {
157
+ value = Effect.void
158
+
159
+ onStart<A, E, R>(
160
+ _context: Context.Context<R>,
161
+ _effect: Effect.Effect<A, E, R>,
162
+ parent: Option.Option<Fiber.RuntimeFiber<any, any>>,
163
+ fiber: Fiber.RuntimeFiber<A, E>,
164
+ ) {
165
+ const childId = FiberId.threadName(fiber.id())
166
+ if (Option.isSome(parent)) {
167
+ const parentId = FiberId.threadName(parent.value.id())
168
+ if (childId !== parentId) {
169
+ store.fiberParents.set(childId, parentId)
170
+ }
171
+ }
172
+
173
+ const span = fiber.currentSpan
174
+ const annotations: Record<string, unknown> = {}
175
+ const spanAnnotations = fiber.getFiberRef(FiberRef.currentTracerSpanAnnotations)
176
+ HashMap.forEach(spanAnnotations, (value, key) => {
177
+ annotations[key] = value
178
+ })
179
+ const logAnnotations = fiber.getFiberRef(FiberRef.currentLogAnnotations)
180
+ HashMap.forEach(logAnnotations, (value, key) => {
181
+ annotations[key] = value
182
+ })
183
+
184
+ store.fiberContexts.set(childId, {
185
+ spanName: span?._tag === "Span" ? span.name : undefined,
186
+ traceId: span ? span.traceId : undefined,
187
+ annotations,
188
+ })
189
+ }
190
+
191
+ onEnd<A, E>(exit: Exit.Exit<A, E>, fiber: Fiber.RuntimeFiber<A, E>) {
192
+ if (Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause)) {
193
+ const error: ConsoleStore.ConsoleError = {
194
+ id: String(++errorId),
195
+ date: new Date(),
196
+ fiberId: FiberId.threadName(fiber.id()),
197
+ interrupted: Cause.isInterrupted(exit.cause),
198
+ prettyPrint: Cause.pretty(exit.cause, { renderErrorCause: true }),
199
+ details: extractDetails(exit.cause),
200
+ }
201
+ store.errors.push(error)
202
+ Effect.runSync(PubSub.publish(store.events, { _tag: "Error", error }))
203
+ }
204
+ }
205
+ })()
206
+ }
207
+
208
+ export const layer: Layer.Layer<never, never, ConsoleStore.ConsoleStore> = Layer.unwrapEffect(
209
+ Effect.gen(function* () {
210
+ const store = yield* ConsoleStore.ConsoleStore
211
+ return Supervisor.addSupervisor(make(store))
212
+ }),
213
+ )
@@ -0,0 +1,56 @@
1
+ import * as Cause from "effect/Cause"
2
+ import * as Effect from "effect/Effect"
3
+ import * as FiberId from "effect/FiberId"
4
+ import * as FiberRef from "effect/FiberRef"
5
+ import * as HashMap from "effect/HashMap"
6
+ import * as HashSet from "effect/HashSet"
7
+ import * as Layer from "effect/Layer"
8
+ import * as List from "effect/List"
9
+ import * as Logger from "effect/Logger"
10
+ import * as PubSub from "effect/PubSub"
11
+ import * as ConsoleStore from "./ConsoleStore.ts"
12
+
13
+ let logId = 0
14
+
15
+ export const layer: Layer.Layer<never, never, ConsoleStore.ConsoleStore> = Layer.effectDiscard(
16
+ Effect.gen(function* () {
17
+ const store = yield* ConsoleStore.ConsoleStore
18
+
19
+ const logger = Logger.make(
20
+ ({ message, logLevel, cause, fiberId, spans, annotations, date }) => {
21
+ const levelMap: Record<string, ConsoleStore.ConsoleLog["level"]> = {
22
+ Debug: "DEBUG",
23
+ Info: "INFO",
24
+ Warning: "WARNING",
25
+ Error: "ERROR",
26
+ Fatal: "FATAL",
27
+ }
28
+ const level = levelMap[logLevel._tag] ?? "INFO"
29
+ const causeStr = !Cause.isEmpty(cause)
30
+ ? Cause.pretty(cause, { renderErrorCause: true })
31
+ : undefined
32
+ const spanNames: Array<string> = []
33
+ List.forEach(spans, (s) => spanNames.push(s.label))
34
+ const ann: Record<string, unknown> = {}
35
+ HashMap.forEach(annotations, (v, k) => {
36
+ ann[k] = v
37
+ })
38
+
39
+ const log: ConsoleStore.ConsoleLog = {
40
+ id: String(++logId),
41
+ date,
42
+ level,
43
+ message: String(message),
44
+ fiberId: FiberId.threadName(fiberId),
45
+ cause: causeStr,
46
+ spans: spanNames,
47
+ annotations: ann,
48
+ }
49
+ store.logs.push(log)
50
+ Effect.runSync(PubSub.publish(store.events, { _tag: "Log", log }))
51
+ },
52
+ )
53
+
54
+ yield* FiberRef.update(FiberRef.currentLoggers, (loggers) => HashSet.add(loggers, logger))
55
+ }),
56
+ )
@@ -0,0 +1,72 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Layer from "effect/Layer"
3
+ import * as Metric from "effect/Metric"
4
+ import * as MetricKeyType from "effect/MetricKeyType"
5
+ import * as PubSub from "effect/PubSub"
6
+ import * as Schedule from "effect/Schedule"
7
+ import * as ConsoleStore from "./ConsoleStore.ts"
8
+
9
+ export const layer: Layer.Layer<never, never, ConsoleStore.ConsoleStore> = Layer.scopedDiscard(
10
+ Effect.gen(function* () {
11
+ const store = yield* ConsoleStore.ConsoleStore
12
+
13
+ yield* Effect.forkScoped(
14
+ Effect.schedule(
15
+ Effect.sync(() => {
16
+ const pairs = Metric.unsafeSnapshot()
17
+ const snapshots: Array<ConsoleStore.ConsoleMetricSnapshot> = []
18
+
19
+ for (const pair of pairs) {
20
+ const key = pair.metricKey
21
+ const state = pair.metricState as any
22
+ let type: ConsoleStore.ConsoleMetricSnapshot["type"] = "counter"
23
+ let value: unknown = 0
24
+
25
+ if (MetricKeyType.CounterKeyTypeTypeId in key.keyType) {
26
+ type = "counter"
27
+ value = state.count
28
+ } else if (MetricKeyType.GaugeKeyTypeTypeId in key.keyType) {
29
+ type = "gauge"
30
+ value = state.value
31
+ } else if (MetricKeyType.HistogramKeyTypeTypeId in key.keyType) {
32
+ type = "histogram"
33
+ value = {
34
+ buckets: state.buckets,
35
+ count: state.count,
36
+ sum: state.sum,
37
+ min: state.min,
38
+ max: state.max,
39
+ }
40
+ } else if (MetricKeyType.FrequencyKeyTypeTypeId in key.keyType) {
41
+ type = "frequency"
42
+ value = Object.fromEntries(state.occurrences)
43
+ } else if (MetricKeyType.SummaryKeyTypeTypeId in key.keyType) {
44
+ type = "summary"
45
+ value = {
46
+ quantiles: state.quantiles,
47
+ count: state.count,
48
+ sum: state.sum,
49
+ min: state.min,
50
+ max: state.max,
51
+ }
52
+ }
53
+
54
+ snapshots.push({
55
+ name: key.name,
56
+ type,
57
+ value,
58
+ tags: key.tags.map((t: any) => ({ key: t.key, value: t.value })),
59
+ timestamp: Date.now(),
60
+ })
61
+ }
62
+
63
+ store.metrics = snapshots
64
+ Effect.runSync(
65
+ PubSub.publish(store.events, { _tag: "MetricsSnapshot", metrics: snapshots }),
66
+ )
67
+ }),
68
+ Schedule.spaced("2 seconds"),
69
+ ),
70
+ )
71
+ }),
72
+ )