@kozojs/core 0.2.8 → 0.3.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/lib/chunk-W44TTZNJ.js +205 -0
- package/lib/index.js +63 -1081
- package/lib/middleware/index.js +16 -189
- package/package.json +17 -10
- package/lib/index.d.ts +0 -658
- package/lib/index.js.map +0 -1
- package/lib/middleware/index.d.ts +0 -160
- package/lib/middleware/index.js.map +0 -1
- package/lib/wasm/radix.wasm +0 -0
package/lib/index.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BadRequestError,
|
|
3
|
+
ConflictError,
|
|
4
|
+
ForbiddenError,
|
|
5
|
+
HttpError,
|
|
6
|
+
InternalServerError,
|
|
7
|
+
NotFoundError,
|
|
8
|
+
UnauthorizedError,
|
|
9
|
+
applyFileSystemRouting,
|
|
10
|
+
clearRateLimitStore,
|
|
11
|
+
cors,
|
|
12
|
+
createFileSystemRouting,
|
|
13
|
+
errorHandler,
|
|
14
|
+
logger,
|
|
15
|
+
rateLimit
|
|
16
|
+
} from "./chunk-W44TTZNJ.js";
|
|
17
|
+
|
|
1
18
|
// src/app.ts
|
|
2
19
|
import { Hono } from "hono/quick";
|
|
3
20
|
import { serve } from "@hono/node-server";
|
|
4
|
-
import { createServer } from "http";
|
|
5
21
|
|
|
6
22
|
// src/client-generator.ts
|
|
7
23
|
function generateMethodName(method, path) {
|
|
@@ -388,19 +404,19 @@ var ValidationFailedError = class extends KozoError {
|
|
|
388
404
|
return new Response(JSON.stringify(body), INIT_400);
|
|
389
405
|
}
|
|
390
406
|
};
|
|
391
|
-
var
|
|
407
|
+
var NotFoundError2 = class extends KozoError {
|
|
392
408
|
constructor(message = "Resource Not Found") {
|
|
393
409
|
super(message, 404, "not-found");
|
|
394
410
|
this.name = "NotFoundError";
|
|
395
411
|
}
|
|
396
412
|
};
|
|
397
|
-
var
|
|
413
|
+
var UnauthorizedError2 = class extends KozoError {
|
|
398
414
|
constructor(message = "Unauthorized") {
|
|
399
415
|
super(message, 401, "unauthorized");
|
|
400
416
|
this.name = "UnauthorizedError";
|
|
401
417
|
}
|
|
402
418
|
};
|
|
403
|
-
var
|
|
419
|
+
var ForbiddenError2 = class extends KozoError {
|
|
404
420
|
constructor(message = "Forbidden") {
|
|
405
421
|
super(message, 403, "forbidden");
|
|
406
422
|
this.name = "ForbiddenError";
|
|
@@ -501,11 +517,11 @@ function uwsWrite404(uwsRes) {
|
|
|
501
517
|
});
|
|
502
518
|
}
|
|
503
519
|
function getFreePort() {
|
|
504
|
-
return new Promise((
|
|
520
|
+
return new Promise((resolve, reject) => {
|
|
505
521
|
const srv = netCreateServer();
|
|
506
522
|
srv.listen(0, "0.0.0.0", () => {
|
|
507
523
|
const port = srv.address().port;
|
|
508
|
-
srv.close((err) => err ? reject(err) :
|
|
524
|
+
srv.close((err) => err ? reject(err) : resolve(port));
|
|
509
525
|
});
|
|
510
526
|
});
|
|
511
527
|
}
|
|
@@ -522,7 +538,7 @@ async function createUwsServer(opts) {
|
|
|
522
538
|
const { uws, routes } = opts;
|
|
523
539
|
const port = opts.port === 0 ? await getFreePort() : opts.port;
|
|
524
540
|
const emptyParams = Object.freeze({});
|
|
525
|
-
return new Promise((
|
|
541
|
+
return new Promise((resolve, reject) => {
|
|
526
542
|
const uwsApp = uws.App();
|
|
527
543
|
for (const route of routes) {
|
|
528
544
|
const fn = UWS_METHOD[route.method];
|
|
@@ -590,7 +606,7 @@ async function createUwsServer(opts) {
|
|
|
590
606
|
return;
|
|
591
607
|
}
|
|
592
608
|
listenToken = token;
|
|
593
|
-
|
|
609
|
+
resolve({
|
|
594
610
|
port,
|
|
595
611
|
server: {
|
|
596
612
|
close() {
|
|
@@ -1252,569 +1268,6 @@ function parseNativeQuery(url) {
|
|
|
1252
1268
|
});
|
|
1253
1269
|
return result;
|
|
1254
1270
|
}
|
|
1255
|
-
function readNativeBody(req) {
|
|
1256
|
-
return new Promise((resolve2, reject) => {
|
|
1257
|
-
const chunks = [];
|
|
1258
|
-
req.on("data", (chunk) => chunks.push(chunk));
|
|
1259
|
-
req.on("end", () => {
|
|
1260
|
-
try {
|
|
1261
|
-
const str = Buffer.concat(chunks).toString("utf8");
|
|
1262
|
-
resolve2(str ? JSON.parse(str) : {});
|
|
1263
|
-
} catch {
|
|
1264
|
-
resolve2({});
|
|
1265
|
-
}
|
|
1266
|
-
});
|
|
1267
|
-
req.on("error", reject);
|
|
1268
|
-
});
|
|
1269
|
-
}
|
|
1270
|
-
function compileNativeHandler(handler, schema, services, compiled) {
|
|
1271
|
-
const { validateBody, validateQuery, validateParams, serialize } = compiled;
|
|
1272
|
-
const hasServices = services != null && Object.keys(services).length > 0;
|
|
1273
|
-
const hasQuery = !!validateQuery;
|
|
1274
|
-
const hasParams = !!validateParams;
|
|
1275
|
-
const hasBody = !!validateBody;
|
|
1276
|
-
const hasSer = !!serialize;
|
|
1277
|
-
const handlerIgnoresArgs = handler.length === 0;
|
|
1278
|
-
if (!hasBody && !hasQuery && !hasParams && !hasServices) {
|
|
1279
|
-
if (hasSer) {
|
|
1280
|
-
const ser = serialize;
|
|
1281
|
-
if (handlerIgnoresArgs) {
|
|
1282
|
-
return function native_s1_bare_ser_noargs(req, res, _p) {
|
|
1283
|
-
try {
|
|
1284
|
-
const result = handler();
|
|
1285
|
-
if (result != null && typeof result.then === "function") {
|
|
1286
|
-
result.then(
|
|
1287
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1288
|
-
(err) => fastWriteError(err, res)
|
|
1289
|
-
);
|
|
1290
|
-
return;
|
|
1291
|
-
}
|
|
1292
|
-
fastWriteJson(res, ser(result));
|
|
1293
|
-
} catch (err) {
|
|
1294
|
-
fastWriteError(err, res);
|
|
1295
|
-
}
|
|
1296
|
-
};
|
|
1297
|
-
}
|
|
1298
|
-
return function native_s1_bare_ser(req, res, _p) {
|
|
1299
|
-
try {
|
|
1300
|
-
const result = handler({ req });
|
|
1301
|
-
if (result != null && typeof result.then === "function") {
|
|
1302
|
-
result.then(
|
|
1303
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1304
|
-
(err) => fastWriteError(err, res)
|
|
1305
|
-
);
|
|
1306
|
-
return;
|
|
1307
|
-
}
|
|
1308
|
-
fastWriteJson(res, ser(result));
|
|
1309
|
-
} catch (err) {
|
|
1310
|
-
fastWriteError(err, res);
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
}
|
|
1314
|
-
if (handlerIgnoresArgs) {
|
|
1315
|
-
return function native_s1_bare_noargs(req, res, _p) {
|
|
1316
|
-
try {
|
|
1317
|
-
const result = handler();
|
|
1318
|
-
if (result != null && typeof result.then === "function") {
|
|
1319
|
-
result.then(
|
|
1320
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1321
|
-
(err) => fastWriteError(err, res)
|
|
1322
|
-
);
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1326
|
-
} catch (err) {
|
|
1327
|
-
fastWriteError(err, res);
|
|
1328
|
-
}
|
|
1329
|
-
};
|
|
1330
|
-
}
|
|
1331
|
-
return function native_s1_bare(req, res, _p) {
|
|
1332
|
-
try {
|
|
1333
|
-
const result = handler({ req });
|
|
1334
|
-
if (result != null && typeof result.then === "function") {
|
|
1335
|
-
result.then(
|
|
1336
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1337
|
-
(err) => fastWriteError(err, res)
|
|
1338
|
-
);
|
|
1339
|
-
return;
|
|
1340
|
-
}
|
|
1341
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1342
|
-
} catch (err) {
|
|
1343
|
-
fastWriteError(err, res);
|
|
1344
|
-
}
|
|
1345
|
-
};
|
|
1346
|
-
}
|
|
1347
|
-
if (!hasBody && !hasQuery && !hasParams && hasServices) {
|
|
1348
|
-
if (hasSer) {
|
|
1349
|
-
const ser = serialize;
|
|
1350
|
-
if (handlerIgnoresArgs) {
|
|
1351
|
-
return function native_s1_svc_ser_noargs(req, res, _p) {
|
|
1352
|
-
try {
|
|
1353
|
-
const result = handler();
|
|
1354
|
-
if (result != null && typeof result.then === "function") {
|
|
1355
|
-
result.then(
|
|
1356
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1357
|
-
(err) => fastWriteError(err, res)
|
|
1358
|
-
);
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
fastWriteJson(res, ser(result));
|
|
1362
|
-
} catch (err) {
|
|
1363
|
-
fastWriteError(err, res);
|
|
1364
|
-
}
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
return function native_s1_svc_ser(req, res, _p) {
|
|
1368
|
-
try {
|
|
1369
|
-
const result = handler({ req, services });
|
|
1370
|
-
if (result != null && typeof result.then === "function") {
|
|
1371
|
-
result.then(
|
|
1372
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1373
|
-
(err) => fastWriteError(err, res)
|
|
1374
|
-
);
|
|
1375
|
-
return;
|
|
1376
|
-
}
|
|
1377
|
-
fastWriteJson(res, ser(result));
|
|
1378
|
-
} catch (err) {
|
|
1379
|
-
fastWriteError(err, res);
|
|
1380
|
-
}
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
if (handlerIgnoresArgs) {
|
|
1384
|
-
return function native_s1_svc_noargs(req, res, _p) {
|
|
1385
|
-
try {
|
|
1386
|
-
const result = handler();
|
|
1387
|
-
if (result != null && typeof result.then === "function") {
|
|
1388
|
-
result.then(
|
|
1389
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1390
|
-
(err) => fastWriteError(err, res)
|
|
1391
|
-
);
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1395
|
-
} catch (err) {
|
|
1396
|
-
fastWriteError(err, res);
|
|
1397
|
-
}
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
return function native_s1_svc(req, res, _p) {
|
|
1401
|
-
try {
|
|
1402
|
-
const result = handler({ req, services });
|
|
1403
|
-
if (result != null && typeof result.then === "function") {
|
|
1404
|
-
result.then(
|
|
1405
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1406
|
-
(err) => fastWriteError(err, res)
|
|
1407
|
-
);
|
|
1408
|
-
return;
|
|
1409
|
-
}
|
|
1410
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1411
|
-
} catch (err) {
|
|
1412
|
-
fastWriteError(err, res);
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
}
|
|
1416
|
-
if (hasQuery && !hasParams && !hasBody) {
|
|
1417
|
-
const vq = validateQuery;
|
|
1418
|
-
if (hasSer) {
|
|
1419
|
-
const ser = serialize;
|
|
1420
|
-
if (!hasServices) {
|
|
1421
|
-
return function native_s2a_q_ser(req, res, _p) {
|
|
1422
|
-
try {
|
|
1423
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1424
|
-
if (!vq(query)) {
|
|
1425
|
-
fastWrite400("query", vq.errors, res);
|
|
1426
|
-
return;
|
|
1427
|
-
}
|
|
1428
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query });
|
|
1429
|
-
if (result != null && typeof result.then === "function") {
|
|
1430
|
-
result.then(
|
|
1431
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1432
|
-
(err) => fastWriteError(err, res)
|
|
1433
|
-
);
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
fastWriteJson(res, ser(result));
|
|
1437
|
-
} catch (err) {
|
|
1438
|
-
fastWriteError(err, res);
|
|
1439
|
-
}
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
return function native_s2a_q_svc_ser(req, res, _p) {
|
|
1443
|
-
try {
|
|
1444
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1445
|
-
if (!vq(query)) {
|
|
1446
|
-
fastWrite400("query", vq.errors, res);
|
|
1447
|
-
return;
|
|
1448
|
-
}
|
|
1449
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, services });
|
|
1450
|
-
if (result != null && typeof result.then === "function") {
|
|
1451
|
-
result.then(
|
|
1452
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1453
|
-
(err) => fastWriteError(err, res)
|
|
1454
|
-
);
|
|
1455
|
-
return;
|
|
1456
|
-
}
|
|
1457
|
-
fastWriteJson(res, ser(result));
|
|
1458
|
-
} catch (err) {
|
|
1459
|
-
fastWriteError(err, res);
|
|
1460
|
-
}
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
if (!hasServices) {
|
|
1464
|
-
return function native_s2a_q(req, res, _p) {
|
|
1465
|
-
try {
|
|
1466
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1467
|
-
if (!vq(query)) {
|
|
1468
|
-
fastWrite400("query", vq.errors, res);
|
|
1469
|
-
return;
|
|
1470
|
-
}
|
|
1471
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query });
|
|
1472
|
-
if (result != null && typeof result.then === "function") {
|
|
1473
|
-
result.then(
|
|
1474
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1475
|
-
(err) => fastWriteError(err, res)
|
|
1476
|
-
);
|
|
1477
|
-
return;
|
|
1478
|
-
}
|
|
1479
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1480
|
-
} catch (err) {
|
|
1481
|
-
fastWriteError(err, res);
|
|
1482
|
-
}
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
return function native_s2a_q_svc(req, res, _p) {
|
|
1486
|
-
try {
|
|
1487
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1488
|
-
if (!vq(query)) {
|
|
1489
|
-
fastWrite400("query", vq.errors, res);
|
|
1490
|
-
return;
|
|
1491
|
-
}
|
|
1492
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, services });
|
|
1493
|
-
if (result != null && typeof result.then === "function") {
|
|
1494
|
-
result.then(
|
|
1495
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1496
|
-
(err) => fastWriteError(err, res)
|
|
1497
|
-
);
|
|
1498
|
-
return;
|
|
1499
|
-
}
|
|
1500
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1501
|
-
} catch (err) {
|
|
1502
|
-
fastWriteError(err, res);
|
|
1503
|
-
}
|
|
1504
|
-
};
|
|
1505
|
-
}
|
|
1506
|
-
if (hasParams && !hasQuery && !hasBody) {
|
|
1507
|
-
const vp = validateParams;
|
|
1508
|
-
if (hasSer) {
|
|
1509
|
-
const ser = serialize;
|
|
1510
|
-
if (!hasServices) {
|
|
1511
|
-
return function native_s2b_p_ser(req, res, params) {
|
|
1512
|
-
try {
|
|
1513
|
-
if (!vp(params)) {
|
|
1514
|
-
fastWrite400("params", vp.errors, res);
|
|
1515
|
-
return;
|
|
1516
|
-
}
|
|
1517
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, params });
|
|
1518
|
-
if (result != null && typeof result.then === "function") {
|
|
1519
|
-
result.then(
|
|
1520
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1521
|
-
(err) => fastWriteError(err, res)
|
|
1522
|
-
);
|
|
1523
|
-
return;
|
|
1524
|
-
}
|
|
1525
|
-
fastWriteJson(res, ser(result));
|
|
1526
|
-
} catch (err) {
|
|
1527
|
-
fastWriteError(err, res);
|
|
1528
|
-
}
|
|
1529
|
-
};
|
|
1530
|
-
}
|
|
1531
|
-
return function native_s2b_p_svc_ser(req, res, params) {
|
|
1532
|
-
try {
|
|
1533
|
-
if (!vp(params)) {
|
|
1534
|
-
fastWrite400("params", vp.errors, res);
|
|
1535
|
-
return;
|
|
1536
|
-
}
|
|
1537
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, params, services });
|
|
1538
|
-
if (result != null && typeof result.then === "function") {
|
|
1539
|
-
result.then(
|
|
1540
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1541
|
-
(err) => fastWriteError(err, res)
|
|
1542
|
-
);
|
|
1543
|
-
return;
|
|
1544
|
-
}
|
|
1545
|
-
fastWriteJson(res, ser(result));
|
|
1546
|
-
} catch (err) {
|
|
1547
|
-
fastWriteError(err, res);
|
|
1548
|
-
}
|
|
1549
|
-
};
|
|
1550
|
-
}
|
|
1551
|
-
if (!hasServices) {
|
|
1552
|
-
return function native_s2b_p(req, res, params) {
|
|
1553
|
-
try {
|
|
1554
|
-
if (!vp(params)) {
|
|
1555
|
-
fastWrite400("params", vp.errors, res);
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, params });
|
|
1559
|
-
if (result != null && typeof result.then === "function") {
|
|
1560
|
-
result.then(
|
|
1561
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1562
|
-
(err) => fastWriteError(err, res)
|
|
1563
|
-
);
|
|
1564
|
-
return;
|
|
1565
|
-
}
|
|
1566
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1567
|
-
} catch (err) {
|
|
1568
|
-
fastWriteError(err, res);
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
}
|
|
1572
|
-
return function native_s2b_p_svc(req, res, params) {
|
|
1573
|
-
try {
|
|
1574
|
-
if (!vp(params)) {
|
|
1575
|
-
fastWrite400("params", vp.errors, res);
|
|
1576
|
-
return;
|
|
1577
|
-
}
|
|
1578
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, params, services });
|
|
1579
|
-
if (result != null && typeof result.then === "function") {
|
|
1580
|
-
result.then(
|
|
1581
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1582
|
-
(err) => fastWriteError(err, res)
|
|
1583
|
-
);
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1587
|
-
} catch (err) {
|
|
1588
|
-
fastWriteError(err, res);
|
|
1589
|
-
}
|
|
1590
|
-
};
|
|
1591
|
-
}
|
|
1592
|
-
if (hasQuery && hasParams && !hasBody) {
|
|
1593
|
-
const vq = validateQuery;
|
|
1594
|
-
const vp = validateParams;
|
|
1595
|
-
if (hasSer) {
|
|
1596
|
-
const ser = serialize;
|
|
1597
|
-
if (!hasServices) {
|
|
1598
|
-
return function native_s2c_qp_ser(req, res, params) {
|
|
1599
|
-
try {
|
|
1600
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1601
|
-
if (!vq(query)) {
|
|
1602
|
-
fastWrite400("query", vq.errors, res);
|
|
1603
|
-
return;
|
|
1604
|
-
}
|
|
1605
|
-
if (!vp(params)) {
|
|
1606
|
-
fastWrite400("params", vp.errors, res);
|
|
1607
|
-
return;
|
|
1608
|
-
}
|
|
1609
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, params });
|
|
1610
|
-
if (result != null && typeof result.then === "function") {
|
|
1611
|
-
result.then(
|
|
1612
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1613
|
-
(err) => fastWriteError(err, res)
|
|
1614
|
-
);
|
|
1615
|
-
return;
|
|
1616
|
-
}
|
|
1617
|
-
fastWriteJson(res, ser(result));
|
|
1618
|
-
} catch (err) {
|
|
1619
|
-
fastWriteError(err, res);
|
|
1620
|
-
}
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
return function native_s2c_qp_svc_ser(req, res, params) {
|
|
1624
|
-
try {
|
|
1625
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1626
|
-
if (!vq(query)) {
|
|
1627
|
-
fastWrite400("query", vq.errors, res);
|
|
1628
|
-
return;
|
|
1629
|
-
}
|
|
1630
|
-
if (!vp(params)) {
|
|
1631
|
-
fastWrite400("params", vp.errors, res);
|
|
1632
|
-
return;
|
|
1633
|
-
}
|
|
1634
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, params, services });
|
|
1635
|
-
if (result != null && typeof result.then === "function") {
|
|
1636
|
-
result.then(
|
|
1637
|
-
(r) => fastWriteJson(res, ser(r)),
|
|
1638
|
-
(err) => fastWriteError(err, res)
|
|
1639
|
-
);
|
|
1640
|
-
return;
|
|
1641
|
-
}
|
|
1642
|
-
fastWriteJson(res, ser(result));
|
|
1643
|
-
} catch (err) {
|
|
1644
|
-
fastWriteError(err, res);
|
|
1645
|
-
}
|
|
1646
|
-
};
|
|
1647
|
-
}
|
|
1648
|
-
if (!hasServices) {
|
|
1649
|
-
return function native_s2c_qp(req, res, params) {
|
|
1650
|
-
try {
|
|
1651
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1652
|
-
if (!vq(query)) {
|
|
1653
|
-
fastWrite400("query", vq.errors, res);
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
if (!vp(params)) {
|
|
1657
|
-
fastWrite400("params", vp.errors, res);
|
|
1658
|
-
return;
|
|
1659
|
-
}
|
|
1660
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, params });
|
|
1661
|
-
if (result != null && typeof result.then === "function") {
|
|
1662
|
-
result.then(
|
|
1663
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1664
|
-
(err) => fastWriteError(err, res)
|
|
1665
|
-
);
|
|
1666
|
-
return;
|
|
1667
|
-
}
|
|
1668
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1669
|
-
} catch (err) {
|
|
1670
|
-
fastWriteError(err, res);
|
|
1671
|
-
}
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
return function native_s2c_qp_svc(req, res, params) {
|
|
1675
|
-
try {
|
|
1676
|
-
const query = parseNativeQuery(req.url ?? "/");
|
|
1677
|
-
if (!vq(query)) {
|
|
1678
|
-
fastWrite400("query", vq.errors, res);
|
|
1679
|
-
return;
|
|
1680
|
-
}
|
|
1681
|
-
if (!vp(params)) {
|
|
1682
|
-
fastWrite400("params", vp.errors, res);
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
const result = handlerIgnoresArgs ? handler() : handler({ req, query, params, services });
|
|
1686
|
-
if (result != null && typeof result.then === "function") {
|
|
1687
|
-
result.then(
|
|
1688
|
-
(r) => fastWriteJson(res, toJsonBody(r)),
|
|
1689
|
-
(err) => fastWriteError(err, res)
|
|
1690
|
-
);
|
|
1691
|
-
return;
|
|
1692
|
-
}
|
|
1693
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1694
|
-
} catch (err) {
|
|
1695
|
-
fastWriteError(err, res);
|
|
1696
|
-
}
|
|
1697
|
-
};
|
|
1698
|
-
}
|
|
1699
|
-
if (hasBody && hasSer) {
|
|
1700
|
-
const vb = validateBody;
|
|
1701
|
-
const ser = serialize;
|
|
1702
|
-
const vq = validateQuery;
|
|
1703
|
-
const vp = validateParams;
|
|
1704
|
-
if (!hasServices) {
|
|
1705
|
-
return async function native_s3_body_ser(req, res, params) {
|
|
1706
|
-
try {
|
|
1707
|
-
const body = await readNativeBody(req);
|
|
1708
|
-
if (!vb(body)) {
|
|
1709
|
-
fastWrite400("body", vb.errors, res);
|
|
1710
|
-
return;
|
|
1711
|
-
}
|
|
1712
|
-
let query;
|
|
1713
|
-
if (vq) {
|
|
1714
|
-
query = parseNativeQuery(req.url ?? "/");
|
|
1715
|
-
if (!vq(query)) {
|
|
1716
|
-
fastWrite400("query", vq.errors, res);
|
|
1717
|
-
return;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
if (vp && !vp(params)) {
|
|
1721
|
-
fastWrite400("params", vp.errors, res);
|
|
1722
|
-
return;
|
|
1723
|
-
}
|
|
1724
|
-
const result = await handler({ req, body, query, params });
|
|
1725
|
-
fastWriteJson(res, ser(result));
|
|
1726
|
-
} catch (err) {
|
|
1727
|
-
fastWriteError(err, res);
|
|
1728
|
-
}
|
|
1729
|
-
};
|
|
1730
|
-
}
|
|
1731
|
-
return async function native_s3_body_ser_svc(req, res, params) {
|
|
1732
|
-
try {
|
|
1733
|
-
const body = await readNativeBody(req);
|
|
1734
|
-
if (!vb(body)) {
|
|
1735
|
-
fastWrite400("body", vb.errors, res);
|
|
1736
|
-
return;
|
|
1737
|
-
}
|
|
1738
|
-
let query;
|
|
1739
|
-
if (vq) {
|
|
1740
|
-
query = parseNativeQuery(req.url ?? "/");
|
|
1741
|
-
if (!vq(query)) {
|
|
1742
|
-
fastWrite400("query", vq.errors, res);
|
|
1743
|
-
return;
|
|
1744
|
-
}
|
|
1745
|
-
}
|
|
1746
|
-
if (vp && !vp(params)) {
|
|
1747
|
-
fastWrite400("params", vp.errors, res);
|
|
1748
|
-
return;
|
|
1749
|
-
}
|
|
1750
|
-
const result = await handler({ req, body, query, params, services });
|
|
1751
|
-
fastWriteJson(res, ser(result));
|
|
1752
|
-
} catch (err) {
|
|
1753
|
-
fastWriteError(err, res);
|
|
1754
|
-
}
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
if (hasBody && !hasSer) {
|
|
1758
|
-
const vb = validateBody;
|
|
1759
|
-
const vq = validateQuery;
|
|
1760
|
-
const vp = validateParams;
|
|
1761
|
-
if (!hasServices) {
|
|
1762
|
-
return async function native_s4_body_noser(req, res, params) {
|
|
1763
|
-
try {
|
|
1764
|
-
const body = await readNativeBody(req);
|
|
1765
|
-
if (!vb(body)) {
|
|
1766
|
-
fastWrite400("body", vb.errors, res);
|
|
1767
|
-
return;
|
|
1768
|
-
}
|
|
1769
|
-
let query;
|
|
1770
|
-
if (vq) {
|
|
1771
|
-
query = parseNativeQuery(req.url ?? "/");
|
|
1772
|
-
if (!vq(query)) {
|
|
1773
|
-
fastWrite400("query", vq.errors, res);
|
|
1774
|
-
return;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
if (vp && !vp(params)) {
|
|
1778
|
-
fastWrite400("params", vp.errors, res);
|
|
1779
|
-
return;
|
|
1780
|
-
}
|
|
1781
|
-
const result = await handler({ req, body, query, params });
|
|
1782
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1783
|
-
} catch (err) {
|
|
1784
|
-
fastWriteError(err, res);
|
|
1785
|
-
}
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
return async function native_s4_body_noser_svc(req, res, params) {
|
|
1789
|
-
try {
|
|
1790
|
-
const body = await readNativeBody(req);
|
|
1791
|
-
if (!vb(body)) {
|
|
1792
|
-
fastWrite400("body", vb.errors, res);
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
let query;
|
|
1796
|
-
if (vq) {
|
|
1797
|
-
query = parseNativeQuery(req.url ?? "/");
|
|
1798
|
-
if (!vq(query)) {
|
|
1799
|
-
fastWrite400("query", vq.errors, res);
|
|
1800
|
-
return;
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
if (vp && !vp(params)) {
|
|
1804
|
-
fastWrite400("params", vp.errors, res);
|
|
1805
|
-
return;
|
|
1806
|
-
}
|
|
1807
|
-
const result = await handler({ req, body, query, params, services });
|
|
1808
|
-
fastWriteJson(res, toJsonBody(result));
|
|
1809
|
-
} catch (err) {
|
|
1810
|
-
fastWriteError(err, res);
|
|
1811
|
-
}
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
return function native_fallback(req, res, _p) {
|
|
1815
|
-
fastWriteError(new Error("Route scenario not implemented"), res);
|
|
1816
|
-
};
|
|
1817
|
-
}
|
|
1818
1271
|
function compileUwsNativeHandler(handler, schema, services, compiled) {
|
|
1819
1272
|
const { validateBody, validateQuery, validateParams, serialize } = compiled;
|
|
1820
1273
|
const hasServices = services != null && Object.keys(services).length > 0;
|
|
@@ -2422,8 +1875,8 @@ function createInflightTracker() {
|
|
|
2422
1875
|
function trackRequest(tracker) {
|
|
2423
1876
|
tracker.count++;
|
|
2424
1877
|
let resolvePromise;
|
|
2425
|
-
const promise = new Promise((
|
|
2426
|
-
resolvePromise =
|
|
1878
|
+
const promise = new Promise((resolve) => {
|
|
1879
|
+
resolvePromise = resolve;
|
|
2427
1880
|
});
|
|
2428
1881
|
tracker.requests.add(promise);
|
|
2429
1882
|
return () => {
|
|
@@ -2518,10 +1971,10 @@ var ShutdownManager = class {
|
|
|
2518
1971
|
});
|
|
2519
1972
|
}
|
|
2520
1973
|
const drainPromise = this.drainRequests();
|
|
2521
|
-
const timeoutPromise = new Promise((
|
|
1974
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
2522
1975
|
setTimeout(() => {
|
|
2523
1976
|
onShutdownTimeout?.(this.tracker.count);
|
|
2524
|
-
|
|
1977
|
+
resolve();
|
|
2525
1978
|
}, timeoutMs);
|
|
2526
1979
|
});
|
|
2527
1980
|
await Promise.race([drainPromise, timeoutPromise]);
|
|
@@ -2701,192 +2154,14 @@ function routeScore(path) {
|
|
|
2701
2154
|
return score;
|
|
2702
2155
|
}
|
|
2703
2156
|
|
|
2704
|
-
// src/wasm-router.ts
|
|
2705
|
-
import { readFile } from "fs/promises";
|
|
2706
|
-
import { fileURLToPath } from "url";
|
|
2707
|
-
import { dirname, join as join2 } from "path";
|
|
2708
|
-
var METHOD_INDEX = {
|
|
2709
|
-
GET: 0,
|
|
2710
|
-
POST: 1,
|
|
2711
|
-
PUT: 2,
|
|
2712
|
-
PATCH: 3,
|
|
2713
|
-
DELETE: 4,
|
|
2714
|
-
OPTIONS: 5,
|
|
2715
|
-
HEAD: 6
|
|
2716
|
-
};
|
|
2717
|
-
function methodToIndex(m) {
|
|
2718
|
-
switch (m.length) {
|
|
2719
|
-
case 3:
|
|
2720
|
-
return m.charCodeAt(0) === 71 ? 0 : 2;
|
|
2721
|
-
case 4:
|
|
2722
|
-
return m.charCodeAt(0) === 80 ? 1 : 6;
|
|
2723
|
-
case 5:
|
|
2724
|
-
return 3;
|
|
2725
|
-
case 6:
|
|
2726
|
-
return 4;
|
|
2727
|
-
case 7:
|
|
2728
|
-
return 5;
|
|
2729
|
-
default:
|
|
2730
|
-
return METHOD_INDEX[m] ?? -1;
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
var WasmRadixRouter = class {
|
|
2734
|
-
exports = null;
|
|
2735
|
-
urlView = null;
|
|
2736
|
-
paramView = null;
|
|
2737
|
-
patternView = null;
|
|
2738
|
-
// v2: plain arrays indexed by route_id — O(1) with no hash overhead
|
|
2739
|
-
handlers = [];
|
|
2740
|
-
paramNames = [];
|
|
2741
|
-
/** monotonic route counter */
|
|
2742
|
-
nextRouteId = 0;
|
|
2743
|
-
/** whether the WASM module loaded successfully */
|
|
2744
|
-
ready = false;
|
|
2745
|
-
// v3: pre-allocated match result — reused across calls to avoid allocation.
|
|
2746
|
-
// Safe because dispatch() reads result synchronously and never stores it.
|
|
2747
|
-
_emptyParams = Object.freeze({});
|
|
2748
|
-
// ── Lifecycle ─────────────────────────────────────────────────────────
|
|
2749
|
-
/**
|
|
2750
|
-
* Attempt to load and instantiate the WASM module.
|
|
2751
|
-
* Returns `true` on success, `false` if the .wasm file is missing or
|
|
2752
|
-
* instantiation fails (caller should fall back to JS routing).
|
|
2753
|
-
*/
|
|
2754
|
-
async init() {
|
|
2755
|
-
try {
|
|
2756
|
-
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
2757
|
-
const wasmPath = join2(thisDir, "wasm", "radix.wasm");
|
|
2758
|
-
const wasmBytes = await readFile(wasmPath);
|
|
2759
|
-
const { instance } = await WebAssembly.instantiate(wasmBytes);
|
|
2760
|
-
this.exports = instance.exports;
|
|
2761
|
-
const mem = this.exports.memory;
|
|
2762
|
-
const urlPtr = this.exports.get_url_buf_ptr();
|
|
2763
|
-
const paramPtr = this.exports.get_param_buf_ptr();
|
|
2764
|
-
const patPtr = this.exports.get_pattern_buf_ptr();
|
|
2765
|
-
this.urlView = new Uint8Array(mem.buffer, urlPtr, 4096);
|
|
2766
|
-
this.paramView = new DataView(mem.buffer, paramPtr, 32);
|
|
2767
|
-
this.patternView = new Uint8Array(mem.buffer, patPtr, 2048);
|
|
2768
|
-
this.exports.init();
|
|
2769
|
-
this.ready = true;
|
|
2770
|
-
return true;
|
|
2771
|
-
} catch {
|
|
2772
|
-
this.ready = false;
|
|
2773
|
-
return false;
|
|
2774
|
-
}
|
|
2775
|
-
}
|
|
2776
|
-
/** Is the WASM module loaded and operational? */
|
|
2777
|
-
get isReady() {
|
|
2778
|
-
return this.ready;
|
|
2779
|
-
}
|
|
2780
|
-
// ── Route registration (called at startup, not hot-path) ──────────────
|
|
2781
|
-
/**
|
|
2782
|
-
* Register a route in the WASM radix trie.
|
|
2783
|
-
*
|
|
2784
|
-
* @param method HTTP method (GET, POST, …)
|
|
2785
|
-
* @param path Express-style pattern, e.g. `/api/users/:id` or `/blog/*`
|
|
2786
|
-
* @param handler Compiled native handler (same type used by nativeListen)
|
|
2787
|
-
* @returns The assigned route ID
|
|
2788
|
-
*/
|
|
2789
|
-
addRoute(method, path, handler) {
|
|
2790
|
-
if (!this.ready) throw new Error("WasmRadixRouter not initialized");
|
|
2791
|
-
const routeId = this.nextRouteId++;
|
|
2792
|
-
const methodIdx = METHOD_INDEX[method.toUpperCase()];
|
|
2793
|
-
if (methodIdx === void 0) throw new Error(`Unsupported method: ${method}`);
|
|
2794
|
-
const names = [];
|
|
2795
|
-
const segments = path.split("/");
|
|
2796
|
-
for (const seg of segments) {
|
|
2797
|
-
if (seg.startsWith(":")) names.push(seg.slice(1));
|
|
2798
|
-
else if (seg === "*") names.push("*");
|
|
2799
|
-
}
|
|
2800
|
-
this.paramNames[routeId] = names;
|
|
2801
|
-
this.handlers[routeId] = handler;
|
|
2802
|
-
const patLen = this.writeAscii(this.patternView, path);
|
|
2803
|
-
this.exports.insert_route(methodIdx, patLen, routeId);
|
|
2804
|
-
return routeId;
|
|
2805
|
-
}
|
|
2806
|
-
// ── Hot-path matching ─────────────────────────────────────────────────
|
|
2807
|
-
/**
|
|
2808
|
-
* Match a request path against the radix trie.
|
|
2809
|
-
*
|
|
2810
|
-
* ZERO-COPY: the path string is written byte-by-byte into WASM memory
|
|
2811
|
-
* without allocating a Buffer or Uint8Array. Param values are read
|
|
2812
|
-
* back as slices of the **original JS string** (the offsets produced by
|
|
2813
|
-
* WASM coincide with JS character indices for ASCII paths).
|
|
2814
|
-
*
|
|
2815
|
-
* v3: Returns a pre-allocated result object that is reused across calls.
|
|
2816
|
-
* For static routes (paramCount === 0), the params object is a frozen
|
|
2817
|
-
* singleton — ZERO allocations per request on the hot path.
|
|
2818
|
-
*
|
|
2819
|
-
* @returns `null` on miss; otherwise the handler + extracted params.
|
|
2820
|
-
*/
|
|
2821
|
-
match(method, path) {
|
|
2822
|
-
const methodIdx = methodToIndex(method);
|
|
2823
|
-
if (methodIdx === -1) return null;
|
|
2824
|
-
const urlView = this.urlView;
|
|
2825
|
-
const len = path.length;
|
|
2826
|
-
for (let i = 0; i < len; i++) {
|
|
2827
|
-
urlView[i] = path.charCodeAt(i);
|
|
2828
|
-
}
|
|
2829
|
-
const routeId = this.exports.match_url(methodIdx, len);
|
|
2830
|
-
if (routeId === -1) return null;
|
|
2831
|
-
const handler = this.handlers[routeId];
|
|
2832
|
-
if (!handler) return null;
|
|
2833
|
-
const paramCount = this.exports.get_param_count();
|
|
2834
|
-
if (paramCount === 0) {
|
|
2835
|
-
return { handler, params: this._emptyParams };
|
|
2836
|
-
}
|
|
2837
|
-
const names = this.paramNames[routeId];
|
|
2838
|
-
const params = {};
|
|
2839
|
-
const pv = this.paramView;
|
|
2840
|
-
for (let i = 0; i < paramCount && i < names.length; i++) {
|
|
2841
|
-
const byteOff = i * 4;
|
|
2842
|
-
const offset = pv.getUint16(byteOff, true);
|
|
2843
|
-
const plen = pv.getUint16(byteOff + 2, true);
|
|
2844
|
-
params[names[i]] = path.slice(offset, offset + plen);
|
|
2845
|
-
}
|
|
2846
|
-
return { handler, params };
|
|
2847
|
-
}
|
|
2848
|
-
// ── Exposed internals for inline dispatch in app.ts ────────────────────
|
|
2849
|
-
/**
|
|
2850
|
-
* Expose raw WASM buffers + handler arrays for zero-overhead inline
|
|
2851
|
-
* dispatch in app.ts. Avoids the match() → WasmMatchResult wrapper
|
|
2852
|
-
* allocation on dynamic routes (~30-50 ns saved per request).
|
|
2853
|
-
*
|
|
2854
|
-
* SAFETY: single-threaded — no concurrent access.
|
|
2855
|
-
*/
|
|
2856
|
-
getInternals() {
|
|
2857
|
-
return {
|
|
2858
|
-
urlView: this.urlView,
|
|
2859
|
-
exports: this.exports,
|
|
2860
|
-
handlers: this.handlers,
|
|
2861
|
-
paramNames: this.paramNames,
|
|
2862
|
-
paramView: this.paramView,
|
|
2863
|
-
emptyParams: this._emptyParams
|
|
2864
|
-
};
|
|
2865
|
-
}
|
|
2866
|
-
// ── Internals ─────────────────────────────────────────────────────────
|
|
2867
|
-
/**
|
|
2868
|
-
* Fast ASCII write — avoids TextEncoder allocation overhead (~50 ns).
|
|
2869
|
-
* URL paths are always ASCII, so charCodeAt() is byte-identical.
|
|
2870
|
-
*/
|
|
2871
|
-
writeAscii(view, str) {
|
|
2872
|
-
const len = str.length;
|
|
2873
|
-
for (let i = 0; i < len; i++) {
|
|
2874
|
-
view[i] = str.charCodeAt(i);
|
|
2875
|
-
}
|
|
2876
|
-
return len;
|
|
2877
|
-
}
|
|
2878
|
-
};
|
|
2879
|
-
|
|
2880
2157
|
// src/app.ts
|
|
2881
2158
|
var Kozo = class {
|
|
2882
2159
|
app;
|
|
2883
2160
|
services;
|
|
2884
2161
|
routes = [];
|
|
2885
|
-
|
|
2162
|
+
/** Routes registered directly with the uWS C++ radix router. */
|
|
2163
|
+
uwsRoutes = [];
|
|
2886
2164
|
shutdownManager = new ShutdownManager();
|
|
2887
|
-
wasmRouter = new WasmRadixRouter();
|
|
2888
|
-
/** Maps each NativeRouteHandler to its zero-shim uWS counterpart */
|
|
2889
|
-
uwsHandlerMap = /* @__PURE__ */ new Map();
|
|
2890
2165
|
_routesDir;
|
|
2891
2166
|
constructor(config = {}) {
|
|
2892
2167
|
this.app = new Hono();
|
|
@@ -2925,8 +2200,15 @@ var Kozo = class {
|
|
|
2925
2200
|
this.services,
|
|
2926
2201
|
compiled
|
|
2927
2202
|
);
|
|
2928
|
-
this.routes.push({ method, path, schema });
|
|
2203
|
+
this.routes.push({ method, path, schema, meta: module.meta });
|
|
2929
2204
|
this.app[method](path, optimizedHandler);
|
|
2205
|
+
const paramNames = [];
|
|
2206
|
+
path.replace(/:([^/]+)/g, (_, name) => {
|
|
2207
|
+
paramNames.push(name);
|
|
2208
|
+
return name;
|
|
2209
|
+
});
|
|
2210
|
+
const uwsHandler = compileUwsNativeHandler((ctx) => userHandler(ctx), schema, this.services, compiled);
|
|
2211
|
+
this.uwsRoutes.push({ method: method.toUpperCase(), path, paramNames, handler: uwsHandler });
|
|
2930
2212
|
}
|
|
2931
2213
|
return this;
|
|
2932
2214
|
}
|
|
@@ -2964,147 +2246,37 @@ var Kozo = class {
|
|
|
2964
2246
|
compiled
|
|
2965
2247
|
);
|
|
2966
2248
|
this.app[method](path, optimizedHandler);
|
|
2967
|
-
const nativeHandler = compileNativeHandler(handler, schema, this.services, compiled);
|
|
2968
|
-
const uwsHandler = compileUwsNativeHandler(handler, schema, this.services, compiled);
|
|
2969
|
-
this.uwsHandlerMap.set(nativeHandler, uwsHandler);
|
|
2970
2249
|
const paramNames = [];
|
|
2971
|
-
|
|
2250
|
+
path.replace(/:([^/]+)/g, (_, name) => {
|
|
2972
2251
|
paramNames.push(name);
|
|
2973
|
-
return
|
|
2252
|
+
return name;
|
|
2974
2253
|
});
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
} else {
|
|
2978
|
-
this.nativeRoutes.push({
|
|
2979
|
-
method: method.toUpperCase(),
|
|
2980
|
-
path,
|
|
2981
|
-
regex: new RegExp(`^${regexStr}$`),
|
|
2982
|
-
paramNames,
|
|
2983
|
-
handler: nativeHandler
|
|
2984
|
-
});
|
|
2985
|
-
}
|
|
2254
|
+
const uwsHandler = compileUwsNativeHandler(handler, schema, this.services, compiled);
|
|
2255
|
+
this.uwsRoutes.push({ method: method.toUpperCase(), path, paramNames, handler: uwsHandler });
|
|
2986
2256
|
return this;
|
|
2987
2257
|
}
|
|
2988
2258
|
/**
|
|
2989
|
-
* Start a
|
|
2990
|
-
* Writes directly to ServerResponse — eliminates Web API Request/Response
|
|
2991
|
-
* allocation and adapter overhead for maximum throughput.
|
|
2992
|
-
*
|
|
2993
|
-
* When the Zig WASM radix trie is available (`src/wasm/radix.wasm`),
|
|
2994
|
-
* ALL route matching (static + dynamic) runs through the zero-copy
|
|
2995
|
-
* trie — one call into WASM per request.
|
|
2259
|
+
* Start a uWebSockets.js HTTP server.
|
|
2996
2260
|
*
|
|
2997
|
-
*
|
|
2998
|
-
*
|
|
2261
|
+
* All routes are registered directly with uWS's C++ radix trie router —
|
|
2262
|
+
* zero JS routing overhead per request. The C++ HTTP parser (µHttpParser)
|
|
2263
|
+
* eliminates all IncomingMessage/ServerResponse allocations.
|
|
2999
2264
|
*
|
|
3000
|
-
*
|
|
2265
|
+
* Throws if uWebSockets.js is not installed.
|
|
2266
|
+
* Returns { port, server } so callers can close the server when done.
|
|
3001
2267
|
*/
|
|
3002
2268
|
async nativeListen(port = 3e3) {
|
|
3003
|
-
const wasmOk = await this.wasmRouter.init();
|
|
3004
|
-
if (wasmOk) {
|
|
3005
|
-
for (const entry of this.nativeRoutes) {
|
|
3006
|
-
const path = entry.staticPath ?? entry.regex?.source.replace(/^\^/, "").replace(/\$$/, "").replace(/\(\[\\^\/\]\+\)/g, () => `:${entry.paramNames?.shift() ?? "p"}`) ?? "/";
|
|
3007
|
-
this.wasmRouter.addRoute(entry.method, entry.staticPath ?? path, entry.handler);
|
|
3008
|
-
}
|
|
3009
|
-
console.log("\u26A1 WASM radix router active (zero-copy Zig trie)");
|
|
3010
|
-
} else {
|
|
3011
|
-
console.log("\u2139\uFE0F WASM radix router unavailable \u2014 using JS fallback");
|
|
3012
|
-
}
|
|
3013
|
-
const staticMap = /* @__PURE__ */ new Map();
|
|
3014
|
-
const dynamicRoutes = [];
|
|
3015
|
-
for (const entry of this.nativeRoutes) {
|
|
3016
|
-
if (entry.staticPath !== void 0) {
|
|
3017
|
-
staticMap.set(`${entry.method}:${entry.staticPath}`, entry.handler);
|
|
3018
|
-
} else {
|
|
3019
|
-
dynamicRoutes.push({
|
|
3020
|
-
method: entry.method,
|
|
3021
|
-
regex: entry.regex,
|
|
3022
|
-
paramNames: entry.paramNames,
|
|
3023
|
-
handler: entry.handler
|
|
3024
|
-
});
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
const wasm = wasmOk ? this.wasmRouter : null;
|
|
3028
|
-
const emptyParams = Object.freeze({});
|
|
3029
|
-
const matchRoute = (method, path) => {
|
|
3030
|
-
if (wasm) {
|
|
3031
|
-
const r = wasm.match(method, path);
|
|
3032
|
-
if (r) return { handler: r.handler, params: r.params };
|
|
3033
|
-
return null;
|
|
3034
|
-
}
|
|
3035
|
-
const staticHandler = staticMap.get(`${method}:${path}`);
|
|
3036
|
-
if (staticHandler) return { handler: staticHandler, params: emptyParams };
|
|
3037
|
-
for (const route of dynamicRoutes) {
|
|
3038
|
-
if (route.method !== method) continue;
|
|
3039
|
-
const m = route.regex.exec(path);
|
|
3040
|
-
if (!m) continue;
|
|
3041
|
-
const params = {};
|
|
3042
|
-
for (let i = 0; i < route.paramNames.length; i++) {
|
|
3043
|
-
params[route.paramNames[i]] = m[i + 1];
|
|
3044
|
-
}
|
|
3045
|
-
return { handler: route.handler, params };
|
|
3046
|
-
}
|
|
3047
|
-
return null;
|
|
3048
|
-
};
|
|
3049
2269
|
const uwsBindings = await tryLoadUws();
|
|
3050
|
-
if (uwsBindings) {
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
for (const entry of this.nativeRoutes) {
|
|
3055
|
-
const uwsH = uwsMap.get(entry.handler);
|
|
3056
|
-
if (!uwsH) continue;
|
|
3057
|
-
uwsRoutes.push({
|
|
3058
|
-
method: entry.method,
|
|
3059
|
-
path: entry.path,
|
|
3060
|
-
paramNames: entry.paramNames ?? [],
|
|
3061
|
-
handler: uwsH
|
|
3062
|
-
});
|
|
3063
|
-
}
|
|
3064
|
-
return createUwsServer({
|
|
3065
|
-
uws: uwsBindings,
|
|
3066
|
-
routes: uwsRoutes,
|
|
3067
|
-
port
|
|
3068
|
-
});
|
|
2270
|
+
if (!uwsBindings) {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
"[Kozo] uWebSockets.js is required but not installed.\nRun: pnpm add uWebSockets.js"
|
|
2273
|
+
);
|
|
3069
2274
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
if (wasm) {
|
|
3076
|
-
const result = wasm.match(method, path);
|
|
3077
|
-
if (result) {
|
|
3078
|
-
result.handler(req, res, result.params);
|
|
3079
|
-
return;
|
|
3080
|
-
}
|
|
3081
|
-
fastWrite404(res);
|
|
3082
|
-
return;
|
|
3083
|
-
}
|
|
3084
|
-
const staticHandler = staticMap.get(`${method}:${path}`);
|
|
3085
|
-
if (staticHandler) {
|
|
3086
|
-
staticHandler(req, res, emptyParams);
|
|
3087
|
-
return;
|
|
3088
|
-
}
|
|
3089
|
-
for (const route of dynamicRoutes) {
|
|
3090
|
-
if (route.method !== method) continue;
|
|
3091
|
-
const match = route.regex.exec(path);
|
|
3092
|
-
if (!match) continue;
|
|
3093
|
-
const params = {};
|
|
3094
|
-
for (let i = 0; i < route.paramNames.length; i++) {
|
|
3095
|
-
params[route.paramNames[i]] = match[i + 1];
|
|
3096
|
-
}
|
|
3097
|
-
route.handler(req, res, params);
|
|
3098
|
-
return;
|
|
3099
|
-
}
|
|
3100
|
-
fastWrite404(res);
|
|
3101
|
-
};
|
|
3102
|
-
return new Promise((resolve2) => {
|
|
3103
|
-
const server = createServer(dispatch);
|
|
3104
|
-
server.listen(port, () => {
|
|
3105
|
-
const addr = server.address();
|
|
3106
|
-
resolve2({ port: addr.port, server });
|
|
3107
|
-
});
|
|
2275
|
+
console.log("\u{1F680} uWebSockets.js transport active (C++ HTTP parser + native radix router)");
|
|
2276
|
+
return createUwsServer({
|
|
2277
|
+
uws: uwsBindings,
|
|
2278
|
+
routes: this.uwsRoutes,
|
|
2279
|
+
port
|
|
3108
2280
|
});
|
|
3109
2281
|
}
|
|
3110
2282
|
async listen(port) {
|
|
@@ -3543,212 +2715,23 @@ function generateSwaggerHtml(specUrl, title = "API Documentation") {
|
|
|
3543
2715
|
function createOpenAPIGenerator(config) {
|
|
3544
2716
|
return new OpenAPIGenerator(config);
|
|
3545
2717
|
}
|
|
3546
|
-
|
|
3547
|
-
// src/middleware/logger.ts
|
|
3548
|
-
function logger(options = {}) {
|
|
3549
|
-
const { prefix = "\u{1F310}", colorize = true } = options;
|
|
3550
|
-
return async (c, next) => {
|
|
3551
|
-
const start = Date.now();
|
|
3552
|
-
const method = c.req.method;
|
|
3553
|
-
const path = new URL(c.req.url).pathname;
|
|
3554
|
-
await next();
|
|
3555
|
-
const duration = Date.now() - start;
|
|
3556
|
-
const status = c.res.status;
|
|
3557
|
-
const statusColor = status >= 500 ? "\u{1F534}" : status >= 400 ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
3558
|
-
const log = `${prefix} ${method.padEnd(6)} ${path} ${statusColor} ${status} ${duration}ms`;
|
|
3559
|
-
console.log(log);
|
|
3560
|
-
};
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
|
-
// src/middleware/cors.ts
|
|
3564
|
-
import { cors as honoCors } from "hono/cors";
|
|
3565
|
-
function cors(options = {}) {
|
|
3566
|
-
return honoCors({
|
|
3567
|
-
origin: options.origin || "*",
|
|
3568
|
-
allowMethods: options.allowMethods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
3569
|
-
allowHeaders: options.allowHeaders || ["Content-Type", "Authorization"],
|
|
3570
|
-
exposeHeaders: options.exposeHeaders || [],
|
|
3571
|
-
maxAge: options.maxAge || 86400,
|
|
3572
|
-
credentials: options.credentials || false
|
|
3573
|
-
});
|
|
3574
|
-
}
|
|
3575
|
-
|
|
3576
|
-
// src/middleware/rate-limit.ts
|
|
3577
|
-
var store = /* @__PURE__ */ new Map();
|
|
3578
|
-
function rateLimit(options) {
|
|
3579
|
-
const {
|
|
3580
|
-
max = 100,
|
|
3581
|
-
window = 60,
|
|
3582
|
-
keyGenerator = (c) => c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip") ?? "anonymous",
|
|
3583
|
-
message = "Too many requests"
|
|
3584
|
-
} = options;
|
|
3585
|
-
return async (c, next) => {
|
|
3586
|
-
const key = keyGenerator(c);
|
|
3587
|
-
const now = Date.now();
|
|
3588
|
-
const windowMs = window * 1e3;
|
|
3589
|
-
let record = store.get(key);
|
|
3590
|
-
if (!record || now > record.resetAt) {
|
|
3591
|
-
record = { count: 0, resetAt: now + windowMs };
|
|
3592
|
-
}
|
|
3593
|
-
record.count++;
|
|
3594
|
-
store.set(key, record);
|
|
3595
|
-
c.header("X-RateLimit-Limit", String(max));
|
|
3596
|
-
c.header("X-RateLimit-Remaining", String(Math.max(0, max - record.count)));
|
|
3597
|
-
c.header("X-RateLimit-Reset", String(Math.ceil(record.resetAt / 1e3)));
|
|
3598
|
-
if (record.count > max) {
|
|
3599
|
-
return c.json({ error: message }, 429);
|
|
3600
|
-
}
|
|
3601
|
-
await next();
|
|
3602
|
-
};
|
|
3603
|
-
}
|
|
3604
|
-
function clearRateLimitStore() {
|
|
3605
|
-
store.clear();
|
|
3606
|
-
}
|
|
3607
|
-
|
|
3608
|
-
// src/middleware/error-handler.ts
|
|
3609
|
-
var HttpError = class extends Error {
|
|
3610
|
-
constructor(statusCode, message, details) {
|
|
3611
|
-
super(message);
|
|
3612
|
-
this.statusCode = statusCode;
|
|
3613
|
-
this.details = details;
|
|
3614
|
-
this.name = "HttpError";
|
|
3615
|
-
}
|
|
3616
|
-
};
|
|
3617
|
-
var BadRequestError = class extends HttpError {
|
|
3618
|
-
constructor(message = "Bad Request", details) {
|
|
3619
|
-
super(400, message, details);
|
|
3620
|
-
}
|
|
3621
|
-
};
|
|
3622
|
-
var UnauthorizedError2 = class extends HttpError {
|
|
3623
|
-
constructor(message = "Unauthorized") {
|
|
3624
|
-
super(401, message);
|
|
3625
|
-
}
|
|
3626
|
-
};
|
|
3627
|
-
var ForbiddenError2 = class extends HttpError {
|
|
3628
|
-
constructor(message = "Forbidden") {
|
|
3629
|
-
super(403, message);
|
|
3630
|
-
}
|
|
3631
|
-
};
|
|
3632
|
-
var NotFoundError2 = class extends HttpError {
|
|
3633
|
-
constructor(message = "Not Found") {
|
|
3634
|
-
super(404, message);
|
|
3635
|
-
}
|
|
3636
|
-
};
|
|
3637
|
-
var ConflictError = class extends HttpError {
|
|
3638
|
-
constructor(message = "Conflict", details) {
|
|
3639
|
-
super(409, message, details);
|
|
3640
|
-
}
|
|
3641
|
-
};
|
|
3642
|
-
var InternalServerError = class extends HttpError {
|
|
3643
|
-
constructor(message = "Internal Server Error") {
|
|
3644
|
-
super(500, message);
|
|
3645
|
-
}
|
|
3646
|
-
};
|
|
3647
|
-
function errorHandler() {
|
|
3648
|
-
return async (c, next) => {
|
|
3649
|
-
try {
|
|
3650
|
-
await next();
|
|
3651
|
-
} catch (err) {
|
|
3652
|
-
if (err instanceof HttpError) {
|
|
3653
|
-
return c.json({
|
|
3654
|
-
error: err.message,
|
|
3655
|
-
status: err.statusCode,
|
|
3656
|
-
...err.details ? { details: err.details } : {}
|
|
3657
|
-
}, err.statusCode);
|
|
3658
|
-
}
|
|
3659
|
-
console.error("Unhandled error:", err);
|
|
3660
|
-
return c.json({
|
|
3661
|
-
error: "Internal Server Error",
|
|
3662
|
-
status: 500
|
|
3663
|
-
}, 500);
|
|
3664
|
-
}
|
|
3665
|
-
};
|
|
3666
|
-
}
|
|
3667
|
-
|
|
3668
|
-
// src/middleware/fileSystemRouting.ts
|
|
3669
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
3670
|
-
import { resolve } from "path";
|
|
3671
|
-
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
3672
|
-
async function readManifest(manifestPath, onMissing) {
|
|
3673
|
-
try {
|
|
3674
|
-
const raw = await readFile2(manifestPath, "utf-8");
|
|
3675
|
-
return JSON.parse(raw);
|
|
3676
|
-
} catch (err) {
|
|
3677
|
-
onMissing(err instanceof Error ? err : new Error(String(err)));
|
|
3678
|
-
return null;
|
|
3679
|
-
}
|
|
3680
|
-
}
|
|
3681
|
-
async function importHandler(handlerPath) {
|
|
3682
|
-
try {
|
|
3683
|
-
const url = handlerPath.startsWith("file://") ? handlerPath : pathToFileURL2(handlerPath).href;
|
|
3684
|
-
const mod = await import(url);
|
|
3685
|
-
if (typeof mod.default !== "function") {
|
|
3686
|
-
console.warn(
|
|
3687
|
-
`[kozo:fsr] Skipping ${handlerPath}: no default export function`
|
|
3688
|
-
);
|
|
3689
|
-
return null;
|
|
3690
|
-
}
|
|
3691
|
-
return mod.default;
|
|
3692
|
-
} catch (err) {
|
|
3693
|
-
console.warn(
|
|
3694
|
-
`[kozo:fsr] Failed to import handler ${handlerPath}:`,
|
|
3695
|
-
err.message
|
|
3696
|
-
);
|
|
3697
|
-
return null;
|
|
3698
|
-
}
|
|
3699
|
-
}
|
|
3700
|
-
async function applyFileSystemRouting(app, options = {}) {
|
|
3701
|
-
const {
|
|
3702
|
-
manifestPath = resolve(process.cwd(), "routes-manifest.json"),
|
|
3703
|
-
verbose = false,
|
|
3704
|
-
onMissingManifest = () => {
|
|
3705
|
-
},
|
|
3706
|
-
logger: logger2 = console.log
|
|
3707
|
-
} = options;
|
|
3708
|
-
const manifest = await readManifest(manifestPath, onMissingManifest);
|
|
3709
|
-
if (!manifest) return;
|
|
3710
|
-
const log = logger2;
|
|
3711
|
-
if (verbose) {
|
|
3712
|
-
log(
|
|
3713
|
-
`
|
|
3714
|
-
\u{1F4CB} [kozo:fsr] Loading ${manifest.routes.length} route(s) from manifest
|
|
3715
|
-
`
|
|
3716
|
-
);
|
|
3717
|
-
}
|
|
3718
|
-
for (const route of manifest.routes) {
|
|
3719
|
-
const handler = await importHandler(route.handler);
|
|
3720
|
-
if (!handler) continue;
|
|
3721
|
-
app[route.method](route.path, handler);
|
|
3722
|
-
if (verbose) {
|
|
3723
|
-
log(
|
|
3724
|
-
` ${route.method.toUpperCase().padEnd(6)} ${route.path} \u2192 ${route.handler}`
|
|
3725
|
-
);
|
|
3726
|
-
}
|
|
3727
|
-
}
|
|
3728
|
-
if (verbose) {
|
|
3729
|
-
log("");
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
function createFileSystemRouting(options = {}) {
|
|
3733
|
-
return (app) => applyFileSystemRouting(app, options);
|
|
3734
|
-
}
|
|
3735
2718
|
export {
|
|
3736
2719
|
ERROR_RESPONSES,
|
|
3737
|
-
ForbiddenError,
|
|
2720
|
+
ForbiddenError2 as ForbiddenError,
|
|
3738
2721
|
BadRequestError as HttpBadRequestError,
|
|
3739
2722
|
ConflictError as HttpConflictError,
|
|
3740
2723
|
HttpError,
|
|
3741
|
-
|
|
2724
|
+
ForbiddenError as HttpForbiddenError,
|
|
3742
2725
|
InternalServerError as HttpInternalServerError,
|
|
3743
|
-
|
|
3744
|
-
|
|
2726
|
+
NotFoundError as HttpNotFoundError,
|
|
2727
|
+
UnauthorizedError as HttpUnauthorizedError,
|
|
3745
2728
|
Kozo,
|
|
3746
2729
|
KozoError,
|
|
3747
|
-
NotFoundError,
|
|
2730
|
+
NotFoundError2 as NotFoundError,
|
|
3748
2731
|
OpenAPIGenerator,
|
|
3749
2732
|
SchemaCompiler,
|
|
3750
2733
|
ShutdownManager,
|
|
3751
|
-
UnauthorizedError,
|
|
2734
|
+
UnauthorizedError2 as UnauthorizedError,
|
|
3752
2735
|
ValidationFailedError,
|
|
3753
2736
|
applyFileSystemRouting,
|
|
3754
2737
|
buildNativeContext,
|
|
@@ -3784,4 +2767,3 @@ export {
|
|
|
3784
2767
|
validationErrorResponse,
|
|
3785
2768
|
z
|
|
3786
2769
|
};
|
|
3787
|
-
//# sourceMappingURL=index.js.map
|