effect-start 0.14.0 → 0.16.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/package.json +8 -9
- package/src/Commander.test.ts +507 -245
- package/src/ContentNegotiation.test.ts +603 -0
- package/src/ContentNegotiation.ts +542 -0
- package/src/Entity.test.ts +592 -0
- package/src/Entity.ts +362 -0
- package/src/FileRouter.ts +16 -12
- package/src/{FileRouterCodegen.test.ts → FileRouterCodegen.todo.ts} +384 -219
- package/src/FileRouterCodegen.ts +6 -6
- package/src/FileRouterPattern.test.ts +93 -62
- package/src/FileRouter_files.test.ts +5 -5
- package/src/FileRouter_path.test.ts +121 -69
- package/src/FileRouter_tree.test.ts +62 -56
- package/src/FileSystemExtra.test.ts +46 -30
- package/src/Http.test.ts +319 -0
- package/src/Http.ts +167 -0
- package/src/HttpAppExtra.test.ts +39 -20
- package/src/HttpAppExtra.ts +0 -1
- package/src/HttpUtils.test.ts +35 -18
- package/src/HttpUtils.ts +2 -0
- package/src/PathPattern.test.ts +648 -0
- package/src/PathPattern.ts +485 -0
- package/src/Route.ts +266 -1069
- package/src/RouteBody.test.ts +234 -0
- package/src/RouteBody.ts +193 -0
- package/src/RouteHook.test.ts +40 -0
- package/src/RouteHook.ts +106 -0
- package/src/RouteHttp.test.ts +2906 -0
- package/src/RouteHttp.ts +427 -0
- package/src/RouteHttpTracer.ts +92 -0
- package/src/RouteMount.test.ts +481 -0
- package/src/RouteMount.ts +470 -0
- package/src/RouteSchema.test.ts +427 -0
- package/src/RouteSchema.ts +423 -0
- package/src/RouteTree.test.ts +494 -0
- package/src/RouteTree.ts +219 -0
- package/src/RouteTrie.test.ts +322 -0
- package/src/RouteTrie.ts +224 -0
- package/src/RouterPattern.test.ts +569 -548
- package/src/RouterPattern.ts +7 -7
- package/src/Start.ts +3 -3
- package/src/StreamExtra.ts +21 -1
- package/src/TuplePathPattern.ts +64 -0
- package/src/Values.test.ts +263 -0
- package/src/Values.ts +76 -0
- package/src/bun/BunBundle.test.ts +36 -42
- package/src/bun/BunBundle.ts +2 -2
- package/src/bun/BunBundle_imports.test.ts +4 -6
- package/src/bun/BunHttpServer.test.ts +183 -6
- package/src/bun/BunHttpServer.ts +72 -32
- package/src/bun/BunHttpServer_web.ts +18 -6
- package/src/bun/BunImportTrackerPlugin.test.ts +3 -3
- package/src/bun/BunRoute.test.ts +124 -442
- package/src/bun/BunRoute.ts +146 -286
- package/src/{BundleHttp.test.ts → bundler/BundleHttp.test.ts} +34 -60
- package/src/{BundleHttp.ts → bundler/BundleHttp.ts} +1 -2
- package/src/client/index.ts +1 -1
- package/src/{Effect_HttpRouter.test.ts → effect/HttpRouter.test.ts} +69 -90
- package/src/experimental/EncryptedCookies.test.ts +125 -64
- package/src/experimental/SseHttpResponse.ts +0 -1
- package/src/hyper/Hyper.ts +89 -0
- package/src/{HyperHtml.test.ts → hyper/HyperHtml.test.ts} +13 -13
- package/src/{HyperHtml.ts → hyper/HyperHtml.ts} +2 -2
- package/src/{jsx.d.ts → hyper/jsx.d.ts} +1 -1
- package/src/index.ts +3 -4
- package/src/middlewares/BasicAuthMiddleware.test.ts +29 -19
- package/src/{NodeFileSystem.ts → node/FileSystem.ts} +6 -2
- package/src/testing/TestHttpClient.test.ts +26 -26
- package/src/testing/TestLogger.test.ts +27 -14
- package/src/testing/TestLogger.ts +15 -9
- package/src/x/datastar/Datastar.test.ts +47 -48
- package/src/x/datastar/Datastar.ts +1 -1
- package/src/x/tailwind/TailwindPlugin.test.ts +56 -58
- package/src/x/tailwind/plugin.ts +1 -1
- package/src/FileHttpRouter.test.ts +0 -239
- package/src/FileHttpRouter.ts +0 -194
- package/src/Hyper.ts +0 -194
- package/src/Route.test.ts +0 -1370
- package/src/RouteRender.ts +0 -40
- package/src/Router.test.ts +0 -375
- package/src/Router.ts +0 -255
- package/src/bun/BunRoute_bundles.test.ts +0 -219
- /package/src/{Bundle.ts → bundler/Bundle.ts} +0 -0
- /package/src/{BundleFiles.ts → bundler/BundleFiles.ts} +0 -0
- /package/src/{HyperNode.ts → hyper/HyperNode.ts} +0 -0
- /package/src/{jsx-runtime.ts → hyper/jsx-runtime.ts} +0 -0
- /package/src/{NodeUtils.ts → node/Utils.ts} +0 -0
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
2
2
|
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
3
|
-
import
|
|
4
|
-
describe,
|
|
5
|
-
expect,
|
|
6
|
-
it,
|
|
7
|
-
} from "bun:test"
|
|
3
|
+
import * as test from "bun:test"
|
|
8
4
|
import * as Effect from "effect/Effect"
|
|
9
5
|
import * as BasicAuthMiddleware from "./BasicAuthMiddleware.js"
|
|
10
6
|
|
|
@@ -36,39 +32,53 @@ const runWithAuth = (authHeader: string | undefined) => {
|
|
|
36
32
|
)
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
describe("BasicAuthMiddleware", () => {
|
|
40
|
-
it("returns 401 when no authorization header is present", async () => {
|
|
35
|
+
test.describe("BasicAuthMiddleware", () => {
|
|
36
|
+
test.it("returns 401 when no authorization header is present", async () => {
|
|
41
37
|
const response = await runWithAuth(undefined)
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
test
|
|
39
|
+
.expect(response.status)
|
|
40
|
+
.toBe(401)
|
|
41
|
+
test
|
|
42
|
+
.expect(response.headers["www-authenticate"])
|
|
43
|
+
.toBe("Basic")
|
|
44
44
|
})
|
|
45
45
|
|
|
46
|
-
it("returns 401 when authorization header does not start with Basic", async () => {
|
|
46
|
+
test.it("returns 401 when authorization header does not start with Basic", async () => {
|
|
47
47
|
const response = await runWithAuth("Bearer token")
|
|
48
|
-
|
|
48
|
+
test
|
|
49
|
+
.expect(response.status)
|
|
50
|
+
.toBe(401)
|
|
49
51
|
})
|
|
50
52
|
|
|
51
|
-
it("returns 401 when credentials are invalid", async () => {
|
|
53
|
+
test.it("returns 401 when credentials are invalid", async () => {
|
|
52
54
|
const invalidCredentials = btoa("wrong:credentials")
|
|
53
55
|
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
54
|
-
|
|
56
|
+
test
|
|
57
|
+
.expect(response.status)
|
|
58
|
+
.toBe(401)
|
|
55
59
|
})
|
|
56
60
|
|
|
57
|
-
it("returns 401 when username is wrong", async () => {
|
|
61
|
+
test.it("returns 401 when username is wrong", async () => {
|
|
58
62
|
const invalidCredentials = btoa("wronguser:secret")
|
|
59
63
|
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
60
|
-
|
|
64
|
+
test
|
|
65
|
+
.expect(response.status)
|
|
66
|
+
.toBe(401)
|
|
61
67
|
})
|
|
62
68
|
|
|
63
|
-
it("returns 401 when password is wrong", async () => {
|
|
69
|
+
test.it("returns 401 when password is wrong", async () => {
|
|
64
70
|
const invalidCredentials = btoa("admin:wrongpassword")
|
|
65
71
|
const response = await runWithAuth(`Basic ${invalidCredentials}`)
|
|
66
|
-
|
|
72
|
+
test
|
|
73
|
+
.expect(response.status)
|
|
74
|
+
.toBe(401)
|
|
67
75
|
})
|
|
68
76
|
|
|
69
|
-
it("passes through to app when credentials are valid", async () => {
|
|
77
|
+
test.it("passes through to app when credentials are valid", async () => {
|
|
70
78
|
const validCredentials = btoa("admin:secret")
|
|
71
79
|
const response = await runWithAuth(`Basic ${validCredentials}`)
|
|
72
|
-
|
|
80
|
+
test
|
|
81
|
+
.expect(response.status)
|
|
82
|
+
.toBe(200)
|
|
73
83
|
})
|
|
74
84
|
})
|
|
@@ -243,15 +243,19 @@ const makeFile = (() => {
|
|
|
243
243
|
|
|
244
244
|
class FileImpl implements FileSystem.File {
|
|
245
245
|
readonly [FileSystem.FileTypeId]: FileSystem.FileTypeId
|
|
246
|
+
readonly fd: FileSystem.File.Descriptor
|
|
247
|
+
private readonly append: boolean
|
|
246
248
|
|
|
247
249
|
private readonly semaphore = Effect.unsafeMakeSemaphore(1)
|
|
248
250
|
private position: bigint = 0n
|
|
249
251
|
|
|
250
252
|
constructor(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
fd: FileSystem.File.Descriptor,
|
|
254
|
+
append: boolean,
|
|
253
255
|
) {
|
|
254
256
|
this[FileSystem.FileTypeId] = FileSystem.FileTypeId
|
|
257
|
+
this.fd = fd
|
|
258
|
+
this.append = append
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
get stat() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
2
2
|
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
3
|
-
import * as
|
|
3
|
+
import * as test from "bun:test"
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
5
|
import { effectFn } from "./index.ts"
|
|
6
6
|
import * as TestHttpClient from "./TestHttpClient.ts"
|
|
@@ -21,49 +21,45 @@ const AppClient = TestHttpClient.make(App)
|
|
|
21
21
|
|
|
22
22
|
const effect = effectFn()
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
test.it("ok", () =>
|
|
25
25
|
effect(function*() {
|
|
26
26
|
const res = yield* AppClient.get("/")
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
.expect(
|
|
30
|
-
res.status,
|
|
31
|
-
)
|
|
28
|
+
test
|
|
29
|
+
.expect(res.status)
|
|
32
30
|
.toEqual(200)
|
|
33
|
-
|
|
34
|
-
.expect(
|
|
35
|
-
yield* res.text,
|
|
36
|
-
)
|
|
31
|
+
test
|
|
32
|
+
.expect(yield* res.text)
|
|
37
33
|
.toEqual("Hello, World!")
|
|
38
34
|
}))
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
test.it("not found", () =>
|
|
41
37
|
effect(function*() {
|
|
42
38
|
const res = yield* AppClient.get("/nope")
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
.expect(
|
|
46
|
-
res.status,
|
|
47
|
-
)
|
|
40
|
+
test
|
|
41
|
+
.expect(res.status)
|
|
48
42
|
.toEqual(404)
|
|
49
|
-
|
|
50
|
-
.expect(
|
|
51
|
-
yield* res.text,
|
|
52
|
-
)
|
|
43
|
+
test
|
|
44
|
+
.expect(yield* res.text)
|
|
53
45
|
.toEqual("Not Found")
|
|
54
46
|
}))
|
|
55
47
|
|
|
56
|
-
|
|
48
|
+
test.describe("FetchHandler", () => {
|
|
57
49
|
const FetchClient = TestHttpClient.make((req) =>
|
|
58
50
|
new Response(`Hello from ${req.url}`, { status: 200 })
|
|
59
51
|
)
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
test.it("works with sync handler", () =>
|
|
62
54
|
effect(function*() {
|
|
63
55
|
const res = yield* FetchClient.get("/test")
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
57
|
+
test
|
|
58
|
+
.expect(res.status)
|
|
59
|
+
.toEqual(200)
|
|
60
|
+
test
|
|
61
|
+
.expect(yield* res.text)
|
|
62
|
+
.toContain("/test")
|
|
67
63
|
}))
|
|
68
64
|
|
|
69
65
|
const AsyncFetchClient = TestHttpClient.make(async (req) => {
|
|
@@ -73,11 +69,15 @@ t.describe("FetchHandler", () => {
|
|
|
73
69
|
})
|
|
74
70
|
})
|
|
75
71
|
|
|
76
|
-
|
|
72
|
+
test.it("works with async handler", () =>
|
|
77
73
|
effect(function*() {
|
|
78
74
|
const res = yield* AsyncFetchClient.post("/async-path")
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
test
|
|
77
|
+
.expect(res.status)
|
|
78
|
+
.toEqual(201)
|
|
79
|
+
test
|
|
80
|
+
.expect(yield* res.text)
|
|
81
|
+
.toEqual("Async: POST /async-path")
|
|
82
82
|
}))
|
|
83
83
|
})
|
|
@@ -1,38 +1,51 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as test from "bun:test"
|
|
2
2
|
import * as Effect from "effect/Effect"
|
|
3
|
-
import * as Logger from "effect/Logger"
|
|
4
3
|
import * as Ref from "effect/Ref"
|
|
5
4
|
import * as TestLogger from "./TestLogger.ts"
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
test.it("TestLogger captures log messages", () =>
|
|
8
7
|
Effect
|
|
9
8
|
.gen(function*() {
|
|
10
9
|
const logger = yield* TestLogger.TestLogger
|
|
11
10
|
|
|
12
|
-
// Log some messages
|
|
13
11
|
yield* Effect.logError("This is an error")
|
|
14
12
|
yield* Effect.logWarning("This is a warning")
|
|
15
13
|
yield* Effect.logInfo("This is info")
|
|
16
14
|
|
|
17
|
-
// Read captured messages
|
|
18
15
|
const messages = yield* Ref.get(logger.messages)
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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")
|
|
27
38
|
})
|
|
28
39
|
.pipe(Effect.provide(TestLogger.layer()), Effect.runPromise))
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
test.it("TestLogger starts with empty messages", () =>
|
|
31
42
|
Effect
|
|
32
43
|
.gen(function*() {
|
|
33
44
|
const logger = yield* TestLogger.TestLogger
|
|
34
45
|
const messages = yield* Ref.get(logger.messages)
|
|
35
46
|
|
|
36
|
-
|
|
47
|
+
test
|
|
48
|
+
.expect(messages)
|
|
49
|
+
.toHaveLength(0)
|
|
37
50
|
})
|
|
38
51
|
.pipe(Effect.provide(TestLogger.layer()), Effect.runPromise))
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause"
|
|
1
2
|
import * as Context from "effect/Context"
|
|
2
3
|
import * as Effect from "effect/Effect"
|
|
3
4
|
import * as FiberRef from "effect/FiberRef"
|
|
4
5
|
import * as HashSet from "effect/HashSet"
|
|
5
6
|
import * as Layer from "effect/Layer"
|
|
6
7
|
import * as Logger from "effect/Logger"
|
|
8
|
+
import * as MutableRef from "effect/MutableRef"
|
|
7
9
|
import * as Ref from "effect/Ref"
|
|
8
10
|
|
|
9
11
|
export type TestLoggerContext = {
|
|
@@ -20,13 +22,19 @@ export function layer(): Layer.Layer<TestLogger> {
|
|
|
20
22
|
TestLogger,
|
|
21
23
|
Effect.gen(function*() {
|
|
22
24
|
const messages = yield* Ref.make<Array<string>>([])
|
|
25
|
+
const mutableRef = (messages as any).ref as MutableRef.MutableRef<
|
|
26
|
+
Array<string>
|
|
27
|
+
>
|
|
23
28
|
|
|
24
|
-
const customLogger = Logger.make(({ message, logLevel }) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const customLogger = Logger.make(({ message, logLevel, cause }) => {
|
|
30
|
+
const causeStr = !Cause.isEmpty(cause)
|
|
31
|
+
? ` ${Cause.pretty(cause)}`
|
|
32
|
+
: ""
|
|
33
|
+
MutableRef.update(
|
|
34
|
+
mutableRef,
|
|
35
|
+
(
|
|
36
|
+
msgs,
|
|
37
|
+
) => [...msgs, `[${logLevel._tag}] ${String(message)}${causeStr}`],
|
|
30
38
|
)
|
|
31
39
|
})
|
|
32
40
|
|
|
@@ -39,9 +47,7 @@ export function layer(): Layer.Layer<TestLogger> {
|
|
|
39
47
|
),
|
|
40
48
|
)
|
|
41
49
|
|
|
42
|
-
return {
|
|
43
|
-
messages,
|
|
44
|
-
}
|
|
50
|
+
return { messages }
|
|
45
51
|
}),
|
|
46
52
|
)
|
|
47
53
|
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as HyperHtml from "../../HyperHtml.ts"
|
|
3
|
-
import * as HyperNode from "../../HyperNode.ts"
|
|
4
|
-
import { jsx } from "../../jsx-runtime.ts"
|
|
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
5
|
import * as Datastar from "./Datastar.ts"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
test.it("data-signals object serialization", () => {
|
|
8
8
|
const node = HyperNode.make("div", {
|
|
9
9
|
"data-signals": { foo: 1, bar: { baz: "hello" } } as any,
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
test
|
|
15
15
|
.expect(html)
|
|
16
16
|
.toBe(
|
|
17
17
|
"<div data-signals=\"{"foo":1,"bar":{"baz":"hello"}}\"></div>",
|
|
18
18
|
)
|
|
19
19
|
})
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
test.it("data-signals string passthrough", () => {
|
|
22
22
|
const node = HyperNode.make("div", {
|
|
23
23
|
"data-signals": "$mySignal",
|
|
24
24
|
})
|
|
25
25
|
|
|
26
26
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
test
|
|
29
29
|
.expect(html)
|
|
30
30
|
.toBe("<div data-signals=\"$mySignal\"></div>")
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
test.it("data-signals-* object serialization", () => {
|
|
34
34
|
const node = HyperNode.make("div", {
|
|
35
35
|
"data-signals-user": { name: "John", age: 30 } as any,
|
|
36
36
|
})
|
|
37
37
|
|
|
38
38
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
test
|
|
41
41
|
.expect(html)
|
|
42
42
|
.toBe(
|
|
43
43
|
"<div data-signals-user=\"{"name":"John","age":30}\"></div>",
|
|
44
44
|
)
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
test.it("non-data attributes unchanged", () => {
|
|
48
48
|
const node = HyperNode.make("div", {
|
|
49
49
|
id: "test",
|
|
50
50
|
class: "my-class",
|
|
@@ -54,21 +54,21 @@ t.it("non-data attributes unchanged", () => {
|
|
|
54
54
|
|
|
55
55
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
test
|
|
58
58
|
.expect(html)
|
|
59
59
|
.toContain("id=\"test\"")
|
|
60
|
-
|
|
60
|
+
test
|
|
61
61
|
.expect(html)
|
|
62
62
|
.toContain("class=\"my-class\"")
|
|
63
|
-
|
|
63
|
+
test
|
|
64
64
|
.expect(html)
|
|
65
65
|
.toContain("data-text=\"$count\"")
|
|
66
|
-
|
|
66
|
+
test
|
|
67
67
|
.expect(html)
|
|
68
68
|
.toContain("data-signals=\"{"count":0}\"")
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
test.it("null and undefined values ignored", () => {
|
|
72
72
|
const node = HyperNode.make("div", {
|
|
73
73
|
"data-signals": null,
|
|
74
74
|
"data-other": undefined,
|
|
@@ -76,12 +76,12 @@ t.it("null and undefined values ignored", () => {
|
|
|
76
76
|
|
|
77
77
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
test
|
|
80
80
|
.expect(html)
|
|
81
81
|
.toBe("<div></div>")
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
test.it("complex nested objects serialization", () => {
|
|
85
85
|
const complexObject = {
|
|
86
86
|
user: { name: "John Doe", preferences: { theme: "dark" } },
|
|
87
87
|
items: [1, 2, 3],
|
|
@@ -93,15 +93,15 @@ t.it("complex nested objects serialization", () => {
|
|
|
93
93
|
|
|
94
94
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
test
|
|
97
97
|
.expect(html)
|
|
98
98
|
.toContain("data-signals=")
|
|
99
|
-
|
|
99
|
+
test
|
|
100
100
|
.expect(html)
|
|
101
101
|
.toContain("John Doe")
|
|
102
102
|
})
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
test.it("non-signals data attributes serialized", () => {
|
|
105
105
|
const node = HyperNode.make("div", {
|
|
106
106
|
"data-class": { hidden: true, visible: false } as any,
|
|
107
107
|
"data-style": { color: "red", display: "none" } as any,
|
|
@@ -111,39 +111,39 @@ t.it("non-signals data attributes serialized", () => {
|
|
|
111
111
|
|
|
112
112
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
test
|
|
115
115
|
.expect(html)
|
|
116
116
|
.toContain(
|
|
117
117
|
"data-class=\"{"hidden":true,"visible":false}\"",
|
|
118
118
|
)
|
|
119
|
-
|
|
119
|
+
test
|
|
120
120
|
.expect(html)
|
|
121
121
|
.toContain(
|
|
122
122
|
"data-style=\"{"color":"red","display":"none"}\"",
|
|
123
123
|
)
|
|
124
|
-
|
|
124
|
+
test
|
|
125
125
|
.expect(html)
|
|
126
126
|
.toContain("data-show=\"true\"")
|
|
127
|
-
|
|
127
|
+
test
|
|
128
128
|
.expect(html)
|
|
129
129
|
.toContain("data-text=\"$count\"")
|
|
130
130
|
})
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
test.it("data-attr object serialization", () => {
|
|
133
133
|
const node = HyperNode.make("div", {
|
|
134
134
|
"data-attr": { disabled: true, tabindex: 0 } as any,
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
test
|
|
140
140
|
.expect(html)
|
|
141
141
|
.toBe(
|
|
142
142
|
"<div data-attr=\"{"disabled":true,"tabindex":0}\"></div>",
|
|
143
143
|
)
|
|
144
144
|
})
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
test.it("boolean attributes converted to strings", () => {
|
|
147
147
|
const node = HyperNode.make("div", {
|
|
148
148
|
"data-ignore": false as any,
|
|
149
149
|
"data-ignore-morph": true as any,
|
|
@@ -151,20 +151,20 @@ t.it("boolean attributes converted to strings", () => {
|
|
|
151
151
|
|
|
152
152
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
test
|
|
155
155
|
.expect(html)
|
|
156
156
|
.not
|
|
157
157
|
.toContain("data-ignore=\"")
|
|
158
|
-
|
|
158
|
+
test
|
|
159
159
|
.expect(html)
|
|
160
160
|
.toContain("data-ignore-morph")
|
|
161
|
-
|
|
161
|
+
test
|
|
162
162
|
.expect(html)
|
|
163
163
|
.not
|
|
164
164
|
.toContain("data-ignore-morph=")
|
|
165
165
|
})
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
test.it("data-ignore attributes only present when true", () => {
|
|
168
168
|
const nodeTrue = HyperNode.make("div", {
|
|
169
169
|
"data-ignore": true as any,
|
|
170
170
|
})
|
|
@@ -176,20 +176,20 @@ t.it("data-ignore attributes only present when true", () => {
|
|
|
176
176
|
const htmlTrue = HyperHtml.renderToString(nodeTrue, Datastar.HyperHooks)
|
|
177
177
|
const htmlFalse = HyperHtml.renderToString(nodeFalse, Datastar.HyperHooks)
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
test
|
|
180
180
|
.expect(htmlTrue)
|
|
181
181
|
.toContain("data-ignore")
|
|
182
|
-
|
|
182
|
+
test
|
|
183
183
|
.expect(htmlTrue)
|
|
184
184
|
.not
|
|
185
185
|
.toContain("data-ignore=")
|
|
186
|
-
|
|
186
|
+
test
|
|
187
187
|
.expect(htmlFalse)
|
|
188
188
|
.not
|
|
189
189
|
.toContain("data-ignore")
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
test.it("dynamic attributes with suffixes", () => {
|
|
193
193
|
const node = HyperNode.make("div", {
|
|
194
194
|
"data-class-active": "hidden" as any,
|
|
195
195
|
"data-attr-tabindex": "5" as any,
|
|
@@ -198,18 +198,18 @@ t.it("dynamic attributes with suffixes", () => {
|
|
|
198
198
|
|
|
199
199
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
test
|
|
202
202
|
.expect(html)
|
|
203
203
|
.toContain("data-class-active=\"hidden\"")
|
|
204
|
-
|
|
204
|
+
test
|
|
205
205
|
.expect(html)
|
|
206
206
|
.toContain("data-attr-tabindex=\"5\"")
|
|
207
|
-
|
|
207
|
+
test
|
|
208
208
|
.expect(html)
|
|
209
209
|
.toContain("data-style-opacity=\"0.5\"")
|
|
210
210
|
})
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
test.it("JSX with data-signals object", () => {
|
|
213
213
|
const node = jsx("div", {
|
|
214
214
|
"data-signals": { isOpen: false, count: 42 } as any,
|
|
215
215
|
children: "content",
|
|
@@ -217,18 +217,18 @@ t.it("JSX with data-signals object", () => {
|
|
|
217
217
|
|
|
218
218
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
test
|
|
221
221
|
.expect(html)
|
|
222
222
|
.toBe(
|
|
223
223
|
"<div data-signals=\"{"isOpen":false,"count":42}\">content</div>",
|
|
224
224
|
)
|
|
225
|
-
|
|
225
|
+
test
|
|
226
226
|
.expect(html)
|
|
227
227
|
.not
|
|
228
228
|
.toContain("[object Object]")
|
|
229
229
|
})
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
test.it("JSX component returning element with data-signals", () => {
|
|
232
232
|
function TestComponent() {
|
|
233
233
|
return jsx("div", {
|
|
234
234
|
"data-signals": { isOpen: false } as any,
|
|
@@ -240,27 +240,26 @@ t.it("JSX component returning element with data-signals", () => {
|
|
|
240
240
|
|
|
241
241
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
242
242
|
|
|
243
|
-
|
|
243
|
+
test
|
|
244
244
|
.expect(html)
|
|
245
245
|
.toBe(
|
|
246
246
|
"<div data-signals=\"{"isOpen":false}\"><span>nested content</span></div>",
|
|
247
247
|
)
|
|
248
|
-
|
|
248
|
+
test
|
|
249
249
|
.expect(html)
|
|
250
250
|
.not
|
|
251
251
|
.toContain("[object Object]")
|
|
252
252
|
})
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
test.it("debug hook execution", () => {
|
|
255
255
|
const node = jsx("div", {
|
|
256
256
|
"data-signals": { isOpen: false, count: 42 } as any,
|
|
257
257
|
children: "content",
|
|
258
258
|
})
|
|
259
259
|
|
|
260
260
|
const html = HyperHtml.renderToString(node, Datastar.HyperHooks)
|
|
261
|
-
console.log("Final HTML:", html)
|
|
262
261
|
|
|
263
|
-
|
|
262
|
+
test
|
|
264
263
|
.expect(html)
|
|
265
264
|
.not
|
|
266
265
|
.toContain("[object Object]")
|