@ereo/server 0.2.37 → 0.2.39
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/dist/bun-server.d.ts +4 -0
- package/dist/bun-server.d.ts.map +1 -1
- package/dist/index.js +201 -94
- package/dist/middleware.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/bun-server.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ export declare class BunServer {
|
|
|
90
90
|
private staticHandler;
|
|
91
91
|
private options;
|
|
92
92
|
private wsUpgradeHandlers;
|
|
93
|
+
private fetchHandler;
|
|
93
94
|
constructor(options?: ServerOptions);
|
|
94
95
|
/**
|
|
95
96
|
* Setup default middleware.
|
|
@@ -190,6 +191,8 @@ export declare class BunServer {
|
|
|
190
191
|
/**
|
|
191
192
|
* Build HTML string for meta tags from descriptors.
|
|
192
193
|
*/
|
|
194
|
+
private static readonly ALLOWED_LINK_ATTRS;
|
|
195
|
+
private static escapeAttr;
|
|
193
196
|
private buildMetaHtml;
|
|
194
197
|
/**
|
|
195
198
|
* Inject route meta tags into layout-rendered HTML.
|
|
@@ -239,6 +242,7 @@ export declare class BunServer {
|
|
|
239
242
|
stop(): void;
|
|
240
243
|
/**
|
|
241
244
|
* Reload the server (for HMR).
|
|
245
|
+
* Reuses the full fetch handler to preserve WebSocket upgrade logic.
|
|
242
246
|
*/
|
|
243
247
|
reload(): Promise<void>;
|
|
244
248
|
/**
|
package/dist/bun-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bun-server.d.ts","sourceRoot":"","sources":["../src/bun-server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,KAAK,EAAmE,iBAAiB,EAA0C,MAAM,YAAY,CAAC;AAC7J,OAAO,EAAiC,OAAO,EAAiB,MAAM,YAAY,CAAC;AACnF,OAAO,EAAE,UAAU,EAAwD,MAAM,cAAc,CAAC;AAChG,OAAO,EAIL,IAAI,EACJ,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAA+C,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"bun-server.d.ts","sourceRoot":"","sources":["../src/bun-server.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -28,8 +28,16 @@ class MiddlewareChain {
|
|
|
28
28
|
let index = 0;
|
|
29
29
|
const next = async () => {
|
|
30
30
|
if (index < applicable.length) {
|
|
31
|
-
const
|
|
32
|
-
|
|
31
|
+
const currentIndex = index++;
|
|
32
|
+
const middleware = applicable[currentIndex];
|
|
33
|
+
let called = false;
|
|
34
|
+
return middleware.handler(request, context, async () => {
|
|
35
|
+
if (called) {
|
|
36
|
+
throw new Error("next() called multiple times in middleware");
|
|
37
|
+
}
|
|
38
|
+
called = true;
|
|
39
|
+
return next();
|
|
40
|
+
});
|
|
33
41
|
}
|
|
34
42
|
return final();
|
|
35
43
|
};
|
|
@@ -110,7 +118,15 @@ function cors(options = {}) {
|
|
|
110
118
|
if (responseOrigin) {
|
|
111
119
|
headers.set("Access-Control-Allow-Origin", responseOrigin);
|
|
112
120
|
if (shouldVaryOrigin) {
|
|
113
|
-
headers.
|
|
121
|
+
const existingVary = headers.get("Vary");
|
|
122
|
+
if (existingVary) {
|
|
123
|
+
const parts = existingVary.split(",").map((s) => s.trim().toLowerCase());
|
|
124
|
+
if (!parts.includes("origin")) {
|
|
125
|
+
headers.set("Vary", `${existingVary}, Origin`);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
headers.set("Vary", "Origin");
|
|
129
|
+
}
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
132
|
if (exposedHeaders.length > 0) {
|
|
@@ -208,6 +224,14 @@ function rateLimit(options = {}) {
|
|
|
208
224
|
for (const k of expired) {
|
|
209
225
|
requests.delete(k);
|
|
210
226
|
}
|
|
227
|
+
if (requests.size > MAX_ENTRIES) {
|
|
228
|
+
const entries = Array.from(requests.entries());
|
|
229
|
+
entries.sort((a, b) => a[1].resetTime - b[1].resetTime);
|
|
230
|
+
const toRemove = Math.max(1, Math.floor(entries.length * 0.2));
|
|
231
|
+
for (let i = 0;i < toRemove; i++) {
|
|
232
|
+
requests.delete(entries[i][0]);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
211
235
|
}
|
|
212
236
|
if (record.count > max) {
|
|
213
237
|
return new Response(JSON.stringify({ error: "Too many requests" }), {
|
|
@@ -693,28 +717,40 @@ async function enforceAuthConfig(authConfig, request, context, params) {
|
|
|
693
717
|
}
|
|
694
718
|
|
|
695
719
|
// src/bun-server.ts
|
|
720
|
+
var _cachedRenderer = null;
|
|
721
|
+
var _rendererPromise = null;
|
|
696
722
|
async function getStreamingRenderer() {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
723
|
+
if (_cachedRenderer)
|
|
724
|
+
return _cachedRenderer;
|
|
725
|
+
if (_rendererPromise)
|
|
726
|
+
return _rendererPromise;
|
|
727
|
+
_rendererPromise = (async () => {
|
|
728
|
+
try {
|
|
729
|
+
const browserServer = await import("react-dom/server.browser");
|
|
730
|
+
if (typeof browserServer.renderToReadableStream === "function") {
|
|
731
|
+
_cachedRenderer = {
|
|
732
|
+
renderToReadableStream: browserServer.renderToReadableStream,
|
|
733
|
+
renderToString: browserServer.renderToString
|
|
734
|
+
};
|
|
735
|
+
return _cachedRenderer;
|
|
736
|
+
}
|
|
737
|
+
} catch {}
|
|
738
|
+
try {
|
|
739
|
+
const server = await import("react-dom/server");
|
|
740
|
+
_cachedRenderer = {
|
|
741
|
+
renderToReadableStream: server.renderToReadableStream,
|
|
742
|
+
renderToString: server.renderToString
|
|
703
743
|
};
|
|
744
|
+
return _cachedRenderer;
|
|
745
|
+
} catch {
|
|
746
|
+
_cachedRenderer = {
|
|
747
|
+
renderToReadableStream: undefined,
|
|
748
|
+
renderToString: (await import("react-dom/server")).renderToString
|
|
749
|
+
};
|
|
750
|
+
return _cachedRenderer;
|
|
704
751
|
}
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const server = await import("react-dom/server");
|
|
708
|
-
return {
|
|
709
|
-
renderToReadableStream: server.renderToReadableStream,
|
|
710
|
-
renderToString: server.renderToString
|
|
711
|
-
};
|
|
712
|
-
} catch {
|
|
713
|
-
return {
|
|
714
|
-
renderToReadableStream: undefined,
|
|
715
|
-
renderToString: (await import("react-dom/server")).renderToString
|
|
716
|
-
};
|
|
717
|
-
}
|
|
752
|
+
})();
|
|
753
|
+
return _rendererPromise;
|
|
718
754
|
}
|
|
719
755
|
|
|
720
756
|
class BunServer {
|
|
@@ -728,6 +764,7 @@ class BunServer {
|
|
|
728
764
|
staticHandler = null;
|
|
729
765
|
options;
|
|
730
766
|
wsUpgradeHandlers = [];
|
|
767
|
+
fetchHandler = null;
|
|
731
768
|
constructor(options = {}) {
|
|
732
769
|
this.options = {
|
|
733
770
|
port: 3000,
|
|
@@ -870,7 +907,8 @@ class BunServer {
|
|
|
870
907
|
});
|
|
871
908
|
return context.applyToResponse(response);
|
|
872
909
|
} catch (error) {
|
|
873
|
-
|
|
910
|
+
const errorResponse = this.handleError(error, context);
|
|
911
|
+
return context.applyToResponse(errorResponse);
|
|
874
912
|
}
|
|
875
913
|
}
|
|
876
914
|
async executeRouteMiddleware(request, context, middlewareChain, handler) {
|
|
@@ -905,8 +943,16 @@ class BunServer {
|
|
|
905
943
|
if (index >= middleware.length) {
|
|
906
944
|
return handler();
|
|
907
945
|
}
|
|
908
|
-
const
|
|
909
|
-
|
|
946
|
+
const currentIndex = index++;
|
|
947
|
+
const mw = middleware[currentIndex];
|
|
948
|
+
let called = false;
|
|
949
|
+
return mw(request, context, async () => {
|
|
950
|
+
if (called) {
|
|
951
|
+
throw new Error("next() called multiple times in middleware");
|
|
952
|
+
}
|
|
953
|
+
called = true;
|
|
954
|
+
return next();
|
|
955
|
+
});
|
|
910
956
|
};
|
|
911
957
|
return next();
|
|
912
958
|
}
|
|
@@ -1163,7 +1209,8 @@ class BunServer {
|
|
|
1163
1209
|
const headBytes = encoder.encode(head);
|
|
1164
1210
|
const tailBytes = hasDeferred ? null : encoder.encode(tail);
|
|
1165
1211
|
const abortController = new AbortController;
|
|
1166
|
-
|
|
1212
|
+
const STREAM_TIMEOUT = 1e4;
|
|
1213
|
+
timeoutId = setTimeout(() => abortController.abort(), STREAM_TIMEOUT);
|
|
1167
1214
|
const reactStream = await renderToReadableStream(element, {
|
|
1168
1215
|
bootstrapModules: [clientEntry],
|
|
1169
1216
|
signal: abortController.signal,
|
|
@@ -1184,13 +1231,19 @@ class BunServer {
|
|
|
1184
1231
|
const { done, value } = await reader.read();
|
|
1185
1232
|
if (done) {
|
|
1186
1233
|
if (hasDeferred) {
|
|
1234
|
+
clearTimeout(timeoutId);
|
|
1187
1235
|
let resolvedData;
|
|
1236
|
+
let deferredTimeoutId;
|
|
1188
1237
|
try {
|
|
1189
1238
|
resolvedData = await Promise.race([
|
|
1190
1239
|
resolveAllDeferred2(loaderData),
|
|
1191
|
-
new Promise((_, reject) =>
|
|
1240
|
+
new Promise((_, reject) => {
|
|
1241
|
+
deferredTimeoutId = setTimeout(() => reject(new Error("Deferred data resolution timed out")), STREAM_TIMEOUT);
|
|
1242
|
+
})
|
|
1192
1243
|
]);
|
|
1244
|
+
clearTimeout(deferredTimeoutId);
|
|
1193
1245
|
} catch (error) {
|
|
1246
|
+
clearTimeout(deferredTimeoutId);
|
|
1194
1247
|
console.error("Deferred data resolution failed:", error);
|
|
1195
1248
|
resolvedData = null;
|
|
1196
1249
|
}
|
|
@@ -1263,7 +1316,8 @@ class BunServer {
|
|
|
1263
1316
|
const clientEntry = this.options.clientEntry;
|
|
1264
1317
|
const encoder = new TextEncoder;
|
|
1265
1318
|
const abortController = new AbortController;
|
|
1266
|
-
|
|
1319
|
+
const STREAM_TIMEOUT = 1e4;
|
|
1320
|
+
timeoutId = setTimeout(() => abortController.abort(), STREAM_TIMEOUT);
|
|
1267
1321
|
const reactStream = await renderToReadableStream(element, {
|
|
1268
1322
|
bootstrapModules: [clientEntry],
|
|
1269
1323
|
signal: abortController.signal,
|
|
@@ -1275,6 +1329,7 @@ class BunServer {
|
|
|
1275
1329
|
const metaHtml = metaDescriptors.length > 0 ? this.buildMetaHtml(metaDescriptors) : "";
|
|
1276
1330
|
let metaInjected = metaHtml.length === 0;
|
|
1277
1331
|
const decoder = new TextDecoder;
|
|
1332
|
+
let pendingChunk = "";
|
|
1278
1333
|
const reader = reactStream.getReader();
|
|
1279
1334
|
let done = false;
|
|
1280
1335
|
const self = this;
|
|
@@ -1284,14 +1339,25 @@ class BunServer {
|
|
|
1284
1339
|
return;
|
|
1285
1340
|
const result = await reader.read();
|
|
1286
1341
|
if (result.done) {
|
|
1342
|
+
if (pendingChunk) {
|
|
1343
|
+
controller.enqueue(encoder.encode(pendingChunk));
|
|
1344
|
+
pendingChunk = "";
|
|
1345
|
+
metaInjected = true;
|
|
1346
|
+
}
|
|
1287
1347
|
if (hasDeferred) {
|
|
1348
|
+
clearTimeout(timeoutId);
|
|
1288
1349
|
let resolvedData;
|
|
1350
|
+
let deferredTimeoutId;
|
|
1289
1351
|
try {
|
|
1290
1352
|
resolvedData = await Promise.race([
|
|
1291
1353
|
resolveAllDeferred2(loaderData),
|
|
1292
|
-
new Promise((_, reject) =>
|
|
1354
|
+
new Promise((_, reject) => {
|
|
1355
|
+
deferredTimeoutId = setTimeout(() => reject(new Error("Deferred data resolution timed out")), STREAM_TIMEOUT);
|
|
1356
|
+
})
|
|
1293
1357
|
]);
|
|
1358
|
+
clearTimeout(deferredTimeoutId);
|
|
1294
1359
|
} catch (error) {
|
|
1360
|
+
clearTimeout(deferredTimeoutId);
|
|
1295
1361
|
console.error("Deferred data resolution failed:", error);
|
|
1296
1362
|
resolvedData = null;
|
|
1297
1363
|
}
|
|
@@ -1304,13 +1370,30 @@ class BunServer {
|
|
|
1304
1370
|
done = true;
|
|
1305
1371
|
} else {
|
|
1306
1372
|
if (!metaInjected) {
|
|
1307
|
-
const chunk = decoder.decode(result.value, { stream: true });
|
|
1373
|
+
const chunk = pendingChunk + decoder.decode(result.value, { stream: true });
|
|
1374
|
+
pendingChunk = "";
|
|
1308
1375
|
if (chunk.includes("</head>")) {
|
|
1309
1376
|
const injected = self.injectMetaIntoHtml(chunk, metaDescriptors);
|
|
1310
1377
|
controller.enqueue(encoder.encode(injected));
|
|
1311
1378
|
metaInjected = true;
|
|
1312
1379
|
return;
|
|
1313
1380
|
}
|
|
1381
|
+
const tail = chunk.slice(-6);
|
|
1382
|
+
const headTag = "</head>";
|
|
1383
|
+
let bufferFrom = -1;
|
|
1384
|
+
for (let i = 1;i < headTag.length; i++) {
|
|
1385
|
+
if (chunk.endsWith(headTag.slice(0, i))) {
|
|
1386
|
+
bufferFrom = chunk.length - i;
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
if (bufferFrom >= 0) {
|
|
1391
|
+
controller.enqueue(encoder.encode(chunk.slice(0, bufferFrom)));
|
|
1392
|
+
pendingChunk = chunk.slice(bufferFrom);
|
|
1393
|
+
} else {
|
|
1394
|
+
controller.enqueue(encoder.encode(chunk));
|
|
1395
|
+
}
|
|
1396
|
+
return;
|
|
1314
1397
|
}
|
|
1315
1398
|
controller.enqueue(result.value);
|
|
1316
1399
|
}
|
|
@@ -1423,9 +1506,14 @@ class BunServer {
|
|
|
1423
1506
|
if (method !== "POST") {
|
|
1424
1507
|
return method;
|
|
1425
1508
|
}
|
|
1509
|
+
const headerOverride = request.headers.get("X-HTTP-Method-Override");
|
|
1510
|
+
if (headerOverride) {
|
|
1511
|
+
const normalized = headerOverride.toUpperCase();
|
|
1512
|
+
if (BunServer.METHOD_OVERRIDE_ALLOWED.has(normalized))
|
|
1513
|
+
return normalized;
|
|
1514
|
+
}
|
|
1426
1515
|
const contentType = request.headers.get("Content-Type") || "";
|
|
1427
|
-
|
|
1428
|
-
if (!isFormSubmission) {
|
|
1516
|
+
if (!contentType.includes("application/x-www-form-urlencoded")) {
|
|
1429
1517
|
return method;
|
|
1430
1518
|
}
|
|
1431
1519
|
try {
|
|
@@ -1478,22 +1566,42 @@ class BunServer {
|
|
|
1478
1566
|
}
|
|
1479
1567
|
return tags;
|
|
1480
1568
|
}
|
|
1569
|
+
static ALLOWED_LINK_ATTRS = new Set([
|
|
1570
|
+
"rel",
|
|
1571
|
+
"href",
|
|
1572
|
+
"type",
|
|
1573
|
+
"media",
|
|
1574
|
+
"sizes",
|
|
1575
|
+
"crossorigin",
|
|
1576
|
+
"as",
|
|
1577
|
+
"hreflang",
|
|
1578
|
+
"title",
|
|
1579
|
+
"integrity",
|
|
1580
|
+
"referrerpolicy",
|
|
1581
|
+
"fetchpriority",
|
|
1582
|
+
"imagesizes",
|
|
1583
|
+
"imagesrcset",
|
|
1584
|
+
"disabled"
|
|
1585
|
+
]);
|
|
1586
|
+
static escapeAttr(s) {
|
|
1587
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
1588
|
+
}
|
|
1481
1589
|
buildMetaHtml(meta) {
|
|
1482
1590
|
let html = "";
|
|
1483
1591
|
for (const descriptor of meta) {
|
|
1484
1592
|
if (descriptor.name && descriptor.content) {
|
|
1485
|
-
const name = descriptor.name
|
|
1486
|
-
const content = descriptor.content
|
|
1593
|
+
const name = BunServer.escapeAttr(descriptor.name);
|
|
1594
|
+
const content = BunServer.escapeAttr(descriptor.content);
|
|
1487
1595
|
html += `<meta name="${name}" content="${content}"/>`;
|
|
1488
1596
|
} else if (descriptor.property && descriptor.content) {
|
|
1489
|
-
const property = descriptor.property
|
|
1490
|
-
const content = descriptor.content
|
|
1597
|
+
const property = BunServer.escapeAttr(descriptor.property);
|
|
1598
|
+
const content = BunServer.escapeAttr(descriptor.content);
|
|
1491
1599
|
html += `<meta property="${property}" content="${content}"/>`;
|
|
1492
1600
|
} else if (descriptor["script:ld+json"]) {
|
|
1493
1601
|
const safeJsonLd = String(descriptor["script:ld+json"]).replace(/<\//g, "<\\/");
|
|
1494
1602
|
html += `<script type="application/ld+json">${safeJsonLd}</script>`;
|
|
1495
1603
|
} else if (descriptor.tagName === "link") {
|
|
1496
|
-
const attrs = Object.entries(descriptor).filter(([k]) => k !== "tagName").map(([k, v]) => `${k}="${String(v)
|
|
1604
|
+
const attrs = Object.entries(descriptor).filter(([k]) => k !== "tagName" && BunServer.ALLOWED_LINK_ATTRS.has(k)).map(([k, v]) => `${k}="${BunServer.escapeAttr(String(v))}"`).join(" ");
|
|
1497
1605
|
html += `<link ${attrs}/>`;
|
|
1498
1606
|
}
|
|
1499
1607
|
}
|
|
@@ -1746,70 +1854,69 @@ class BunServer {
|
|
|
1746
1854
|
resolveHandler(ws)?.drain?.(ws);
|
|
1747
1855
|
}
|
|
1748
1856
|
};
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1857
|
+
this.fetchHandler = async (request, server) => {
|
|
1858
|
+
const url = new URL(request.url);
|
|
1859
|
+
if (websocket && url.pathname === "/__hmr") {
|
|
1860
|
+
if (server.upgrade(request, { data: { _wsType: "hmr" } }))
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
if (url.pathname === "/__ereo/trace-ws" && typeof this.options.trace === "object" && this.options.trace.traceWebSocket) {
|
|
1864
|
+
const traceWsType = "__ereo_trace_ws";
|
|
1865
|
+
if (!upgradeHandlers.some((h) => h.path === traceWsType)) {
|
|
1866
|
+
upgradeHandlers.push({
|
|
1867
|
+
path: traceWsType,
|
|
1868
|
+
upgrader: () => true,
|
|
1869
|
+
wsConfig: this.options.trace.traceWebSocket.websocket
|
|
1870
|
+
});
|
|
1757
1871
|
}
|
|
1758
|
-
if (
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
}
|
|
1872
|
+
if (server.upgrade(request, { data: { _wsType: traceWsType } }))
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
const upgradeHeader = request.headers.get("Upgrade");
|
|
1876
|
+
if (upgradeHeader?.toLowerCase() === "websocket") {
|
|
1877
|
+
for (const handler of upgradeHandlers) {
|
|
1878
|
+
if (url.pathname === handler.path) {
|
|
1879
|
+
const data = { _wsType: handler.path, subscriptions: new Map, ctx: {}, originalRequest: request };
|
|
1880
|
+
if (server.upgrade(request, { data }))
|
|
1881
|
+
return;
|
|
1766
1882
|
}
|
|
1767
|
-
if (server.upgrade(request, { data: { _wsType: traceWsType } }))
|
|
1768
|
-
return;
|
|
1769
1883
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1884
|
+
if (this.router && typeof this.router.getRoutes === "function") {
|
|
1885
|
+
const routes = this.router.getRoutes();
|
|
1886
|
+
const matchResult = matchWithLayouts(url.pathname, routes);
|
|
1887
|
+
if (matchResult) {
|
|
1888
|
+
await this.router.loadModule(matchResult.route);
|
|
1889
|
+
const mod = matchResult.route.module;
|
|
1890
|
+
if (mod?.websocket) {
|
|
1891
|
+
const wsType = "route:" + url.pathname;
|
|
1892
|
+
if (!upgradeHandlers.some((h) => h.path === wsType)) {
|
|
1893
|
+
upgradeHandlers.push({
|
|
1894
|
+
path: wsType,
|
|
1895
|
+
upgrader: () => true,
|
|
1896
|
+
wsConfig: mod.websocket
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
if (typeof mod.GET === "function") {
|
|
1900
|
+
return this.handleRequest(request, wsType);
|
|
1901
|
+
}
|
|
1902
|
+
if (server.upgrade(request, { data: { _wsType: wsType } })) {
|
|
1776
1903
|
return;
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
if (this.router && typeof this.router.getRoutes === "function") {
|
|
1780
|
-
const routes = this.router.getRoutes();
|
|
1781
|
-
const matchResult = matchWithLayouts(url.pathname, routes);
|
|
1782
|
-
if (matchResult) {
|
|
1783
|
-
await this.router.loadModule(matchResult.route);
|
|
1784
|
-
const mod = matchResult.route.module;
|
|
1785
|
-
if (mod?.websocket) {
|
|
1786
|
-
const wsType = "route:" + url.pathname;
|
|
1787
|
-
if (!upgradeHandlers.some((h) => h.path === wsType)) {
|
|
1788
|
-
upgradeHandlers.push({
|
|
1789
|
-
path: wsType,
|
|
1790
|
-
upgrader: () => true,
|
|
1791
|
-
wsConfig: mod.websocket
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
if (typeof mod.GET === "function") {
|
|
1795
|
-
return this.handleRequest(request, wsType);
|
|
1796
|
-
}
|
|
1797
|
-
if (server.upgrade(request, { data: { _wsType: wsType } })) {
|
|
1798
|
-
return;
|
|
1799
|
-
}
|
|
1800
|
-
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
1801
1904
|
}
|
|
1905
|
+
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
1802
1906
|
}
|
|
1803
1907
|
}
|
|
1804
1908
|
}
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1909
|
+
}
|
|
1910
|
+
return this.handleRequest(request);
|
|
1911
|
+
};
|
|
1912
|
+
const serverOptions = {
|
|
1913
|
+
port,
|
|
1914
|
+
hostname,
|
|
1915
|
+
fetch: this.fetchHandler,
|
|
1916
|
+
error: (error) => this.handleError(error, createContext(new Request("http://localhost/"))),
|
|
1917
|
+
websocket: mergedWebSocket,
|
|
1918
|
+
tls: tls || undefined
|
|
1808
1919
|
};
|
|
1809
|
-
if (tls) {
|
|
1810
|
-
serverOptions.tls = tls;
|
|
1811
|
-
}
|
|
1812
|
-
serverOptions.websocket = mergedWebSocket;
|
|
1813
1920
|
this.server = Bun.serve(serverOptions);
|
|
1814
1921
|
const protocol = tls ? "https" : "http";
|
|
1815
1922
|
console.log(`Server running at ${protocol}://${hostname}:${port}`);
|
|
@@ -1822,9 +1929,9 @@ class BunServer {
|
|
|
1822
1929
|
}
|
|
1823
1930
|
}
|
|
1824
1931
|
async reload() {
|
|
1825
|
-
if (this.server) {
|
|
1932
|
+
if (this.server && this.fetchHandler) {
|
|
1826
1933
|
this.server.reload({
|
|
1827
|
-
fetch:
|
|
1934
|
+
fetch: this.fetchHandler
|
|
1828
1935
|
});
|
|
1829
1936
|
}
|
|
1830
1937
|
}
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAC;AAG9E;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,WAAW,CAA8B;IAEjD;;OAEG;IACH,GAAG,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IACrC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAUnD;;;;;;;OAOG;IACG,OAAO,CACX,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAC;AAG9E;;;;;;;;;GASG;AACH,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,6EAA6E;IAC7E,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,WAAW,CAA8B;IAEjD;;OAEG;IACH,GAAG,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IACrC,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAUnD;;;;;;;OAOG;IACG,OAAO,CACX,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAgCpB;;OAEG;IACH,OAAO,CAAC,SAAS;CAQlB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CAEvD;AAMD;;GAEG;AACH,wBAAgB,MAAM,IAAI,iBAAiB,CAc1C;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,iBAAiB,CAwFjE;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,qBAAqB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACvC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,KAAK,CAAC;IAC9C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,iBAAiB,CAmCvF;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,iBAAiB,CA+B5C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;CAC7C;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,iBAAiB,CAyE3E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ereo/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.39",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Enoch Kujem Abassey",
|
|
6
6
|
"homepage": "https://ereojs.github.io/ereoJS",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
"typecheck": "tsc --noEmit"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ereo/core": "^0.2.
|
|
36
|
-
"@ereo/client": "^0.2.
|
|
37
|
-
"@ereo/router": "^0.2.
|
|
38
|
-
"@ereo/data": "^0.2.
|
|
35
|
+
"@ereo/core": "^0.2.39",
|
|
36
|
+
"@ereo/client": "^0.2.39",
|
|
37
|
+
"@ereo/router": "^0.2.39",
|
|
38
|
+
"@ereo/data": "^0.2.39"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/bun": "^1.1.0",
|