effect-start 0.29.0 → 0.30.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.
- package/README.md +1 -1
- package/dist/{_Development.d.ts → Development.d.ts} +2 -1
- package/dist/Development.d.ts.map +1 -0
- package/dist/{_Development.js → Development.js} +2 -1
- package/dist/Development.js.map +1 -0
- package/dist/Fetch.d.ts +0 -8
- package/dist/Fetch.d.ts.map +1 -1
- package/dist/Fetch.js +6 -22
- package/dist/Fetch.js.map +1 -1
- package/dist/FileRouter.js +1 -1
- package/dist/FileRouter.js.map +1 -1
- package/dist/GlobalLayer.d.ts.map +1 -1
- package/dist/GlobalLayer.js +1 -1
- package/dist/GlobalLayer.js.map +1 -1
- package/dist/Html.d.ts +32 -0
- package/dist/Html.d.ts.map +1 -0
- package/dist/{hyper/HyperHtml.js → Html.js} +45 -26
- package/dist/Html.js.map +1 -0
- package/dist/Route.d.ts +20 -7
- package/dist/Route.d.ts.map +1 -1
- package/dist/Route.js +24 -3
- package/dist/Route.js.map +1 -1
- package/dist/RouteBody.d.ts +13 -6
- package/dist/RouteBody.d.ts.map +1 -1
- package/dist/RouteBody.js +38 -27
- package/dist/RouteBody.js.map +1 -1
- package/dist/RouteHttp.d.ts.map +1 -1
- package/dist/RouteHttp.js +18 -1
- package/dist/RouteHttp.js.map +1 -1
- package/dist/RouteMount.js +1 -1
- package/dist/RouteMount.js.map +1 -1
- package/dist/System.d.ts +1 -1
- package/dist/System.d.ts.map +1 -1
- package/dist/System.js.map +1 -1
- package/dist/_ChildProcess.d.ts +1 -1
- package/dist/_ChildProcess.d.ts.map +1 -1
- package/dist/_ChildProcess.js.map +1 -1
- package/dist/bun/BunRoute.d.ts +1 -1
- package/dist/bun/BunRoute.d.ts.map +1 -1
- package/dist/bun/BunRoute.js +102 -33
- package/dist/bun/BunRoute.js.map +1 -1
- package/dist/bun/BunServer.d.ts.map +1 -1
- package/dist/bun/BunServer.js.map +1 -1
- package/dist/cloudflare/CloudflareTunnel.d.ts +12 -0
- package/dist/cloudflare/CloudflareTunnel.d.ts.map +1 -0
- package/dist/{x/cloudflare → cloudflare}/CloudflareTunnel.js +1 -1
- package/dist/cloudflare/CloudflareTunnel.js.map +1 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +8 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/{hyper/jsx-runtime.js → jsx-runtime.js} +2 -2
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/studio/routes/errors/route.d.ts.map +1 -1
- package/dist/studio/routes/errors/route.js +3 -4
- package/dist/studio/routes/errors/route.js.map +1 -1
- package/dist/studio/routes/fiberDetail.d.ts.map +1 -1
- package/dist/studio/routes/fiberDetail.js +1 -2
- package/dist/studio/routes/fiberDetail.js.map +1 -1
- package/dist/studio/routes/fibers/route.d.ts.map +1 -1
- package/dist/studio/routes/fibers/route.js +3 -4
- package/dist/studio/routes/fibers/route.js.map +1 -1
- package/dist/studio/routes/logs/route.d.ts.map +1 -1
- package/dist/studio/routes/logs/route.js +3 -4
- package/dist/studio/routes/logs/route.js.map +1 -1
- package/dist/studio/routes/metrics/route.d.ts.map +1 -1
- package/dist/studio/routes/metrics/route.js +3 -4
- package/dist/studio/routes/metrics/route.js.map +1 -1
- package/dist/studio/routes/route.d.ts +1 -1
- package/dist/studio/routes/routes/route.d.ts.map +1 -1
- package/dist/studio/routes/routes/route.js +1 -2
- package/dist/studio/routes/routes/route.js.map +1 -1
- package/dist/studio/routes/services/route.d.ts.map +1 -1
- package/dist/studio/routes/services/route.js +1 -2
- package/dist/studio/routes/services/route.js.map +1 -1
- package/dist/studio/routes/system/route.d.ts.map +1 -1
- package/dist/studio/routes/system/route.js +3 -4
- package/dist/studio/routes/system/route.js.map +1 -1
- package/dist/studio/routes/traceDetail.d.ts.map +1 -1
- package/dist/studio/routes/traceDetail.js +1 -2
- package/dist/studio/routes/traceDetail.js.map +1 -1
- package/dist/studio/routes/traces/route.d.ts.map +1 -1
- package/dist/studio/routes/traces/route.js +3 -4
- package/dist/studio/routes/traces/route.js.map +1 -1
- package/dist/studio/routes/tree.d.ts +1 -1
- package/dist/studio/ui/Errors.d.ts +1 -1
- package/dist/studio/ui/Errors.d.ts.map +1 -1
- package/dist/studio/ui/Fibers.d.ts +2 -2
- package/dist/studio/ui/Fibers.d.ts.map +1 -1
- package/dist/studio/ui/Logs.d.ts +1 -1
- package/dist/studio/ui/Logs.d.ts.map +1 -1
- package/dist/studio/ui/Metrics.d.ts +1 -1
- package/dist/studio/ui/Metrics.d.ts.map +1 -1
- package/dist/studio/ui/Routes.d.ts +1 -1
- package/dist/studio/ui/Routes.d.ts.map +1 -1
- package/dist/studio/ui/Services.d.ts +1 -1
- package/dist/studio/ui/Services.d.ts.map +1 -1
- package/dist/studio/ui/Shell.d.ts +2 -2
- package/dist/studio/ui/Shell.d.ts.map +1 -1
- package/dist/studio/ui/System.d.ts +1 -1
- package/dist/studio/ui/System.d.ts.map +1 -1
- package/dist/studio/ui/Traces.d.ts +3 -3
- package/dist/studio/ui/Traces.d.ts.map +1 -1
- package/dist/tailscale/TailscaleTunnel.d.ts +16 -0
- package/dist/tailscale/TailscaleTunnel.d.ts.map +1 -0
- package/dist/{x/tailscale → tailscale}/TailscaleTunnel.js +2 -2
- package/dist/tailscale/TailscaleTunnel.js.map +1 -0
- package/dist/tailscale/index.d.ts.map +1 -0
- package/dist/tailscale/index.js.map +1 -0
- package/dist/tailwind/TailwindPlugin.d.ts.map +1 -0
- package/dist/tailwind/TailwindPlugin.js.map +1 -0
- package/dist/tailwind/compile.d.ts.map +1 -0
- package/dist/{x/tailwind → tailwind}/compile.js +1 -1
- package/dist/tailwind/compile.js.map +1 -0
- package/dist/tailwind/index.d.ts +3 -0
- package/dist/tailwind/index.d.ts.map +1 -0
- package/dist/tailwind/index.js +3 -0
- package/dist/tailwind/index.js.map +1 -0
- package/dist/tailwind/plugin.d.ts.map +1 -0
- package/dist/{x/tailwind → tailwind}/plugin.js +1 -1
- package/dist/tailwind/plugin.js.map +1 -0
- package/package.json +37 -37
- package/src/{_Development.ts → Development.ts} +5 -0
- package/src/Fetch.ts +10 -37
- package/src/FileRouter.ts +1 -1
- package/src/GlobalLayer.ts +3 -1
- package/src/{hyper/HyperHtml.ts → Html.ts} +90 -30
- package/src/Route.ts +67 -11
- package/src/RouteBody.ts +87 -62
- package/src/RouteHttp.ts +19 -1
- package/src/RouteMount.ts +1 -1
- package/src/System.ts +1 -1
- package/src/_ChildProcess.ts +1 -1
- package/src/bun/BunRoute.ts +125 -37
- package/src/bun/BunServer.ts +1 -0
- package/src/{x/cloudflare → cloudflare}/CloudflareTunnel.ts +1 -1
- package/src/index.ts +3 -1
- package/src/jsx-runtime.ts +15 -0
- package/src/{hyper/jsx.d.ts → jsx.d.ts} +3 -3
- package/src/studio/routes/errors/route.tsx +3 -4
- package/src/studio/routes/fiberDetail.tsx +1 -2
- package/src/studio/routes/fibers/route.tsx +3 -4
- package/src/studio/routes/logs/route.tsx +3 -4
- package/src/studio/routes/metrics/route.tsx +3 -4
- package/src/studio/routes/routes/route.tsx +1 -2
- package/src/studio/routes/services/route.tsx +1 -2
- package/src/studio/routes/system/route.tsx +3 -4
- package/src/studio/routes/traceDetail.tsx +1 -2
- package/src/studio/routes/traces/route.tsx +3 -4
- package/src/{x/tailscale → tailscale}/TailscaleTunnel.ts +2 -2
- package/src/{x/tailwind → tailwind}/compile.ts +1 -1
- package/src/tailwind/index.ts +2 -0
- package/src/{x/tailwind → tailwind}/plugin.ts +1 -1
- package/dist/_Development.d.ts.map +0 -1
- package/dist/_Development.js.map +0 -1
- package/dist/hyper/Hyper.d.ts +0 -26
- package/dist/hyper/Hyper.d.ts.map +0 -1
- package/dist/hyper/Hyper.js +0 -24
- package/dist/hyper/Hyper.js.map +0 -1
- package/dist/hyper/HyperHtml.d.ts +0 -24
- package/dist/hyper/HyperHtml.d.ts.map +0 -1
- package/dist/hyper/HyperHtml.js.map +0 -1
- package/dist/hyper/HyperHtml.test.d.ts +0 -2
- package/dist/hyper/HyperHtml.test.d.ts.map +0 -1
- package/dist/hyper/HyperHtml.test.js +0 -283
- package/dist/hyper/HyperHtml.test.js.map +0 -1
- package/dist/hyper/HyperNode.d.ts +0 -14
- package/dist/hyper/HyperNode.d.ts.map +0 -1
- package/dist/hyper/HyperNode.js +0 -12
- package/dist/hyper/HyperNode.js.map +0 -1
- package/dist/hyper/HyperRoute.d.ts +0 -9
- package/dist/hyper/HyperRoute.d.ts.map +0 -1
- package/dist/hyper/HyperRoute.js +0 -33
- package/dist/hyper/HyperRoute.js.map +0 -1
- package/dist/hyper/HyperRoute.test.d.ts +0 -2
- package/dist/hyper/HyperRoute.test.d.ts.map +0 -1
- package/dist/hyper/HyperRoute.test.js +0 -84
- package/dist/hyper/HyperRoute.test.js.map +0 -1
- package/dist/hyper/html.d.ts +0 -12
- package/dist/hyper/html.d.ts.map +0 -1
- package/dist/hyper/html.js +0 -31
- package/dist/hyper/html.js.map +0 -1
- package/dist/hyper/index.d.ts +0 -7
- package/dist/hyper/index.d.ts.map +0 -1
- package/dist/hyper/index.js +0 -6
- package/dist/hyper/index.js.map +0 -1
- package/dist/hyper/jsx-runtime.d.ts +0 -8
- package/dist/hyper/jsx-runtime.d.ts.map +0 -1
- package/dist/hyper/jsx-runtime.js.map +0 -1
- package/dist/x/cloudflare/CloudflareTunnel.d.ts +0 -12
- package/dist/x/cloudflare/CloudflareTunnel.d.ts.map +0 -1
- package/dist/x/cloudflare/CloudflareTunnel.js.map +0 -1
- package/dist/x/cloudflare/index.d.ts.map +0 -1
- package/dist/x/cloudflare/index.js.map +0 -1
- package/dist/x/tailscale/TailscaleTunnel.d.ts +0 -16
- package/dist/x/tailscale/TailscaleTunnel.d.ts.map +0 -1
- package/dist/x/tailscale/TailscaleTunnel.js.map +0 -1
- package/dist/x/tailscale/index.d.ts.map +0 -1
- package/dist/x/tailscale/index.js.map +0 -1
- package/dist/x/tailwind/TailwindPlugin.d.ts.map +0 -1
- package/dist/x/tailwind/TailwindPlugin.js.map +0 -1
- package/dist/x/tailwind/compile.d.ts.map +0 -1
- package/dist/x/tailwind/compile.js.map +0 -1
- package/dist/x/tailwind/plugin.d.ts.map +0 -1
- package/dist/x/tailwind/plugin.js.map +0 -1
- package/src/hyper/Hyper.ts +0 -55
- package/src/hyper/HyperHtml.test.tsx +0 -395
- package/src/hyper/HyperNode.ts +0 -33
- package/src/hyper/HyperRoute.test.tsx +0 -166
- package/src/hyper/HyperRoute.ts +0 -59
- package/src/hyper/html.ts +0 -47
- package/src/hyper/index.ts +0 -6
- package/src/hyper/jsx-runtime.ts +0 -15
- /package/dist/{x/cloudflare → cloudflare}/index.d.ts +0 -0
- /package/dist/{x/cloudflare → cloudflare}/index.js +0 -0
- /package/dist/{x/tailscale → tailscale}/index.d.ts +0 -0
- /package/dist/{x/tailscale → tailscale}/index.js +0 -0
- /package/dist/{x/tailwind → tailwind}/TailwindPlugin.d.ts +0 -0
- /package/dist/{x/tailwind → tailwind}/TailwindPlugin.js +0 -0
- /package/dist/{x/tailwind → tailwind}/compile.d.ts +0 -0
- /package/dist/{x/tailwind → tailwind}/plugin.d.ts +0 -0
- /package/src/{x/cloudflare → cloudflare}/index.ts +0 -0
- /package/src/{x/tailscale → tailscale}/index.ts +0 -0
- /package/src/{x/tailwind → tailwind}/TailwindPlugin.ts +0 -0
|
@@ -1,27 +1,46 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Renders Hyper JSX nodes to HTML.
|
|
3
|
-
*
|
|
4
|
-
* Effect Start comes with {@link Hyper} and {@link JsxRuntime} to enable
|
|
5
|
-
* JSX support. The advantage of using JSX over HTML strings or templates
|
|
6
|
-
* is type safety and better editor support.
|
|
7
|
-
*
|
|
8
|
-
* JSX nodes are compatible with React's and Solid's.
|
|
9
|
-
|
|
10
|
-
* You can enable JSX support by updating `tsconfig.json`:
|
|
11
|
-
*
|
|
12
|
-
* {
|
|
13
|
-
* compilerOptions: {
|
|
14
|
-
* jsx: "react-jsx",
|
|
15
|
-
* jsxImportSource: "effect-start" | "react" | "praect" // etc.
|
|
16
|
-
* }
|
|
17
|
-
* }
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import type * as Hyper from "./Hyper.ts"
|
|
21
|
-
import type * as HyperNode from "./HyperNode.ts"
|
|
22
|
-
import type * as JsxRuntime from "./jsx-runtime.ts"
|
|
23
1
|
import type { JSX } from "./jsx.d.ts"
|
|
24
2
|
|
|
3
|
+
export const TypeId = "~effect-start/HyperNode" as const
|
|
4
|
+
|
|
5
|
+
const NoChildren: ReadonlyArray<never> = Object.freeze([])
|
|
6
|
+
|
|
7
|
+
type Primitive = string | number | boolean | null | undefined
|
|
8
|
+
|
|
9
|
+
export type ElementType = string | Component
|
|
10
|
+
|
|
11
|
+
export type ElemenetProps = {
|
|
12
|
+
[key: string]:
|
|
13
|
+
| Primitive
|
|
14
|
+
| Element
|
|
15
|
+
| Iterable<Primitive | Element>
|
|
16
|
+
| Record<string, unknown>
|
|
17
|
+
| ((window: Window) => void)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type Component = (props: ElemenetProps) => Element | Primitive
|
|
21
|
+
|
|
22
|
+
export interface Element {
|
|
23
|
+
type: ElementType
|
|
24
|
+
props: ElemenetProps
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function make(type: ElementType, props: ElemenetProps): Element {
|
|
28
|
+
return {
|
|
29
|
+
type,
|
|
30
|
+
props: {
|
|
31
|
+
...props,
|
|
32
|
+
children: props.children ?? NoChildren,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isGenericJsxObject(value: unknown): value is {
|
|
38
|
+
type: any
|
|
39
|
+
props: any
|
|
40
|
+
} {
|
|
41
|
+
return typeof value === "object" && value !== null && "type" in value && "props" in value
|
|
42
|
+
}
|
|
43
|
+
|
|
25
44
|
const EMPTY_TAGS = [
|
|
26
45
|
"area",
|
|
27
46
|
"base",
|
|
@@ -53,12 +72,11 @@ let map = {
|
|
|
53
72
|
|
|
54
73
|
const RAW_TEXT_TAGS = ["script", "style"]
|
|
55
74
|
|
|
56
|
-
// Prevents closing html tags in embedded css/js source
|
|
57
75
|
const escapeRawText = (text: string) => text.replaceAll("</", "<\\/")
|
|
58
76
|
|
|
59
77
|
export function renderToString(
|
|
60
78
|
node: JSX.Children,
|
|
61
|
-
hooks?: { onNode?: (node:
|
|
79
|
+
hooks?: { onNode?: (node: Element) => void },
|
|
62
80
|
): string {
|
|
63
81
|
const stack: Array<any> = [node]
|
|
64
82
|
let result = ""
|
|
@@ -68,7 +86,6 @@ export function renderToString(
|
|
|
68
86
|
|
|
69
87
|
if (typeof current === "string") {
|
|
70
88
|
if (current.startsWith("<") && current.endsWith(">")) {
|
|
71
|
-
// This is a closing tag, don't escape it
|
|
72
89
|
result += current
|
|
73
90
|
} else {
|
|
74
91
|
result += esc(current)
|
|
@@ -82,17 +99,14 @@ export function renderToString(
|
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
if (typeof current === "boolean") {
|
|
85
|
-
// React-like behavior: booleans render nothing
|
|
86
102
|
continue
|
|
87
103
|
}
|
|
88
104
|
|
|
89
105
|
if (current === null || current === undefined) {
|
|
90
|
-
// React-like behavior: null/undefined render nothing
|
|
91
106
|
continue
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
if (Array.isArray(current)) {
|
|
95
|
-
// Handle arrays by pushing all items to stack in reverse order
|
|
96
110
|
for (let i = current.length - 1; i >= 0; i--) {
|
|
97
111
|
stack.push(current[i])
|
|
98
112
|
}
|
|
@@ -165,10 +179,56 @@ export function renderToString(
|
|
|
165
179
|
}
|
|
166
180
|
}
|
|
167
181
|
} else if (current && typeof current === "object") {
|
|
168
|
-
// Handle objects without type property - convert to string or ignore
|
|
169
|
-
// This prevents [object Object] from appearing
|
|
170
182
|
continue
|
|
171
183
|
}
|
|
172
184
|
}
|
|
173
185
|
return result
|
|
174
186
|
}
|
|
187
|
+
|
|
188
|
+
const HtmlStringSymbol = Symbol.for("HtmlString")
|
|
189
|
+
|
|
190
|
+
export interface HtmlString {
|
|
191
|
+
readonly [HtmlStringSymbol]: true
|
|
192
|
+
readonly value: string
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const makeHtmlString = (value: string): HtmlString => ({
|
|
196
|
+
[HtmlStringSymbol]: true,
|
|
197
|
+
value,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const isHtmlString = (value: unknown): value is HtmlString =>
|
|
201
|
+
typeof value === "object" && value !== null && HtmlStringSymbol in value
|
|
202
|
+
|
|
203
|
+
type HtmlValue =
|
|
204
|
+
| string
|
|
205
|
+
| number
|
|
206
|
+
| bigint
|
|
207
|
+
| boolean
|
|
208
|
+
| null
|
|
209
|
+
| undefined
|
|
210
|
+
| HtmlString
|
|
211
|
+
| Function
|
|
212
|
+
| Record<string, unknown>
|
|
213
|
+
| ReadonlyArray<HtmlValue>
|
|
214
|
+
|
|
215
|
+
const resolveValue = (value: HtmlValue): string => {
|
|
216
|
+
if (value === null || value === undefined || value === false || value === true) return ""
|
|
217
|
+
if (isHtmlString(value)) return value.value
|
|
218
|
+
if (Array.isArray(value)) return (value as Array<HtmlValue>).map(resolveValue).join("")
|
|
219
|
+
if (typeof value === "function") return value.toString()
|
|
220
|
+
if (typeof value === "object") return JSON.stringify(value)
|
|
221
|
+
if (typeof value === "string") return value
|
|
222
|
+
return String(value)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const html = (strings: TemplateStringsArray, ...values: Array<HtmlValue>): HtmlString => {
|
|
226
|
+
let result = strings[0]
|
|
227
|
+
for (let i = 0; i < values.length; i++) {
|
|
228
|
+
result += resolveValue(values[i])
|
|
229
|
+
result += strings[i + 1]
|
|
230
|
+
}
|
|
231
|
+
return makeHtmlString(result)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
html.raw = (value: string): HtmlString => makeHtmlString(value)
|
package/src/Route.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import * as Context from "effect/Context"
|
|
2
2
|
import * as Effect from "effect/Effect"
|
|
3
3
|
import * as Layer from "effect/Layer"
|
|
4
|
+
import * as Option from "effect/Option"
|
|
4
5
|
import * as Pipeable from "effect/Pipeable"
|
|
5
6
|
import * as Predicate from "effect/Predicate"
|
|
7
|
+
import * as Development from "./Development.ts"
|
|
6
8
|
import * as Entity from "./Entity.ts"
|
|
7
9
|
import * as RouteBody from "./RouteBody.ts"
|
|
8
10
|
import * as RouteTree from "./RouteTree.ts"
|
|
9
11
|
import type * as Values from "./_Values.ts"
|
|
12
|
+
import * as Html from "./Html.ts"
|
|
13
|
+
import type { JSX } from "./jsx.d.ts"
|
|
14
|
+
|
|
15
|
+
export const render = RouteBody.render
|
|
10
16
|
|
|
11
17
|
export const RouteItems: unique symbol = Symbol()
|
|
12
18
|
export const RouteDescriptor: unique symbol = Symbol()
|
|
@@ -162,6 +168,7 @@ export function items<T extends RouteSet.Data<any, any, any>>(self: T): RouteSet
|
|
|
162
168
|
export function descriptor<T extends RouteSet.Data<any, any, any>>(
|
|
163
169
|
self: T,
|
|
164
170
|
): T[typeof RouteDescriptor]
|
|
171
|
+
export function descriptor<E extends RouteDescriptor.Any>(self: RouteSet.Data<any, any, any>): E
|
|
165
172
|
export function descriptor<T extends RouteSet.Data<any, any, any>>(
|
|
166
173
|
self: Iterable<T>,
|
|
167
174
|
): Array<T[typeof RouteDescriptor]>
|
|
@@ -203,8 +210,10 @@ export const text = RouteBody.build<string, "text">({
|
|
|
203
210
|
format: "text",
|
|
204
211
|
})
|
|
205
212
|
|
|
206
|
-
export const html = RouteBody.build<string, "html">({
|
|
213
|
+
export const html = RouteBody.build<string | JSX.Children, string, "html">({
|
|
207
214
|
format: "html",
|
|
215
|
+
handle: (body) =>
|
|
216
|
+
typeof body === "string" ? body : Html.renderToString(body as JSX.Children),
|
|
208
217
|
})
|
|
209
218
|
|
|
210
219
|
export const json = RouteBody.build<Values.Json, "json">({
|
|
@@ -215,20 +224,31 @@ export const bytes = RouteBody.build<Uint8Array, "bytes">({
|
|
|
215
224
|
format: "bytes",
|
|
216
225
|
})
|
|
217
226
|
|
|
218
|
-
export { render } from "./RouteBody.ts"
|
|
219
|
-
|
|
220
227
|
export { sse } from "./RouteSse.ts"
|
|
221
228
|
|
|
222
|
-
export function redirect(
|
|
229
|
+
export function redirect<D extends RouteDescriptor.Any, B, I extends Route.Tuple>(
|
|
223
230
|
url: string | URL,
|
|
224
231
|
options?: { status?: 301 | 302 | 303 | 307 | 308 },
|
|
225
|
-
):
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
): (
|
|
233
|
+
self: RouteSet.RouteSet<D, B, I>,
|
|
234
|
+
) => RouteSet.RouteSet<D, B, [...I, Route.Route<{}, {}, "", never, never>]> {
|
|
235
|
+
const route = make<{}, {}, "">(
|
|
236
|
+
() =>
|
|
237
|
+
Effect.succeed(
|
|
238
|
+
Entity.make("", {
|
|
239
|
+
status: options?.status ?? 302,
|
|
240
|
+
headers: {
|
|
241
|
+
location: url instanceof URL ? url.href : url,
|
|
242
|
+
},
|
|
243
|
+
}),
|
|
244
|
+
),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
return (self) =>
|
|
248
|
+
set<D, B, [...I, Route.Route<{}, {}, "", never, never>]>(
|
|
249
|
+
[...items(self), route],
|
|
250
|
+
descriptor(self),
|
|
251
|
+
)
|
|
232
252
|
}
|
|
233
253
|
|
|
234
254
|
export class Routes extends Context.Tag("effect-start/Routes")<Routes, RouteTree.RouteTree>() {}
|
|
@@ -237,6 +257,42 @@ export function layer(routes: RouteTree.RouteMap | RouteTree.RouteTree) {
|
|
|
237
257
|
return Layer.sync(Routes, () => (RouteTree.isRouteTree(routes) ? routes : RouteTree.make(routes)))
|
|
238
258
|
}
|
|
239
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Creates a route that short-curcits in development.
|
|
262
|
+
*
|
|
263
|
+
* Note that when we convert the routes to web handles in {@link import("./RouteHttp.ts")},
|
|
264
|
+
* we exclude them altogeteher in development.
|
|
265
|
+
*/
|
|
266
|
+
export function devOnly<D extends RouteDescriptor.Any, B, I extends Route.Tuple>(
|
|
267
|
+
self: RouteSet.RouteSet<D, B, I>,
|
|
268
|
+
): RouteSet.RouteSet<D, B, [...I, Route.Route<{ dev: true }, { dev: true }, unknown, any, any>]> {
|
|
269
|
+
const route: Route.Route<{ dev: true }, { dev: true }, unknown, any, any> = make<
|
|
270
|
+
{ dev: true },
|
|
271
|
+
{ dev: true },
|
|
272
|
+
unknown,
|
|
273
|
+
any,
|
|
274
|
+
any
|
|
275
|
+
>(
|
|
276
|
+
(context, next) =>
|
|
277
|
+
Effect.flatMap(Development.option, (developmentOption) =>
|
|
278
|
+
Option.isSome(developmentOption)
|
|
279
|
+
? Effect.succeed(next({ ...context, dev: true }))
|
|
280
|
+
: Effect.succeed(Entity.make("", { status: 404 })),
|
|
281
|
+
),
|
|
282
|
+
{ dev: true },
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
const nextItems: [...I, Route.Route<{ dev: true }, { dev: true }, unknown, any, any>] = [
|
|
286
|
+
...items(self),
|
|
287
|
+
route,
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
return set<D, B, [...I, Route.Route<{ dev: true }, { dev: true }, unknown, any, any>]>(
|
|
291
|
+
nextItems,
|
|
292
|
+
descriptor(self),
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
240
296
|
export { make as tree } from "./RouteTree.ts"
|
|
241
297
|
|
|
242
298
|
export function lazy<T extends RouteSet.Any>(
|
package/src/RouteBody.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type * as Stream from "effect/Stream"
|
|
|
3
3
|
import type * as Utils from "effect/Utils"
|
|
4
4
|
import * as Entity from "./Entity.ts"
|
|
5
5
|
import * as Route from "./Route.ts"
|
|
6
|
+
import * as StreamExtra from "./_StreamExtra.ts"
|
|
6
7
|
import type * as Values from "./_Values.ts"
|
|
7
8
|
|
|
8
9
|
export type Format = "text" | "html" | "json" | "bytes" | "*"
|
|
@@ -21,21 +22,36 @@ type YieldError<T> = T extends Utils.YieldWrap<Effect.Effect<any, infer E, any>>
|
|
|
21
22
|
|
|
22
23
|
type YieldContext<T> = T extends Utils.YieldWrap<Effect.Effect<any, any, infer R>> ? R : never
|
|
23
24
|
|
|
25
|
+
type Next<B, A> = (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<UnwrapStream<A>>
|
|
26
|
+
|
|
27
|
+
type HandlerReturn<A> =
|
|
28
|
+
| A
|
|
29
|
+
| Entity.Entity<A>
|
|
30
|
+
| ((self: Route.RouteSet.Any) => Route.RouteSet.Any)
|
|
31
|
+
|
|
32
|
+
type HandlerFunction<B, A, E, R> = (
|
|
33
|
+
context: Values.Simplify<B>,
|
|
34
|
+
next: Next<B, A>,
|
|
35
|
+
) =>
|
|
36
|
+
| Effect.Effect<HandlerReturn<A>, E, R>
|
|
37
|
+
| Generator<Utils.YieldWrap<Effect.Effect<unknown, E, R>>, HandlerReturn<A>, unknown>
|
|
38
|
+
|
|
24
39
|
export type GeneratorHandler<B, A, Y> = (
|
|
25
40
|
context: Values.Simplify<B>,
|
|
26
|
-
next:
|
|
27
|
-
) => Generator<Y,
|
|
41
|
+
next: Next<B, A>,
|
|
42
|
+
) => Generator<Y, HandlerReturn<A>, never>
|
|
28
43
|
|
|
29
44
|
export type HandlerInput<B, A, E, R> =
|
|
30
45
|
| A
|
|
31
46
|
| Entity.Entity<A>
|
|
32
|
-
| Effect.Effect<
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
| Effect.Effect<HandlerReturn<A>, E, R>
|
|
48
|
+
| HandlerFunction<B, A, E, R>
|
|
49
|
+
|
|
50
|
+
function isHandlerFunction<B, A, E, R>(
|
|
51
|
+
handler: HandlerInput<B, A, E, R>,
|
|
52
|
+
): handler is HandlerFunction<B, A, E, R> {
|
|
53
|
+
return typeof handler === "function"
|
|
54
|
+
}
|
|
39
55
|
|
|
40
56
|
export function handle<B, A, Y extends Utils.YieldWrap<Effect.Effect<any, any, any>>>(
|
|
41
57
|
handler: GeneratorHandler<B, A, Y>,
|
|
@@ -46,38 +62,44 @@ export function handle<B, A, E, R>(
|
|
|
46
62
|
export function handle<B, A, E, R>(
|
|
47
63
|
handler: HandlerInput<B, A, E, R>,
|
|
48
64
|
): Route.Route.Handler<B, A, E, R> {
|
|
49
|
-
if (
|
|
50
|
-
return (
|
|
51
|
-
context
|
|
52
|
-
next: (context?: Partial<B> & Record<string, unknown>) => Entity.Entity<A>,
|
|
53
|
-
): Effect.Effect<Entity.Entity<A>, E, R> => {
|
|
54
|
-
const result = (handler as Function)(context, next)
|
|
65
|
+
if (isHandlerFunction(handler)) {
|
|
66
|
+
return ((context: any, next: any) => {
|
|
67
|
+
const result = handler(context, next)
|
|
55
68
|
const effect = Effect.isEffect(result)
|
|
56
|
-
?
|
|
57
|
-
:
|
|
69
|
+
? result
|
|
70
|
+
: Effect.gen(function* () {
|
|
58
71
|
return yield* result
|
|
59
|
-
})
|
|
60
|
-
return Effect.
|
|
61
|
-
}
|
|
72
|
+
})
|
|
73
|
+
return Effect.flatMap(effect, normalizeToEntity)
|
|
74
|
+
}) as Route.Route.Handler<B, A, E, R>
|
|
62
75
|
}
|
|
63
76
|
if (Effect.isEffect(handler)) {
|
|
64
|
-
return (_context, _next) =>
|
|
65
|
-
Effect.
|
|
77
|
+
return ((_context: any, _next: any) =>
|
|
78
|
+
Effect.flatMap(handler, normalizeToEntity)) as Route.Route.Handler<B, A, E, R>
|
|
66
79
|
}
|
|
67
80
|
if (Entity.isEntity(handler)) {
|
|
68
81
|
return (_context, _next) => Effect.succeed(handler as Entity.Entity<A>)
|
|
69
82
|
}
|
|
70
|
-
return (_context, _next) =>
|
|
83
|
+
return ((_context: any, _next: any) =>
|
|
84
|
+
normalizeToEntity(handler)) as Route.Route.Handler<B, A, E, R>
|
|
71
85
|
}
|
|
72
86
|
|
|
73
|
-
function normalizeToEntity
|
|
87
|
+
function normalizeToEntity(value: unknown): Effect.Effect<Entity.Entity<any>> {
|
|
88
|
+
if (typeof value === "function") {
|
|
89
|
+
const result = (value as (self: Route.RouteSet.Any) => Route.RouteSet.Any)(Route.empty)
|
|
90
|
+
const routes = Route.items(result)
|
|
91
|
+
const route = routes[0]
|
|
92
|
+
if (route) {
|
|
93
|
+
return route.handler({}, () => Entity.make("")) as Effect.Effect<Entity.Entity<any>>
|
|
94
|
+
}
|
|
95
|
+
}
|
|
74
96
|
if (Entity.isEntity(value)) {
|
|
75
|
-
return value
|
|
97
|
+
return Effect.succeed(value)
|
|
76
98
|
}
|
|
77
|
-
return Entity.make(value
|
|
99
|
+
return Effect.succeed(Entity.make(value, { status: 200 }))
|
|
78
100
|
}
|
|
79
101
|
|
|
80
|
-
export interface BuildReturn<Value, F extends Format> {
|
|
102
|
+
export interface BuildReturn<Value, F extends Format, Body = never> {
|
|
81
103
|
<
|
|
82
104
|
D extends Route.RouteDescriptor.Any,
|
|
83
105
|
B,
|
|
@@ -91,7 +113,7 @@ export interface BuildReturn<Value, F extends Format> {
|
|
|
91
113
|
) => Route.RouteSet.RouteSet<
|
|
92
114
|
D,
|
|
93
115
|
B,
|
|
94
|
-
[...I, Route.Route.Route<{ format: F }, {}, A, YieldError<Y>, YieldContext<Y>>]
|
|
116
|
+
[...I, Route.Route.Route<{ format: F }, {}, [Body] extends [never] ? A : Body, YieldError<Y>, YieldContext<Y>>]
|
|
95
117
|
>
|
|
96
118
|
|
|
97
119
|
<
|
|
@@ -105,50 +127,54 @@ export interface BuildReturn<Value, F extends Format> {
|
|
|
105
127
|
handler: HandlerInput<NoInfer<D & B & Route.ExtractBindings<I> & { format: F }>, A, E, R>,
|
|
106
128
|
): (
|
|
107
129
|
self: Route.RouteSet.RouteSet<D, B, I>,
|
|
108
|
-
) => Route.RouteSet.RouteSet<D, B, [...I, Route.Route.Route<{ format: F }, {}, A, E, R>]>
|
|
130
|
+
) => Route.RouteSet.RouteSet<D, B, [...I, Route.Route.Route<{ format: F }, {}, [Body] extends [never] ? A : Body, E, R>]>
|
|
109
131
|
}
|
|
110
132
|
|
|
111
|
-
export function build<Value, F extends Format>(
|
|
133
|
+
export function build<Value, F extends Format>(options: { format: F }): BuildReturn<Value, F>
|
|
134
|
+
export function build<Value, Body, F extends Format>(options: {
|
|
135
|
+
format: F
|
|
136
|
+
handle: (body: Value) => Body
|
|
137
|
+
}): BuildReturn<Value, F, Body>
|
|
138
|
+
export function build<Value, F extends Format>(options: {
|
|
139
|
+
format: F
|
|
140
|
+
handle?: (body: any) => any
|
|
141
|
+
}): any {
|
|
142
|
+
const { handle: handleBody, ...descriptors } = options
|
|
112
143
|
return function <
|
|
113
144
|
D extends Route.RouteDescriptor.Any,
|
|
114
|
-
B
|
|
145
|
+
B,
|
|
115
146
|
I extends Route.Route.Tuple,
|
|
116
147
|
A extends F extends "json" ? Value : Value | Stream.Stream<Value, any, any>,
|
|
117
148
|
E = never,
|
|
118
149
|
R = never,
|
|
119
150
|
>(handler: HandlerInput<NoInfer<D & B & Route.ExtractBindings<I> & { format: F }>, A, E, R>) {
|
|
120
|
-
return
|
|
151
|
+
return (self: Route.RouteSet.RouteSet<D, B, I>) => {
|
|
121
152
|
const contentType = formatToContentType[descriptors.format]
|
|
122
153
|
const baseHandler = handle(handler)
|
|
123
|
-
const wrappedHandler: Route.Route.Handler<
|
|
124
|
-
D & B & Route.ExtractBindings<I> & { format: F },
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
const wrappedHandler: Route.Route.Handler<{ format: F }, A, E, R> = (ctx, next) =>
|
|
155
|
+
baseHandler(ctx as D & B & Route.ExtractBindings<I> & { format: F }, next).pipe(
|
|
156
|
+
Effect.map((entity) => {
|
|
157
|
+
const body = handleBody && !StreamExtra.isStream(entity.body) ? handleBody(entity.body) : entity.body
|
|
158
|
+
if (body === entity.body && (entity.headers["content-type"] || contentType === undefined))
|
|
159
|
+
return entity
|
|
160
|
+
return Entity.make(body as A, {
|
|
161
|
+
status: entity.status,
|
|
162
|
+
url: entity.url,
|
|
163
|
+
headers: entity.headers["content-type"] || contentType === undefined
|
|
164
|
+
? entity.headers
|
|
165
|
+
: { ...entity.headers, "content-type": contentType },
|
|
166
|
+
})
|
|
167
|
+
}),
|
|
137
168
|
)
|
|
138
169
|
|
|
139
|
-
const route = Route.make<{ format: F }, {}, A, E, R>(wrappedHandler
|
|
140
|
-
|
|
141
|
-
const items: [...I, Route.Route.Route<{ format: F }, {}, A, E, R>] = [
|
|
142
|
-
...Route.items(self),
|
|
143
|
-
route,
|
|
144
|
-
]
|
|
170
|
+
const route = Route.make<{ format: F }, {}, A, E, R>(wrappedHandler, descriptors)
|
|
145
171
|
|
|
146
172
|
return Route.set<D, B, [...I, Route.Route.Route<{ format: F }, {}, A, E, R>]>(
|
|
147
|
-
items,
|
|
173
|
+
[...Route.items(self), route],
|
|
148
174
|
Route.descriptor(self),
|
|
149
175
|
)
|
|
150
176
|
}
|
|
151
|
-
} as
|
|
177
|
+
} as BuildReturn<Value, F>
|
|
152
178
|
}
|
|
153
179
|
|
|
154
180
|
export type RenderValue = string | Uint8Array | Stream.Stream<string | Uint8Array, any, any>
|
|
@@ -188,16 +214,15 @@ export function render<
|
|
|
188
214
|
E = never,
|
|
189
215
|
R = never,
|
|
190
216
|
>(handler: HandlerInput<NoInfer<D & B & Route.ExtractBindings<I> & { format: "*" }>, A, E, R>) {
|
|
191
|
-
return
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
]
|
|
217
|
+
return (self: Route.RouteSet.RouteSet<D, B, I>) => {
|
|
218
|
+
const baseHandler = handle(handler)
|
|
219
|
+
const route = Route.make<{ format: "*" }, {}, A, E, R>(
|
|
220
|
+
(ctx, next) => baseHandler(ctx as D & B & Route.ExtractBindings<I> & { format: "*" }, next),
|
|
221
|
+
{ format: "*" },
|
|
222
|
+
)
|
|
198
223
|
|
|
199
224
|
return Route.set<D, B, [...I, Route.Route.Route<{ format: "*" }, {}, A, E, R>]>(
|
|
200
|
-
items,
|
|
225
|
+
[...Route.items(self), route],
|
|
201
226
|
Route.descriptor(self),
|
|
202
227
|
)
|
|
203
228
|
}
|
package/src/RouteHttp.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type * as ParseResult from "effect/ParseResult"
|
|
|
8
8
|
import * as Runtime from "effect/Runtime"
|
|
9
9
|
import * as Stream from "effect/Stream"
|
|
10
10
|
import * as ContentNegotiation from "./_ContentNegotiation.ts"
|
|
11
|
+
import * as Development from "./Development.ts"
|
|
11
12
|
import * as Entity from "./Entity.ts"
|
|
12
13
|
import type * as Http from "./_Http.ts"
|
|
13
14
|
import * as Route from "./Route.ts"
|
|
@@ -388,9 +389,26 @@ export function* walkHandles(
|
|
|
388
389
|
runtime: Runtime.Runtime<never> = Runtime.defaultRuntime,
|
|
389
390
|
): Generator<[path: string, handler: Http.WebHandler]> {
|
|
390
391
|
const pathGroups = new Map<string, Array<RouteMount.MountedRoute>>()
|
|
392
|
+
const runSync = Runtime.runSync(runtime)
|
|
393
|
+
const inDevelopment = Option.isSome(runSync(Development.option))
|
|
394
|
+
const developmentPaths = new Set<string>()
|
|
391
395
|
|
|
392
396
|
for (const route of RouteTree.walk(tree)) {
|
|
393
|
-
const
|
|
397
|
+
const descriptor = Route.descriptor<{ path: string; dev?: boolean }>(route)
|
|
398
|
+
if (descriptor.dev === true) {
|
|
399
|
+
developmentPaths.add(descriptor.path)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
for (const route of RouteTree.walk(tree)) {
|
|
404
|
+
const descriptor = Route.descriptor<{ path: string; dev?: boolean }>(route)
|
|
405
|
+
if (descriptor.dev === true) {
|
|
406
|
+
continue
|
|
407
|
+
}
|
|
408
|
+
const path = descriptor.path
|
|
409
|
+
if (!inDevelopment && developmentPaths.has(path)) {
|
|
410
|
+
continue
|
|
411
|
+
}
|
|
394
412
|
const group = pathGroups.get(path) ?? []
|
|
395
413
|
group.push(route)
|
|
396
414
|
pathGroups.set(path, group)
|
package/src/RouteMount.ts
CHANGED
|
@@ -83,7 +83,7 @@ function makeMethodDescriber<M extends RouteMount.Method>(method: M): RouteMount
|
|
|
83
83
|
const result = f(methodSet)
|
|
84
84
|
const resultItems = Route.items(result)
|
|
85
85
|
|
|
86
|
-
// Items are already flat (only Routes),
|
|
86
|
+
// Items are already flat (only Routes), merge method into each descriptor
|
|
87
87
|
const flattenedItems = resultItems.map((item) => {
|
|
88
88
|
const itemDescriptor = Route.descriptor(item)
|
|
89
89
|
const newDescriptor = { method, ...itemDescriptor }
|
package/src/System.ts
CHANGED
|
@@ -67,7 +67,7 @@ export const which = (name: string): Effect.Effect<string, SystemError> =>
|
|
|
67
67
|
)
|
|
68
68
|
|
|
69
69
|
export const spawn = (
|
|
70
|
-
cmd:
|
|
70
|
+
cmd: [string, ...Array<string>] | string[],
|
|
71
71
|
options?: ChildProcess.Command.Options,
|
|
72
72
|
): Effect.Effect<
|
|
73
73
|
ChildProcess.ChildProcessHandle,
|
package/src/_ChildProcess.ts
CHANGED
|
@@ -57,7 +57,7 @@ const CommandProto = {
|
|
|
57
57
|
export const isCommand = (u: unknown): u is Command => Predicate.hasProperty(u, TypeId)
|
|
58
58
|
|
|
59
59
|
export const make = (
|
|
60
|
-
cmd:
|
|
60
|
+
cmd: [string, ...Array<string>] | string[],
|
|
61
61
|
options?: Command.Options,
|
|
62
62
|
): Command =>
|
|
63
63
|
Object.assign(Object.create(CommandProto), {
|