effect-start 0.34.0 → 0.35.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 +303 -36
- package/dist/Fetch.d.ts +1 -1
- package/dist/FileRouter.d.ts +1 -1
- package/dist/FileRouterCodegen.d.ts.map +1 -1
- package/dist/FileRouterCodegen.js +8 -2
- package/dist/FileRouterCodegen.js.map +1 -1
- package/dist/Job.d.ts +94 -0
- package/dist/Job.d.ts.map +1 -0
- package/dist/Job.js +157 -0
- package/dist/Job.js.map +1 -0
- package/dist/Password.d.ts +1 -1
- package/dist/Route.d.ts +20 -15
- package/dist/Route.d.ts.map +1 -1
- package/dist/Route.js +12 -0
- package/dist/Route.js.map +1 -1
- package/dist/RouteBody.d.ts +7 -7
- package/dist/RouteBody.d.ts.map +1 -1
- package/dist/RouteBody.js.map +1 -1
- package/dist/RouteHook.d.ts +1 -1
- package/dist/RouteHook.d.ts.map +1 -1
- package/dist/RouteHook.js.map +1 -1
- package/dist/RouteHttp.d.ts.map +1 -1
- package/dist/RouteHttp.js +10 -4
- package/dist/RouteHttp.js.map +1 -1
- package/dist/RouteLink.d.ts +16 -0
- package/dist/RouteLink.d.ts.map +1 -0
- package/dist/RouteLink.js +23 -0
- package/dist/RouteLink.js.map +1 -0
- package/dist/RouteMount.d.ts +29 -32
- package/dist/RouteMount.d.ts.map +1 -1
- package/dist/RouteMount.js.map +1 -1
- package/dist/RouteSchema.d.ts +81 -28
- package/dist/RouteSchema.d.ts.map +1 -1
- package/dist/RouteSchema.js +56 -101
- package/dist/RouteSchema.js.map +1 -1
- package/dist/RouteSse.d.ts +1 -1
- package/dist/RouteSse.d.ts.map +1 -1
- package/dist/RouteSse.js.map +1 -1
- package/dist/Socket.d.ts +1 -1
- package/dist/Start.js +1 -1
- package/dist/Start.js.map +1 -1
- package/dist/StaticFiles.d.ts +4 -10
- package/dist/StaticFiles.d.ts.map +1 -1
- package/dist/StaticFiles.js +3 -10
- package/dist/StaticFiles.js.map +1 -1
- package/dist/System.d.ts +1 -1
- package/dist/_Docker.d.ts +1 -1
- package/dist/_HtmlScanner.d.ts +42 -0
- package/dist/_HtmlScanner.d.ts.map +1 -0
- package/dist/_HtmlScanner.js +385 -0
- package/dist/_HtmlScanner.js.map +1 -0
- package/dist/_RouteLink.d.ts +16 -0
- package/dist/_RouteLink.d.ts.map +1 -0
- package/dist/_RouteLink.js +22 -0
- package/dist/_RouteLink.js.map +1 -0
- package/dist/bun/BunRoute.d.ts +4 -6
- package/dist/bun/BunRoute.d.ts.map +1 -1
- package/dist/bun/BunRoute.js +1 -1
- package/dist/bun/BunRoute.js.map +1 -1
- package/dist/bundler/Bundle.d.ts +1 -1
- package/dist/bundler/BundleRoute.d.ts +5 -6
- package/dist/bundler/BundleRoute.d.ts.map +1 -1
- package/dist/bundler/BundleRoute.js +5 -11
- package/dist/bundler/BundleRoute.js.map +1 -1
- package/dist/datastar/watchers/patchElements.js +1 -1
- package/dist/datastar/watchers/patchElements.js.map +1 -1
- package/dist/experimental/CsrfProtection.d.ts +67 -0
- package/dist/experimental/CsrfProtection.d.ts.map +1 -0
- package/dist/experimental/CsrfProtection.js +100 -0
- package/dist/experimental/CsrfProtection.js.map +1 -0
- package/dist/experimental/EncryptedCookies.d.ts +1 -1
- package/dist/experimental/KeyValueStore.d.ts +1 -1
- package/dist/lint/plugin.js +4 -0
- package/dist/lint/plugin.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +1 -1
- package/dist/studio/Studio.d.ts +1 -1
- package/dist/studio/Studio.d.ts.map +1 -1
- package/dist/studio/Studio.js +4 -10
- package/dist/studio/Studio.js.map +1 -1
- package/dist/studio/routes/errors/route.d.ts +3 -3
- package/dist/studio/routes/errors/route.d.ts.map +1 -1
- package/dist/studio/routes/errors/route.js +3 -2
- package/dist/studio/routes/errors/route.js.map +1 -1
- package/dist/studio/routes/fiberDetail.d.ts +3 -7
- package/dist/studio/routes/fiberDetail.d.ts.map +1 -1
- package/dist/studio/routes/fibers/route.d.ts +3 -3
- package/dist/studio/routes/layout.d.ts +3 -3
- package/dist/studio/routes/logs/route.d.ts +3 -3
- package/dist/studio/routes/logs/route.d.ts.map +1 -1
- package/dist/studio/routes/logs/route.js +3 -2
- package/dist/studio/routes/logs/route.js.map +1 -1
- package/dist/studio/routes/metrics/route.d.ts +3 -3
- package/dist/studio/routes/route.d.ts +2 -2
- package/dist/studio/routes/routes/route.d.ts +2 -2
- package/dist/studio/routes/services/route.d.ts +2 -2
- package/dist/studio/routes/system/route.d.ts +3 -3
- package/dist/studio/routes/traceDetail.d.ts +3 -7
- package/dist/studio/routes/traceDetail.d.ts.map +1 -1
- package/dist/studio/routes/traces/route.d.ts +3 -3
- package/dist/studio/routes/traces/route.d.ts.map +1 -1
- package/dist/studio/routes/traces/route.js +3 -2
- package/dist/studio/routes/traces/route.js.map +1 -1
- package/dist/studio/routes/tree.d.ts +43 -51
- package/dist/studio/routes/tree.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/FileRouterCodegen.ts +8 -2
- package/src/Route.ts +55 -34
- package/src/RouteBody.ts +15 -15
- package/src/RouteHook.ts +3 -3
- package/src/RouteHttp.ts +10 -4
- package/src/RouteLink.ts +56 -0
- package/src/RouteMount.ts +43 -48
- package/src/RouteSchema.ts +299 -166
- package/src/RouteSse.ts +3 -3
- package/src/Start.ts +1 -1
- package/src/StaticFiles.ts +10 -24
- package/src/_HtmlScanner.ts +415 -0
- package/src/bun/BunRoute.ts +11 -11
- package/src/bundler/BundleRoute.ts +8 -19
- package/src/datastar/watchers/patchElements.ts +1 -1
- package/src/dev.d.ts +3 -0
- package/src/experimental/CsrfProtection.ts +153 -0
- package/src/lint/plugin.js +2 -0
- package/src/studio/Studio.ts +4 -14
- package/src/studio/routes/errors/route.tsx +3 -2
- package/src/studio/routes/logs/route.tsx +3 -2
- package/src/studio/routes/traces/route.tsx +3 -2
package/src/StaticFiles.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect"
|
|
2
|
-
import * as Layer from "effect/Layer"
|
|
3
2
|
import * as Option from "effect/Option"
|
|
4
3
|
import * as Schema from "effect/Schema"
|
|
5
4
|
import * as Entity from "./Entity.ts"
|
|
6
5
|
import * as FileSystem from "./FileSystem.ts"
|
|
7
|
-
import * as PathPattern from "./_PathPattern.ts"
|
|
6
|
+
import type * as PathPattern from "./_PathPattern.ts"
|
|
8
7
|
import * as Mime from "./_Mime.ts"
|
|
9
8
|
import * as Route from "./Route.ts"
|
|
10
9
|
import * as RouteSchema from "./RouteSchema.ts"
|
|
11
|
-
import * as RouteTree from "./RouteTree.ts"
|
|
12
|
-
import * as System from "./System.ts"
|
|
13
10
|
|
|
14
11
|
const defaultMountPath = "/assets"
|
|
15
12
|
const PathParamsSchema = Schema.Struct({
|
|
@@ -55,26 +52,15 @@ export const make = (directory: string) =>
|
|
|
55
52
|
export const layer = (options: {
|
|
56
53
|
directory: string
|
|
57
54
|
path?: string
|
|
58
|
-
}) =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
trimmedMountPath.length > 0 ? `/${trimmedMountPath}/:path+` : "/:path+"
|
|
68
|
-
) as PathPattern.PathPattern
|
|
69
|
-
const staticFilesTree = Route.tree({
|
|
70
|
-
[mountPattern]: make(options.directory),
|
|
71
|
-
})
|
|
72
|
-
if (!existing) {
|
|
73
|
-
return staticFilesTree
|
|
74
|
-
}
|
|
75
|
-
return RouteTree.merge(existing, staticFilesTree)
|
|
76
|
-
}),
|
|
77
|
-
)
|
|
55
|
+
}) => {
|
|
56
|
+
const trimmedMountPath = (options.path ?? defaultMountPath).trim().replace(/^\/+|\/+$/g, "")
|
|
57
|
+
const mountPattern = (
|
|
58
|
+
trimmedMountPath.length > 0 ? `/${trimmedMountPath}/:path+` : "/:path+"
|
|
59
|
+
) as PathPattern.PathPattern
|
|
60
|
+
return Route.layerMerge({
|
|
61
|
+
[mountPattern]: make(options.directory),
|
|
62
|
+
})
|
|
63
|
+
}
|
|
78
64
|
|
|
79
65
|
function normalizeRelativePath(path: string): string | null {
|
|
80
66
|
if (path.length === 0 || path.startsWith("/") || path.startsWith("\\")) {
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
const TEXT = 0
|
|
2
|
+
const TAG_OPEN = 1
|
|
3
|
+
const TAG_NAME = 2
|
|
4
|
+
const BEFORE_ATTR_NAME = 3
|
|
5
|
+
const ATTR_NAME = 4
|
|
6
|
+
const AFTER_ATTR_NAME = 5
|
|
7
|
+
const BEFORE_ATTR_VALUE = 6
|
|
8
|
+
const ATTR_VALUE_DOUBLE_QUOTED = 7
|
|
9
|
+
const ATTR_VALUE_SINGLE_QUOTED = 8
|
|
10
|
+
const ATTR_VALUE_UNQUOTED = 9
|
|
11
|
+
const SELF_CLOSING = 10
|
|
12
|
+
const BOGUS_COMMENT = 11
|
|
13
|
+
|
|
14
|
+
const RAW_TEXT_TAGS = ["script", "style", "textarea", "title"]
|
|
15
|
+
|
|
16
|
+
interface Span {
|
|
17
|
+
start: number
|
|
18
|
+
end: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface AttrInternal {
|
|
22
|
+
value: string
|
|
23
|
+
nameSpan: Span
|
|
24
|
+
valueSpan: Span | undefined
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TokenizedElement {
|
|
28
|
+
tag: string
|
|
29
|
+
attrs: Map<string, AttrInternal>
|
|
30
|
+
end: number
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function* tokenize(html: string): Generator<TokenizedElement> {
|
|
34
|
+
const len = html.length
|
|
35
|
+
let i = 0
|
|
36
|
+
let state = TEXT
|
|
37
|
+
let tag = ""
|
|
38
|
+
let attrName = ""
|
|
39
|
+
let attrNameStart = 0
|
|
40
|
+
let attrValue = ""
|
|
41
|
+
let attrValueStart = 0
|
|
42
|
+
let attrs = new Map<string, AttrInternal>()
|
|
43
|
+
|
|
44
|
+
let pending: TokenizedElement | undefined
|
|
45
|
+
|
|
46
|
+
const emit = (): string | undefined => {
|
|
47
|
+
const lower = tag.toLowerCase()
|
|
48
|
+
pending = { tag, attrs, end: i }
|
|
49
|
+
tag = ""
|
|
50
|
+
attrs = new Map()
|
|
51
|
+
return RAW_TEXT_TAGS.includes(lower) ? lower : undefined
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const emitAndContinue = () => {
|
|
55
|
+
const rawTag = emit()
|
|
56
|
+
state = TEXT
|
|
57
|
+
if (rawTag) {
|
|
58
|
+
const needle = `</${rawTag}`
|
|
59
|
+
const idx = html.toLowerCase().indexOf(needle, i)
|
|
60
|
+
if (idx === -1) {
|
|
61
|
+
i = len
|
|
62
|
+
} else {
|
|
63
|
+
const end = html.indexOf(">", idx + needle.length)
|
|
64
|
+
i = end === -1 ? len : end + 1
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const commitAttr = () => {
|
|
70
|
+
if (attrName) {
|
|
71
|
+
attrs.set(attrName.toLowerCase(), {
|
|
72
|
+
value: attrValue,
|
|
73
|
+
nameSpan: { start: attrNameStart, end: attrNameStart + attrName.length },
|
|
74
|
+
valueSpan: attrValueStart > 0
|
|
75
|
+
? { start: attrValueStart, end: attrValueStart + attrValue.length }
|
|
76
|
+
: undefined,
|
|
77
|
+
})
|
|
78
|
+
attrName = ""
|
|
79
|
+
attrValue = ""
|
|
80
|
+
attrValueStart = 0
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
while (i < len) {
|
|
85
|
+
const ch = html[i]
|
|
86
|
+
|
|
87
|
+
switch (state) {
|
|
88
|
+
case TEXT:
|
|
89
|
+
if (ch === "<") {
|
|
90
|
+
const next = html[i + 1]
|
|
91
|
+
if (next === "!" && html[i + 2] === "-" && html[i + 3] === "-") {
|
|
92
|
+
const end = html.indexOf("-->", i + 4)
|
|
93
|
+
i = end === -1 ? len : end + 3
|
|
94
|
+
} else if (next === "!" || next === "?") {
|
|
95
|
+
state = BOGUS_COMMENT
|
|
96
|
+
i++
|
|
97
|
+
} else {
|
|
98
|
+
state = TAG_OPEN
|
|
99
|
+
i++
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
i++
|
|
103
|
+
}
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
case BOGUS_COMMENT: {
|
|
107
|
+
const end = html.indexOf(">", i)
|
|
108
|
+
i = end === -1 ? len : end + 1
|
|
109
|
+
state = TEXT
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
case TAG_OPEN:
|
|
114
|
+
if (ch === "/") {
|
|
115
|
+
const end = html.indexOf(">", i)
|
|
116
|
+
i = end === -1 ? len : end + 1
|
|
117
|
+
state = TEXT
|
|
118
|
+
} else if ((ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z")) {
|
|
119
|
+
tag = ch
|
|
120
|
+
state = TAG_NAME
|
|
121
|
+
i++
|
|
122
|
+
} else {
|
|
123
|
+
state = TEXT
|
|
124
|
+
}
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
case TAG_NAME:
|
|
128
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
129
|
+
state = BEFORE_ATTR_NAME
|
|
130
|
+
i++
|
|
131
|
+
} else if (ch === "/") {
|
|
132
|
+
state = SELF_CLOSING
|
|
133
|
+
i++
|
|
134
|
+
} else if (ch === ">") {
|
|
135
|
+
i++
|
|
136
|
+
emitAndContinue()
|
|
137
|
+
} else {
|
|
138
|
+
tag += ch
|
|
139
|
+
i++
|
|
140
|
+
}
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
case BEFORE_ATTR_NAME:
|
|
144
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
145
|
+
i++
|
|
146
|
+
} else if (ch === "/") {
|
|
147
|
+
state = SELF_CLOSING
|
|
148
|
+
i++
|
|
149
|
+
} else if (ch === ">") {
|
|
150
|
+
i++
|
|
151
|
+
emitAndContinue()
|
|
152
|
+
} else {
|
|
153
|
+
attrName = ch
|
|
154
|
+
attrNameStart = i
|
|
155
|
+
state = ATTR_NAME
|
|
156
|
+
i++
|
|
157
|
+
}
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
case ATTR_NAME:
|
|
161
|
+
if (ch === "=") {
|
|
162
|
+
state = BEFORE_ATTR_VALUE
|
|
163
|
+
i++
|
|
164
|
+
} else if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
165
|
+
state = AFTER_ATTR_NAME
|
|
166
|
+
i++
|
|
167
|
+
} else if (ch === "/" || ch === ">") {
|
|
168
|
+
commitAttr()
|
|
169
|
+
if (ch === "/") {
|
|
170
|
+
state = SELF_CLOSING
|
|
171
|
+
i++
|
|
172
|
+
} else {
|
|
173
|
+
i++
|
|
174
|
+
emitAndContinue()
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
attrName += ch
|
|
178
|
+
i++
|
|
179
|
+
}
|
|
180
|
+
break
|
|
181
|
+
|
|
182
|
+
case AFTER_ATTR_NAME:
|
|
183
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
184
|
+
i++
|
|
185
|
+
} else if (ch === "=") {
|
|
186
|
+
state = BEFORE_ATTR_VALUE
|
|
187
|
+
i++
|
|
188
|
+
} else if (ch === "/") {
|
|
189
|
+
commitAttr()
|
|
190
|
+
state = SELF_CLOSING
|
|
191
|
+
i++
|
|
192
|
+
} else if (ch === ">") {
|
|
193
|
+
commitAttr()
|
|
194
|
+
i++
|
|
195
|
+
emitAndContinue()
|
|
196
|
+
} else {
|
|
197
|
+
commitAttr()
|
|
198
|
+
attrName = ch
|
|
199
|
+
attrNameStart = i
|
|
200
|
+
state = ATTR_NAME
|
|
201
|
+
i++
|
|
202
|
+
}
|
|
203
|
+
break
|
|
204
|
+
|
|
205
|
+
case BEFORE_ATTR_VALUE:
|
|
206
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
207
|
+
i++
|
|
208
|
+
} else if (ch === '"') {
|
|
209
|
+
attrValueStart = i + 1
|
|
210
|
+
state = ATTR_VALUE_DOUBLE_QUOTED
|
|
211
|
+
i++
|
|
212
|
+
} else if (ch === "'") {
|
|
213
|
+
attrValueStart = i + 1
|
|
214
|
+
state = ATTR_VALUE_SINGLE_QUOTED
|
|
215
|
+
i++
|
|
216
|
+
} else if (ch === ">") {
|
|
217
|
+
commitAttr()
|
|
218
|
+
i++
|
|
219
|
+
emitAndContinue()
|
|
220
|
+
} else {
|
|
221
|
+
attrValue = ch
|
|
222
|
+
attrValueStart = i
|
|
223
|
+
state = ATTR_VALUE_UNQUOTED
|
|
224
|
+
i++
|
|
225
|
+
}
|
|
226
|
+
break
|
|
227
|
+
|
|
228
|
+
case ATTR_VALUE_DOUBLE_QUOTED:
|
|
229
|
+
if (ch === '"') {
|
|
230
|
+
commitAttr()
|
|
231
|
+
state = BEFORE_ATTR_NAME
|
|
232
|
+
i++
|
|
233
|
+
} else {
|
|
234
|
+
attrValue += ch
|
|
235
|
+
i++
|
|
236
|
+
}
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
case ATTR_VALUE_SINGLE_QUOTED:
|
|
240
|
+
if (ch === "'") {
|
|
241
|
+
commitAttr()
|
|
242
|
+
state = BEFORE_ATTR_NAME
|
|
243
|
+
i++
|
|
244
|
+
} else {
|
|
245
|
+
attrValue += ch
|
|
246
|
+
i++
|
|
247
|
+
}
|
|
248
|
+
break
|
|
249
|
+
|
|
250
|
+
case ATTR_VALUE_UNQUOTED:
|
|
251
|
+
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f") {
|
|
252
|
+
commitAttr()
|
|
253
|
+
state = BEFORE_ATTR_NAME
|
|
254
|
+
i++
|
|
255
|
+
} else if (ch === ">") {
|
|
256
|
+
commitAttr()
|
|
257
|
+
i++
|
|
258
|
+
emitAndContinue()
|
|
259
|
+
} else {
|
|
260
|
+
attrValue += ch
|
|
261
|
+
i++
|
|
262
|
+
}
|
|
263
|
+
break
|
|
264
|
+
|
|
265
|
+
case SELF_CLOSING:
|
|
266
|
+
if (ch === ">") {
|
|
267
|
+
i++
|
|
268
|
+
emitAndContinue()
|
|
269
|
+
} else {
|
|
270
|
+
state = BEFORE_ATTR_NAME
|
|
271
|
+
}
|
|
272
|
+
break
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (pending) {
|
|
276
|
+
yield pending
|
|
277
|
+
pending = undefined
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface ParsedElement {
|
|
283
|
+
readonly tagName: string
|
|
284
|
+
readonly attributes: ReadonlyMap<string, string>
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function* parse(html: string): Generator<ParsedElement> {
|
|
288
|
+
for (const tok of tokenize(html)) {
|
|
289
|
+
const attributes = new Map<string, string>()
|
|
290
|
+
for (const [name, attr] of tok.attrs) {
|
|
291
|
+
attributes.set(name, attr.value)
|
|
292
|
+
}
|
|
293
|
+
yield { tagName: tok.tag.toLowerCase(), attributes }
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
class MutableElement {
|
|
298
|
+
readonly tagName: string
|
|
299
|
+
private _attrs: Map<string, AttrInternal>
|
|
300
|
+
private _edits: Array<{ offset: number; deleteCount: number; insert: string }>
|
|
301
|
+
private _html: string
|
|
302
|
+
private _end: number
|
|
303
|
+
|
|
304
|
+
constructor(
|
|
305
|
+
tok: TokenizedElement,
|
|
306
|
+
edits: Array<{ offset: number; deleteCount: number; insert: string }>,
|
|
307
|
+
html: string,
|
|
308
|
+
) {
|
|
309
|
+
this.tagName = tok.tag.toLowerCase()
|
|
310
|
+
this._attrs = tok.attrs
|
|
311
|
+
this._edits = edits
|
|
312
|
+
this._html = html
|
|
313
|
+
this._end = tok.end
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
getAttribute(name: string): string | null {
|
|
317
|
+
const attr = this._attrs.get(name.toLowerCase())
|
|
318
|
+
return attr ? attr.value : null
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
hasAttribute(name: string): boolean {
|
|
322
|
+
return this._attrs.has(name.toLowerCase())
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
setAttribute(name: string, value: string): this {
|
|
326
|
+
const lower = name.toLowerCase()
|
|
327
|
+
const attr = this._attrs.get(lower)
|
|
328
|
+
if (attr && attr.valueSpan) {
|
|
329
|
+
this._edits.push({
|
|
330
|
+
offset: attr.valueSpan.start,
|
|
331
|
+
deleteCount: attr.valueSpan.end - attr.valueSpan.start,
|
|
332
|
+
insert: value,
|
|
333
|
+
})
|
|
334
|
+
attr.value = value
|
|
335
|
+
} else if (attr) {
|
|
336
|
+
this._edits.push({
|
|
337
|
+
offset: attr.nameSpan.start,
|
|
338
|
+
deleteCount: attr.nameSpan.end - attr.nameSpan.start,
|
|
339
|
+
insert: `${name}="${value}"`,
|
|
340
|
+
})
|
|
341
|
+
attr.value = value
|
|
342
|
+
attr.valueSpan = { start: -1, end: -1 }
|
|
343
|
+
} else {
|
|
344
|
+
const insertAt = this._end - 1
|
|
345
|
+
this._edits.push({
|
|
346
|
+
offset: insertAt,
|
|
347
|
+
deleteCount: 0,
|
|
348
|
+
insert: ` ${name}="${value}"`,
|
|
349
|
+
})
|
|
350
|
+
this._attrs.set(lower, {
|
|
351
|
+
value,
|
|
352
|
+
nameSpan: { start: -1, end: -1 },
|
|
353
|
+
valueSpan: { start: -1, end: -1 },
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
return this
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
removeAttribute(name: string): this {
|
|
360
|
+
const lower = name.toLowerCase()
|
|
361
|
+
const attr = this._attrs.get(lower)
|
|
362
|
+
if (!attr || attr.nameSpan.start === -1) return this
|
|
363
|
+
|
|
364
|
+
let start = attr.nameSpan.start
|
|
365
|
+
const end = attr.valueSpan
|
|
366
|
+
? attr.valueSpan.end + 1
|
|
367
|
+
: attr.nameSpan.end
|
|
368
|
+
|
|
369
|
+
while (start > 0 && " \t\n\r\f".includes(this._html[start - 1])) {
|
|
370
|
+
start--
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
this._edits.push({ offset: start, deleteCount: end - start, insert: "" })
|
|
374
|
+
this._attrs.delete(lower)
|
|
375
|
+
return this
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
get attributes(): IterableIterator<[string, string]> {
|
|
379
|
+
const entries = this._attrs.entries()
|
|
380
|
+
return (function* () {
|
|
381
|
+
for (const [name, attr] of entries) {
|
|
382
|
+
yield [name, attr.value] as [string, string]
|
|
383
|
+
}
|
|
384
|
+
})()
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
interface RewriteResult extends Iterable<MutableElement> {
|
|
389
|
+
toString(): string
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export const rewrite = (html: string): RewriteResult => {
|
|
393
|
+
const edits: Array<{ offset: number; deleteCount: number; insert: string }> = []
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
*[Symbol.iterator]() {
|
|
397
|
+
for (const tok of tokenize(html)) {
|
|
398
|
+
yield new MutableElement(tok, edits, html)
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
toString() {
|
|
402
|
+
if (edits.length === 0) return html
|
|
403
|
+
|
|
404
|
+
edits.sort((a, b) => b.offset - a.offset)
|
|
405
|
+
let result = html
|
|
406
|
+
for (const edit of edits) {
|
|
407
|
+
result =
|
|
408
|
+
result.slice(0, edit.offset) +
|
|
409
|
+
edit.insert +
|
|
410
|
+
result.slice(edit.offset + edit.deleteCount)
|
|
411
|
+
}
|
|
412
|
+
return result
|
|
413
|
+
},
|
|
414
|
+
}
|
|
415
|
+
}
|
package/src/bun/BunRoute.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type BunDescriptors = {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function descriptors(
|
|
28
|
-
route: Route.Route
|
|
28
|
+
route: Route.Route<any, any, any, any, any>,
|
|
29
29
|
): BunDescriptors | undefined {
|
|
30
30
|
const descriptor = Route.descriptor(route) as Partial<BunDescriptors>
|
|
31
31
|
if (typeof descriptor.bunPrefix === "string" && typeof descriptor.bunLoad === "function") {
|
|
@@ -45,29 +45,29 @@ export function htmlBundle(load: () => HTMLBundleModule | Promise<HTMLBundleModu
|
|
|
45
45
|
const descriptors = { bunPrefix, bunLoad, format: "html" as const }
|
|
46
46
|
|
|
47
47
|
return function <D extends Route.RouteDescriptor.Any, B extends {}, I extends Route.Route.Tuple>(
|
|
48
|
-
self: Route.RouteSet
|
|
49
|
-
): Route.RouteSet
|
|
48
|
+
self: Route.RouteSet<D, B, I>,
|
|
49
|
+
): Route.RouteSet<
|
|
50
50
|
D,
|
|
51
51
|
B,
|
|
52
52
|
[
|
|
53
53
|
...I,
|
|
54
|
-
Route.Route
|
|
54
|
+
Route.Route<
|
|
55
55
|
BunDescriptors & { format: "html" },
|
|
56
|
-
{
|
|
56
|
+
{},
|
|
57
57
|
string,
|
|
58
58
|
BunRouteError,
|
|
59
|
-
BunServer.BunServer
|
|
59
|
+
BunServer.BunServer | Route.Request
|
|
60
60
|
>,
|
|
61
61
|
]
|
|
62
62
|
> {
|
|
63
63
|
const handler: Route.Route.Handler<
|
|
64
|
-
BunDescriptors & { format: "html" }
|
|
64
|
+
BunDescriptors & { format: "html" },
|
|
65
65
|
string,
|
|
66
66
|
BunRouteError,
|
|
67
|
-
BunServer.BunServer
|
|
67
|
+
BunServer.BunServer | Route.Request
|
|
68
68
|
> = (context, next) =>
|
|
69
69
|
Effect.gen(function* () {
|
|
70
|
-
const originalRequest =
|
|
70
|
+
const originalRequest = yield* Route.Request
|
|
71
71
|
const bundleDepth = yield* FiberRef.get(bundleDepthRef)
|
|
72
72
|
|
|
73
73
|
if (originalRequest.headers.get(INTERNAL_FETCH_HEADER) === "true") {
|
|
@@ -142,10 +142,10 @@ export function htmlBundle(load: () => HTMLBundleModule | Promise<HTMLBundleModu
|
|
|
142
142
|
|
|
143
143
|
const route = Route.make<
|
|
144
144
|
BunDescriptors & { format: "html" },
|
|
145
|
-
{
|
|
145
|
+
{},
|
|
146
146
|
string,
|
|
147
147
|
BunRouteError,
|
|
148
|
-
BunServer.BunServer
|
|
148
|
+
BunServer.BunServer | Route.Request
|
|
149
149
|
>(handler, descriptors)
|
|
150
150
|
|
|
151
151
|
return Route.set([...Route.items(self), route] as any, Route.descriptor(self))
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type * as Context from "effect/Context"
|
|
2
2
|
import * as Effect from "effect/Effect"
|
|
3
|
-
import * as Layer from "effect/Layer"
|
|
4
|
-
import * as Option from "effect/Option"
|
|
5
3
|
import * as Entity from "../Entity.ts"
|
|
6
4
|
import * as PathPattern from "../_PathPattern.ts"
|
|
7
5
|
import * as Route from "../Route.ts"
|
|
8
|
-
import * as RouteTree from "../RouteTree.ts"
|
|
9
6
|
import * as Values from "../_Values.ts"
|
|
10
7
|
import * as Bundle from "./Bundle.ts"
|
|
11
8
|
|
|
@@ -23,7 +20,8 @@ export const make = <Tag extends Context.Tag<any, Bundle.BundleContext>>(tag: Ta
|
|
|
23
20
|
Route.get(
|
|
24
21
|
Route.render(function* (ctx) {
|
|
25
22
|
const bundle = yield* tag
|
|
26
|
-
const
|
|
23
|
+
const request = yield* Route.Request
|
|
24
|
+
const url = new URL(request.url)
|
|
27
25
|
const mountPath = (ctx as unknown as { path?: string }).path ?? "/"
|
|
28
26
|
const params = PathPattern.match(mountPath, url.pathname)
|
|
29
27
|
const artifactPath = params ? Values.firstValue(params) : undefined
|
|
@@ -49,18 +47,9 @@ export const client = () => make(Bundle.ClientBundle)
|
|
|
49
47
|
export const layer = (options?: {
|
|
50
48
|
bundle?: Context.Tag<any, Bundle.BundleContext>
|
|
51
49
|
path?: PathPattern.PathPattern
|
|
52
|
-
}) =>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
const path = options?.path ?? "/_bundle/:path+"
|
|
60
|
-
const bundleTree = Route.tree({
|
|
61
|
-
[path]: make(options?.bundle ?? Bundle.ClientBundle),
|
|
62
|
-
})
|
|
63
|
-
if (!existing) return bundleTree
|
|
64
|
-
return RouteTree.merge(existing, bundleTree)
|
|
65
|
-
}),
|
|
66
|
-
)
|
|
50
|
+
}) => {
|
|
51
|
+
const path = options?.path ?? "/_bundle/:path+"
|
|
52
|
+
return Route.layerMerge({
|
|
53
|
+
[path]: make(options?.bundle ?? Bundle.ClientBundle),
|
|
54
|
+
})
|
|
55
|
+
}
|
|
@@ -403,7 +403,7 @@ const removeNode = (node: Node): void => {
|
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
const moveBefore: (parentNode: Node, node: Node, after: Node | null) => void = removeNode.call.bind(
|
|
406
|
-
// @ts-
|
|
406
|
+
// @ts-ignore
|
|
407
407
|
ctxPantry.moveBefore ?? ctxPantry.insertBefore,
|
|
408
408
|
)
|
|
409
409
|
|
package/src/dev.d.ts
ADDED