effect-start 0.17.2 → 0.19.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/dist/Development.d.ts +7 -2
- package/dist/Development.js +12 -6
- package/dist/PlatformRuntime.d.ts +4 -0
- package/dist/PlatformRuntime.js +9 -0
- package/dist/Route.d.ts +6 -2
- package/dist/Route.js +22 -0
- package/dist/RouteHttp.d.ts +1 -1
- package/dist/RouteHttp.js +12 -19
- package/dist/RouteMount.d.ts +2 -1
- package/dist/Start.d.ts +1 -5
- package/dist/Start.js +1 -8
- package/dist/Unique.d.ts +50 -0
- package/dist/Unique.js +187 -0
- package/dist/bun/BunHttpServer.js +5 -6
- package/dist/bun/BunRoute.d.ts +1 -1
- package/dist/bun/BunRoute.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/node/Effectify.d.ts +209 -0
- package/dist/node/Effectify.js +19 -0
- package/dist/node/FileSystem.d.ts +3 -5
- package/dist/node/FileSystem.js +42 -62
- package/dist/node/PlatformError.d.ts +46 -0
- package/dist/node/PlatformError.js +43 -0
- package/dist/testing/TestLogger.js +1 -1
- package/package.json +10 -5
- package/src/Development.ts +13 -18
- package/src/PlatformRuntime.ts +11 -0
- package/src/Route.ts +31 -2
- package/src/RouteHttp.ts +15 -31
- package/src/RouteMount.ts +1 -1
- package/src/Start.ts +1 -15
- package/src/Unique.ts +232 -0
- package/src/bun/BunHttpServer.ts +6 -9
- package/src/bun/BunRoute.ts +3 -3
- package/src/index.ts +1 -0
- package/src/node/Effectify.ts +262 -0
- package/src/node/FileSystem.ts +59 -97
- package/src/node/PlatformError.ts +102 -0
- package/src/testing/TestLogger.ts +1 -1
- package/dist/Random.d.ts +0 -5
- package/dist/Random.js +0 -49
- package/src/Commander.test.ts +0 -1639
- package/src/ContentNegotiation.test.ts +0 -603
- package/src/Development.test.ts +0 -119
- package/src/Entity.test.ts +0 -592
- package/src/FileRouterPattern.test.ts +0 -147
- package/src/FileRouter_files.test.ts +0 -64
- package/src/FileRouter_path.test.ts +0 -145
- package/src/FileRouter_tree.test.ts +0 -132
- package/src/Http.test.ts +0 -319
- package/src/HttpAppExtra.test.ts +0 -103
- package/src/HttpUtils.test.ts +0 -85
- package/src/PathPattern.test.ts +0 -648
- package/src/Random.ts +0 -59
- package/src/RouteBody.test.ts +0 -232
- package/src/RouteHook.test.ts +0 -40
- package/src/RouteHttp.test.ts +0 -2909
- package/src/RouteMount.test.ts +0 -481
- package/src/RouteSchema.test.ts +0 -427
- package/src/RouteSse.test.ts +0 -249
- package/src/RouteTree.test.ts +0 -494
- package/src/RouteTrie.test.ts +0 -322
- package/src/RouterPattern.test.ts +0 -676
- package/src/Values.test.ts +0 -263
- package/src/bun/BunBundle.test.ts +0 -268
- package/src/bun/BunBundle_imports.test.ts +0 -48
- package/src/bun/BunHttpServer.test.ts +0 -251
- package/src/bun/BunImportTrackerPlugin.test.ts +0 -77
- package/src/bun/BunRoute.test.ts +0 -162
- package/src/bundler/BundleHttp.test.ts +0 -132
- package/src/effect/HttpRouter.test.ts +0 -548
- package/src/experimental/EncryptedCookies.test.ts +0 -488
- package/src/hyper/HyperHtml.test.ts +0 -209
- package/src/hyper/HyperRoute.test.tsx +0 -197
- package/src/middlewares/BasicAuthMiddleware.test.ts +0 -84
- package/src/testing/TestHttpClient.test.ts +0 -83
- package/src/testing/TestLogger.test.ts +0 -51
- package/src/x/datastar/Datastar.test.ts +0 -266
- package/src/x/tailwind/TailwindPlugin.test.ts +0 -333
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource effect-start */
|
|
2
|
-
import * as test from "bun:test"
|
|
3
|
-
import * as Effect from "effect/Effect"
|
|
4
|
-
import * as Entity from "../Entity.ts"
|
|
5
|
-
import * as Http from "../Http.ts"
|
|
6
|
-
import * as Route from "../Route.ts"
|
|
7
|
-
import * as RouteHttp from "../RouteHttp.ts"
|
|
8
|
-
import * as HyperRoute from "./HyperRoute.ts"
|
|
9
|
-
|
|
10
|
-
test.describe("HyperRoute.html", () => {
|
|
11
|
-
test.it("renders JSX to HTML string", async () => {
|
|
12
|
-
const handler = RouteHttp.toWebHandler(
|
|
13
|
-
Route.get(
|
|
14
|
-
HyperRoute.html(
|
|
15
|
-
<div>
|
|
16
|
-
Hello World
|
|
17
|
-
</div>,
|
|
18
|
-
),
|
|
19
|
-
),
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
23
|
-
|
|
24
|
-
test.expect(response.status).toBe(200)
|
|
25
|
-
test.expect(response.headers.get("Content-Type")).toBe("text/html; charset=utf-8")
|
|
26
|
-
test.expect(await response.text()).toBe("<div>Hello World</div>")
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
test.it("renders nested JSX elements", async () => {
|
|
30
|
-
const handler = RouteHttp.toWebHandler(
|
|
31
|
-
Route.get(
|
|
32
|
-
HyperRoute.html(
|
|
33
|
-
<div class="container">
|
|
34
|
-
<h1>
|
|
35
|
-
Title
|
|
36
|
-
</h1>
|
|
37
|
-
<p>
|
|
38
|
-
Paragraph
|
|
39
|
-
</p>
|
|
40
|
-
</div>,
|
|
41
|
-
),
|
|
42
|
-
),
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
46
|
-
|
|
47
|
-
test.expect(await response.text()).toBe(
|
|
48
|
-
"<div class=\"container\"><h1>Title</h1><p>Paragraph</p></div>",
|
|
49
|
-
)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
test.it("renders JSX from Effect", async () => {
|
|
53
|
-
const handler = RouteHttp.toWebHandler(
|
|
54
|
-
Route.get(
|
|
55
|
-
HyperRoute.html(
|
|
56
|
-
Effect.succeed(
|
|
57
|
-
<span>
|
|
58
|
-
From Effect
|
|
59
|
-
</span>,
|
|
60
|
-
),
|
|
61
|
-
),
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
66
|
-
|
|
67
|
-
test.expect(await response.text()).toBe("<span>From Effect</span>")
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test.it("renders JSX from generator function", async () => {
|
|
71
|
-
const handler = RouteHttp.toWebHandler(
|
|
72
|
-
Route.get(
|
|
73
|
-
HyperRoute.html(
|
|
74
|
-
Effect.gen(function*() {
|
|
75
|
-
const name = yield* Effect.succeed("World")
|
|
76
|
-
return (
|
|
77
|
-
<div>
|
|
78
|
-
Hello {name}
|
|
79
|
-
</div>
|
|
80
|
-
)
|
|
81
|
-
}),
|
|
82
|
-
),
|
|
83
|
-
),
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
87
|
-
|
|
88
|
-
test.expect(await response.text()).toBe("<div>Hello World</div>")
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test.it("renders JSX from handler function", async () => {
|
|
92
|
-
const handler = RouteHttp.toWebHandler(
|
|
93
|
-
Route.get(
|
|
94
|
-
HyperRoute.html((context) =>
|
|
95
|
-
Effect.succeed(
|
|
96
|
-
<div>
|
|
97
|
-
Request received
|
|
98
|
-
</div>,
|
|
99
|
-
)
|
|
100
|
-
),
|
|
101
|
-
),
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
105
|
-
|
|
106
|
-
test.expect(await response.text()).toBe("<div>Request received</div>")
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
test.it("renders JSX with dynamic content", async () => {
|
|
110
|
-
const items = ["Apple", "Banana", "Cherry"]
|
|
111
|
-
|
|
112
|
-
const handler = RouteHttp.toWebHandler(
|
|
113
|
-
Route.get(
|
|
114
|
-
HyperRoute.html(
|
|
115
|
-
<ul>
|
|
116
|
-
{items.map((item) => (
|
|
117
|
-
<li>
|
|
118
|
-
{item}
|
|
119
|
-
</li>
|
|
120
|
-
))}
|
|
121
|
-
</ul>,
|
|
122
|
-
),
|
|
123
|
-
),
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
127
|
-
|
|
128
|
-
test.expect(await response.text()).toBe(
|
|
129
|
-
"<ul><li>Apple</li><li>Banana</li><li>Cherry</li></ul>",
|
|
130
|
-
)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
test.it("handles Entity with JSX body", async () => {
|
|
134
|
-
const handler = RouteHttp.toWebHandler(
|
|
135
|
-
Route.get(
|
|
136
|
-
HyperRoute.html(
|
|
137
|
-
Entity.make(
|
|
138
|
-
<div>
|
|
139
|
-
With Entity
|
|
140
|
-
</div>,
|
|
141
|
-
{ status: 201 },
|
|
142
|
-
),
|
|
143
|
-
),
|
|
144
|
-
),
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
148
|
-
|
|
149
|
-
test.expect(response.status).toBe(201)
|
|
150
|
-
test.expect(await response.text()).toBe("<div>With Entity</div>")
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
test.it("renders data-* attributes with object values as JSON", async () => {
|
|
154
|
-
const handler = RouteHttp.toWebHandler(
|
|
155
|
-
Route.get(
|
|
156
|
-
HyperRoute.html(
|
|
157
|
-
<div
|
|
158
|
-
data-signals={{
|
|
159
|
-
draft: "",
|
|
160
|
-
pendingDraft: "",
|
|
161
|
-
username: "User123",
|
|
162
|
-
}}
|
|
163
|
-
>
|
|
164
|
-
Content
|
|
165
|
-
</div>,
|
|
166
|
-
),
|
|
167
|
-
),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
171
|
-
|
|
172
|
-
test.expect(await response.text()).toBe(
|
|
173
|
-
"<div data-signals=\"{"draft":"","pendingDraft":"","username":"User123"}\">Content</div>",
|
|
174
|
-
)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
test.it("renders script with function child as IIFE", async () => {
|
|
178
|
-
const handler = RouteHttp.toWebHandler(
|
|
179
|
-
Route.get(
|
|
180
|
-
HyperRoute.html(
|
|
181
|
-
<script>
|
|
182
|
-
{(window) => {
|
|
183
|
-
console.log("Hello from", window.document.title)
|
|
184
|
-
}}
|
|
185
|
-
</script>,
|
|
186
|
-
),
|
|
187
|
-
),
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
const response = await Http.fetch(handler, { path: "/" })
|
|
191
|
-
const text = await response.text()
|
|
192
|
-
|
|
193
|
-
test.expect(text).toContain("<script>(")
|
|
194
|
-
test.expect(text).toContain(")(window)</script>")
|
|
195
|
-
test.expect(text).toContain("window.document.title")
|
|
196
|
-
})
|
|
197
|
-
})
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
2
|
-
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
3
|
-
import * as test from "bun:test"
|
|
4
|
-
import * as Effect from "effect/Effect"
|
|
5
|
-
import * as BasicAuthMiddleware from "./BasicAuthMiddleware.js"
|
|
6
|
-
|
|
7
|
-
const mockApp = Effect.succeed(
|
|
8
|
-
HttpServerResponse.text("OK", { status: 200 }),
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
const config: BasicAuthMiddleware.BasicAuthConfig = {
|
|
12
|
-
username: "admin",
|
|
13
|
-
password: "secret",
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const runWithAuth = (authHeader: string | undefined) => {
|
|
17
|
-
const middleware = BasicAuthMiddleware.make(config)
|
|
18
|
-
const wrappedApp = middleware(mockApp)
|
|
19
|
-
|
|
20
|
-
const headers: Record<string, string> = {}
|
|
21
|
-
if (authHeader !== undefined) {
|
|
22
|
-
headers.authorization = authHeader
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const mockRequest = HttpServerRequest.fromWeb(
|
|
26
|
-
new Request("http://localhost/test", { headers }),
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
return wrappedApp.pipe(
|
|
30
|
-
Effect.provideService(HttpServerRequest.HttpServerRequest, mockRequest),
|
|
31
|
-
Effect.runPromise,
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
test.describe("BasicAuthMiddleware", () => {
|
|
36
|
-
test.it("returns 401 when no authorization header is present", async () => {
|
|
37
|
-
const response = await runWithAuth(undefined)
|
|
38
|
-
test
|
|
39
|
-
.expect(response.status)
|
|
40
|
-
.toBe(401)
|
|
41
|
-
test
|
|
42
|
-
.expect(response.headers["www-authenticate"])
|
|
43
|
-
.toBe("Basic")
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test.it("returns 401 when authorization header does not start with Basic", async () => {
|
|
47
|
-
const response = await runWithAuth("Bearer token")
|
|
48
|
-
test
|
|
49
|
-
.expect(response.status)
|
|
50
|
-
.toBe(401)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test.it("returns 401 when credentials are invalid", async () => {
|
|
54
|
-
const invalidCredentials = btoa("wrong:credentials")
|
|
55
|
-
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
56
|
-
test
|
|
57
|
-
.expect(response.status)
|
|
58
|
-
.toBe(401)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test.it("returns 401 when username is wrong", async () => {
|
|
62
|
-
const invalidCredentials = btoa("wronguser:secret")
|
|
63
|
-
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
64
|
-
test
|
|
65
|
-
.expect(response.status)
|
|
66
|
-
.toBe(401)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
test.it("returns 401 when password is wrong", async () => {
|
|
70
|
-
const invalidCredentials = btoa("admin:wrongpassword")
|
|
71
|
-
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
72
|
-
test
|
|
73
|
-
.expect(response.status)
|
|
74
|
-
.toBe(401)
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
test.it("passes through to app when credentials are valid", async () => {
|
|
78
|
-
const validCredentials = btoa("admin:secret")
|
|
79
|
-
const response = await runWithAuth(`Basic ${validCredentials}`)
|
|
80
|
-
test
|
|
81
|
-
.expect(response.status)
|
|
82
|
-
.toBe(200)
|
|
83
|
-
})
|
|
84
|
-
})
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
2
|
-
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
3
|
-
import * as test from "bun:test"
|
|
4
|
-
import * as Effect from "effect/Effect"
|
|
5
|
-
import { effectFn } from "./index.ts"
|
|
6
|
-
import * as TestHttpClient from "./TestHttpClient.ts"
|
|
7
|
-
|
|
8
|
-
const App = Effect.gen(function*() {
|
|
9
|
-
const req = yield* HttpServerRequest.HttpServerRequest
|
|
10
|
-
|
|
11
|
-
if (req.url == "/") {
|
|
12
|
-
return HttpServerResponse.text("Hello, World!")
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return HttpServerResponse.text("Not Found", {
|
|
16
|
-
status: 404,
|
|
17
|
-
})
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const AppClient = TestHttpClient.make(App)
|
|
21
|
-
|
|
22
|
-
const effect = effectFn()
|
|
23
|
-
|
|
24
|
-
test.it("ok", () =>
|
|
25
|
-
effect(function*() {
|
|
26
|
-
const res = yield* AppClient.get("/")
|
|
27
|
-
|
|
28
|
-
test
|
|
29
|
-
.expect(res.status)
|
|
30
|
-
.toEqual(200)
|
|
31
|
-
test
|
|
32
|
-
.expect(yield* res.text)
|
|
33
|
-
.toEqual("Hello, World!")
|
|
34
|
-
}))
|
|
35
|
-
|
|
36
|
-
test.it("not found", () =>
|
|
37
|
-
effect(function*() {
|
|
38
|
-
const res = yield* AppClient.get("/nope")
|
|
39
|
-
|
|
40
|
-
test
|
|
41
|
-
.expect(res.status)
|
|
42
|
-
.toEqual(404)
|
|
43
|
-
test
|
|
44
|
-
.expect(yield* res.text)
|
|
45
|
-
.toEqual("Not Found")
|
|
46
|
-
}))
|
|
47
|
-
|
|
48
|
-
test.describe("FetchHandler", () => {
|
|
49
|
-
const FetchClient = TestHttpClient.make((req) =>
|
|
50
|
-
new Response(`Hello from ${req.url}`, { status: 200 })
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
test.it("works with sync handler", () =>
|
|
54
|
-
effect(function*() {
|
|
55
|
-
const res = yield* FetchClient.get("/test")
|
|
56
|
-
|
|
57
|
-
test
|
|
58
|
-
.expect(res.status)
|
|
59
|
-
.toEqual(200)
|
|
60
|
-
test
|
|
61
|
-
.expect(yield* res.text)
|
|
62
|
-
.toContain("/test")
|
|
63
|
-
}))
|
|
64
|
-
|
|
65
|
-
const AsyncFetchClient = TestHttpClient.make(async (req) => {
|
|
66
|
-
await Promise.resolve()
|
|
67
|
-
return new Response(`Async: ${req.method} ${new URL(req.url).pathname}`, {
|
|
68
|
-
status: 201,
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test.it("works with async handler", () =>
|
|
73
|
-
effect(function*() {
|
|
74
|
-
const res = yield* AsyncFetchClient.post("/async-path")
|
|
75
|
-
|
|
76
|
-
test
|
|
77
|
-
.expect(res.status)
|
|
78
|
-
.toEqual(201)
|
|
79
|
-
test
|
|
80
|
-
.expect(yield* res.text)
|
|
81
|
-
.toEqual("Async: POST /async-path")
|
|
82
|
-
}))
|
|
83
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as test from "bun:test"
|
|
2
|
-
import * as Effect from "effect/Effect"
|
|
3
|
-
import * as Ref from "effect/Ref"
|
|
4
|
-
import * as TestLogger from "./TestLogger.ts"
|
|
5
|
-
|
|
6
|
-
test.it("TestLogger captures log messages", () =>
|
|
7
|
-
Effect
|
|
8
|
-
.gen(function*() {
|
|
9
|
-
const logger = yield* TestLogger.TestLogger
|
|
10
|
-
|
|
11
|
-
yield* Effect.logError("This is an error")
|
|
12
|
-
yield* Effect.logWarning("This is a warning")
|
|
13
|
-
yield* Effect.logInfo("This is info")
|
|
14
|
-
|
|
15
|
-
const messages = yield* Ref.get(logger.messages)
|
|
16
|
-
|
|
17
|
-
test
|
|
18
|
-
.expect(messages)
|
|
19
|
-
.toHaveLength(3)
|
|
20
|
-
test
|
|
21
|
-
.expect(messages[0])
|
|
22
|
-
.toContain("[Error]")
|
|
23
|
-
test
|
|
24
|
-
.expect(messages[0])
|
|
25
|
-
.toContain("This is an error")
|
|
26
|
-
test
|
|
27
|
-
.expect(messages[1])
|
|
28
|
-
.toContain("[Warning]")
|
|
29
|
-
test
|
|
30
|
-
.expect(messages[1])
|
|
31
|
-
.toContain("This is a warning")
|
|
32
|
-
test
|
|
33
|
-
.expect(messages[2])
|
|
34
|
-
.toContain("[Info]")
|
|
35
|
-
test
|
|
36
|
-
.expect(messages[2])
|
|
37
|
-
.toContain("This is info")
|
|
38
|
-
})
|
|
39
|
-
.pipe(Effect.provide(TestLogger.layer()), Effect.runPromise))
|
|
40
|
-
|
|
41
|
-
test.it("TestLogger starts with empty messages", () =>
|
|
42
|
-
Effect
|
|
43
|
-
.gen(function*() {
|
|
44
|
-
const logger = yield* TestLogger.TestLogger
|
|
45
|
-
const messages = yield* Ref.get(logger.messages)
|
|
46
|
-
|
|
47
|
-
test
|
|
48
|
-
.expect(messages)
|
|
49
|
-
.toHaveLength(0)
|
|
50
|
-
})
|
|
51
|
-
.pipe(Effect.provide(TestLogger.layer()), Effect.runPromise))
|
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import * as test from "bun:test"
|
|
2
|
-
import * as HyperHtml from "../../hyper/HyperHtml.ts"
|
|
3
|
-
import * as HyperNode from "../../hyper/HyperNode.ts"
|
|
4
|
-
import { jsx } from "../../hyper/jsx-runtime.ts"
|
|
5
|
-
import * as Datastar from "./Datastar.ts"
|
|
6
|
-
|
|
7
|
-
test.it("data-signals object serialization", () => {
|
|
8
|
-
const node = HyperNode.make("div", {
|
|
9
|
-
"data-signals": { foo: 1, bar: { baz: "hello" } } as any,
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
13
|
-
|
|
14
|
-
test
|
|
15
|
-
.expect(html)
|
|
16
|
-
.toBe(
|
|
17
|
-
"<div data-signals=\"{"foo":1,"bar":{"baz":"hello"}}\"></div>",
|
|
18
|
-
)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test.it("data-signals string passthrough", () => {
|
|
22
|
-
const node = HyperNode.make("div", {
|
|
23
|
-
"data-signals": "$mySignal",
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
27
|
-
|
|
28
|
-
test
|
|
29
|
-
.expect(html)
|
|
30
|
-
.toBe("<div data-signals=\"$mySignal\"></div>")
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test.it("data-signals-* object serialization", () => {
|
|
34
|
-
const node = HyperNode.make("div", {
|
|
35
|
-
"data-signals-user": { name: "John", age: 30 } as any,
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
39
|
-
|
|
40
|
-
test
|
|
41
|
-
.expect(html)
|
|
42
|
-
.toBe(
|
|
43
|
-
"<div data-signals-user=\"{"name":"John","age":30}\"></div>",
|
|
44
|
-
)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test.it("non-data attributes unchanged", () => {
|
|
48
|
-
const node = HyperNode.make("div", {
|
|
49
|
-
id: "test",
|
|
50
|
-
class: "my-class",
|
|
51
|
-
"data-text": "$count",
|
|
52
|
-
"data-signals": { count: 0 } as any,
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
56
|
-
|
|
57
|
-
test
|
|
58
|
-
.expect(html)
|
|
59
|
-
.toContain("id=\"test\"")
|
|
60
|
-
test
|
|
61
|
-
.expect(html)
|
|
62
|
-
.toContain("class=\"my-class\"")
|
|
63
|
-
test
|
|
64
|
-
.expect(html)
|
|
65
|
-
.toContain("data-text=\"$count\"")
|
|
66
|
-
test
|
|
67
|
-
.expect(html)
|
|
68
|
-
.toContain("data-signals=\"{"count":0}\"")
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test.it("null and undefined values ignored", () => {
|
|
72
|
-
const node = HyperNode.make("div", {
|
|
73
|
-
"data-signals": null,
|
|
74
|
-
"data-other": undefined,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
78
|
-
|
|
79
|
-
test
|
|
80
|
-
.expect(html)
|
|
81
|
-
.toBe("<div></div>")
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test.it("complex nested objects serialization", () => {
|
|
85
|
-
const complexObject = {
|
|
86
|
-
user: { name: "John Doe", preferences: { theme: "dark" } },
|
|
87
|
-
items: [1, 2, 3],
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const node = HyperNode.make("div", {
|
|
91
|
-
"data-signals": complexObject as any,
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
95
|
-
|
|
96
|
-
test
|
|
97
|
-
.expect(html)
|
|
98
|
-
.toContain("data-signals=")
|
|
99
|
-
test
|
|
100
|
-
.expect(html)
|
|
101
|
-
.toContain("John Doe")
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test.it("non-signals data attributes serialized", () => {
|
|
105
|
-
const node = HyperNode.make("div", {
|
|
106
|
-
"data-class": { hidden: true, visible: false } as any,
|
|
107
|
-
"data-style": { color: "red", display: "none" } as any,
|
|
108
|
-
"data-show": true as any,
|
|
109
|
-
"data-text": "$count",
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
113
|
-
|
|
114
|
-
test
|
|
115
|
-
.expect(html)
|
|
116
|
-
.toContain(
|
|
117
|
-
"data-class=\"{"hidden":true,"visible":false}\"",
|
|
118
|
-
)
|
|
119
|
-
test
|
|
120
|
-
.expect(html)
|
|
121
|
-
.toContain(
|
|
122
|
-
"data-style=\"{"color":"red","display":"none"}\"",
|
|
123
|
-
)
|
|
124
|
-
test
|
|
125
|
-
.expect(html)
|
|
126
|
-
.toContain("data-show=\"true\"")
|
|
127
|
-
test
|
|
128
|
-
.expect(html)
|
|
129
|
-
.toContain("data-text=\"$count\"")
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
test.it("data-attr object serialization", () => {
|
|
133
|
-
const node = HyperNode.make("div", {
|
|
134
|
-
"data-attr": { disabled: true, tabindex: 0 } as any,
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
138
|
-
|
|
139
|
-
test
|
|
140
|
-
.expect(html)
|
|
141
|
-
.toBe(
|
|
142
|
-
"<div data-attr=\"{"disabled":true,"tabindex":0}\"></div>",
|
|
143
|
-
)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test.it("boolean attributes converted to strings", () => {
|
|
147
|
-
const node = HyperNode.make("div", {
|
|
148
|
-
"data-ignore": false as any,
|
|
149
|
-
"data-ignore-morph": true as any,
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
153
|
-
|
|
154
|
-
test
|
|
155
|
-
.expect(html)
|
|
156
|
-
.not
|
|
157
|
-
.toContain("data-ignore=\"")
|
|
158
|
-
test
|
|
159
|
-
.expect(html)
|
|
160
|
-
.toContain("data-ignore-morph")
|
|
161
|
-
test
|
|
162
|
-
.expect(html)
|
|
163
|
-
.not
|
|
164
|
-
.toContain("data-ignore-morph=")
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
test.it("data-ignore attributes only present when true", () => {
|
|
168
|
-
const nodeTrue = HyperNode.make("div", {
|
|
169
|
-
"data-ignore": true as any,
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
const nodeFalse = HyperNode.make("div", {
|
|
173
|
-
"data-ignore": false as any,
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
const htmlTrue = HyperHtml.renderToString(nodeTrue, Datastar.HyperHooks)
|
|
177
|
-
const htmlFalse = HyperHtml.renderToString(nodeFalse, Datastar.HyperHooks)
|
|
178
|
-
|
|
179
|
-
test
|
|
180
|
-
.expect(htmlTrue)
|
|
181
|
-
.toContain("data-ignore")
|
|
182
|
-
test
|
|
183
|
-
.expect(htmlTrue)
|
|
184
|
-
.not
|
|
185
|
-
.toContain("data-ignore=")
|
|
186
|
-
test
|
|
187
|
-
.expect(htmlFalse)
|
|
188
|
-
.not
|
|
189
|
-
.toContain("data-ignore")
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
test.it("dynamic attributes with suffixes", () => {
|
|
193
|
-
const node = HyperNode.make("div", {
|
|
194
|
-
"data-class-active": "hidden" as any,
|
|
195
|
-
"data-attr-tabindex": "5" as any,
|
|
196
|
-
"data-style-opacity": "0.5" as any,
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
200
|
-
|
|
201
|
-
test
|
|
202
|
-
.expect(html)
|
|
203
|
-
.toContain("data-class-active=\"hidden\"")
|
|
204
|
-
test
|
|
205
|
-
.expect(html)
|
|
206
|
-
.toContain("data-attr-tabindex=\"5\"")
|
|
207
|
-
test
|
|
208
|
-
.expect(html)
|
|
209
|
-
.toContain("data-style-opacity=\"0.5\"")
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
test.it("JSX with data-signals object", () => {
|
|
213
|
-
const node = jsx("div", {
|
|
214
|
-
"data-signals": { isOpen: false, count: 42 } as any,
|
|
215
|
-
children: "content",
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
219
|
-
|
|
220
|
-
test
|
|
221
|
-
.expect(html)
|
|
222
|
-
.toBe(
|
|
223
|
-
"<div data-signals=\"{"isOpen":false,"count":42}\">content</div>",
|
|
224
|
-
)
|
|
225
|
-
test
|
|
226
|
-
.expect(html)
|
|
227
|
-
.not
|
|
228
|
-
.toContain("[object Object]")
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
test.it("JSX component returning element with data-signals", () => {
|
|
232
|
-
function TestComponent() {
|
|
233
|
-
return jsx("div", {
|
|
234
|
-
"data-signals": { isOpen: false } as any,
|
|
235
|
-
children: jsx("span", { children: "nested content" }),
|
|
236
|
-
})
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const node = jsx(TestComponent, {})
|
|
240
|
-
|
|
241
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
242
|
-
|
|
243
|
-
test
|
|
244
|
-
.expect(html)
|
|
245
|
-
.toBe(
|
|
246
|
-
"<div data-signals=\"{"isOpen":false}\"><span>nested content</span></div>",
|
|
247
|
-
)
|
|
248
|
-
test
|
|
249
|
-
.expect(html)
|
|
250
|
-
.not
|
|
251
|
-
.toContain("[object Object]")
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
test.it("debug hook execution", () => {
|
|
255
|
-
const node = jsx("div", {
|
|
256
|
-
"data-signals": { isOpen: false, count: 42 } as any,
|
|
257
|
-
children: "content",
|
|
258
|
-
})
|
|
259
|
-
|
|
260
|
-
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
261
|
-
|
|
262
|
-
test
|
|
263
|
-
.expect(html)
|
|
264
|
-
.not
|
|
265
|
-
.toContain("[object Object]")
|
|
266
|
-
})
|