@dangao/bun-server 0.4.0 → 1.0.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/dist/cache/cache-module.d.ts +9 -0
- package/dist/cache/cache-module.d.ts.map +1 -0
- package/dist/cache/decorators.d.ts +110 -0
- package/dist/cache/decorators.d.ts.map +1 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/service.d.ts +76 -0
- package/dist/cache/service.d.ts.map +1 -0
- package/dist/cache/types.d.ts +160 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/controller/decorators.d.ts +9 -1
- package/dist/controller/decorators.d.ts.map +1 -1
- package/dist/controller/param-binder.d.ts +4 -1
- package/dist/controller/param-binder.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1123 -25
- package/dist/middleware/pipeline.d.ts.map +1 -1
- package/dist/queue/decorators.d.ts +63 -0
- package/dist/queue/decorators.d.ts.map +1 -0
- package/dist/queue/index.d.ts +6 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/queue-module.d.ts +9 -0
- package/dist/queue/queue-module.d.ts.map +1 -0
- package/dist/queue/service.d.ts +93 -0
- package/dist/queue/service.d.ts.map +1 -0
- package/dist/queue/types.d.ts +204 -0
- package/dist/queue/types.d.ts.map +1 -0
- package/dist/router/router.d.ts +16 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/session/decorators.d.ts +16 -0
- package/dist/session/decorators.d.ts.map +1 -0
- package/dist/session/index.d.ts +8 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/middleware.d.ts +8 -0
- package/dist/session/middleware.d.ts.map +1 -0
- package/dist/session/service.d.ts +86 -0
- package/dist/session/service.d.ts.map +1 -0
- package/dist/session/session-module.d.ts +9 -0
- package/dist/session/session-module.d.ts.map +1 -0
- package/dist/session/types.d.ts +193 -0
- package/dist/session/types.d.ts.map +1 -0
- package/package.json +1 -1
- package/readme.md +98 -117
package/dist/index.js
CHANGED
|
@@ -969,21 +969,23 @@ class MiddlewarePipeline {
|
|
|
969
969
|
if (length === 0) {
|
|
970
970
|
return await finalHandler();
|
|
971
971
|
}
|
|
972
|
+
let currentIndex = 0;
|
|
972
973
|
const called = new Array(length).fill(false);
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
if (called[i]) {
|
|
974
|
+
const createNext = (index) => {
|
|
975
|
+
if (index >= length) {
|
|
976
|
+
return finalHandler;
|
|
977
|
+
}
|
|
978
|
+
return async () => {
|
|
979
|
+
if (called[index]) {
|
|
980
980
|
throw new Error("next() called multiple times");
|
|
981
981
|
}
|
|
982
|
-
called[
|
|
983
|
-
|
|
982
|
+
called[index] = true;
|
|
983
|
+
currentIndex = index + 1;
|
|
984
|
+
const middleware = this.middlewares[index];
|
|
985
|
+
return await middleware(context, createNext(index + 1));
|
|
984
986
|
};
|
|
985
|
-
}
|
|
986
|
-
return await
|
|
987
|
+
};
|
|
988
|
+
return await createNext(0)();
|
|
987
989
|
}
|
|
988
990
|
}
|
|
989
991
|
|
|
@@ -1053,6 +1055,7 @@ class Router {
|
|
|
1053
1055
|
routes = [];
|
|
1054
1056
|
staticRoutes = new Map;
|
|
1055
1057
|
dynamicRoutes = [];
|
|
1058
|
+
matchCache = new Map;
|
|
1056
1059
|
normalizePath(path) {
|
|
1057
1060
|
if (path.length > 1 && path.endsWith("/")) {
|
|
1058
1061
|
return path.slice(0, -1);
|
|
@@ -1069,6 +1072,7 @@ class Router {
|
|
|
1069
1072
|
} else {
|
|
1070
1073
|
this.dynamicRoutes.push(route);
|
|
1071
1074
|
}
|
|
1075
|
+
this.matchCache.clear();
|
|
1072
1076
|
}
|
|
1073
1077
|
get(path, handler, middlewares = []) {
|
|
1074
1078
|
this.register("GET", path, handler, middlewares);
|
|
@@ -1086,14 +1090,29 @@ class Router {
|
|
|
1086
1090
|
this.register("PATCH", path, handler, middlewares);
|
|
1087
1091
|
}
|
|
1088
1092
|
findRoute(method, path) {
|
|
1089
|
-
const
|
|
1093
|
+
const result = this.findRouteWithMatch(method, path);
|
|
1094
|
+
return result?.route;
|
|
1095
|
+
}
|
|
1096
|
+
findRouteWithMatch(method, path) {
|
|
1097
|
+
const normalizedPath = this.normalizePath(path);
|
|
1098
|
+
const cacheKey = `${method}:${normalizedPath}`;
|
|
1099
|
+
const cached = this.matchCache.get(cacheKey);
|
|
1100
|
+
if (cached && cached.match.matched) {
|
|
1101
|
+
return cached;
|
|
1102
|
+
}
|
|
1103
|
+
const staticRoute = this.staticRoutes.get(cacheKey);
|
|
1090
1104
|
if (staticRoute) {
|
|
1091
|
-
|
|
1105
|
+
const match = { matched: true, params: {} };
|
|
1106
|
+
const result = { route: staticRoute, match };
|
|
1107
|
+
this.matchCache.set(cacheKey, result);
|
|
1108
|
+
return result;
|
|
1092
1109
|
}
|
|
1093
1110
|
for (const route of this.dynamicRoutes) {
|
|
1094
|
-
const match = route.match(method,
|
|
1111
|
+
const match = route.match(method, normalizedPath);
|
|
1095
1112
|
if (match.matched) {
|
|
1096
|
-
|
|
1113
|
+
const result = { route, match };
|
|
1114
|
+
this.matchCache.set(cacheKey, result);
|
|
1115
|
+
return result;
|
|
1097
1116
|
}
|
|
1098
1117
|
}
|
|
1099
1118
|
return;
|
|
@@ -1101,11 +1120,11 @@ class Router {
|
|
|
1101
1120
|
async preHandle(context) {
|
|
1102
1121
|
const method = context.method;
|
|
1103
1122
|
const path = this.normalizePath(context.path);
|
|
1104
|
-
const
|
|
1105
|
-
if (!
|
|
1123
|
+
const result = this.findRouteWithMatch(method, path);
|
|
1124
|
+
if (!result) {
|
|
1106
1125
|
return;
|
|
1107
1126
|
}
|
|
1108
|
-
const match =
|
|
1127
|
+
const { route, match } = result;
|
|
1109
1128
|
if (match.matched) {
|
|
1110
1129
|
context.params = match.params;
|
|
1111
1130
|
}
|
|
@@ -1117,13 +1136,22 @@ class Router {
|
|
|
1117
1136
|
}
|
|
1118
1137
|
}
|
|
1119
1138
|
async handle(context) {
|
|
1120
|
-
await this.preHandle(context);
|
|
1121
1139
|
const method = context.method;
|
|
1122
1140
|
const path = this.normalizePath(context.path);
|
|
1123
|
-
const
|
|
1124
|
-
if (!
|
|
1141
|
+
const result = this.findRouteWithMatch(method, path);
|
|
1142
|
+
if (!result) {
|
|
1125
1143
|
return;
|
|
1126
1144
|
}
|
|
1145
|
+
const { route, match } = result;
|
|
1146
|
+
if (match.matched) {
|
|
1147
|
+
context.params = match.params;
|
|
1148
|
+
}
|
|
1149
|
+
if (route.controllerClass && route.methodName) {
|
|
1150
|
+
context.routeHandler = {
|
|
1151
|
+
controller: route.controllerClass,
|
|
1152
|
+
method: route.methodName
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1127
1155
|
return await route.execute(context);
|
|
1128
1156
|
}
|
|
1129
1157
|
getRoutes() {
|
|
@@ -1133,6 +1161,7 @@ class Router {
|
|
|
1133
1161
|
this.routes.length = 0;
|
|
1134
1162
|
this.dynamicRoutes.length = 0;
|
|
1135
1163
|
this.staticRoutes.clear();
|
|
1164
|
+
this.matchCache.clear();
|
|
1136
1165
|
}
|
|
1137
1166
|
}
|
|
1138
1167
|
|
|
@@ -1521,14 +1550,162 @@ function getParamMetadata(target, propertyKey) {
|
|
|
1521
1550
|
return Reflect.getMetadata(PARAM_METADATA_KEY, target, propertyKey) || [];
|
|
1522
1551
|
}
|
|
1523
1552
|
|
|
1553
|
+
// src/session/types.ts
|
|
1554
|
+
class MemorySessionStore {
|
|
1555
|
+
store = new Map;
|
|
1556
|
+
cleanupInterval;
|
|
1557
|
+
constructor(options) {
|
|
1558
|
+
if (options?.cleanupInterval !== undefined) {
|
|
1559
|
+
this.cleanupInterval = setInterval(() => {
|
|
1560
|
+
this.cleanup();
|
|
1561
|
+
}, options.cleanupInterval);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
async get(sessionId) {
|
|
1565
|
+
const session = this.store.get(sessionId);
|
|
1566
|
+
if (!session) {
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
if (Date.now() > session.expiresAt) {
|
|
1570
|
+
this.store.delete(sessionId);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
return session;
|
|
1574
|
+
}
|
|
1575
|
+
async set(session, maxAge) {
|
|
1576
|
+
const now = Date.now();
|
|
1577
|
+
session.expiresAt = now + maxAge;
|
|
1578
|
+
session.lastAccessedAt = now;
|
|
1579
|
+
this.store.set(session.id, session);
|
|
1580
|
+
return true;
|
|
1581
|
+
}
|
|
1582
|
+
async delete(sessionId) {
|
|
1583
|
+
return this.store.delete(sessionId);
|
|
1584
|
+
}
|
|
1585
|
+
async has(sessionId) {
|
|
1586
|
+
const session = this.store.get(sessionId);
|
|
1587
|
+
if (!session) {
|
|
1588
|
+
return false;
|
|
1589
|
+
}
|
|
1590
|
+
if (Date.now() > session.expiresAt) {
|
|
1591
|
+
this.store.delete(sessionId);
|
|
1592
|
+
return false;
|
|
1593
|
+
}
|
|
1594
|
+
return true;
|
|
1595
|
+
}
|
|
1596
|
+
async touch(sessionId) {
|
|
1597
|
+
const session = this.store.get(sessionId);
|
|
1598
|
+
if (!session) {
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
if (Date.now() > session.expiresAt) {
|
|
1602
|
+
this.store.delete(sessionId);
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
session.lastAccessedAt = Date.now();
|
|
1606
|
+
return true;
|
|
1607
|
+
}
|
|
1608
|
+
async clear() {
|
|
1609
|
+
this.store.clear();
|
|
1610
|
+
return true;
|
|
1611
|
+
}
|
|
1612
|
+
cleanup() {
|
|
1613
|
+
const now = Date.now();
|
|
1614
|
+
for (const [sessionId, session] of this.store.entries()) {
|
|
1615
|
+
if (now > session.expiresAt) {
|
|
1616
|
+
this.store.delete(sessionId);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
destroy() {
|
|
1621
|
+
if (this.cleanupInterval) {
|
|
1622
|
+
clearInterval(this.cleanupInterval);
|
|
1623
|
+
this.cleanupInterval = undefined;
|
|
1624
|
+
}
|
|
1625
|
+
this.store.clear();
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
class RedisSessionStore {
|
|
1630
|
+
client;
|
|
1631
|
+
keyPrefix;
|
|
1632
|
+
constructor(options) {
|
|
1633
|
+
this.client = options.client;
|
|
1634
|
+
this.keyPrefix = options.keyPrefix ?? "session:";
|
|
1635
|
+
}
|
|
1636
|
+
getKey(sessionId) {
|
|
1637
|
+
return `${this.keyPrefix}${sessionId}`;
|
|
1638
|
+
}
|
|
1639
|
+
async get(sessionId) {
|
|
1640
|
+
try {
|
|
1641
|
+
const value = await this.client.get(this.getKey(sessionId));
|
|
1642
|
+
if (value === null) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
return JSON.parse(value);
|
|
1646
|
+
} catch {
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
async set(session, maxAge) {
|
|
1651
|
+
try {
|
|
1652
|
+
const serialized = JSON.stringify(session);
|
|
1653
|
+
await this.client.set(this.getKey(session.id), serialized, {
|
|
1654
|
+
PX: maxAge
|
|
1655
|
+
});
|
|
1656
|
+
return true;
|
|
1657
|
+
} catch {
|
|
1658
|
+
return false;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
async delete(sessionId) {
|
|
1662
|
+
try {
|
|
1663
|
+
await this.client.del(this.getKey(sessionId));
|
|
1664
|
+
return true;
|
|
1665
|
+
} catch {
|
|
1666
|
+
return false;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
async has(sessionId) {
|
|
1670
|
+
try {
|
|
1671
|
+
const result = await this.client.exists(this.getKey(sessionId));
|
|
1672
|
+
return result === 1;
|
|
1673
|
+
} catch {
|
|
1674
|
+
return false;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
async touch(sessionId) {
|
|
1678
|
+
try {
|
|
1679
|
+
const session = await this.get(sessionId);
|
|
1680
|
+
if (!session) {
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
session.lastAccessedAt = Date.now();
|
|
1684
|
+
const remainingTime = session.expiresAt - Date.now();
|
|
1685
|
+
if (remainingTime > 0) {
|
|
1686
|
+
await this.client.expire(this.getKey(sessionId), Math.floor(remainingTime / 1000));
|
|
1687
|
+
return true;
|
|
1688
|
+
}
|
|
1689
|
+
return false;
|
|
1690
|
+
} catch {
|
|
1691
|
+
return false;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
async clear() {
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
var SESSION_SERVICE_TOKEN = Symbol("@dangao/bun-server:session:service");
|
|
1699
|
+
var SESSION_OPTIONS_TOKEN = Symbol("@dangao/bun-server:session:options");
|
|
1700
|
+
|
|
1524
1701
|
// src/controller/param-binder.ts
|
|
1525
1702
|
class ParamBinder {
|
|
1526
|
-
static async bind(target, propertyKey, context) {
|
|
1703
|
+
static async bind(target, propertyKey, context, container) {
|
|
1527
1704
|
const metadata = getParamMetadata(target, propertyKey);
|
|
1528
1705
|
const params = [];
|
|
1529
1706
|
metadata.sort((a, b) => a.index - b.index);
|
|
1530
1707
|
for (const meta of metadata) {
|
|
1531
|
-
const value = await this.getValue(meta, context);
|
|
1708
|
+
const value = await this.getValue(meta, context, container);
|
|
1532
1709
|
params[meta.index] = value;
|
|
1533
1710
|
}
|
|
1534
1711
|
const maxIndex = metadata.length > 0 ? Math.max(...metadata.map((m) => m.index)) : -1;
|
|
@@ -1539,7 +1716,7 @@ class ParamBinder {
|
|
|
1539
1716
|
}
|
|
1540
1717
|
return params;
|
|
1541
1718
|
}
|
|
1542
|
-
static async getValue(meta, context) {
|
|
1719
|
+
static async getValue(meta, context, container) {
|
|
1543
1720
|
switch (meta.type) {
|
|
1544
1721
|
case "body" /* BODY */:
|
|
1545
1722
|
return await this.getBodyValue(meta.key, context);
|
|
@@ -1558,6 +1735,24 @@ class ParamBinder {
|
|
|
1558
1735
|
throw new Error("@Header() decorator requires a key parameter");
|
|
1559
1736
|
}
|
|
1560
1737
|
return this.getHeaderValue(meta.key, context);
|
|
1738
|
+
case "session" /* SESSION */:
|
|
1739
|
+
if (!container) {
|
|
1740
|
+
throw new Error("@Session() decorator requires a Container instance");
|
|
1741
|
+
}
|
|
1742
|
+
const session = context.session;
|
|
1743
|
+
if (session) {
|
|
1744
|
+
return session;
|
|
1745
|
+
}
|
|
1746
|
+
try {
|
|
1747
|
+
const sessionService = container.resolve(SESSION_SERVICE_TOKEN);
|
|
1748
|
+
if (sessionService) {
|
|
1749
|
+
const newSession = await sessionService.create();
|
|
1750
|
+
context.session = newSession;
|
|
1751
|
+
context.sessionId = newSession.id;
|
|
1752
|
+
return newSession;
|
|
1753
|
+
}
|
|
1754
|
+
} catch {}
|
|
1755
|
+
return;
|
|
1561
1756
|
default:
|
|
1562
1757
|
return;
|
|
1563
1758
|
}
|
|
@@ -2116,7 +2311,7 @@ class ControllerRegistry {
|
|
|
2116
2311
|
const controllerContainer = this.controllerContainers.get(controllerClass) ?? this.container;
|
|
2117
2312
|
const controllerInstance = controllerContainer.resolve(controllerClass);
|
|
2118
2313
|
const prototype2 = controllerClass.prototype;
|
|
2119
|
-
const params = await ParamBinder.bind(prototype2, propertyKey, context);
|
|
2314
|
+
const params = await ParamBinder.bind(prototype2, propertyKey, context, controllerContainer);
|
|
2120
2315
|
const validationMetadata = getValidationMetadata(prototype2, propertyKey);
|
|
2121
2316
|
if (validationMetadata.length > 0) {
|
|
2122
2317
|
validateParameters(params, validationMetadata);
|
|
@@ -5181,6 +5376,885 @@ class StressTester {
|
|
|
5181
5376
|
};
|
|
5182
5377
|
}
|
|
5183
5378
|
}
|
|
5379
|
+
// src/cache/types.ts
|
|
5380
|
+
class MemoryCacheStore {
|
|
5381
|
+
store = new Map;
|
|
5382
|
+
cleanupInterval;
|
|
5383
|
+
constructor(options) {
|
|
5384
|
+
if (options?.cleanupInterval !== undefined) {
|
|
5385
|
+
this.cleanupInterval = setInterval(() => {
|
|
5386
|
+
this.cleanup();
|
|
5387
|
+
}, options.cleanupInterval);
|
|
5388
|
+
}
|
|
5389
|
+
}
|
|
5390
|
+
async get(key) {
|
|
5391
|
+
const entry = this.store.get(key);
|
|
5392
|
+
if (!entry) {
|
|
5393
|
+
return;
|
|
5394
|
+
}
|
|
5395
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
5396
|
+
this.store.delete(key);
|
|
5397
|
+
return;
|
|
5398
|
+
}
|
|
5399
|
+
return entry.value;
|
|
5400
|
+
}
|
|
5401
|
+
async set(key, value, ttl) {
|
|
5402
|
+
const expiresAt = ttl && ttl > 0 ? Date.now() + ttl : undefined;
|
|
5403
|
+
this.store.set(key, { value, expiresAt });
|
|
5404
|
+
return true;
|
|
5405
|
+
}
|
|
5406
|
+
async delete(key) {
|
|
5407
|
+
return this.store.delete(key);
|
|
5408
|
+
}
|
|
5409
|
+
async has(key) {
|
|
5410
|
+
const entry = this.store.get(key);
|
|
5411
|
+
if (!entry) {
|
|
5412
|
+
return false;
|
|
5413
|
+
}
|
|
5414
|
+
if (entry.expiresAt && Date.now() > entry.expiresAt) {
|
|
5415
|
+
this.store.delete(key);
|
|
5416
|
+
return false;
|
|
5417
|
+
}
|
|
5418
|
+
return true;
|
|
5419
|
+
}
|
|
5420
|
+
async clear() {
|
|
5421
|
+
this.store.clear();
|
|
5422
|
+
return true;
|
|
5423
|
+
}
|
|
5424
|
+
async getMany(keys) {
|
|
5425
|
+
const result = new Map;
|
|
5426
|
+
const now = Date.now();
|
|
5427
|
+
for (const key of keys) {
|
|
5428
|
+
const entry = this.store.get(key);
|
|
5429
|
+
if (entry) {
|
|
5430
|
+
if (entry.expiresAt && now > entry.expiresAt) {
|
|
5431
|
+
this.store.delete(key);
|
|
5432
|
+
continue;
|
|
5433
|
+
}
|
|
5434
|
+
result.set(key, entry.value);
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
5437
|
+
return result;
|
|
5438
|
+
}
|
|
5439
|
+
async setMany(entries, ttl) {
|
|
5440
|
+
const expiresAt = ttl && ttl > 0 ? Date.now() + ttl : undefined;
|
|
5441
|
+
for (const { key, value } of entries) {
|
|
5442
|
+
this.store.set(key, { value, expiresAt });
|
|
5443
|
+
}
|
|
5444
|
+
return true;
|
|
5445
|
+
}
|
|
5446
|
+
async deleteMany(keys) {
|
|
5447
|
+
const deleted = [];
|
|
5448
|
+
for (const key of keys) {
|
|
5449
|
+
if (this.store.delete(key)) {
|
|
5450
|
+
deleted.push(key);
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
return deleted;
|
|
5454
|
+
}
|
|
5455
|
+
cleanup() {
|
|
5456
|
+
const now = Date.now();
|
|
5457
|
+
for (const [key, entry] of this.store.entries()) {
|
|
5458
|
+
if (entry.expiresAt && now > entry.expiresAt) {
|
|
5459
|
+
this.store.delete(key);
|
|
5460
|
+
}
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
destroy() {
|
|
5464
|
+
if (this.cleanupInterval) {
|
|
5465
|
+
clearInterval(this.cleanupInterval);
|
|
5466
|
+
this.cleanupInterval = undefined;
|
|
5467
|
+
}
|
|
5468
|
+
this.store.clear();
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
|
|
5472
|
+
class RedisCacheStore {
|
|
5473
|
+
client;
|
|
5474
|
+
keyPrefix;
|
|
5475
|
+
constructor(options) {
|
|
5476
|
+
this.client = options.client;
|
|
5477
|
+
this.keyPrefix = options.keyPrefix ?? "cache:";
|
|
5478
|
+
}
|
|
5479
|
+
getKey(key) {
|
|
5480
|
+
return `${this.keyPrefix}${key}`;
|
|
5481
|
+
}
|
|
5482
|
+
async get(key) {
|
|
5483
|
+
const value = await this.client.get(this.getKey(key));
|
|
5484
|
+
if (value === null) {
|
|
5485
|
+
return;
|
|
5486
|
+
}
|
|
5487
|
+
try {
|
|
5488
|
+
return JSON.parse(value);
|
|
5489
|
+
} catch {
|
|
5490
|
+
return;
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
async set(key, value, ttl) {
|
|
5494
|
+
try {
|
|
5495
|
+
const serialized = JSON.stringify(value);
|
|
5496
|
+
if (ttl && ttl > 0) {
|
|
5497
|
+
await this.client.set(this.getKey(key), serialized, { PX: ttl });
|
|
5498
|
+
} else {
|
|
5499
|
+
await this.client.set(this.getKey(key), serialized);
|
|
5500
|
+
}
|
|
5501
|
+
return true;
|
|
5502
|
+
} catch {
|
|
5503
|
+
return false;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
async delete(key) {
|
|
5507
|
+
try {
|
|
5508
|
+
await this.client.del(this.getKey(key));
|
|
5509
|
+
return true;
|
|
5510
|
+
} catch {
|
|
5511
|
+
return false;
|
|
5512
|
+
}
|
|
5513
|
+
}
|
|
5514
|
+
async has(key) {
|
|
5515
|
+
try {
|
|
5516
|
+
const result = await this.client.exists(this.getKey(key));
|
|
5517
|
+
return result === 1;
|
|
5518
|
+
} catch {
|
|
5519
|
+
return false;
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
async clear() {
|
|
5523
|
+
try {
|
|
5524
|
+
await this.client.flushdb();
|
|
5525
|
+
return true;
|
|
5526
|
+
} catch {
|
|
5527
|
+
return false;
|
|
5528
|
+
}
|
|
5529
|
+
}
|
|
5530
|
+
async getMany(keys) {
|
|
5531
|
+
const result = new Map;
|
|
5532
|
+
if (keys.length === 0) {
|
|
5533
|
+
return result;
|
|
5534
|
+
}
|
|
5535
|
+
try {
|
|
5536
|
+
const prefixedKeys = keys.map((k) => this.getKey(k));
|
|
5537
|
+
const values = await this.client.mget(prefixedKeys);
|
|
5538
|
+
for (let i = 0;i < keys.length; i++) {
|
|
5539
|
+
const value = values[i];
|
|
5540
|
+
if (value !== null) {
|
|
5541
|
+
try {
|
|
5542
|
+
result.set(keys[i], JSON.parse(value));
|
|
5543
|
+
} catch {}
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
} catch {}
|
|
5547
|
+
return result;
|
|
5548
|
+
}
|
|
5549
|
+
async setMany(entries, ttl) {
|
|
5550
|
+
if (entries.length === 0) {
|
|
5551
|
+
return true;
|
|
5552
|
+
}
|
|
5553
|
+
try {
|
|
5554
|
+
const redisEntries = entries.map(({ key, value }) => ({
|
|
5555
|
+
key: this.getKey(key),
|
|
5556
|
+
value: JSON.stringify(value)
|
|
5557
|
+
}));
|
|
5558
|
+
await this.client.mset(redisEntries);
|
|
5559
|
+
if (ttl && ttl > 0) {
|
|
5560
|
+
for (const entry of redisEntries) {
|
|
5561
|
+
await this.client.set(entry.key, entry.value, { PX: ttl });
|
|
5562
|
+
}
|
|
5563
|
+
}
|
|
5564
|
+
return true;
|
|
5565
|
+
} catch {
|
|
5566
|
+
return false;
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5569
|
+
async deleteMany(keys) {
|
|
5570
|
+
if (keys.length === 0) {
|
|
5571
|
+
return [];
|
|
5572
|
+
}
|
|
5573
|
+
try {
|
|
5574
|
+
const prefixedKeys = keys.map((k) => this.getKey(k));
|
|
5575
|
+
const deleted = [];
|
|
5576
|
+
for (const key of prefixedKeys) {
|
|
5577
|
+
try {
|
|
5578
|
+
await this.client.del(key);
|
|
5579
|
+
deleted.push(key.replace(this.keyPrefix, ""));
|
|
5580
|
+
} catch {}
|
|
5581
|
+
}
|
|
5582
|
+
return deleted;
|
|
5583
|
+
} catch {
|
|
5584
|
+
return [];
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
var CACHE_SERVICE_TOKEN = Symbol("@dangao/bun-server:cache:service");
|
|
5589
|
+
var CACHE_OPTIONS_TOKEN = Symbol("@dangao/bun-server:cache:options");
|
|
5590
|
+
|
|
5591
|
+
// src/cache/service.ts
|
|
5592
|
+
class CacheService {
|
|
5593
|
+
store;
|
|
5594
|
+
defaultTtl;
|
|
5595
|
+
keyPrefix;
|
|
5596
|
+
constructor(options) {
|
|
5597
|
+
this.store = options.store;
|
|
5598
|
+
this.defaultTtl = options.defaultTtl ?? 3600000;
|
|
5599
|
+
this.keyPrefix = options.keyPrefix ?? "";
|
|
5600
|
+
}
|
|
5601
|
+
async get(key) {
|
|
5602
|
+
return this.store.get(this.getKey(key));
|
|
5603
|
+
}
|
|
5604
|
+
async set(key, value, ttl) {
|
|
5605
|
+
const finalTtl = ttl !== undefined ? ttl : this.defaultTtl;
|
|
5606
|
+
return this.store.set(this.getKey(key), value, finalTtl);
|
|
5607
|
+
}
|
|
5608
|
+
async delete(key) {
|
|
5609
|
+
return this.store.delete(this.getKey(key));
|
|
5610
|
+
}
|
|
5611
|
+
async has(key) {
|
|
5612
|
+
return this.store.has(this.getKey(key));
|
|
5613
|
+
}
|
|
5614
|
+
async clear() {
|
|
5615
|
+
return this.store.clear();
|
|
5616
|
+
}
|
|
5617
|
+
async getMany(keys) {
|
|
5618
|
+
const prefixedKeys = keys.map((k) => this.getKey(k));
|
|
5619
|
+
const result = await this.store.getMany(prefixedKeys);
|
|
5620
|
+
const map = new Map;
|
|
5621
|
+
for (const [key, value] of result.entries()) {
|
|
5622
|
+
map.set(key.replace(this.keyPrefix, ""), value);
|
|
5623
|
+
}
|
|
5624
|
+
return map;
|
|
5625
|
+
}
|
|
5626
|
+
async setMany(entries, ttl) {
|
|
5627
|
+
const finalTtl = ttl !== undefined ? ttl : this.defaultTtl;
|
|
5628
|
+
const prefixedEntries = entries.map(({ key, value }) => ({
|
|
5629
|
+
key: this.getKey(key),
|
|
5630
|
+
value
|
|
5631
|
+
}));
|
|
5632
|
+
return this.store.setMany(prefixedEntries, finalTtl);
|
|
5633
|
+
}
|
|
5634
|
+
async deleteMany(keys) {
|
|
5635
|
+
const prefixedKeys = keys.map((k) => this.getKey(k));
|
|
5636
|
+
const deleted = await this.store.deleteMany(prefixedKeys);
|
|
5637
|
+
return deleted.map((k) => k.replace(this.keyPrefix, ""));
|
|
5638
|
+
}
|
|
5639
|
+
async getOrSet(key, factory, ttl) {
|
|
5640
|
+
const cached = await this.get(key);
|
|
5641
|
+
if (cached !== undefined) {
|
|
5642
|
+
return cached;
|
|
5643
|
+
}
|
|
5644
|
+
const value = await factory();
|
|
5645
|
+
await this.set(key, value, ttl);
|
|
5646
|
+
return value;
|
|
5647
|
+
}
|
|
5648
|
+
getKey(key) {
|
|
5649
|
+
return this.keyPrefix ? `${this.keyPrefix}${key}` : key;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
CacheService = __legacyDecorateClassTS([
|
|
5653
|
+
Injectable(),
|
|
5654
|
+
__legacyDecorateParamTS(0, Inject(CACHE_OPTIONS_TOKEN)),
|
|
5655
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
5656
|
+
typeof CacheModuleOptions === "undefined" ? Object : CacheModuleOptions
|
|
5657
|
+
])
|
|
5658
|
+
], CacheService);
|
|
5659
|
+
|
|
5660
|
+
// src/cache/cache-module.ts
|
|
5661
|
+
class CacheModule {
|
|
5662
|
+
static forRoot(options = {}) {
|
|
5663
|
+
const providers2 = [];
|
|
5664
|
+
const store = options.store ?? new MemoryCacheStore({
|
|
5665
|
+
cleanupInterval: 60000
|
|
5666
|
+
});
|
|
5667
|
+
const service = new CacheService({
|
|
5668
|
+
store,
|
|
5669
|
+
defaultTtl: options.defaultTtl,
|
|
5670
|
+
keyPrefix: options.keyPrefix
|
|
5671
|
+
});
|
|
5672
|
+
providers2.push({
|
|
5673
|
+
provide: CACHE_SERVICE_TOKEN,
|
|
5674
|
+
useValue: service
|
|
5675
|
+
}, {
|
|
5676
|
+
provide: CACHE_OPTIONS_TOKEN,
|
|
5677
|
+
useValue: options
|
|
5678
|
+
}, CacheService);
|
|
5679
|
+
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, CacheModule) || {};
|
|
5680
|
+
const metadata = {
|
|
5681
|
+
...existingMetadata,
|
|
5682
|
+
providers: [...existingMetadata.providers || [], ...providers2],
|
|
5683
|
+
exports: [
|
|
5684
|
+
...existingMetadata.exports || [],
|
|
5685
|
+
CACHE_SERVICE_TOKEN,
|
|
5686
|
+
CACHE_OPTIONS_TOKEN,
|
|
5687
|
+
CacheService
|
|
5688
|
+
]
|
|
5689
|
+
};
|
|
5690
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, CacheModule);
|
|
5691
|
+
return CacheModule;
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
CacheModule = __legacyDecorateClassTS([
|
|
5695
|
+
Module({
|
|
5696
|
+
providers: []
|
|
5697
|
+
})
|
|
5698
|
+
], CacheModule);
|
|
5699
|
+
// src/cache/decorators.ts
|
|
5700
|
+
import"reflect-metadata";
|
|
5701
|
+
var CACHEABLE_METADATA_KEY = Symbol("@dangao/bun-server:cache:cacheable");
|
|
5702
|
+
var CACHE_EVICT_METADATA_KEY = Symbol("@dangao/bun-server:cache:cache-evict");
|
|
5703
|
+
var CACHE_PUT_METADATA_KEY = Symbol("@dangao/bun-server:cache:cache-put");
|
|
5704
|
+
function Cacheable(options = {}) {
|
|
5705
|
+
return function(target, propertyKey, descriptor) {
|
|
5706
|
+
const metadata = {
|
|
5707
|
+
key: options.key,
|
|
5708
|
+
keyPrefix: options.keyPrefix,
|
|
5709
|
+
ttl: options.ttl,
|
|
5710
|
+
condition: options.condition
|
|
5711
|
+
};
|
|
5712
|
+
Reflect.defineMetadata(CACHEABLE_METADATA_KEY, metadata, descriptor.value);
|
|
5713
|
+
};
|
|
5714
|
+
}
|
|
5715
|
+
function CacheEvict(options = {}) {
|
|
5716
|
+
return function(target, propertyKey, descriptor) {
|
|
5717
|
+
const metadata = {
|
|
5718
|
+
key: options.key,
|
|
5719
|
+
keyPrefix: options.keyPrefix,
|
|
5720
|
+
beforeInvocation: options.beforeInvocation ?? false,
|
|
5721
|
+
allEntries: options.allEntries ?? false
|
|
5722
|
+
};
|
|
5723
|
+
Reflect.defineMetadata(CACHE_EVICT_METADATA_KEY, metadata, descriptor.value);
|
|
5724
|
+
};
|
|
5725
|
+
}
|
|
5726
|
+
function CachePut(options = {}) {
|
|
5727
|
+
return function(target, propertyKey, descriptor) {
|
|
5728
|
+
const metadata = {
|
|
5729
|
+
key: options.key,
|
|
5730
|
+
keyPrefix: options.keyPrefix,
|
|
5731
|
+
ttl: options.ttl,
|
|
5732
|
+
condition: options.condition
|
|
5733
|
+
};
|
|
5734
|
+
Reflect.defineMetadata(CACHE_PUT_METADATA_KEY, metadata, descriptor.value);
|
|
5735
|
+
};
|
|
5736
|
+
}
|
|
5737
|
+
// src/queue/types.ts
|
|
5738
|
+
class MemoryQueueStore {
|
|
5739
|
+
queues = new Map;
|
|
5740
|
+
jobCounter = 0;
|
|
5741
|
+
async add(queueName, job) {
|
|
5742
|
+
const queue = this.getQueue(queueName);
|
|
5743
|
+
const jobId = job.options?.jobId ?? `job_${++this.jobCounter}_${Date.now()}`;
|
|
5744
|
+
const now = Date.now();
|
|
5745
|
+
const fullJob = {
|
|
5746
|
+
id: jobId,
|
|
5747
|
+
name: job.name,
|
|
5748
|
+
data: job.data,
|
|
5749
|
+
options: job.options,
|
|
5750
|
+
status: job.options?.delay ? "delayed" : "waiting",
|
|
5751
|
+
createdAt: now,
|
|
5752
|
+
updatedAt: now
|
|
5753
|
+
};
|
|
5754
|
+
queue.set(jobId, fullJob);
|
|
5755
|
+
return jobId;
|
|
5756
|
+
}
|
|
5757
|
+
async get(queueName, jobId) {
|
|
5758
|
+
const queue = this.getQueue(queueName);
|
|
5759
|
+
return queue.get(jobId);
|
|
5760
|
+
}
|
|
5761
|
+
async getNext(queueName) {
|
|
5762
|
+
const queue = this.getQueue(queueName);
|
|
5763
|
+
const now = Date.now();
|
|
5764
|
+
let nextJob;
|
|
5765
|
+
let highestPriority = -Infinity;
|
|
5766
|
+
for (const job of queue.values()) {
|
|
5767
|
+
if (job.status === "delayed" && job.options?.delay) {
|
|
5768
|
+
const delayEndTime = job.createdAt + job.options.delay;
|
|
5769
|
+
if (now >= delayEndTime) {
|
|
5770
|
+
if ((job.options.priority ?? 0) > highestPriority || !nextJob) {
|
|
5771
|
+
nextJob = job;
|
|
5772
|
+
highestPriority = job.options?.priority ?? 0;
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
} else if (job.status === "waiting") {
|
|
5776
|
+
if ((job.options?.priority ?? 0) > highestPriority || !nextJob) {
|
|
5777
|
+
nextJob = job;
|
|
5778
|
+
highestPriority = job.options?.priority ?? 0;
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5781
|
+
}
|
|
5782
|
+
return nextJob;
|
|
5783
|
+
}
|
|
5784
|
+
async updateStatus(queueName, jobId, status) {
|
|
5785
|
+
const queue = this.getQueue(queueName);
|
|
5786
|
+
const job = queue.get(jobId);
|
|
5787
|
+
if (!job) {
|
|
5788
|
+
return false;
|
|
5789
|
+
}
|
|
5790
|
+
job.status = status;
|
|
5791
|
+
job.updatedAt = Date.now();
|
|
5792
|
+
return true;
|
|
5793
|
+
}
|
|
5794
|
+
async delete(queueName, jobId) {
|
|
5795
|
+
const queue = this.getQueue(queueName);
|
|
5796
|
+
return queue.delete(jobId);
|
|
5797
|
+
}
|
|
5798
|
+
async clear(queueName) {
|
|
5799
|
+
const queue = this.getQueue(queueName);
|
|
5800
|
+
queue.clear();
|
|
5801
|
+
return true;
|
|
5802
|
+
}
|
|
5803
|
+
async count(queueName) {
|
|
5804
|
+
const queue = this.getQueue(queueName);
|
|
5805
|
+
return queue.size;
|
|
5806
|
+
}
|
|
5807
|
+
getQueue(queueName) {
|
|
5808
|
+
if (!this.queues.has(queueName)) {
|
|
5809
|
+
this.queues.set(queueName, new Map);
|
|
5810
|
+
}
|
|
5811
|
+
return this.queues.get(queueName);
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
var QUEUE_SERVICE_TOKEN = Symbol("@dangao/bun-server:queue:service");
|
|
5815
|
+
var QUEUE_OPTIONS_TOKEN = Symbol("@dangao/bun-server:queue:options");
|
|
5816
|
+
|
|
5817
|
+
// src/queue/service.ts
|
|
5818
|
+
class QueueService {
|
|
5819
|
+
store;
|
|
5820
|
+
defaultQueue;
|
|
5821
|
+
enableWorker;
|
|
5822
|
+
concurrency;
|
|
5823
|
+
workers = new Map;
|
|
5824
|
+
cronJobs = new Map;
|
|
5825
|
+
cronTimers = new Map;
|
|
5826
|
+
constructor(options) {
|
|
5827
|
+
this.store = options.store;
|
|
5828
|
+
this.defaultQueue = options.defaultQueue ?? "default";
|
|
5829
|
+
this.enableWorker = options.enableWorker ?? true;
|
|
5830
|
+
this.concurrency = options.concurrency ?? 1;
|
|
5831
|
+
if (this.enableWorker) {
|
|
5832
|
+
this.startWorker(this.defaultQueue);
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
async add(jobName, data, options, queueName) {
|
|
5836
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5837
|
+
const jobId = await this.store.add(queue, {
|
|
5838
|
+
name: jobName,
|
|
5839
|
+
data,
|
|
5840
|
+
options
|
|
5841
|
+
});
|
|
5842
|
+
if (this.enableWorker && !this.workers.has(queue)) {
|
|
5843
|
+
this.startWorker(queue);
|
|
5844
|
+
}
|
|
5845
|
+
return jobId;
|
|
5846
|
+
}
|
|
5847
|
+
async get(jobId, queueName) {
|
|
5848
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5849
|
+
return this.store.get(queue, jobId);
|
|
5850
|
+
}
|
|
5851
|
+
async delete(jobId, queueName) {
|
|
5852
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5853
|
+
return this.store.delete(queue, jobId);
|
|
5854
|
+
}
|
|
5855
|
+
async clear(queueName) {
|
|
5856
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5857
|
+
return this.store.clear(queue);
|
|
5858
|
+
}
|
|
5859
|
+
async count(queueName) {
|
|
5860
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5861
|
+
return this.store.count(queue);
|
|
5862
|
+
}
|
|
5863
|
+
async registerHandler(jobName, handler, queueName) {
|
|
5864
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5865
|
+
const key = `${queue}:${jobName}`;
|
|
5866
|
+
this.handlers = this.handlers ?? new Map;
|
|
5867
|
+
this.handlers.set(key, handler);
|
|
5868
|
+
}
|
|
5869
|
+
async registerCron(jobName, handler, options, queueName) {
|
|
5870
|
+
const queue = queueName ?? this.defaultQueue;
|
|
5871
|
+
const key = `${queue}:${jobName}`;
|
|
5872
|
+
this.cronJobs.set(key, { handler, options });
|
|
5873
|
+
if (options.runOnInit) {
|
|
5874
|
+
await this.add(jobName, {}, undefined, queue);
|
|
5875
|
+
}
|
|
5876
|
+
const interval = this.parseCronInterval(options.pattern);
|
|
5877
|
+
if (interval > 0) {
|
|
5878
|
+
const timer = setInterval(async () => {
|
|
5879
|
+
await this.add(jobName, {}, undefined, queue);
|
|
5880
|
+
}, interval);
|
|
5881
|
+
this.cronTimers.set(key, timer);
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
5884
|
+
startWorker(queueName) {
|
|
5885
|
+
if (this.workers.has(queueName)) {
|
|
5886
|
+
return;
|
|
5887
|
+
}
|
|
5888
|
+
const workerSet = new Set;
|
|
5889
|
+
this.workers.set(queueName, workerSet);
|
|
5890
|
+
for (let i = 0;i < this.concurrency; i++) {
|
|
5891
|
+
const worker = this.processQueue(queueName);
|
|
5892
|
+
workerSet.add(worker);
|
|
5893
|
+
}
|
|
5894
|
+
}
|
|
5895
|
+
async processQueue(queueName) {
|
|
5896
|
+
while (true) {
|
|
5897
|
+
try {
|
|
5898
|
+
const job = await this.store.getNext(queueName);
|
|
5899
|
+
if (!job) {
|
|
5900
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1000));
|
|
5901
|
+
continue;
|
|
5902
|
+
}
|
|
5903
|
+
await this.store.updateStatus(queueName, job.id, "active");
|
|
5904
|
+
try {
|
|
5905
|
+
const handler = this.getHandler(job.name, queueName);
|
|
5906
|
+
if (handler) {
|
|
5907
|
+
await handler(job);
|
|
5908
|
+
await this.store.updateStatus(queueName, job.id, "completed");
|
|
5909
|
+
} else {
|
|
5910
|
+
await this.store.updateStatus(queueName, job.id, "failed");
|
|
5911
|
+
}
|
|
5912
|
+
} catch (error) {
|
|
5913
|
+
await this.store.updateStatus(queueName, job.id, "failed");
|
|
5914
|
+
console.error(`Job ${job.id} failed:`, error);
|
|
5915
|
+
}
|
|
5916
|
+
} catch (error) {
|
|
5917
|
+
console.error(`Error processing queue ${queueName}:`, error);
|
|
5918
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1000));
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5922
|
+
getHandler(jobName, queueName) {
|
|
5923
|
+
const key = `${queueName}:${jobName}`;
|
|
5924
|
+
return this.handlers?.get(key);
|
|
5925
|
+
}
|
|
5926
|
+
parseCronInterval(pattern) {
|
|
5927
|
+
const parts = pattern.trim().split(/\s+/);
|
|
5928
|
+
if (parts.length !== 5) {
|
|
5929
|
+
return -1;
|
|
5930
|
+
}
|
|
5931
|
+
const [minute, hour, day, month, weekday] = parts;
|
|
5932
|
+
if (minute === "*" && hour === "*" && day === "*" && month === "*" && weekday === "*") {
|
|
5933
|
+
return 60000;
|
|
5934
|
+
}
|
|
5935
|
+
if (/^\d+$/.test(minute) && hour === "*" && day === "*" && month === "*" && weekday === "*") {
|
|
5936
|
+
return parseInt(minute, 10) * 60000;
|
|
5937
|
+
}
|
|
5938
|
+
return -1;
|
|
5939
|
+
}
|
|
5940
|
+
destroy() {
|
|
5941
|
+
for (const timer of this.cronTimers.values()) {
|
|
5942
|
+
clearInterval(timer);
|
|
5943
|
+
}
|
|
5944
|
+
this.cronTimers.clear();
|
|
5945
|
+
this.cronJobs.clear();
|
|
5946
|
+
this.workers.clear();
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
QueueService = __legacyDecorateClassTS([
|
|
5950
|
+
Injectable(),
|
|
5951
|
+
__legacyDecorateParamTS(0, Inject(QUEUE_OPTIONS_TOKEN)),
|
|
5952
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
5953
|
+
typeof QueueModuleOptions === "undefined" ? Object : QueueModuleOptions
|
|
5954
|
+
])
|
|
5955
|
+
], QueueService);
|
|
5956
|
+
|
|
5957
|
+
// src/queue/queue-module.ts
|
|
5958
|
+
class QueueModule {
|
|
5959
|
+
static forRoot(options = {}) {
|
|
5960
|
+
const providers2 = [];
|
|
5961
|
+
const store = options.store ?? new MemoryQueueStore;
|
|
5962
|
+
const service = new QueueService({
|
|
5963
|
+
store,
|
|
5964
|
+
defaultQueue: options.defaultQueue,
|
|
5965
|
+
enableWorker: options.enableWorker,
|
|
5966
|
+
concurrency: options.concurrency
|
|
5967
|
+
});
|
|
5968
|
+
providers2.push({
|
|
5969
|
+
provide: QUEUE_SERVICE_TOKEN,
|
|
5970
|
+
useValue: service
|
|
5971
|
+
}, {
|
|
5972
|
+
provide: QUEUE_OPTIONS_TOKEN,
|
|
5973
|
+
useValue: options
|
|
5974
|
+
}, QueueService);
|
|
5975
|
+
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, QueueModule) || {};
|
|
5976
|
+
const metadata = {
|
|
5977
|
+
...existingMetadata,
|
|
5978
|
+
providers: [...existingMetadata.providers || [], ...providers2],
|
|
5979
|
+
exports: [
|
|
5980
|
+
...existingMetadata.exports || [],
|
|
5981
|
+
QUEUE_SERVICE_TOKEN,
|
|
5982
|
+
QUEUE_OPTIONS_TOKEN,
|
|
5983
|
+
QueueService
|
|
5984
|
+
]
|
|
5985
|
+
};
|
|
5986
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, QueueModule);
|
|
5987
|
+
return QueueModule;
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
QueueModule = __legacyDecorateClassTS([
|
|
5991
|
+
Module({
|
|
5992
|
+
providers: []
|
|
5993
|
+
})
|
|
5994
|
+
], QueueModule);
|
|
5995
|
+
// src/queue/decorators.ts
|
|
5996
|
+
import"reflect-metadata";
|
|
5997
|
+
var QUEUE_METADATA_KEY = Symbol("@dangao/bun-server:queue:queue");
|
|
5998
|
+
var CRON_METADATA_KEY = Symbol("@dangao/bun-server:queue:cron");
|
|
5999
|
+
function Queue(options = {}) {
|
|
6000
|
+
return function(target, propertyKey, descriptor) {
|
|
6001
|
+
const metadata = {
|
|
6002
|
+
name: options.name
|
|
6003
|
+
};
|
|
6004
|
+
Reflect.defineMetadata(QUEUE_METADATA_KEY, metadata, descriptor.value);
|
|
6005
|
+
};
|
|
6006
|
+
}
|
|
6007
|
+
function Cron(options) {
|
|
6008
|
+
return function(target, propertyKey, descriptor) {
|
|
6009
|
+
const metadata = {
|
|
6010
|
+
pattern: options.pattern,
|
|
6011
|
+
timezone: options.timezone,
|
|
6012
|
+
runOnInit: options.runOnInit ?? false,
|
|
6013
|
+
queueName: options.queueName
|
|
6014
|
+
};
|
|
6015
|
+
Reflect.defineMetadata(CRON_METADATA_KEY, metadata, descriptor.value);
|
|
6016
|
+
};
|
|
6017
|
+
}
|
|
6018
|
+
// src/session/service.ts
|
|
6019
|
+
import { randomBytes } from "crypto";
|
|
6020
|
+
class SessionService {
|
|
6021
|
+
store;
|
|
6022
|
+
name;
|
|
6023
|
+
maxAge;
|
|
6024
|
+
rolling;
|
|
6025
|
+
cookieOptions;
|
|
6026
|
+
constructor(options) {
|
|
6027
|
+
this.store = options.store;
|
|
6028
|
+
this.name = options.name ?? "sessionId";
|
|
6029
|
+
this.maxAge = options.maxAge ?? 86400000;
|
|
6030
|
+
this.rolling = options.rolling ?? true;
|
|
6031
|
+
this.cookieOptions = {
|
|
6032
|
+
secure: options.cookie?.secure ?? false,
|
|
6033
|
+
httpOnly: options.cookie?.httpOnly ?? true,
|
|
6034
|
+
path: options.cookie?.path ?? "/",
|
|
6035
|
+
domain: options.cookie?.domain,
|
|
6036
|
+
sameSite: options.cookie?.sameSite ?? "lax"
|
|
6037
|
+
};
|
|
6038
|
+
}
|
|
6039
|
+
generateSessionId() {
|
|
6040
|
+
return randomBytes(32).toString("hex");
|
|
6041
|
+
}
|
|
6042
|
+
async create(initialData = {}) {
|
|
6043
|
+
const now = Date.now();
|
|
6044
|
+
const session = {
|
|
6045
|
+
id: this.generateSessionId(),
|
|
6046
|
+
data: initialData,
|
|
6047
|
+
createdAt: now,
|
|
6048
|
+
lastAccessedAt: now,
|
|
6049
|
+
expiresAt: now + this.maxAge
|
|
6050
|
+
};
|
|
6051
|
+
await this.store.set(session, this.maxAge);
|
|
6052
|
+
return session;
|
|
6053
|
+
}
|
|
6054
|
+
async get(sessionId) {
|
|
6055
|
+
const session = await this.store.get(sessionId);
|
|
6056
|
+
if (!session) {
|
|
6057
|
+
return;
|
|
6058
|
+
}
|
|
6059
|
+
if (this.rolling) {
|
|
6060
|
+
await this.touch(sessionId);
|
|
6061
|
+
}
|
|
6062
|
+
return session;
|
|
6063
|
+
}
|
|
6064
|
+
async set(sessionId, data) {
|
|
6065
|
+
const session = await this.store.get(sessionId);
|
|
6066
|
+
if (!session) {
|
|
6067
|
+
return false;
|
|
6068
|
+
}
|
|
6069
|
+
session.data = { ...session.data, ...data };
|
|
6070
|
+
session.lastAccessedAt = Date.now();
|
|
6071
|
+
if (this.rolling) {
|
|
6072
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
6073
|
+
}
|
|
6074
|
+
return this.store.set(session, this.maxAge);
|
|
6075
|
+
}
|
|
6076
|
+
async getValue(sessionId, key) {
|
|
6077
|
+
const session = await this.store.get(sessionId);
|
|
6078
|
+
if (!session) {
|
|
6079
|
+
return;
|
|
6080
|
+
}
|
|
6081
|
+
return session.data[key];
|
|
6082
|
+
}
|
|
6083
|
+
async setValue(sessionId, key, value) {
|
|
6084
|
+
const session = await this.store.get(sessionId);
|
|
6085
|
+
if (!session) {
|
|
6086
|
+
return false;
|
|
6087
|
+
}
|
|
6088
|
+
session.data[key] = value;
|
|
6089
|
+
session.lastAccessedAt = Date.now();
|
|
6090
|
+
if (this.rolling) {
|
|
6091
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
6092
|
+
}
|
|
6093
|
+
return this.store.set(session, this.maxAge);
|
|
6094
|
+
}
|
|
6095
|
+
async deleteValue(sessionId, key) {
|
|
6096
|
+
const session = await this.store.get(sessionId);
|
|
6097
|
+
if (!session) {
|
|
6098
|
+
return false;
|
|
6099
|
+
}
|
|
6100
|
+
delete session.data[key];
|
|
6101
|
+
session.lastAccessedAt = Date.now();
|
|
6102
|
+
if (this.rolling) {
|
|
6103
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
6104
|
+
}
|
|
6105
|
+
return this.store.set(session, this.maxAge);
|
|
6106
|
+
}
|
|
6107
|
+
async delete(sessionId) {
|
|
6108
|
+
return this.store.delete(sessionId);
|
|
6109
|
+
}
|
|
6110
|
+
async touch(sessionId) {
|
|
6111
|
+
return this.store.touch(sessionId);
|
|
6112
|
+
}
|
|
6113
|
+
getCookieName() {
|
|
6114
|
+
return this.name;
|
|
6115
|
+
}
|
|
6116
|
+
getCookieOptions() {
|
|
6117
|
+
return this.cookieOptions;
|
|
6118
|
+
}
|
|
6119
|
+
getMaxAge() {
|
|
6120
|
+
return this.maxAge;
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
6123
|
+
SessionService = __legacyDecorateClassTS([
|
|
6124
|
+
Injectable(),
|
|
6125
|
+
__legacyDecorateParamTS(0, Inject(SESSION_OPTIONS_TOKEN)),
|
|
6126
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
6127
|
+
typeof SessionModuleOptions === "undefined" ? Object : SessionModuleOptions
|
|
6128
|
+
])
|
|
6129
|
+
], SessionService);
|
|
6130
|
+
|
|
6131
|
+
// src/session/session-module.ts
|
|
6132
|
+
class SessionModule {
|
|
6133
|
+
static forRoot(options = {}) {
|
|
6134
|
+
const providers2 = [];
|
|
6135
|
+
const store = options.store ?? new MemorySessionStore({
|
|
6136
|
+
cleanupInterval: 60000
|
|
6137
|
+
});
|
|
6138
|
+
const service = new SessionService({
|
|
6139
|
+
store,
|
|
6140
|
+
name: options.name,
|
|
6141
|
+
maxAge: options.maxAge,
|
|
6142
|
+
rolling: options.rolling,
|
|
6143
|
+
cookie: options.cookie
|
|
6144
|
+
});
|
|
6145
|
+
providers2.push({
|
|
6146
|
+
provide: SESSION_SERVICE_TOKEN,
|
|
6147
|
+
useValue: service
|
|
6148
|
+
}, {
|
|
6149
|
+
provide: SESSION_OPTIONS_TOKEN,
|
|
6150
|
+
useValue: options
|
|
6151
|
+
}, SessionService);
|
|
6152
|
+
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, SessionModule) || {};
|
|
6153
|
+
const metadata = {
|
|
6154
|
+
...existingMetadata,
|
|
6155
|
+
providers: [...existingMetadata.providers || [], ...providers2],
|
|
6156
|
+
exports: [
|
|
6157
|
+
...existingMetadata.exports || [],
|
|
6158
|
+
SESSION_SERVICE_TOKEN,
|
|
6159
|
+
SESSION_OPTIONS_TOKEN,
|
|
6160
|
+
SessionService
|
|
6161
|
+
]
|
|
6162
|
+
};
|
|
6163
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SessionModule);
|
|
6164
|
+
return SessionModule;
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
SessionModule = __legacyDecorateClassTS([
|
|
6168
|
+
Module({
|
|
6169
|
+
providers: []
|
|
6170
|
+
})
|
|
6171
|
+
], SessionModule);
|
|
6172
|
+
// src/session/middleware.ts
|
|
6173
|
+
function createSessionMiddleware(container) {
|
|
6174
|
+
return async (context2, next) => {
|
|
6175
|
+
let sessionService;
|
|
6176
|
+
try {
|
|
6177
|
+
sessionService = container.resolve(SESSION_SERVICE_TOKEN);
|
|
6178
|
+
} catch {
|
|
6179
|
+
return await next();
|
|
6180
|
+
}
|
|
6181
|
+
if (!sessionService) {
|
|
6182
|
+
return await next();
|
|
6183
|
+
}
|
|
6184
|
+
const cookieName = sessionService.getCookieName();
|
|
6185
|
+
const cookieHeader = context2.request.headers.get("cookie");
|
|
6186
|
+
let sessionId;
|
|
6187
|
+
if (cookieHeader) {
|
|
6188
|
+
const cookies = cookieHeader.split(";").map((c) => c.trim());
|
|
6189
|
+
for (const cookie of cookies) {
|
|
6190
|
+
if (cookie.startsWith(`${cookieName}=`)) {
|
|
6191
|
+
sessionId = cookie.substring(cookieName.length + 1).trim();
|
|
6192
|
+
break;
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6196
|
+
if (sessionId) {
|
|
6197
|
+
const session = await sessionService.get(sessionId);
|
|
6198
|
+
if (session) {
|
|
6199
|
+
context2.session = session;
|
|
6200
|
+
context2.sessionId = sessionId;
|
|
6201
|
+
} else {
|
|
6202
|
+
sessionId = undefined;
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
if (!sessionId) {
|
|
6206
|
+
const newSession = await sessionService.create();
|
|
6207
|
+
context2.session = newSession;
|
|
6208
|
+
context2.sessionId = newSession.id;
|
|
6209
|
+
sessionId = newSession.id;
|
|
6210
|
+
}
|
|
6211
|
+
const response = await next();
|
|
6212
|
+
const currentSessionId = context2.sessionId;
|
|
6213
|
+
if (currentSessionId) {
|
|
6214
|
+
const cookieOptions = sessionService.getCookieOptions();
|
|
6215
|
+
const maxAge = sessionService.getMaxAge();
|
|
6216
|
+
const cookieValue = buildCookie(cookieName, currentSessionId, {
|
|
6217
|
+
...cookieOptions,
|
|
6218
|
+
maxAge
|
|
6219
|
+
});
|
|
6220
|
+
const newHeaders = new Headers(response.headers);
|
|
6221
|
+
newHeaders.set("Set-Cookie", cookieValue);
|
|
6222
|
+
return new Response(response.body, {
|
|
6223
|
+
status: response.status,
|
|
6224
|
+
statusText: response.statusText,
|
|
6225
|
+
headers: newHeaders
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
return response;
|
|
6229
|
+
};
|
|
6230
|
+
}
|
|
6231
|
+
function buildCookie(name, value, options) {
|
|
6232
|
+
let cookie = `${name}=${value}`;
|
|
6233
|
+
if (options.path) {
|
|
6234
|
+
cookie += `; Path=${options.path}`;
|
|
6235
|
+
}
|
|
6236
|
+
if (options.domain) {
|
|
6237
|
+
cookie += `; Domain=${options.domain}`;
|
|
6238
|
+
}
|
|
6239
|
+
if (options.maxAge) {
|
|
6240
|
+
cookie += `; Max-Age=${Math.floor(options.maxAge / 1000)}`;
|
|
6241
|
+
}
|
|
6242
|
+
if (options.secure) {
|
|
6243
|
+
cookie += "; Secure";
|
|
6244
|
+
}
|
|
6245
|
+
if (options.httpOnly) {
|
|
6246
|
+
cookie += "; HttpOnly";
|
|
6247
|
+
}
|
|
6248
|
+
if (options.sameSite) {
|
|
6249
|
+
cookie += `; SameSite=${options.sameSite}`;
|
|
6250
|
+
}
|
|
6251
|
+
return cookie;
|
|
6252
|
+
}
|
|
6253
|
+
// src/session/decorators.ts
|
|
6254
|
+
import"reflect-metadata";
|
|
6255
|
+
function Session() {
|
|
6256
|
+
return createParamDecorator("session" /* SESSION */);
|
|
6257
|
+
}
|
|
5184
6258
|
export {
|
|
5185
6259
|
requiresAuth,
|
|
5186
6260
|
getTransactionMetadata,
|
|
@@ -5192,6 +6266,7 @@ export {
|
|
|
5192
6266
|
createTokenKeyGenerator,
|
|
5193
6267
|
createSwaggerUIMiddleware,
|
|
5194
6268
|
createStaticFileMiddleware,
|
|
6269
|
+
createSessionMiddleware,
|
|
5195
6270
|
createSecurityFilter,
|
|
5196
6271
|
createRequestLoggingMiddleware,
|
|
5197
6272
|
createRateLimitMiddleware,
|
|
@@ -5216,8 +6291,13 @@ export {
|
|
|
5216
6291
|
SwaggerGenerator,
|
|
5217
6292
|
SwaggerExtension,
|
|
5218
6293
|
StressTester,
|
|
6294
|
+
SessionService,
|
|
6295
|
+
SessionModule,
|
|
6296
|
+
Session,
|
|
5219
6297
|
SecurityModule,
|
|
5220
6298
|
SecurityContextHolder,
|
|
6299
|
+
SESSION_SERVICE_TOKEN,
|
|
6300
|
+
SESSION_OPTIONS_TOKEN,
|
|
5221
6301
|
Router,
|
|
5222
6302
|
RouteRegistry,
|
|
5223
6303
|
Route,
|
|
@@ -5225,8 +6305,15 @@ export {
|
|
|
5225
6305
|
ResponseBuilder,
|
|
5226
6306
|
RequestWrapper,
|
|
5227
6307
|
Repository,
|
|
6308
|
+
RedisSessionStore,
|
|
6309
|
+
RedisCacheStore,
|
|
5228
6310
|
RateLimit,
|
|
6311
|
+
QueueService,
|
|
6312
|
+
QueueModule,
|
|
6313
|
+
Queue,
|
|
5229
6314
|
Query,
|
|
6315
|
+
QUEUE_SERVICE_TOKEN,
|
|
6316
|
+
QUEUE_OPTIONS_TOKEN,
|
|
5230
6317
|
Propagation,
|
|
5231
6318
|
PrometheusFormatter,
|
|
5232
6319
|
PrimaryKey,
|
|
@@ -5252,6 +6339,9 @@ export {
|
|
|
5252
6339
|
MiddlewarePipeline,
|
|
5253
6340
|
MetricsModule,
|
|
5254
6341
|
MetricsCollector,
|
|
6342
|
+
MemorySessionStore,
|
|
6343
|
+
MemoryQueueStore,
|
|
6344
|
+
MemoryCacheStore,
|
|
5255
6345
|
METRICS_SERVICE_TOKEN,
|
|
5256
6346
|
METRICS_OPTIONS_TOKEN,
|
|
5257
6347
|
LoggerModule,
|
|
@@ -5288,6 +6378,7 @@ export {
|
|
|
5288
6378
|
DELETE,
|
|
5289
6379
|
DATABASE_SERVICE_TOKEN,
|
|
5290
6380
|
DATABASE_OPTIONS_TOKEN,
|
|
6381
|
+
Cron,
|
|
5291
6382
|
ControllerRegistry,
|
|
5292
6383
|
Controller,
|
|
5293
6384
|
Context,
|
|
@@ -5296,7 +6387,14 @@ export {
|
|
|
5296
6387
|
ConfigService,
|
|
5297
6388
|
ConfigModule,
|
|
5298
6389
|
Column,
|
|
6390
|
+
Cacheable,
|
|
6391
|
+
CacheService,
|
|
6392
|
+
CachePut,
|
|
6393
|
+
CacheModule,
|
|
6394
|
+
CacheEvict,
|
|
5299
6395
|
CONFIG_SERVICE_TOKEN,
|
|
6396
|
+
CACHE_SERVICE_TOKEN,
|
|
6397
|
+
CACHE_OPTIONS_TOKEN,
|
|
5300
6398
|
BunServer,
|
|
5301
6399
|
BodyParser,
|
|
5302
6400
|
Body,
|