effect-start 0.9.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/LICENSE +21 -0
- package/README.md +109 -0
- package/package.json +57 -0
- package/src/Bundle.ts +167 -0
- package/src/BundleFiles.ts +174 -0
- package/src/BundleHttp.test.ts +160 -0
- package/src/BundleHttp.ts +259 -0
- package/src/Commander.test.ts +1378 -0
- package/src/Commander.ts +672 -0
- package/src/Datastar.test.ts +267 -0
- package/src/Datastar.ts +68 -0
- package/src/Effect_HttpRouter.test.ts +570 -0
- package/src/EncryptedCookies.test.ts +427 -0
- package/src/EncryptedCookies.ts +451 -0
- package/src/FileHttpRouter.test.ts +207 -0
- package/src/FileHttpRouter.ts +122 -0
- package/src/FileRouter.ts +405 -0
- package/src/FileRouterCodegen.test.ts +598 -0
- package/src/FileRouterCodegen.ts +251 -0
- package/src/FileRouter_files.test.ts +64 -0
- package/src/FileRouter_path.test.ts +132 -0
- package/src/FileRouter_tree.test.ts +126 -0
- package/src/FileSystemExtra.ts +102 -0
- package/src/HttpAppExtra.ts +127 -0
- package/src/Hyper.ts +194 -0
- package/src/HyperHtml.test.ts +90 -0
- package/src/HyperHtml.ts +139 -0
- package/src/HyperNode.ts +37 -0
- package/src/JsModule.test.ts +14 -0
- package/src/JsModule.ts +116 -0
- package/src/PublicDirectory.test.ts +280 -0
- package/src/PublicDirectory.ts +108 -0
- package/src/Route.test.ts +873 -0
- package/src/Route.ts +992 -0
- package/src/Router.ts +80 -0
- package/src/SseHttpResponse.ts +55 -0
- package/src/Start.ts +133 -0
- package/src/StartApp.ts +43 -0
- package/src/StartHttp.ts +42 -0
- package/src/StreamExtra.ts +146 -0
- package/src/TestHttpClient.test.ts +54 -0
- package/src/TestHttpClient.ts +100 -0
- package/src/bun/BunBundle.test.ts +277 -0
- package/src/bun/BunBundle.ts +309 -0
- package/src/bun/BunBundle_imports.test.ts +50 -0
- package/src/bun/BunFullstackServer.ts +45 -0
- package/src/bun/BunFullstackServer_httpServer.ts +541 -0
- package/src/bun/BunImportTrackerPlugin.test.ts +77 -0
- package/src/bun/BunImportTrackerPlugin.ts +97 -0
- package/src/bun/BunTailwindPlugin.test.ts +335 -0
- package/src/bun/BunTailwindPlugin.ts +322 -0
- package/src/bun/BunVirtualFilesPlugin.ts +59 -0
- package/src/bun/index.ts +4 -0
- package/src/client/Overlay.ts +34 -0
- package/src/client/ScrollState.ts +120 -0
- package/src/client/index.ts +101 -0
- package/src/index.ts +24 -0
- package/src/jsx-datastar.d.ts +63 -0
- package/src/jsx-runtime.ts +23 -0
- package/src/jsx.d.ts +4402 -0
- package/src/testing.ts +55 -0
- package/src/x/cloudflare/CloudflareTunnel.ts +110 -0
- package/src/x/cloudflare/index.ts +1 -0
- package/src/x/datastar/Datastar.test.ts +267 -0
- package/src/x/datastar/Datastar.ts +68 -0
- package/src/x/datastar/index.ts +4 -0
- package/src/x/datastar/jsx-datastar.d.ts +63 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import * as t from "bun:test"
|
|
2
|
+
import * as Datastar from "./Datastar.ts"
|
|
3
|
+
import * as HyperHtml from "./HyperHtml.ts"
|
|
4
|
+
import * as HyperNode from "./HyperNode.ts"
|
|
5
|
+
import { jsx } from "./jsx-runtime.ts"
|
|
6
|
+
|
|
7
|
+
t.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
|
+
t
|
|
15
|
+
.expect(html)
|
|
16
|
+
.toBe(
|
|
17
|
+
"<div data-signals=\"{"foo":1,"bar":{"baz":"hello"}}\"></div>",
|
|
18
|
+
)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
t.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
|
+
t
|
|
29
|
+
.expect(html)
|
|
30
|
+
.toBe("<div data-signals=\"$mySignal\"></div>")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
t.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
|
+
t
|
|
41
|
+
.expect(html)
|
|
42
|
+
.toBe(
|
|
43
|
+
"<div data-signals-user=\"{"name":"John","age":30}\"></div>",
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
t.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
|
+
t
|
|
58
|
+
.expect(html)
|
|
59
|
+
.toContain("id=\"test\"")
|
|
60
|
+
t
|
|
61
|
+
.expect(html)
|
|
62
|
+
.toContain("class=\"my-class\"")
|
|
63
|
+
t
|
|
64
|
+
.expect(html)
|
|
65
|
+
.toContain("data-text=\"$count\"")
|
|
66
|
+
t
|
|
67
|
+
.expect(html)
|
|
68
|
+
.toContain("data-signals=\"{"count":0}\"")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
t.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
|
+
t
|
|
80
|
+
.expect(html)
|
|
81
|
+
.toBe("<div></div>")
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
t.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
|
+
t
|
|
97
|
+
.expect(html)
|
|
98
|
+
.toContain("data-signals=")
|
|
99
|
+
t
|
|
100
|
+
.expect(html)
|
|
101
|
+
.toContain("John Doe")
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
t.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
|
+
t
|
|
115
|
+
.expect(html)
|
|
116
|
+
.toContain(
|
|
117
|
+
"data-class=\"{"hidden":true,"visible":false}\"",
|
|
118
|
+
)
|
|
119
|
+
t
|
|
120
|
+
.expect(html)
|
|
121
|
+
.toContain(
|
|
122
|
+
"data-style=\"{"color":"red","display":"none"}\"",
|
|
123
|
+
)
|
|
124
|
+
t
|
|
125
|
+
.expect(html)
|
|
126
|
+
.toContain("data-show=\"true\"")
|
|
127
|
+
t
|
|
128
|
+
.expect(html)
|
|
129
|
+
.toContain("data-text=\"$count\"")
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
t.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
|
+
t
|
|
140
|
+
.expect(html)
|
|
141
|
+
.toBe(
|
|
142
|
+
"<div data-attr=\"{"disabled":true,"tabindex":0}\"></div>",
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
t.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
|
+
t
|
|
155
|
+
.expect(html)
|
|
156
|
+
.not
|
|
157
|
+
.toContain("data-ignore=\"")
|
|
158
|
+
t
|
|
159
|
+
.expect(html)
|
|
160
|
+
.toContain("data-ignore-morph")
|
|
161
|
+
t
|
|
162
|
+
.expect(html)
|
|
163
|
+
.not
|
|
164
|
+
.toContain("data-ignore-morph=")
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
t.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
|
+
t
|
|
180
|
+
.expect(htmlTrue)
|
|
181
|
+
.toContain("data-ignore")
|
|
182
|
+
t
|
|
183
|
+
.expect(htmlTrue)
|
|
184
|
+
.not
|
|
185
|
+
.toContain("data-ignore=")
|
|
186
|
+
t
|
|
187
|
+
.expect(htmlFalse)
|
|
188
|
+
.not
|
|
189
|
+
.toContain("data-ignore")
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
t.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
|
+
t
|
|
202
|
+
.expect(html)
|
|
203
|
+
.toContain("data-class-active=\"hidden\"")
|
|
204
|
+
t
|
|
205
|
+
.expect(html)
|
|
206
|
+
.toContain("data-attr-tabindex=\"5\"")
|
|
207
|
+
t
|
|
208
|
+
.expect(html)
|
|
209
|
+
.toContain("data-style-opacity=\"0.5\"")
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
t.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
|
+
t
|
|
221
|
+
.expect(html)
|
|
222
|
+
.toBe(
|
|
223
|
+
"<div data-signals=\"{"isOpen":false,"count":42}\">content</div>",
|
|
224
|
+
)
|
|
225
|
+
t
|
|
226
|
+
.expect(html)
|
|
227
|
+
.not
|
|
228
|
+
.toContain("[object Object]")
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
t.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
|
+
t
|
|
244
|
+
.expect(html)
|
|
245
|
+
.toBe(
|
|
246
|
+
"<div data-signals=\"{"isOpen":false}\"><span>nested content</span></div>",
|
|
247
|
+
)
|
|
248
|
+
t
|
|
249
|
+
.expect(html)
|
|
250
|
+
.not
|
|
251
|
+
.toContain("[object Object]")
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
t.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
|
+
console.log("Final HTML:", html)
|
|
262
|
+
|
|
263
|
+
t
|
|
264
|
+
.expect(html)
|
|
265
|
+
.not
|
|
266
|
+
.toContain("[object Object]")
|
|
267
|
+
})
|
package/src/Datastar.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as HyperNode from "./HyperNode.ts"
|
|
2
|
+
|
|
3
|
+
export const HyperHooks = {
|
|
4
|
+
onNode,
|
|
5
|
+
} as const
|
|
6
|
+
|
|
7
|
+
function onNode(node: HyperNode.HyperNode) {
|
|
8
|
+
const {
|
|
9
|
+
"data-signals": dataSignals,
|
|
10
|
+
"data-class": dataClass,
|
|
11
|
+
"data-attr": dataAttr,
|
|
12
|
+
"data-style": dataStyle,
|
|
13
|
+
"data-show": dataShow,
|
|
14
|
+
"data-ignore": dataIgnore,
|
|
15
|
+
"data-ignore-morph": dataIgnoreMorph,
|
|
16
|
+
} = node.props as any
|
|
17
|
+
|
|
18
|
+
if (typeof dataSignals === "object" && dataSignals !== null) {
|
|
19
|
+
node.props["data-signals"] = JSON.stringify(dataSignals)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof dataClass === "function") {
|
|
23
|
+
node.props["data-class"] = `(${dataClass.toString()})()`
|
|
24
|
+
} else if (typeof dataClass === "object" && dataClass !== null) {
|
|
25
|
+
node.props["data-class"] = JSON.stringify(dataClass)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof dataAttr === "object" && dataAttr !== null) {
|
|
29
|
+
node.props["data-attr"] = JSON.stringify(dataAttr)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof dataStyle === "function") {
|
|
33
|
+
node.props["data-style"] = `(${dataStyle.toString()})()`
|
|
34
|
+
} else if (typeof dataStyle === "object" && dataStyle !== null) {
|
|
35
|
+
node.props["data-style"] = JSON.stringify(dataStyle)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof dataShow === "boolean") {
|
|
39
|
+
node.props["data-show"] = dataShow.toString()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (dataIgnore !== true && dataIgnore !== undefined) {
|
|
43
|
+
delete node.props["data-ignore"]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (dataIgnoreMorph !== true && dataIgnoreMorph !== undefined) {
|
|
47
|
+
delete node.props["data-ignore-morph"]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Handle dynamic attributes with suffixes
|
|
51
|
+
for (const [key, value] of Object.entries(node.props)) {
|
|
52
|
+
if (
|
|
53
|
+
key.startsWith("data-signals-")
|
|
54
|
+
&& typeof value === "object"
|
|
55
|
+
&& value !== null
|
|
56
|
+
) {
|
|
57
|
+
node.props[key] = JSON.stringify(value)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (
|
|
61
|
+
key.startsWith("data-on-")
|
|
62
|
+
&& typeof value === "function"
|
|
63
|
+
) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
node.props[key] = `(${value.toString()})()`
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|