effect-start 0.25.0 → 0.26.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 +18 -86
- package/dist/ChildProcess.js +0 -42
- package/dist/Commander.js +0 -410
- package/dist/ContentNegotiation.js +0 -465
- package/dist/Cookies.js +0 -371
- package/dist/Development.js +0 -94
- package/dist/Effectify.js +0 -27
- package/dist/Entity.js +0 -289
- package/dist/Fetch.js +0 -192
- package/dist/FilePathPattern.js +0 -97
- package/dist/FileRouter.js +0 -204
- package/dist/FileRouterCodegen.js +0 -298
- package/dist/FileSystem.js +0 -132
- package/dist/Http.js +0 -107
- package/dist/PathPattern.js +0 -451
- package/dist/PlatformError.js +0 -40
- package/dist/PlatformRuntime.js +0 -71
- package/dist/Route.js +0 -143
- package/dist/RouteBody.js +0 -92
- package/dist/RouteError.js +0 -76
- package/dist/RouteHook.js +0 -64
- package/dist/RouteHttp.js +0 -367
- package/dist/RouteHttpTracer.js +0 -90
- package/dist/RouteMount.js +0 -86
- package/dist/RouteSchema.js +0 -271
- package/dist/RouteSse.js +0 -94
- package/dist/RouteTree.js +0 -119
- package/dist/RouteTrie.js +0 -179
- package/dist/SchemaExtra.js +0 -99
- package/dist/Socket.js +0 -40
- package/dist/SqlIntrospect.js +0 -515
- package/dist/Start.js +0 -79
- package/dist/StartApp.js +0 -3
- package/dist/StreamExtra.js +0 -135
- package/dist/System.js +0 -38
- package/dist/TuplePathPattern.js +0 -74
- package/dist/Unique.js +0 -226
- package/dist/Values.js +0 -52
- package/dist/bun/BunBundle.js +0 -186
- package/dist/bun/BunChildProcessSpawner.js +0 -142
- package/dist/bun/BunImportTrackerPlugin.js +0 -91
- package/dist/bun/BunRoute.js +0 -157
- package/dist/bun/BunRuntime.js +0 -41
- package/dist/bun/BunServer.js +0 -285
- package/dist/bun/BunVirtualFilesPlugin.js +0 -54
- package/dist/bun/_BunEnhancedResolve.js +0 -127
- package/dist/bun/index.js +0 -5
- package/dist/bundler/Bundle.js +0 -92
- package/dist/bundler/BundleFiles.js +0 -154
- package/dist/bundler/BundleRoute.js +0 -62
- package/dist/client/Overlay.js +0 -33
- package/dist/client/ScrollState.js +0 -106
- package/dist/client/index.js +0 -97
- package/dist/console/Console.js +0 -42
- package/dist/console/ConsoleErrors.js +0 -211
- package/dist/console/ConsoleLogger.js +0 -56
- package/dist/console/ConsoleMetrics.js +0 -72
- package/dist/console/ConsoleProcess.js +0 -59
- package/dist/console/ConsoleStore.js +0 -72
- package/dist/console/ConsoleTracer.js +0 -107
- package/dist/console/Simulation.js +0 -784
- package/dist/console/index.js +0 -3
- package/dist/console/routes/tree.js +0 -30
- package/dist/datastar/actions/fetch.js +0 -536
- package/dist/datastar/actions/peek.js +0 -13
- package/dist/datastar/actions/setAll.js +0 -19
- package/dist/datastar/actions/toggleAll.js +0 -19
- package/dist/datastar/attributes/attr.js +0 -49
- package/dist/datastar/attributes/bind.js +0 -194
- package/dist/datastar/attributes/class.js +0 -54
- package/dist/datastar/attributes/computed.js +0 -25
- package/dist/datastar/attributes/effect.js +0 -10
- package/dist/datastar/attributes/indicator.js +0 -33
- package/dist/datastar/attributes/init.js +0 -27
- package/dist/datastar/attributes/jsonSignals.js +0 -33
- package/dist/datastar/attributes/on.js +0 -81
- package/dist/datastar/attributes/onIntersect.js +0 -53
- package/dist/datastar/attributes/onInterval.js +0 -31
- package/dist/datastar/attributes/onSignalPatch.js +0 -51
- package/dist/datastar/attributes/ref.js +0 -11
- package/dist/datastar/attributes/show.js +0 -32
- package/dist/datastar/attributes/signals.js +0 -18
- package/dist/datastar/attributes/style.js +0 -57
- package/dist/datastar/attributes/text.js +0 -29
- package/dist/datastar/engine.js +0 -1145
- package/dist/datastar/index.js +0 -25
- package/dist/datastar/utils.js +0 -250
- package/dist/datastar/watchers/patchElements.js +0 -486
- package/dist/datastar/watchers/patchSignals.js +0 -14
- package/dist/experimental/EncryptedCookies.js +0 -328
- package/dist/experimental/index.js +0 -1
- package/dist/hyper/Hyper.js +0 -28
- package/dist/hyper/HyperHtml.js +0 -165
- package/dist/hyper/HyperNode.js +0 -13
- package/dist/hyper/HyperRoute.js +0 -45
- package/dist/hyper/html.js +0 -30
- package/dist/hyper/index.js +0 -5
- package/dist/hyper/jsx-runtime.js +0 -14
- package/dist/index.js +0 -8
- package/dist/node/NodeFileSystem.js +0 -675
- package/dist/node/NodeUtils.js +0 -23
- package/dist/sql/Sql.js +0 -8
- package/dist/sql/bun/index.js +0 -142
- package/dist/sql/index.js +0 -1
- package/dist/sql/libsql/index.js +0 -156
- package/dist/sql/mssql/docker.js +0 -110
- package/dist/sql/mssql/index.js +0 -194
- package/dist/testing/TestLogger.js +0 -42
- package/dist/testing/index.js +0 -2
- package/dist/testing/utils.js +0 -61
- package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
- package/dist/x/cloudflare/index.js +0 -1
- package/dist/x/tailscale/TailscaleTunnel.js +0 -94
- package/dist/x/tailscale/index.js +0 -1
- package/dist/x/tailwind/TailwindPlugin.js +0 -294
- package/dist/x/tailwind/compile.js +0 -210
- package/dist/x/tailwind/plugin.js +0 -17
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
import * as Cookies from "../Cookies.js"
|
|
2
|
-
import * as Config from "effect/Config"
|
|
3
|
-
import * as Context from "effect/Context"
|
|
4
|
-
import * as Data from "effect/Data"
|
|
5
|
-
import * as Effect from "effect/Effect"
|
|
6
|
-
import * as Layer from "effect/Layer"
|
|
7
|
-
|
|
8
|
-
export class EncryptedCookiesError extends Data.TaggedError("EncryptedCookiesError") {}
|
|
9
|
-
|
|
10
|
-
export class EncryptedCookies extends Context.Tag("EncryptedCookies")() {}
|
|
11
|
-
|
|
12
|
-
export function layer(options) {
|
|
13
|
-
return Layer.effect(
|
|
14
|
-
EncryptedCookies,
|
|
15
|
-
Effect.gen(function* () {
|
|
16
|
-
const keyMaterial = yield* deriveKeyMaterial(options.secret)
|
|
17
|
-
|
|
18
|
-
// Pre-derive both keys once
|
|
19
|
-
const encryptKey = yield* deriveKey(keyMaterial, ["encrypt"])
|
|
20
|
-
const decryptKey = yield* deriveKey(keyMaterial, ["decrypt"])
|
|
21
|
-
|
|
22
|
-
return EncryptedCookies.of({
|
|
23
|
-
encrypt: (value) => encryptWithDerivedKey(value, encryptKey),
|
|
24
|
-
decrypt: (encryptedValue) => decryptWithDerivedKey(encryptedValue, decryptKey),
|
|
25
|
-
encryptCookie: (cookie) => encryptCookieWithDerivedKey(cookie, encryptKey),
|
|
26
|
-
decryptCookie: (cookie) => decryptCookieWithDerivedKey(cookie, decryptKey),
|
|
27
|
-
})
|
|
28
|
-
}),
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function layerConfig(name = "SECRET_KEY_BASE") {
|
|
33
|
-
return Effect.gen(function* () {
|
|
34
|
-
const secret = yield* Config.nonEmptyString(name).pipe(
|
|
35
|
-
Effect.flatMap((value) => {
|
|
36
|
-
return value.length < 40 ? Effect.fail(new Error("ba")) : Effect.succeed(value)
|
|
37
|
-
}),
|
|
38
|
-
Effect.catchAll((err) => {
|
|
39
|
-
return Effect.dieMessage("SECRET_KEY_BASE must be at least 40 characters")
|
|
40
|
-
}),
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
return layer({ secret })
|
|
44
|
-
}).pipe(Layer.unwrapEffect)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function encodeToBase64Segments(
|
|
48
|
-
ciphertext,
|
|
49
|
-
iv,
|
|
50
|
-
authTag,
|
|
51
|
-
) {
|
|
52
|
-
return [base64urlEncode(ciphertext), base64urlEncode(iv), base64urlEncode(authTag)].join(".")
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function base64urlEncode(data) {
|
|
56
|
-
const base64 = btoa(String.fromCharCode(...data))
|
|
57
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "")
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function decodeFromBase64Segments(
|
|
61
|
-
segments,
|
|
62
|
-
) {
|
|
63
|
-
return Effect.gen(function* () {
|
|
64
|
-
const [ciphertextB64, ivB64, authTagB64] = segments
|
|
65
|
-
|
|
66
|
-
const ciphertext = yield* Effect.try({
|
|
67
|
-
try: () => base64urlDecode(ciphertextB64),
|
|
68
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const iv = yield* Effect.try({
|
|
72
|
-
try: () => base64urlDecode(ivB64),
|
|
73
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const authTag = yield* Effect.try({
|
|
77
|
-
try: () => base64urlDecode(authTagB64),
|
|
78
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
return { ciphertext, iv, authTag }
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function base64urlDecode(data) {
|
|
86
|
-
// Convert base64url back to standard base64
|
|
87
|
-
let base64 = data.replace(/-/g, "+").replace(/_/g, "/")
|
|
88
|
-
|
|
89
|
-
// Add padding if needed
|
|
90
|
-
while (base64.length % 4) {
|
|
91
|
-
base64 += "="
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0))
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Encrypts cookie value using the SECRET_KEY_BASE.
|
|
99
|
-
*/
|
|
100
|
-
function encryptWithDerivedKey(
|
|
101
|
-
value,
|
|
102
|
-
derivedKey,
|
|
103
|
-
) {
|
|
104
|
-
return Effect.gen(function* () {
|
|
105
|
-
if (value === null || value === undefined) {
|
|
106
|
-
return yield* Effect.fail(
|
|
107
|
-
new EncryptedCookiesError({
|
|
108
|
-
cause: "Cannot encrypt empty value",
|
|
109
|
-
}),
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
114
|
-
const data = new TextEncoder().encode(JSON.stringify(value))
|
|
115
|
-
|
|
116
|
-
const encrypted = yield* Effect.tryPromise({
|
|
117
|
-
try: () => crypto.subtle.encrypt({ name: "AES-GCM", iv }, derivedKey, data),
|
|
118
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
const encryptedArray = new Uint8Array(encrypted)
|
|
122
|
-
const authTagLength = 16
|
|
123
|
-
const ciphertext = encryptedArray.slice(0, -authTagLength)
|
|
124
|
-
const authTag = encryptedArray.slice(-authTagLength)
|
|
125
|
-
|
|
126
|
-
return encodeToBase64Segments(ciphertext, iv, authTag)
|
|
127
|
-
})
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function encrypt(
|
|
131
|
-
value,
|
|
132
|
-
options,
|
|
133
|
-
) {
|
|
134
|
-
return Effect.gen(function* () {
|
|
135
|
-
if ("key" in options) {
|
|
136
|
-
return yield* encryptWithDerivedKey(value, options.key)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const keyMaterial = yield* deriveKeyMaterial(options.secret)
|
|
140
|
-
const derivedKey = yield* deriveKey(keyMaterial, ["encrypt"])
|
|
141
|
-
return yield* encryptWithDerivedKey(value, derivedKey)
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function decryptWithDerivedKey(
|
|
146
|
-
encryptedValue,
|
|
147
|
-
derivedKey,
|
|
148
|
-
) {
|
|
149
|
-
return Effect.gen(function* () {
|
|
150
|
-
if (!encryptedValue || encryptedValue === null || encryptedValue === undefined) {
|
|
151
|
-
return yield* Effect.fail(
|
|
152
|
-
new EncryptedCookiesError({
|
|
153
|
-
cause: "Cannot decrypt null, undefined, or empty value",
|
|
154
|
-
}),
|
|
155
|
-
)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const segments = encryptedValue.split(".")
|
|
159
|
-
if (segments.length !== 3) {
|
|
160
|
-
return yield* Effect.fail(
|
|
161
|
-
new EncryptedCookiesError({
|
|
162
|
-
cause: "Invalid encrypted cookie format",
|
|
163
|
-
}),
|
|
164
|
-
)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const { ciphertext, iv, authTag } = yield* decodeFromBase64Segments(segments)
|
|
168
|
-
|
|
169
|
-
const encryptedData = new Uint8Array(ciphertext.length + authTag.length)
|
|
170
|
-
encryptedData.set(ciphertext)
|
|
171
|
-
encryptedData.set(authTag, ciphertext.length)
|
|
172
|
-
|
|
173
|
-
const decrypted = yield* Effect.tryPromise({
|
|
174
|
-
try: () =>
|
|
175
|
-
crypto.subtle.decrypt({ name: "AES-GCM", iv: iv.slice(0) }, derivedKey, encryptedData),
|
|
176
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
const jsonString = new TextDecoder().decode(decrypted)
|
|
180
|
-
|
|
181
|
-
return yield* Effect.try({
|
|
182
|
-
try: () => JSON.parse(jsonString),
|
|
183
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function encryptCookieWithDerivedKey(
|
|
189
|
-
cookie,
|
|
190
|
-
derivedKey,
|
|
191
|
-
) {
|
|
192
|
-
return Effect.gen(function* () {
|
|
193
|
-
const encryptedValue = yield* encryptWithDerivedKey(cookie.value, derivedKey).pipe(
|
|
194
|
-
Effect.mapError(
|
|
195
|
-
(error) =>
|
|
196
|
-
new EncryptedCookiesError({
|
|
197
|
-
cause: error.cause,
|
|
198
|
-
cookie,
|
|
199
|
-
}),
|
|
200
|
-
),
|
|
201
|
-
)
|
|
202
|
-
return Cookies.unsafeMakeCookie(cookie.name, encryptedValue, cookie.options)
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
function decryptCookieWithDerivedKey(
|
|
206
|
-
cookie,
|
|
207
|
-
derivedKey,
|
|
208
|
-
) {
|
|
209
|
-
return Effect.gen(function* () {
|
|
210
|
-
const decryptedValue = yield* decryptWithDerivedKey(cookie.value, derivedKey).pipe(
|
|
211
|
-
Effect.mapError(
|
|
212
|
-
(error) =>
|
|
213
|
-
new EncryptedCookiesError({
|
|
214
|
-
cause: error.cause,
|
|
215
|
-
cookie,
|
|
216
|
-
}),
|
|
217
|
-
),
|
|
218
|
-
)
|
|
219
|
-
return Cookies.unsafeMakeCookie(cookie.name, JSON.stringify(decryptedValue), cookie.options)
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function encryptCookie(
|
|
224
|
-
cookie,
|
|
225
|
-
options,
|
|
226
|
-
) {
|
|
227
|
-
return Effect.gen(function* () {
|
|
228
|
-
if ("key" in options) {
|
|
229
|
-
return yield* encryptCookieWithDerivedKey(cookie, options.key)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const encryptedValue = yield* encrypt(cookie.value, {
|
|
233
|
-
secret: options.secret,
|
|
234
|
-
}).pipe(
|
|
235
|
-
Effect.mapError(
|
|
236
|
-
(error) =>
|
|
237
|
-
new EncryptedCookiesError({
|
|
238
|
-
cause: error.cause,
|
|
239
|
-
cookie,
|
|
240
|
-
}),
|
|
241
|
-
),
|
|
242
|
-
)
|
|
243
|
-
return Cookies.unsafeMakeCookie(cookie.name, encryptedValue, cookie.options)
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
export function decryptCookie(
|
|
248
|
-
cookie,
|
|
249
|
-
options,
|
|
250
|
-
) {
|
|
251
|
-
return Effect.gen(function* () {
|
|
252
|
-
if ("key" in options) {
|
|
253
|
-
return yield* decryptCookieWithDerivedKey(cookie, options.key)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const decryptedValue = yield* decrypt(cookie.value, {
|
|
257
|
-
secret: options.secret,
|
|
258
|
-
}).pipe(
|
|
259
|
-
Effect.mapError(
|
|
260
|
-
(error) =>
|
|
261
|
-
new EncryptedCookiesError({
|
|
262
|
-
cause: error.cause,
|
|
263
|
-
cookie,
|
|
264
|
-
}),
|
|
265
|
-
),
|
|
266
|
-
)
|
|
267
|
-
return Cookies.unsafeMakeCookie(cookie.name, JSON.stringify(decryptedValue), cookie.options)
|
|
268
|
-
})
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function decrypt(
|
|
272
|
-
encryptedValue,
|
|
273
|
-
options,
|
|
274
|
-
) {
|
|
275
|
-
return Effect.gen(function* () {
|
|
276
|
-
if ("key" in options) {
|
|
277
|
-
return yield* decryptWithDerivedKey(encryptedValue, options.key)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const keyMaterial = yield* deriveKeyMaterial(options.secret)
|
|
281
|
-
const derivedKey = yield* deriveKey(keyMaterial, ["decrypt"])
|
|
282
|
-
return yield* decryptWithDerivedKey(encryptedValue, derivedKey)
|
|
283
|
-
})
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function deriveKeyMaterial(secret) {
|
|
287
|
-
return Effect.gen(function* () {
|
|
288
|
-
const encoder = new TextEncoder()
|
|
289
|
-
|
|
290
|
-
const keyMaterial = yield* Effect.tryPromise({
|
|
291
|
-
try: () =>
|
|
292
|
-
crypto.subtle.importKey("raw", encoder.encode(secret), { name: "HKDF" }, false, [
|
|
293
|
-
"deriveKey",
|
|
294
|
-
]),
|
|
295
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
return keyMaterial
|
|
299
|
-
})
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function deriveKey(
|
|
303
|
-
keyMaterial,
|
|
304
|
-
usage,
|
|
305
|
-
) {
|
|
306
|
-
return Effect.gen(function* () {
|
|
307
|
-
const encoder = new TextEncoder()
|
|
308
|
-
|
|
309
|
-
const key = yield* Effect.tryPromise({
|
|
310
|
-
try: () =>
|
|
311
|
-
crypto.subtle.deriveKey(
|
|
312
|
-
{
|
|
313
|
-
name: "HKDF",
|
|
314
|
-
salt: encoder.encode("cookie-encryption"),
|
|
315
|
-
info: encoder.encode("aes-256-gcm"),
|
|
316
|
-
hash: "SHA-256",
|
|
317
|
-
},
|
|
318
|
-
keyMaterial,
|
|
319
|
-
{ name: "AES-GCM", length: 256 },
|
|
320
|
-
false,
|
|
321
|
-
usage,
|
|
322
|
-
),
|
|
323
|
-
catch: (error) => new EncryptedCookiesError({ cause: error }),
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
return key
|
|
327
|
-
})
|
|
328
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * as EncryptedCookies from "./EncryptedCookies.js"
|
package/dist/hyper/Hyper.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import * as Context from "effect/Context"
|
|
2
|
-
import * as Fiber from "effect/Fiber"
|
|
3
|
-
import * as Option from "effect/Option"
|
|
4
|
-
|
|
5
|
-
export class Hyper extends Context.Tag("Hyper")() {}
|
|
6
|
-
|
|
7
|
-
const NoChildren = Object.freeze([])
|
|
8
|
-
|
|
9
|
-
export function h(type, props) {
|
|
10
|
-
return {
|
|
11
|
-
type,
|
|
12
|
-
props: {
|
|
13
|
-
...props,
|
|
14
|
-
children: props.children ?? NoChildren,
|
|
15
|
-
},
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function unsafeUse(tag) {
|
|
20
|
-
const currentFiber = Option.getOrThrow(Fiber.getCurrentFiber())
|
|
21
|
-
const context = currentFiber.currentContext
|
|
22
|
-
|
|
23
|
-
return Context.unsafeGet(context, tag)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function isGenericJsxObject(value) {
|
|
27
|
-
return typeof value === "object" && value !== null && "type" in value && "props" in value
|
|
28
|
-
}
|
package/dist/hyper/HyperHtml.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
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
|
-
const EMPTY_TAGS = [
|
|
21
|
-
"area",
|
|
22
|
-
"base",
|
|
23
|
-
"br",
|
|
24
|
-
"col",
|
|
25
|
-
"command",
|
|
26
|
-
"embed",
|
|
27
|
-
"hr",
|
|
28
|
-
"img",
|
|
29
|
-
"input",
|
|
30
|
-
"keygen",
|
|
31
|
-
"link",
|
|
32
|
-
"meta",
|
|
33
|
-
"param",
|
|
34
|
-
"source",
|
|
35
|
-
"track",
|
|
36
|
-
"wbr",
|
|
37
|
-
]
|
|
38
|
-
|
|
39
|
-
let esc = (str) => String(str).replace(/[&<>"']/g, (s) => `&${map[s]};`)
|
|
40
|
-
let escSQ = (str) => String(str).replace(/[&<>']/g, (s) => `&${map[s]};`)
|
|
41
|
-
let map = {
|
|
42
|
-
"&": "amp",
|
|
43
|
-
"<": "lt",
|
|
44
|
-
">": "gt",
|
|
45
|
-
'"': "quot",
|
|
46
|
-
"'": "#39",
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const RAW_TEXT_TAGS = ["script", "style"]
|
|
50
|
-
|
|
51
|
-
export function renderToString(
|
|
52
|
-
node,
|
|
53
|
-
hooks,
|
|
54
|
-
) {
|
|
55
|
-
const stack = [node]
|
|
56
|
-
let result = ""
|
|
57
|
-
|
|
58
|
-
while (stack.length > 0) {
|
|
59
|
-
const current = stack.pop()
|
|
60
|
-
|
|
61
|
-
if (typeof current === "string") {
|
|
62
|
-
if (current.startsWith("<") && current.endsWith(">")) {
|
|
63
|
-
// This is a closing tag, don't escape it
|
|
64
|
-
result += current
|
|
65
|
-
} else {
|
|
66
|
-
result += esc(current)
|
|
67
|
-
}
|
|
68
|
-
continue
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (typeof current === "number") {
|
|
72
|
-
result += esc(current)
|
|
73
|
-
continue
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (typeof current === "boolean") {
|
|
77
|
-
// React-like behavior: booleans render nothing
|
|
78
|
-
continue
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (current === null || current === undefined) {
|
|
82
|
-
// React-like behavior: null/undefined render nothing
|
|
83
|
-
continue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (Array.isArray(current)) {
|
|
87
|
-
// Handle arrays by pushing all items to stack in reverse order
|
|
88
|
-
for (let i = current.length - 1; i >= 0; i--) {
|
|
89
|
-
stack.push(current[i])
|
|
90
|
-
}
|
|
91
|
-
continue
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (current && typeof current === "object" && current.type) {
|
|
95
|
-
hooks?.onNode?.(current)
|
|
96
|
-
|
|
97
|
-
if (typeof current.type === "function") {
|
|
98
|
-
const componentResult = current.type(current.props)
|
|
99
|
-
if (componentResult != null) {
|
|
100
|
-
stack.push(componentResult)
|
|
101
|
-
}
|
|
102
|
-
continue
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const { type, props } = current
|
|
106
|
-
result += `<${type}`
|
|
107
|
-
|
|
108
|
-
for (const key in props) {
|
|
109
|
-
if (
|
|
110
|
-
key !== "children" &&
|
|
111
|
-
key !== "innerHTML" && // Solid-specific
|
|
112
|
-
key !== "dangerouslySetInnerHTML" && // React-specific
|
|
113
|
-
props[key] !== false &&
|
|
114
|
-
props[key] != null
|
|
115
|
-
) {
|
|
116
|
-
if (props[key] === true) {
|
|
117
|
-
result += ` ${esc(key)}`
|
|
118
|
-
} else {
|
|
119
|
-
const resolvedKey = key === "className" ? "class" : key
|
|
120
|
-
const value = props[key]
|
|
121
|
-
|
|
122
|
-
if (key.startsWith("data-") && typeof value === "function") {
|
|
123
|
-
result += ` ${esc(resolvedKey)}="${esc(value.toString())}"`
|
|
124
|
-
} else if (key.startsWith("data-") && typeof value === "object") {
|
|
125
|
-
result += ` ${esc(resolvedKey)}='${escSQ(JSON.stringify(value))}'`
|
|
126
|
-
} else {
|
|
127
|
-
result += ` ${esc(resolvedKey)}="${esc(value)}"`
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
result += ">"
|
|
134
|
-
|
|
135
|
-
if (!EMPTY_TAGS.includes(type)) {
|
|
136
|
-
stack.push(`</${type}>`)
|
|
137
|
-
|
|
138
|
-
const html = props.dangerouslySetInnerHTML?.__html ?? props.innerHTML
|
|
139
|
-
|
|
140
|
-
if (html) {
|
|
141
|
-
result += html
|
|
142
|
-
} else {
|
|
143
|
-
const children = props.children
|
|
144
|
-
|
|
145
|
-
if (type === "script" && typeof children === "function") {
|
|
146
|
-
result += `(${children.toString()})(window)`
|
|
147
|
-
} else if (RAW_TEXT_TAGS.includes(type) && children != null) {
|
|
148
|
-
result += Array.isArray(children) ? children.join("") : children
|
|
149
|
-
} else if (Array.isArray(children)) {
|
|
150
|
-
for (let i = children.length - 1; i >= 0; i--) {
|
|
151
|
-
stack.push(children[i])
|
|
152
|
-
}
|
|
153
|
-
} else if (children != null) {
|
|
154
|
-
stack.push(children)
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
} else if (current && typeof current === "object") {
|
|
159
|
-
// Handle objects without type property - convert to string or ignore
|
|
160
|
-
// This prevents [object Object] from appearing
|
|
161
|
-
continue
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return result
|
|
165
|
-
}
|
package/dist/hyper/HyperNode.js
DELETED
package/dist/hyper/HyperRoute.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect"
|
|
2
|
-
import * as Entity from "../Entity.js"
|
|
3
|
-
import * as Route from "../Route.js"
|
|
4
|
-
import * as HyperHtml from "./HyperHtml.js"
|
|
5
|
-
|
|
6
|
-
function renderValue(
|
|
7
|
-
value,
|
|
8
|
-
) {
|
|
9
|
-
if (Entity.isEntity(value)) {
|
|
10
|
-
return Entity.make(HyperHtml.renderToString(value.body), {
|
|
11
|
-
status: value.status,
|
|
12
|
-
url: value.url,
|
|
13
|
-
headers: value.headers,
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
return HyperHtml.renderToString(value)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeToEffect(
|
|
20
|
-
handler,
|
|
21
|
-
context,
|
|
22
|
-
next,
|
|
23
|
-
) {
|
|
24
|
-
if (Effect.isEffect(handler)) {
|
|
25
|
-
return handler
|
|
26
|
-
}
|
|
27
|
-
if (typeof handler === "function") {
|
|
28
|
-
const result = (handler)(context, next)
|
|
29
|
-
if (Effect.isEffect(result)) {
|
|
30
|
-
return result
|
|
31
|
-
}
|
|
32
|
-
return Effect.gen(function* () {
|
|
33
|
-
return yield* result
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
return Effect.succeed(handler)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function html(
|
|
40
|
-
handler,
|
|
41
|
-
) {
|
|
42
|
-
return Route.html((context, next) =>
|
|
43
|
-
Effect.map(normalizeToEffect(handler, context, next), renderValue),
|
|
44
|
-
)
|
|
45
|
-
}
|
package/dist/hyper/html.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
const HtmlStringSymbol = Symbol.for("HtmlString")
|
|
2
|
-
|
|
3
|
-
const makeHtmlString = (value) => ({
|
|
4
|
-
[HtmlStringSymbol]: true,
|
|
5
|
-
value,
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
const isHtmlString = (value) =>
|
|
9
|
-
typeof value === "object" && value !== null && HtmlStringSymbol in value
|
|
10
|
-
|
|
11
|
-
const resolveValue = (value) => {
|
|
12
|
-
if (value === null || value === undefined || value === false || value === true) return ""
|
|
13
|
-
if (isHtmlString(value)) return value.value
|
|
14
|
-
if (Array.isArray(value)) return (value).map(resolveValue).join("")
|
|
15
|
-
if (typeof value === "function") return value.toString()
|
|
16
|
-
if (typeof value === "object") return JSON.stringify(value)
|
|
17
|
-
if (typeof value === "string") return value
|
|
18
|
-
return String(value)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const html = (strings, ...values) => {
|
|
22
|
-
let result = strings[0]
|
|
23
|
-
for (let i = 0; i < values.length; i++) {
|
|
24
|
-
result += resolveValue(values[i])
|
|
25
|
-
result += strings[i + 1]
|
|
26
|
-
}
|
|
27
|
-
return makeHtmlString(result)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
html.raw = (value) => makeHtmlString(value)
|
package/dist/hyper/index.js
DELETED
package/dist/index.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export * as Bundle from "./bundler/Bundle.js"
|
|
2
|
-
export * as Development from "./Development.js"
|
|
3
|
-
export * as Entity from "./Entity.js"
|
|
4
|
-
export * as FileRouter from "./FileRouter.js"
|
|
5
|
-
export * as Route from "./Route.js"
|
|
6
|
-
export * as Start from "./Start.js"
|
|
7
|
-
export * as System from "./System.js"
|
|
8
|
-
export * as Unique from "./Unique.js"
|