abret 0.1.1
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 +217 -0
- package/assets/logo.png +0 -0
- package/dist/html.d.ts +62 -0
- package/dist/html.js +586 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.js +161 -0
- package/dist/jsx/index.d.ts +38 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +1 -0
- package/dist/jsx/jsx-dev-runtime.js +65 -0
- package/dist/jsx/jsx-runtime.d.ts +1 -0
- package/dist/jsx/jsx-runtime.js +65 -0
- package/dist/middleware/static/index.d.ts +31 -0
- package/dist/middleware/static/index.js +40 -0
- package/dist/store.d.ts +148 -0
- package/dist/store.js +88 -0
- package/package.json +69 -0
package/dist/html.js
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/store.ts
|
|
3
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
4
|
+
var contextStore = new AsyncLocalStorage;
|
|
5
|
+
function createContext(name, ...args) {
|
|
6
|
+
const [defaultValue] = args;
|
|
7
|
+
const id = Symbol(name);
|
|
8
|
+
if (args.length > 0) {
|
|
9
|
+
const Provider = (props) => {
|
|
10
|
+
return props.children;
|
|
11
|
+
};
|
|
12
|
+
Provider._context = { id, defaultValue };
|
|
13
|
+
return {
|
|
14
|
+
id,
|
|
15
|
+
defaultValue,
|
|
16
|
+
Provider
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
function getContextId(context) {
|
|
22
|
+
if (typeof context === "symbol") {
|
|
23
|
+
return context;
|
|
24
|
+
}
|
|
25
|
+
return context.id;
|
|
26
|
+
}
|
|
27
|
+
function getDefaultValue(context) {
|
|
28
|
+
if (typeof context !== "symbol" && "defaultValue" in context) {
|
|
29
|
+
return context.defaultValue;
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
var setContext = (context, value) => {
|
|
34
|
+
const store = contextStore.getStore();
|
|
35
|
+
if (!store) {
|
|
36
|
+
throw new Error("setContext must be called within a context scope. " + "Ensure you are inside a route handler or use runWithContext.");
|
|
37
|
+
}
|
|
38
|
+
store.set(getContextId(context), value);
|
|
39
|
+
};
|
|
40
|
+
function useContext(context, options) {
|
|
41
|
+
const store = contextStore.getStore();
|
|
42
|
+
const id = getContextId(context);
|
|
43
|
+
const value = store?.get(id);
|
|
44
|
+
if (value !== undefined) {
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
const defaultVal = getDefaultValue(context);
|
|
48
|
+
if (defaultVal !== undefined) {
|
|
49
|
+
return defaultVal;
|
|
50
|
+
}
|
|
51
|
+
if (options?.required) {
|
|
52
|
+
const name = typeof context === "symbol" ? context.description : context.id.description;
|
|
53
|
+
throw new Error(`Context "${name}" is required but not set`);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
var hasContext = (context) => {
|
|
58
|
+
const store = contextStore.getStore();
|
|
59
|
+
return store?.has(getContextId(context)) ?? false;
|
|
60
|
+
};
|
|
61
|
+
var clearContext = (context) => {
|
|
62
|
+
const store = contextStore.getStore();
|
|
63
|
+
if (store) {
|
|
64
|
+
store.delete(getContextId(context));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
function runWithContext(fn) {
|
|
68
|
+
const currentStore = contextStore.getStore();
|
|
69
|
+
const newStore = currentStore ? new Map(currentStore) : new Map;
|
|
70
|
+
return contextStore.run(newStore, fn);
|
|
71
|
+
}
|
|
72
|
+
function runWithContextValue(context, value, fn) {
|
|
73
|
+
const currentStore = contextStore.getStore() || new Map;
|
|
74
|
+
const newStore = new Map(currentStore);
|
|
75
|
+
newStore.set(getContextId(context), value);
|
|
76
|
+
return contextStore.run(newStore, fn);
|
|
77
|
+
}
|
|
78
|
+
var getContextStore = () => contextStore;
|
|
79
|
+
|
|
80
|
+
// src/jsx/index.ts
|
|
81
|
+
class SafeString {
|
|
82
|
+
value;
|
|
83
|
+
constructor(value) {
|
|
84
|
+
this.value = value;
|
|
85
|
+
}
|
|
86
|
+
toString() {
|
|
87
|
+
return this.value;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
class AsyncBuffer extends ReadableStream {
|
|
92
|
+
promise;
|
|
93
|
+
constructor(promise) {
|
|
94
|
+
super({
|
|
95
|
+
async start(controller) {
|
|
96
|
+
try {
|
|
97
|
+
const result = await promise;
|
|
98
|
+
controller.enqueue(new TextEncoder().encode(result.toString()));
|
|
99
|
+
controller.close();
|
|
100
|
+
} catch (e) {
|
|
101
|
+
controller.error(e);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
this.promise = promise;
|
|
106
|
+
}
|
|
107
|
+
then(onfulfilled, onrejected) {
|
|
108
|
+
return this.promise.then(onfulfilled, onrejected);
|
|
109
|
+
}
|
|
110
|
+
catch(onrejected) {
|
|
111
|
+
return this.promise.catch(onrejected);
|
|
112
|
+
}
|
|
113
|
+
finally(onfinally) {
|
|
114
|
+
return this.promise.finally(onfinally);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class VNode {
|
|
119
|
+
tag;
|
|
120
|
+
props;
|
|
121
|
+
children;
|
|
122
|
+
constructor(tag, props, children) {
|
|
123
|
+
this.tag = tag;
|
|
124
|
+
this.props = props;
|
|
125
|
+
this.children = children;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
var Fragment = Symbol("Fragment");
|
|
129
|
+
function jsx(tag, props) {
|
|
130
|
+
const { children, ...rest } = props || {};
|
|
131
|
+
return new VNode(tag, { ...rest, children }, children);
|
|
132
|
+
}
|
|
133
|
+
var jsxs = jsx;
|
|
134
|
+
var jsxDEV = jsx;
|
|
135
|
+
|
|
136
|
+
// src/html.ts
|
|
137
|
+
class HTMLResponse extends Response {
|
|
138
|
+
_bodySource;
|
|
139
|
+
constructor(body, init) {
|
|
140
|
+
let normalizedBody = body;
|
|
141
|
+
if (body instanceof Promise) {
|
|
142
|
+
normalizedBody = new AsyncBuffer(body);
|
|
143
|
+
}
|
|
144
|
+
const headers = new Headers(init?.headers);
|
|
145
|
+
if (!headers.has("Content-Type")) {
|
|
146
|
+
headers.set("Content-Type", "text/html; charset=utf-8");
|
|
147
|
+
}
|
|
148
|
+
super(normalizedBody, { ...init, headers });
|
|
149
|
+
this._bodySource = body;
|
|
150
|
+
}
|
|
151
|
+
init(newInit) {
|
|
152
|
+
const currentHeaders = new Headers(this.headers);
|
|
153
|
+
if (newInit.headers) {
|
|
154
|
+
new Headers(newInit.headers).forEach((v, k) => {
|
|
155
|
+
currentHeaders.set(k, v);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return new HTMLResponse(this._bodySource, {
|
|
159
|
+
status: newInit.status ?? this.status,
|
|
160
|
+
statusText: newInit.statusText ?? this.statusText,
|
|
161
|
+
headers: currentHeaders
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
doctype(dt = true) {
|
|
165
|
+
let prefix = "";
|
|
166
|
+
if (dt === true)
|
|
167
|
+
prefix = "<!DOCTYPE html>";
|
|
168
|
+
else if (typeof dt === "string")
|
|
169
|
+
prefix = dt;
|
|
170
|
+
else
|
|
171
|
+
return this;
|
|
172
|
+
let newBodySource;
|
|
173
|
+
if (this._bodySource instanceof SafeString) {
|
|
174
|
+
newBodySource = raw(prefix + this._bodySource.toString());
|
|
175
|
+
} else if (this._bodySource instanceof Promise || this._bodySource instanceof AsyncBuffer) {
|
|
176
|
+
newBodySource = this._bodySource.then((content) => raw(prefix + content.toString()));
|
|
177
|
+
} else {
|
|
178
|
+
newBodySource = this._bodySource;
|
|
179
|
+
}
|
|
180
|
+
return new HTMLResponse(newBodySource, {
|
|
181
|
+
status: this.status,
|
|
182
|
+
statusText: this.statusText,
|
|
183
|
+
headers: this.headers
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
var MODE_TEXT = 0;
|
|
188
|
+
var MODE_TAGNAME = 1;
|
|
189
|
+
var MODE_WHITESPACE = 2;
|
|
190
|
+
var MODE_PROPNAME = 3;
|
|
191
|
+
var MODE_PROPVAL = 4;
|
|
192
|
+
var MODE_PROPVAL_QUOTE = 5;
|
|
193
|
+
var MODE_CLOSE_TAG = 6;
|
|
194
|
+
function html(bodyOrStrings, ...args) {
|
|
195
|
+
if (Array.isArray(bodyOrStrings) && bodyOrStrings.raw) {
|
|
196
|
+
const vnode = parse(bodyOrStrings, args);
|
|
197
|
+
const rendered2 = render(vnode);
|
|
198
|
+
if (rendered2 instanceof Promise) {
|
|
199
|
+
return new HTMLResponse(rendered2.then((s) => raw(processMetadata(s.toString()))));
|
|
200
|
+
}
|
|
201
|
+
return new HTMLResponse(raw(processMetadata(rendered2.toString())));
|
|
202
|
+
}
|
|
203
|
+
const rendered = render(bodyOrStrings);
|
|
204
|
+
if (rendered instanceof Promise) {
|
|
205
|
+
return new HTMLResponse(rendered.then((s) => raw(processMetadata(s.toString()))));
|
|
206
|
+
}
|
|
207
|
+
return new HTMLResponse(raw(processMetadata(rendered.toString())));
|
|
208
|
+
}
|
|
209
|
+
function parse(statics, fields) {
|
|
210
|
+
let mode = MODE_TEXT;
|
|
211
|
+
let buffer = "";
|
|
212
|
+
let quote = "";
|
|
213
|
+
let char = "";
|
|
214
|
+
let propName;
|
|
215
|
+
const root = { children: [] };
|
|
216
|
+
let active = root;
|
|
217
|
+
const stack = [active];
|
|
218
|
+
for (let i = 0;i < statics.length; i++) {
|
|
219
|
+
if (i) {
|
|
220
|
+
if (mode === MODE_TEXT) {
|
|
221
|
+
commit();
|
|
222
|
+
active.children.push(fields[i - 1]);
|
|
223
|
+
} else if (mode === MODE_TAGNAME) {
|
|
224
|
+
commit();
|
|
225
|
+
const tag = fields[i - 1];
|
|
226
|
+
active = openTag(stack, active, tag);
|
|
227
|
+
mode = MODE_WHITESPACE;
|
|
228
|
+
} else if (mode === MODE_WHITESPACE) {
|
|
229
|
+
const val = fields[i - 1];
|
|
230
|
+
if (buffer === "...") {
|
|
231
|
+
Object.assign(active.props, val);
|
|
232
|
+
buffer = "";
|
|
233
|
+
} else {}
|
|
234
|
+
} else if (mode === MODE_PROPNAME) {
|
|
235
|
+
propName = fields[i - 1];
|
|
236
|
+
mode = MODE_PROPVAL;
|
|
237
|
+
} else if (mode === MODE_PROPVAL) {
|
|
238
|
+
active.props[propName] = fields[i - 1];
|
|
239
|
+
mode = MODE_WHITESPACE;
|
|
240
|
+
} else if (mode === MODE_PROPVAL_QUOTE) {
|
|
241
|
+
buffer += fields[i - 1];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const chunk = statics[i];
|
|
245
|
+
if (!chunk)
|
|
246
|
+
continue;
|
|
247
|
+
for (let j = 0;j < chunk.length; j++) {
|
|
248
|
+
char = chunk[j];
|
|
249
|
+
if (char === undefined)
|
|
250
|
+
continue;
|
|
251
|
+
if (mode === MODE_TEXT) {
|
|
252
|
+
if (char === "<") {
|
|
253
|
+
commit();
|
|
254
|
+
mode = MODE_TAGNAME;
|
|
255
|
+
} else {
|
|
256
|
+
buffer += char;
|
|
257
|
+
}
|
|
258
|
+
} else if (mode === MODE_TAGNAME) {
|
|
259
|
+
if (char === "/" && !buffer) {
|
|
260
|
+
mode = MODE_CLOSE_TAG;
|
|
261
|
+
} else if (char === ">" || char === "/" || /\s/.test(char)) {
|
|
262
|
+
if (buffer) {
|
|
263
|
+
active = openTag(stack, active, buffer);
|
|
264
|
+
}
|
|
265
|
+
if (char === ">")
|
|
266
|
+
mode = MODE_TEXT;
|
|
267
|
+
else if (char === "/") {
|
|
268
|
+
if (stack.length > 1) {
|
|
269
|
+
closeTag(stack);
|
|
270
|
+
active = stack[stack.length - 1];
|
|
271
|
+
}
|
|
272
|
+
} else
|
|
273
|
+
mode = MODE_WHITESPACE;
|
|
274
|
+
buffer = "";
|
|
275
|
+
} else {
|
|
276
|
+
buffer += char;
|
|
277
|
+
}
|
|
278
|
+
} else if (mode === MODE_CLOSE_TAG) {
|
|
279
|
+
if (char === ">") {
|
|
280
|
+
closeTag(stack);
|
|
281
|
+
active = stack[stack.length - 1];
|
|
282
|
+
mode = MODE_TEXT;
|
|
283
|
+
buffer = "";
|
|
284
|
+
}
|
|
285
|
+
} else if (mode === MODE_WHITESPACE) {
|
|
286
|
+
if (char === "/") {
|
|
287
|
+
closeTag(stack);
|
|
288
|
+
active = stack[stack.length - 1];
|
|
289
|
+
} else if (char === ">") {
|
|
290
|
+
mode = MODE_TEXT;
|
|
291
|
+
} else if (!/\s/.test(char)) {
|
|
292
|
+
mode = MODE_PROPNAME;
|
|
293
|
+
buffer = char;
|
|
294
|
+
}
|
|
295
|
+
} else if (mode === MODE_PROPNAME) {
|
|
296
|
+
if (char === "=") {
|
|
297
|
+
propName = buffer;
|
|
298
|
+
buffer = "";
|
|
299
|
+
mode = MODE_PROPVAL;
|
|
300
|
+
} else if (char === ">") {
|
|
301
|
+
active.props[buffer] = true;
|
|
302
|
+
mode = MODE_TEXT;
|
|
303
|
+
buffer = "";
|
|
304
|
+
} else if (/\s/.test(char)) {
|
|
305
|
+
active.props[buffer] = true;
|
|
306
|
+
mode = MODE_WHITESPACE;
|
|
307
|
+
buffer = "";
|
|
308
|
+
} else {
|
|
309
|
+
buffer += char;
|
|
310
|
+
}
|
|
311
|
+
} else if (mode === MODE_PROPVAL) {
|
|
312
|
+
if (char === '"' || char === "'") {
|
|
313
|
+
quote = char;
|
|
314
|
+
mode = MODE_PROPVAL_QUOTE;
|
|
315
|
+
} else if (char === ">") {
|
|
316
|
+
active.props[propName] = buffer;
|
|
317
|
+
mode = MODE_TEXT;
|
|
318
|
+
buffer = "";
|
|
319
|
+
} else if (/\s/.test(char)) {
|
|
320
|
+
active.props[propName] = buffer;
|
|
321
|
+
mode = MODE_WHITESPACE;
|
|
322
|
+
buffer = "";
|
|
323
|
+
} else {
|
|
324
|
+
buffer += char;
|
|
325
|
+
}
|
|
326
|
+
} else if (mode === MODE_PROPVAL_QUOTE) {
|
|
327
|
+
if (char === quote) {
|
|
328
|
+
active.props[propName] = buffer;
|
|
329
|
+
mode = MODE_WHITESPACE;
|
|
330
|
+
buffer = "";
|
|
331
|
+
} else {
|
|
332
|
+
buffer += char;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (mode === MODE_TEXT)
|
|
338
|
+
commit();
|
|
339
|
+
function commit() {
|
|
340
|
+
if (buffer) {
|
|
341
|
+
if (mode === MODE_TEXT) {
|
|
342
|
+
active.children.push(raw(buffer));
|
|
343
|
+
}
|
|
344
|
+
buffer = "";
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function openTag(stack2, current, tag) {
|
|
348
|
+
const newNode = { tag, props: {}, children: [] };
|
|
349
|
+
current.children.push(newNode);
|
|
350
|
+
stack2.push(newNode);
|
|
351
|
+
return newNode;
|
|
352
|
+
}
|
|
353
|
+
function closeTag(stack2) {
|
|
354
|
+
if (stack2.length > 1)
|
|
355
|
+
stack2.pop();
|
|
356
|
+
}
|
|
357
|
+
if (root.children.length === 1)
|
|
358
|
+
return toVNode(root.children[0]);
|
|
359
|
+
return root.children.map(toVNode);
|
|
360
|
+
}
|
|
361
|
+
function toVNode(node) {
|
|
362
|
+
if (node === null || node === undefined)
|
|
363
|
+
return node;
|
|
364
|
+
if (node instanceof SafeString || typeof node !== "object")
|
|
365
|
+
return node;
|
|
366
|
+
if (node instanceof Promise || node instanceof AsyncBuffer)
|
|
367
|
+
return node;
|
|
368
|
+
if (node instanceof VNode)
|
|
369
|
+
return node;
|
|
370
|
+
if (Array.isArray(node))
|
|
371
|
+
return node.map(toVNode);
|
|
372
|
+
if (!node.children || !Array.isArray(node.children)) {
|
|
373
|
+
return node;
|
|
374
|
+
}
|
|
375
|
+
const children = node.children.map(toVNode);
|
|
376
|
+
const props = { ...node.props, children };
|
|
377
|
+
return new VNode(node.tag, props, children);
|
|
378
|
+
}
|
|
379
|
+
function render(node) {
|
|
380
|
+
if (node instanceof HTMLResponse) {
|
|
381
|
+
return render(node._bodySource);
|
|
382
|
+
}
|
|
383
|
+
if (node === null || node === undefined || typeof node === "boolean") {
|
|
384
|
+
return raw("");
|
|
385
|
+
}
|
|
386
|
+
if (node instanceof SafeString)
|
|
387
|
+
return node;
|
|
388
|
+
if (typeof node === "number" || typeof node === "bigint")
|
|
389
|
+
return raw(String(node));
|
|
390
|
+
if (node instanceof Promise || node instanceof AsyncBuffer) {
|
|
391
|
+
return node.then(render);
|
|
392
|
+
}
|
|
393
|
+
if (Array.isArray(node)) {
|
|
394
|
+
const results = node.map(render);
|
|
395
|
+
if (results.some((r) => r instanceof Promise)) {
|
|
396
|
+
return Promise.all(results).then((parts) => raw(parts.map((p) => p.toString()).join("")));
|
|
397
|
+
}
|
|
398
|
+
return raw(results.map((r) => r.toString()).join(""));
|
|
399
|
+
}
|
|
400
|
+
if (node instanceof VNode) {
|
|
401
|
+
if (typeof node.tag === "function") {
|
|
402
|
+
const providerCtx = node.tag._context;
|
|
403
|
+
if (providerCtx) {
|
|
404
|
+
const value = node.props.value;
|
|
405
|
+
const contextStore2 = getContextStore();
|
|
406
|
+
const currentStore = contextStore2.getStore() || new Map;
|
|
407
|
+
const newStore = new Map(currentStore);
|
|
408
|
+
newStore.set(providerCtx.id, value);
|
|
409
|
+
return contextStore2.run(newStore, () => {
|
|
410
|
+
return render(node.props.children);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
let result;
|
|
414
|
+
try {
|
|
415
|
+
result = node.tag(node.props);
|
|
416
|
+
} catch (e) {
|
|
417
|
+
console.error("Error rendering component", e);
|
|
418
|
+
return raw("");
|
|
419
|
+
}
|
|
420
|
+
return render(result);
|
|
421
|
+
}
|
|
422
|
+
if (node.tag === Fragment) {
|
|
423
|
+
return render(node.children);
|
|
424
|
+
}
|
|
425
|
+
if (typeof node.tag === "string") {
|
|
426
|
+
const tag = node.tag;
|
|
427
|
+
const { children, dangerouslySetInnerHTML, ...rest } = node.props;
|
|
428
|
+
const attrs = Object.entries(rest).map(([key, value]) => {
|
|
429
|
+
if (value === null || value === undefined || value === false)
|
|
430
|
+
return "";
|
|
431
|
+
if (key === "className")
|
|
432
|
+
key = "class";
|
|
433
|
+
if (key === "style")
|
|
434
|
+
value = renderStyle(value);
|
|
435
|
+
if (value === true)
|
|
436
|
+
return ` ${key}`;
|
|
437
|
+
return ` ${key}="${escapeHtml(String(value))}"`;
|
|
438
|
+
}).join("");
|
|
439
|
+
if (VOID_TAGS.has(tag)) {
|
|
440
|
+
return raw(`<${tag}${attrs} />`);
|
|
441
|
+
}
|
|
442
|
+
if (dangerouslySetInnerHTML?.__html) {
|
|
443
|
+
return raw(`<${tag}${attrs}>${dangerouslySetInnerHTML.__html}</${tag}>`);
|
|
444
|
+
}
|
|
445
|
+
const childrenList = Array.isArray(children) ? children : [children];
|
|
446
|
+
const renderedChildrenResult = render(childrenList);
|
|
447
|
+
if (renderedChildrenResult instanceof Promise) {
|
|
448
|
+
return renderedChildrenResult.then((c) => raw(`<${tag}${attrs}>${c.toString()}</${tag}>`));
|
|
449
|
+
}
|
|
450
|
+
return raw(`<${tag}${attrs}>${renderedChildrenResult.toString()}</${tag}>`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return raw(escapeHtml(String(node)));
|
|
454
|
+
}
|
|
455
|
+
var VOID_TAGS = new Set([
|
|
456
|
+
"area",
|
|
457
|
+
"base",
|
|
458
|
+
"br",
|
|
459
|
+
"col",
|
|
460
|
+
"embed",
|
|
461
|
+
"hr",
|
|
462
|
+
"img",
|
|
463
|
+
"input",
|
|
464
|
+
"link",
|
|
465
|
+
"meta",
|
|
466
|
+
"param",
|
|
467
|
+
"source",
|
|
468
|
+
"track",
|
|
469
|
+
"wbr"
|
|
470
|
+
]);
|
|
471
|
+
var ESCAPE_REGEX = /[&<>"']/g;
|
|
472
|
+
var ESCAPE_LOOKUP = {
|
|
473
|
+
"&": "&",
|
|
474
|
+
"<": "<",
|
|
475
|
+
">": ">",
|
|
476
|
+
'"': """,
|
|
477
|
+
"'": "'"
|
|
478
|
+
};
|
|
479
|
+
function escapeHtml(str) {
|
|
480
|
+
if (typeof str !== "string")
|
|
481
|
+
return str;
|
|
482
|
+
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
|
483
|
+
}
|
|
484
|
+
function kebabCase(str) {
|
|
485
|
+
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
486
|
+
}
|
|
487
|
+
function renderStyle(style) {
|
|
488
|
+
if (typeof style === "string")
|
|
489
|
+
return style;
|
|
490
|
+
return Object.entries(style).map(([k, v]) => `${kebabCase(k)}:${v}`).join(";");
|
|
491
|
+
}
|
|
492
|
+
function processMetadata(html2) {
|
|
493
|
+
const metaTags = [];
|
|
494
|
+
const linkTags = [];
|
|
495
|
+
let titleTag = null;
|
|
496
|
+
const extractedHtml = html2.replace(/<title(?:\s[^>]*)?>([\s\S]*?)<\/title>|<meta(?:\s[^>]*)?\/?>|<link(?:\s[^>]*)?\/?>/gi, (match) => {
|
|
497
|
+
if (match.toLowerCase().startsWith("<title")) {
|
|
498
|
+
titleTag = match;
|
|
499
|
+
return "";
|
|
500
|
+
}
|
|
501
|
+
if (match.toLowerCase().startsWith("<meta")) {
|
|
502
|
+
const name = match.match(/name=["']([^"']+)["']/i);
|
|
503
|
+
const property = match.match(/property=["']([^"']+)["']/i);
|
|
504
|
+
const charset = match.match(/charset=["']([^"']+)["']/i);
|
|
505
|
+
const httpEquiv = match.match(/http-equiv=["']([^"']+)["']/i);
|
|
506
|
+
let key;
|
|
507
|
+
if (name?.[1])
|
|
508
|
+
key = `name:${name[1]}`;
|
|
509
|
+
else if (property?.[1])
|
|
510
|
+
key = `property:${property[1]}`;
|
|
511
|
+
else if (charset?.[1])
|
|
512
|
+
key = "charset";
|
|
513
|
+
else if (httpEquiv?.[1])
|
|
514
|
+
key = `http-equiv:${httpEquiv[1]}`;
|
|
515
|
+
metaTags.push({ tag: "meta", content: match, key });
|
|
516
|
+
return "";
|
|
517
|
+
}
|
|
518
|
+
if (match.toLowerCase().startsWith("<link")) {
|
|
519
|
+
const rel = match.match(/rel=["']([^"']+)["']/i);
|
|
520
|
+
const key = rel?.[1]?.toLowerCase() === "canonical" ? "canonical" : undefined;
|
|
521
|
+
linkTags.push({ tag: "link", content: match, key });
|
|
522
|
+
return "";
|
|
523
|
+
}
|
|
524
|
+
return match;
|
|
525
|
+
});
|
|
526
|
+
const headContent = [];
|
|
527
|
+
const metaMap = new Map;
|
|
528
|
+
metaTags.forEach((m) => {
|
|
529
|
+
if (m.key)
|
|
530
|
+
metaMap.set(m.key, m.content);
|
|
531
|
+
});
|
|
532
|
+
if (metaMap.has("charset")) {
|
|
533
|
+
const charsetTag = metaMap.get("charset");
|
|
534
|
+
if (charsetTag)
|
|
535
|
+
headContent.push(charsetTag);
|
|
536
|
+
metaMap.delete("charset");
|
|
537
|
+
}
|
|
538
|
+
if (titleTag)
|
|
539
|
+
headContent.push(titleTag);
|
|
540
|
+
if (metaMap.has("name:viewport")) {
|
|
541
|
+
const viewportTag = metaMap.get("name:viewport");
|
|
542
|
+
if (viewportTag)
|
|
543
|
+
headContent.push(viewportTag);
|
|
544
|
+
metaMap.delete("name:viewport");
|
|
545
|
+
}
|
|
546
|
+
metaMap.forEach((v) => {
|
|
547
|
+
headContent.push(v);
|
|
548
|
+
});
|
|
549
|
+
metaTags.filter((m) => !m.key).forEach((m) => {
|
|
550
|
+
headContent.push(m.content);
|
|
551
|
+
});
|
|
552
|
+
const linkMap = new Map;
|
|
553
|
+
linkTags.forEach((l) => {
|
|
554
|
+
if (l.key)
|
|
555
|
+
linkMap.set(l.key, l.content);
|
|
556
|
+
else
|
|
557
|
+
headContent.push(l.content);
|
|
558
|
+
});
|
|
559
|
+
linkMap.forEach((v) => {
|
|
560
|
+
headContent.push(v);
|
|
561
|
+
});
|
|
562
|
+
const headString = headContent.join("");
|
|
563
|
+
if (extractedHtml.includes("<head>")) {
|
|
564
|
+
return extractedHtml.replace("<head>", `<head>${headString}`);
|
|
565
|
+
}
|
|
566
|
+
if (extractedHtml.match(/<html/i)) {
|
|
567
|
+
return extractedHtml.replace(/(<html[^>]*>)/i, `$1<head>${headString}</head>`);
|
|
568
|
+
}
|
|
569
|
+
if (headContent.length > 0) {
|
|
570
|
+
return `<head>${headString}</head>${extractedHtml}`;
|
|
571
|
+
}
|
|
572
|
+
return extractedHtml;
|
|
573
|
+
}
|
|
574
|
+
function raw(str) {
|
|
575
|
+
return new SafeString(str);
|
|
576
|
+
}
|
|
577
|
+
export {
|
|
578
|
+
render,
|
|
579
|
+
raw,
|
|
580
|
+
html,
|
|
581
|
+
VNode,
|
|
582
|
+
SafeString,
|
|
583
|
+
HTMLResponse,
|
|
584
|
+
Fragment,
|
|
585
|
+
AsyncBuffer
|
|
586
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
export { createContext, runWithContext, runWithContextValue, useContext, } from "./store";
|
|
2
|
+
/**
|
|
3
|
+
* Next function type for middleware chain
|
|
4
|
+
*/
|
|
5
|
+
export type NextFunction = () => Response | Promise<Response>;
|
|
6
|
+
/**
|
|
7
|
+
* Middleware function type
|
|
8
|
+
* @param req - The incoming request
|
|
9
|
+
* @param server - The Bun server instance
|
|
10
|
+
* @param next - Function to call the next middleware/handler
|
|
11
|
+
* @returns Response or Promise<Response>
|
|
12
|
+
*/
|
|
13
|
+
export type Middleware<P extends string = string, S = undefined> = (req: Bun.BunRequest<P>, server: Bun.Server<S>, next: NextFunction) => Response | Promise<Response>;
|
|
14
|
+
/**
|
|
15
|
+
* Route handler type - can be a Response, handler function, or method handlers
|
|
16
|
+
*/
|
|
17
|
+
export type RouteValue<P extends string = string, S = undefined> = Bun.Serve.BaseRouteValue | Bun.Serve.Handler<Bun.BunRequest<P>, Bun.Server<S>, Response> | Partial<Record<Bun.Serve.HTTPMethod, Response | Bun.Serve.Handler<Bun.BunRequest<P>, Bun.Server<S>, Response>>>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a route with optional middleware support.
|
|
20
|
+
* Registers the path exactly as defined.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const home = createRoute("/", () => new Response("Hello World"));
|
|
25
|
+
*
|
|
26
|
+
* const api = createRoute(
|
|
27
|
+
* "/api/users",
|
|
28
|
+
* {
|
|
29
|
+
* GET: () => Response.json({ users: [] }),
|
|
30
|
+
* POST: async (req) => Response.json({ created: true }),
|
|
31
|
+
* },
|
|
32
|
+
* authMiddleware,
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const createRoute: <P extends string, S = undefined>(path: P, value: RouteValue<P, S>, ...middlewares: Middleware<P, S>[]) => Record<P, RouteValue<P, S>>;
|
|
37
|
+
/**
|
|
38
|
+
* Helper to create a middleware function with proper typing
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const authMiddleware = createMiddleware(async (req, server, next) => {
|
|
43
|
+
* const token = req.headers.get("Authorization");
|
|
44
|
+
* if (!token) {
|
|
45
|
+
* return new Response("Unauthorized", { status: 401 });
|
|
46
|
+
* }
|
|
47
|
+
* return next();
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const createMiddleware: <P extends string = string, S = undefined>(fn: Middleware<P, S>) => Middleware<P, S>;
|
|
52
|
+
/**
|
|
53
|
+
* Compose multiple middlewares into a single middleware
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const combinedMiddleware = composeMiddlewares(
|
|
58
|
+
* loggingMiddleware,
|
|
59
|
+
* authMiddleware,
|
|
60
|
+
* rateLimitMiddleware
|
|
61
|
+
* );
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare const composeMiddlewares: <P extends string = string, S = undefined>(...middlewares: Middleware<P, S>[]) => Middleware<P, S>;
|
|
65
|
+
/**
|
|
66
|
+
* Type for a single route object returned by createRoute
|
|
67
|
+
* Uses Record<string, any> to allow typed route objects to be merged
|
|
68
|
+
*/
|
|
69
|
+
export type RouteObject = Record<string, any>;
|
|
70
|
+
/**
|
|
71
|
+
* Merges multiple route objects into a single routes object.
|
|
72
|
+
* Preserves type information from each route for proper typing.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const index = createRoute("/", () => new Response("Home"));
|
|
77
|
+
* const api = createRoute("/api", { GET: () => Response.json({ ok: true }) });
|
|
78
|
+
*
|
|
79
|
+
* Bun.serve({
|
|
80
|
+
* routes: mergeRoutes(index, api),
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare const mergeRoutes: <T extends RouteObject[]>(...routes: T) => UnionToIntersection<T[number]>;
|
|
85
|
+
/**
|
|
86
|
+
* Helper type to convert union to intersection
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
90
|
+
/**
|
|
91
|
+
* Creates a route group factory with a common prefix and optional shared middlewares.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* const api = createRouteGroup("/api", [authMiddleware]);
|
|
96
|
+
*
|
|
97
|
+
* const routes = mergeRoutes(
|
|
98
|
+
* api("/users", { GET: () => Response.json([]) }),
|
|
99
|
+
* api("/users/:id", (req) => Response.json({ id: req.params.id })),
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export declare const createRouteGroup: <Prefix extends string, S = undefined>(prefix: Prefix, middlewares?: Middleware<string, S>[]) => <P extends string>(path: P, value: RouteValue<`${Prefix}${P}`, S>) => Record<`${Prefix}${P}`, RouteValue<`${Prefix}${P}`, S>>;
|