@dangao/bun-server 1.0.3 → 1.1.3
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/controller/controller.d.ts +1 -1
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts.map +1 -1
- package/dist/database/database-extension.d.ts.map +1 -1
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/database/orm/transaction-decorator.d.ts +1 -0
- package/dist/database/orm/transaction-decorator.d.ts.map +1 -1
- package/dist/database/orm/transaction-interceptor.d.ts +12 -3
- package/dist/database/orm/transaction-interceptor.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +678 -310
- package/dist/interceptor/base-interceptor.d.ts +94 -0
- package/dist/interceptor/base-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts +69 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/index.d.ts +4 -0
- package/dist/interceptor/builtin/index.d.ts.map +1 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts +56 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts +70 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -0
- package/dist/interceptor/index.d.ts +7 -0
- package/dist/interceptor/index.d.ts.map +1 -0
- package/dist/interceptor/interceptor-chain.d.ts +22 -0
- package/dist/interceptor/interceptor-chain.d.ts.map +1 -0
- package/dist/interceptor/interceptor-registry.d.ts +59 -0
- package/dist/interceptor/interceptor-registry.d.ts.map +1 -0
- package/dist/interceptor/metadata.d.ts +12 -0
- package/dist/interceptor/metadata.d.ts.map +1 -0
- package/dist/interceptor/types.d.ts +42 -0
- package/dist/interceptor/types.d.ts.map +1 -0
- package/dist/middleware/decorators.d.ts +2 -1
- package/dist/middleware/decorators.d.ts.map +1 -1
- package/dist/router/decorators.d.ts.map +1 -1
- package/dist/router/registry.d.ts +2 -1
- package/dist/router/registry.d.ts.map +1 -1
- package/dist/router/route.d.ts +3 -2
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/router.d.ts +2 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/websocket/decorators.d.ts +2 -1
- package/dist/websocket/decorators.d.ts.map +1 -1
- package/docs/api.md +602 -0
- package/docs/best-practices.md +12 -0
- package/docs/custom-decorators.md +440 -0
- package/docs/deployment.md +447 -0
- package/docs/error-handling.md +462 -0
- package/docs/extensions.md +569 -0
- package/docs/guide.md +634 -0
- package/docs/migration.md +10 -0
- package/docs/performance.md +452 -0
- package/docs/troubleshooting.md +286 -0
- package/docs/zh/api.md +168 -0
- package/docs/zh/best-practices.md +38 -0
- package/docs/zh/custom-decorators.md +466 -0
- package/docs/zh/deployment.md +445 -0
- package/docs/zh/error-handling.md +456 -0
- package/docs/zh/extensions.md +584 -0
- package/docs/zh/guide.md +361 -0
- package/docs/zh/migration.md +86 -0
- package/docs/zh/performance.md +451 -0
- package/docs/zh/troubleshooting.md +279 -0
- package/package.json +5 -4
- package/src/controller/controller.ts +41 -14
- package/src/core/application.ts +7 -1
- package/src/database/database-extension.ts +23 -2
- package/src/database/database-module.ts +6 -0
- package/src/database/orm/transaction-decorator.ts +33 -2
- package/src/database/orm/transaction-interceptor.ts +31 -11
- package/src/index.ts +22 -0
- package/src/interceptor/base-interceptor.ts +203 -0
- package/src/interceptor/builtin/cache-interceptor.ts +169 -0
- package/src/interceptor/builtin/index.ts +28 -0
- package/src/interceptor/builtin/log-interceptor.ts +178 -0
- package/src/interceptor/builtin/permission-interceptor.ts +173 -0
- package/src/interceptor/index.ts +26 -0
- package/src/interceptor/interceptor-chain.ts +79 -0
- package/src/interceptor/interceptor-registry.ts +132 -0
- package/src/interceptor/metadata.ts +40 -0
- package/src/interceptor/types.ts +52 -0
- package/src/middleware/decorators.ts +2 -1
- package/src/router/decorators.ts +44 -43
- package/src/router/registry.ts +2 -1
- package/src/router/route.ts +3 -2
- package/src/router/router.ts +2 -1
- package/src/websocket/decorators.ts +3 -1
- package/tests/controller/path-combination.test.ts +207 -0
- package/tests/interceptor/builtin/cache-interceptor.test.ts +137 -0
- package/tests/interceptor/builtin/permission-interceptor.test.ts +182 -0
- package/tests/interceptor/interceptor-advanced-integration.test.ts +592 -0
- package/tests/interceptor/interceptor-arg-modification.test.ts +76 -0
- package/tests/interceptor/interceptor-chain.test.ts +199 -0
- package/tests/interceptor/interceptor-integration.test.ts +230 -0
- package/tests/interceptor/interceptor-registry.test.ts +200 -0
- package/tests/interceptor/perf/interceptor-performance.test.ts +341 -0
- package/tests/router/decorators.test.ts +13 -15
package/dist/index.js
CHANGED
|
@@ -531,181 +531,6 @@ var init_handler = __esm(() => {
|
|
|
531
531
|
init_i18n();
|
|
532
532
|
});
|
|
533
533
|
|
|
534
|
-
// src/database/orm/transaction-types.ts
|
|
535
|
-
var Propagation, IsolationLevel, TransactionStatus, TRANSACTION_SERVICE_TOKEN;
|
|
536
|
-
var init_transaction_types = __esm(() => {
|
|
537
|
-
((Propagation2) => {
|
|
538
|
-
Propagation2["REQUIRED"] = "REQUIRED";
|
|
539
|
-
Propagation2["REQUIRES_NEW"] = "REQUIRES_NEW";
|
|
540
|
-
Propagation2["SUPPORTS"] = "SUPPORTS";
|
|
541
|
-
Propagation2["NOT_SUPPORTED"] = "NOT_SUPPORTED";
|
|
542
|
-
Propagation2["NEVER"] = "NEVER";
|
|
543
|
-
Propagation2["NESTED"] = "NESTED";
|
|
544
|
-
})(Propagation ||= {});
|
|
545
|
-
((IsolationLevel2) => {
|
|
546
|
-
IsolationLevel2["READ_UNCOMMITTED"] = "READ_UNCOMMITTED";
|
|
547
|
-
IsolationLevel2["READ_COMMITTED"] = "READ_COMMITTED";
|
|
548
|
-
IsolationLevel2["REPEATABLE_READ"] = "REPEATABLE_READ";
|
|
549
|
-
IsolationLevel2["SERIALIZABLE"] = "SERIALIZABLE";
|
|
550
|
-
})(IsolationLevel ||= {});
|
|
551
|
-
((TransactionStatus2) => {
|
|
552
|
-
TransactionStatus2["ACTIVE"] = "ACTIVE";
|
|
553
|
-
TransactionStatus2["COMMITTED"] = "COMMITTED";
|
|
554
|
-
TransactionStatus2["ROLLED_BACK"] = "ROLLED_BACK";
|
|
555
|
-
TransactionStatus2["SUSPENDED"] = "SUSPENDED";
|
|
556
|
-
})(TransactionStatus ||= {});
|
|
557
|
-
TRANSACTION_SERVICE_TOKEN = Symbol("@dangao/bun-server:orm:transaction:service");
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
// src/database/orm/transaction-decorator.ts
|
|
561
|
-
var exports_transaction_decorator = {};
|
|
562
|
-
__export(exports_transaction_decorator, {
|
|
563
|
-
getTransactionMetadata: () => getTransactionMetadata,
|
|
564
|
-
Transactional: () => Transactional,
|
|
565
|
-
TRANSACTION_METADATA_KEY: () => TRANSACTION_METADATA_KEY
|
|
566
|
-
});
|
|
567
|
-
import"reflect-metadata";
|
|
568
|
-
function Transactional(options) {
|
|
569
|
-
return (target, propertyKey, descriptor) => {
|
|
570
|
-
if (!descriptor || typeof descriptor.value !== "function") {
|
|
571
|
-
throw new Error("@Transactional() can only be applied to methods");
|
|
572
|
-
}
|
|
573
|
-
const metadata = {
|
|
574
|
-
propagation: options?.propagation ?? "REQUIRED" /* REQUIRED */,
|
|
575
|
-
isolationLevel: options?.isolationLevel,
|
|
576
|
-
timeout: options?.timeout,
|
|
577
|
-
readOnly: options?.readOnly ?? false,
|
|
578
|
-
rollbackFor: options?.rollbackFor ?? [],
|
|
579
|
-
noRollbackFor: options?.noRollbackFor ?? []
|
|
580
|
-
};
|
|
581
|
-
Reflect.defineMetadata(TRANSACTION_METADATA_KEY, metadata, target, propertyKey);
|
|
582
|
-
};
|
|
583
|
-
}
|
|
584
|
-
function getTransactionMetadata(target, propertyKey) {
|
|
585
|
-
if (typeof target === "object" && target !== null) {
|
|
586
|
-
return Reflect.getMetadata(TRANSACTION_METADATA_KEY, target, propertyKey);
|
|
587
|
-
}
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
var TRANSACTION_METADATA_KEY;
|
|
591
|
-
var init_transaction_decorator = __esm(() => {
|
|
592
|
-
init_transaction_types();
|
|
593
|
-
TRANSACTION_METADATA_KEY = Symbol("@dangao/bun-server:orm:transaction");
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
// src/database/orm/transaction-interceptor.ts
|
|
597
|
-
var exports_transaction_interceptor = {};
|
|
598
|
-
__export(exports_transaction_interceptor, {
|
|
599
|
-
TransactionInterceptor: () => TransactionInterceptor
|
|
600
|
-
});
|
|
601
|
-
|
|
602
|
-
class TransactionInterceptor {
|
|
603
|
-
static async executeWithTransaction(target, propertyKey, originalMethod, args, container) {
|
|
604
|
-
const transactionMetadata = getTransactionMetadata(target, propertyKey);
|
|
605
|
-
if (!transactionMetadata) {
|
|
606
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
607
|
-
}
|
|
608
|
-
let transactionManager;
|
|
609
|
-
try {
|
|
610
|
-
transactionManager = container.resolve(TRANSACTION_SERVICE_TOKEN);
|
|
611
|
-
} catch (error) {
|
|
612
|
-
console.warn("TransactionManager not found, executing without transaction");
|
|
613
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
614
|
-
}
|
|
615
|
-
const propagation = transactionMetadata.propagation ?? "REQUIRED" /* REQUIRED */;
|
|
616
|
-
const currentTransaction = transactionManager.getCurrentTransaction();
|
|
617
|
-
switch (propagation) {
|
|
618
|
-
case "REQUIRED" /* REQUIRED */:
|
|
619
|
-
if (currentTransaction) {
|
|
620
|
-
return await this.executeInExistingTransaction(originalMethod, target, args, currentTransaction.id, transactionManager);
|
|
621
|
-
} else {
|
|
622
|
-
return await this.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
623
|
-
}
|
|
624
|
-
case "REQUIRES_NEW" /* REQUIRES_NEW */:
|
|
625
|
-
return await this.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
626
|
-
case "SUPPORTS" /* SUPPORTS */:
|
|
627
|
-
if (currentTransaction) {
|
|
628
|
-
return await this.executeInExistingTransaction(originalMethod, target, args, currentTransaction.id, transactionManager);
|
|
629
|
-
} else {
|
|
630
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
631
|
-
}
|
|
632
|
-
case "NOT_SUPPORTED" /* NOT_SUPPORTED */:
|
|
633
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
634
|
-
case "NEVER" /* NEVER */:
|
|
635
|
-
if (currentTransaction) {
|
|
636
|
-
throw new Error("Transaction propagation NEVER requires no existing transaction");
|
|
637
|
-
}
|
|
638
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
639
|
-
case "NESTED" /* NESTED */:
|
|
640
|
-
if (currentTransaction) {
|
|
641
|
-
return await this.executeInNestedTransaction(originalMethod, target, args, currentTransaction.id, transactionMetadata, transactionManager);
|
|
642
|
-
} else {
|
|
643
|
-
return await this.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
644
|
-
}
|
|
645
|
-
default:
|
|
646
|
-
return await Promise.resolve(originalMethod.apply(target, args));
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
static async executeInNewTransaction(method, target, args, options, transactionManager) {
|
|
650
|
-
const context = await transactionManager.beginTransaction({
|
|
651
|
-
timeout: options.timeout
|
|
652
|
-
});
|
|
653
|
-
try {
|
|
654
|
-
const result = await Promise.resolve(method.apply(target, args));
|
|
655
|
-
await transactionManager.commitTransaction(context.id);
|
|
656
|
-
return result;
|
|
657
|
-
} catch (error) {
|
|
658
|
-
if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
|
|
659
|
-
await transactionManager.rollbackTransaction(context.id);
|
|
660
|
-
} else {
|
|
661
|
-
await transactionManager.commitTransaction(context.id);
|
|
662
|
-
}
|
|
663
|
-
throw error;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
static async executeInExistingTransaction(method, target, args, transactionId, transactionManager) {
|
|
667
|
-
return await Promise.resolve(method.apply(target, args));
|
|
668
|
-
}
|
|
669
|
-
static async executeInNestedTransaction(method, target, args, parentTransactionId, options, transactionManager) {
|
|
670
|
-
const savepointName = await transactionManager.createSavepoint(parentTransactionId);
|
|
671
|
-
try {
|
|
672
|
-
const result = await Promise.resolve(method.apply(target, args));
|
|
673
|
-
return result;
|
|
674
|
-
} catch (error) {
|
|
675
|
-
if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
|
|
676
|
-
await transactionManager.rollbackToSavepoint(parentTransactionId, savepointName);
|
|
677
|
-
}
|
|
678
|
-
throw error;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
static shouldRollback(error, rollbackFor, noRollbackFor) {
|
|
682
|
-
if (!error) {
|
|
683
|
-
return false;
|
|
684
|
-
}
|
|
685
|
-
if (noRollbackFor && noRollbackFor.length > 0) {
|
|
686
|
-
for (const ErrorClass of noRollbackFor) {
|
|
687
|
-
if (error instanceof ErrorClass) {
|
|
688
|
-
return false;
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
if (rollbackFor && rollbackFor.length > 0) {
|
|
693
|
-
for (const ErrorClass of rollbackFor) {
|
|
694
|
-
if (error instanceof ErrorClass) {
|
|
695
|
-
return true;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
return true;
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
var init_transaction_interceptor = __esm(() => {
|
|
704
|
-
init_transaction_types();
|
|
705
|
-
init_transaction_decorator();
|
|
706
|
-
init_transaction_types();
|
|
707
|
-
});
|
|
708
|
-
|
|
709
534
|
// src/request/body-parser.ts
|
|
710
535
|
class BodyParser {
|
|
711
536
|
static async parse(request) {
|
|
@@ -1786,40 +1611,35 @@ import"reflect-metadata";
|
|
|
1786
1611
|
var ROUTE_METADATA_KEY = Symbol("route");
|
|
1787
1612
|
function createRouteDecorator(method, path) {
|
|
1788
1613
|
return function(target, propertyKey, descriptor) {
|
|
1789
|
-
const
|
|
1790
|
-
if (
|
|
1791
|
-
const
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1614
|
+
const constructor = target.constructor;
|
|
1615
|
+
if (constructor && typeof constructor === "function") {
|
|
1616
|
+
const hasController = Reflect.getMetadata(CONTROLLER_METADATA_KEY, constructor);
|
|
1617
|
+
if (hasController === undefined) {}
|
|
1618
|
+
}
|
|
1619
|
+
const existingRoutes = Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
|
|
1620
|
+
let propertyKeyStr;
|
|
1621
|
+
if (propertyKey && propertyKey !== "") {
|
|
1622
|
+
propertyKeyStr = typeof propertyKey === "string" ? propertyKey : String(propertyKey);
|
|
1623
|
+
} else {
|
|
1624
|
+
propertyKeyStr = descriptor.value.name || "";
|
|
1625
|
+
}
|
|
1626
|
+
if (!propertyKeyStr) {
|
|
1627
|
+
const propertyNames = Object.getOwnPropertyNames(target);
|
|
1628
|
+
for (const key of propertyNames) {
|
|
1629
|
+
const targetDescriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
1630
|
+
if (targetDescriptor?.value === descriptor.value) {
|
|
1631
|
+
propertyKeyStr = key;
|
|
1632
|
+
break;
|
|
1806
1633
|
}
|
|
1807
1634
|
}
|
|
1808
|
-
existingRoutes.push({
|
|
1809
|
-
method,
|
|
1810
|
-
path,
|
|
1811
|
-
handler: descriptor.value,
|
|
1812
|
-
propertyKey: propertyKeyStr || undefined
|
|
1813
|
-
});
|
|
1814
|
-
Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
|
|
1815
|
-
} else {
|
|
1816
|
-
const handler = descriptor.value;
|
|
1817
|
-
const registry = RouteRegistry.getInstance();
|
|
1818
|
-
registry.register(method, path, handler);
|
|
1819
|
-
const existingRoutes = Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
|
|
1820
|
-
existingRoutes.push({ method, path, handler });
|
|
1821
|
-
Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
|
|
1822
1635
|
}
|
|
1636
|
+
existingRoutes.push({
|
|
1637
|
+
method,
|
|
1638
|
+
path,
|
|
1639
|
+
handler: descriptor.value,
|
|
1640
|
+
propertyKey: propertyKeyStr || undefined
|
|
1641
|
+
});
|
|
1642
|
+
Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
|
|
1823
1643
|
};
|
|
1824
1644
|
}
|
|
1825
1645
|
function GET(path) {
|
|
@@ -2237,17 +2057,446 @@ function createStaticFileMiddleware(options) {
|
|
|
2237
2057
|
if (enableCache) {
|
|
2238
2058
|
headers.set("Cache-Control", "public, max-age=31536000, immutable");
|
|
2239
2059
|
}
|
|
2240
|
-
if (!headers.has("Content-Type") && file.type) {
|
|
2241
|
-
headers.set("Content-Type", file.type);
|
|
2060
|
+
if (!headers.has("Content-Type") && file.type) {
|
|
2061
|
+
headers.set("Content-Type", file.type);
|
|
2062
|
+
}
|
|
2063
|
+
return new Response(file, {
|
|
2064
|
+
status: 200,
|
|
2065
|
+
headers
|
|
2066
|
+
});
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
// src/controller/controller.ts
|
|
2070
|
+
init_validation();
|
|
2071
|
+
|
|
2072
|
+
// src/interceptor/types.ts
|
|
2073
|
+
var INTERCEPTOR_REGISTRY_TOKEN = Symbol("@dangao/bun-server:interceptor-registry");
|
|
2074
|
+
// src/interceptor/interceptor-registry.ts
|
|
2075
|
+
class InterceptorRegistry {
|
|
2076
|
+
interceptors = new Map;
|
|
2077
|
+
register(metadataKey, interceptor, priority = 100) {
|
|
2078
|
+
if (!this.interceptors.has(metadataKey)) {
|
|
2079
|
+
this.interceptors.set(metadataKey, []);
|
|
2080
|
+
}
|
|
2081
|
+
const metadataList = this.interceptors.get(metadataKey);
|
|
2082
|
+
const exists = metadataList.some((meta) => meta.interceptor === interceptor);
|
|
2083
|
+
if (!exists) {
|
|
2084
|
+
metadataList.push({
|
|
2085
|
+
metadataKey,
|
|
2086
|
+
interceptor,
|
|
2087
|
+
priority
|
|
2088
|
+
});
|
|
2089
|
+
metadataList.sort((a, b) => a.priority - b.priority);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
getInterceptorMetadata(metadataKey) {
|
|
2093
|
+
const metadataList = this.interceptors.get(metadataKey);
|
|
2094
|
+
if (!metadataList || metadataList.length === 0) {
|
|
2095
|
+
return [];
|
|
2096
|
+
}
|
|
2097
|
+
return [...metadataList];
|
|
2098
|
+
}
|
|
2099
|
+
getInterceptors(metadataKey) {
|
|
2100
|
+
const metadataList = this.interceptors.get(metadataKey);
|
|
2101
|
+
if (!metadataList || metadataList.length === 0) {
|
|
2102
|
+
return [];
|
|
2103
|
+
}
|
|
2104
|
+
return metadataList.map((meta) => meta.interceptor);
|
|
2105
|
+
}
|
|
2106
|
+
hasInterceptor(metadataKey) {
|
|
2107
|
+
const metadataList = this.interceptors.get(metadataKey);
|
|
2108
|
+
return metadataList !== undefined && metadataList.length > 0;
|
|
2109
|
+
}
|
|
2110
|
+
getAllMetadataKeys() {
|
|
2111
|
+
return this.interceptors.keys();
|
|
2112
|
+
}
|
|
2113
|
+
clear() {
|
|
2114
|
+
this.interceptors.clear();
|
|
2115
|
+
}
|
|
2116
|
+
remove(metadataKey) {
|
|
2117
|
+
this.interceptors.delete(metadataKey);
|
|
2118
|
+
}
|
|
2119
|
+
count(metadataKey) {
|
|
2120
|
+
if (metadataKey) {
|
|
2121
|
+
const metadataList = this.interceptors.get(metadataKey);
|
|
2122
|
+
return metadataList ? metadataList.length : 0;
|
|
2123
|
+
}
|
|
2124
|
+
let total = 0;
|
|
2125
|
+
for (const metadataList of this.interceptors.values()) {
|
|
2126
|
+
total += metadataList.length;
|
|
2127
|
+
}
|
|
2128
|
+
return total;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
// src/interceptor/interceptor-chain.ts
|
|
2132
|
+
class InterceptorChain {
|
|
2133
|
+
static async execute(interceptors, target, propertyKey, originalMethod, args, container, context) {
|
|
2134
|
+
if (interceptors.length === 0) {
|
|
2135
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
2136
|
+
}
|
|
2137
|
+
let index = 0;
|
|
2138
|
+
let currentArgs = args;
|
|
2139
|
+
const next = async (modifiedArgs) => {
|
|
2140
|
+
if (modifiedArgs && modifiedArgs.length > 0) {
|
|
2141
|
+
currentArgs = modifiedArgs;
|
|
2142
|
+
}
|
|
2143
|
+
if (index >= interceptors.length) {
|
|
2144
|
+
return await Promise.resolve(originalMethod.apply(target, currentArgs));
|
|
2145
|
+
}
|
|
2146
|
+
const interceptor = interceptors[index++];
|
|
2147
|
+
const wrappedNext = (...nextArgs) => {
|
|
2148
|
+
const result = next(nextArgs.length > 0 ? nextArgs : undefined);
|
|
2149
|
+
return result;
|
|
2150
|
+
};
|
|
2151
|
+
return await interceptor.execute(target, propertyKey, wrappedNext, currentArgs, container, context);
|
|
2152
|
+
};
|
|
2153
|
+
return await next();
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
// src/interceptor/metadata.ts
|
|
2157
|
+
import"reflect-metadata";
|
|
2158
|
+
function scanInterceptorMetadata(target, propertyKey, registry) {
|
|
2159
|
+
const interceptorMetadataList = [];
|
|
2160
|
+
for (const metadataKey of registry.getAllMetadataKeys()) {
|
|
2161
|
+
const metadata = Reflect.getMetadata(metadataKey, target, propertyKey);
|
|
2162
|
+
if (metadata !== undefined && metadata !== null) {
|
|
2163
|
+
const metadataList = registry.getInterceptorMetadata(metadataKey);
|
|
2164
|
+
if (metadataList.length > 0) {
|
|
2165
|
+
interceptorMetadataList.push(...metadataList);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
interceptorMetadataList.sort((a, b) => a.priority - b.priority);
|
|
2170
|
+
return interceptorMetadataList.map((meta) => meta.interceptor);
|
|
2171
|
+
}
|
|
2172
|
+
// src/interceptor/base-interceptor.ts
|
|
2173
|
+
import"reflect-metadata";
|
|
2174
|
+
|
|
2175
|
+
class BaseInterceptor {
|
|
2176
|
+
async before(target, propertyKey, args, container, context) {}
|
|
2177
|
+
async after(target, propertyKey, result, container, context) {
|
|
2178
|
+
return result;
|
|
2179
|
+
}
|
|
2180
|
+
async onError(target, propertyKey, error, container, context) {
|
|
2181
|
+
throw error;
|
|
2182
|
+
}
|
|
2183
|
+
getMetadata(target, propertyKey, metadataKey) {
|
|
2184
|
+
if (typeof target !== "object" || target === null) {
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
let metadata = Reflect.getMetadata(metadataKey, target, propertyKey);
|
|
2188
|
+
if (metadata !== undefined) {
|
|
2189
|
+
return metadata;
|
|
2190
|
+
}
|
|
2191
|
+
const prototype = Object.getPrototypeOf(target);
|
|
2192
|
+
if (prototype && prototype !== Object.prototype) {
|
|
2193
|
+
metadata = Reflect.getMetadata(metadataKey, prototype, propertyKey);
|
|
2194
|
+
if (metadata !== undefined) {
|
|
2195
|
+
return metadata;
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
const constructor = target.constructor;
|
|
2199
|
+
if (constructor && typeof constructor === "function") {
|
|
2200
|
+
if (target !== constructor.prototype) {
|
|
2201
|
+
metadata = Reflect.getMetadata(metadataKey, constructor.prototype, propertyKey);
|
|
2202
|
+
if (metadata !== undefined) {
|
|
2203
|
+
return metadata;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
resolveService(container, token) {
|
|
2210
|
+
return container.resolve(token);
|
|
2211
|
+
}
|
|
2212
|
+
getContextValue(context, key) {
|
|
2213
|
+
if (!context) {
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
getHeader(context, headerName) {
|
|
2219
|
+
if (!context) {
|
|
2220
|
+
return null;
|
|
2221
|
+
}
|
|
2222
|
+
return context.getHeader(headerName);
|
|
2223
|
+
}
|
|
2224
|
+
getQuery(context, paramName) {
|
|
2225
|
+
if (!context) {
|
|
2226
|
+
return null;
|
|
2227
|
+
}
|
|
2228
|
+
return context.getQuery(paramName);
|
|
2229
|
+
}
|
|
2230
|
+
getParam(context, paramName) {
|
|
2231
|
+
if (!context) {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
return context.getParam(paramName);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
// src/interceptor/builtin/cache-interceptor.ts
|
|
2238
|
+
import"reflect-metadata";
|
|
2239
|
+
var CACHE_METADATA_KEY = Symbol("@dangao/bun-server:interceptor:cache");
|
|
2240
|
+
function Cache(options = {}) {
|
|
2241
|
+
return (target, propertyKey, descriptor) => {
|
|
2242
|
+
const metadata = {
|
|
2243
|
+
ttl: options.ttl ?? 60000,
|
|
2244
|
+
key: options.key
|
|
2245
|
+
};
|
|
2246
|
+
Reflect.defineMetadata(CACHE_METADATA_KEY, metadata, target, propertyKey);
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
class CacheInterceptor extends BaseInterceptor {
|
|
2250
|
+
static cache = new Map;
|
|
2251
|
+
async execute(target, propertyKey, originalMethod, args, container, context) {
|
|
2252
|
+
const metadata = this.getMetadata(target, propertyKey, CACHE_METADATA_KEY);
|
|
2253
|
+
if (!metadata) {
|
|
2254
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
2255
|
+
}
|
|
2256
|
+
const cacheKey = this.generateCacheKey(target, propertyKey, args, metadata.key);
|
|
2257
|
+
const cached = CacheInterceptor.cache.get(cacheKey);
|
|
2258
|
+
if (cached && cached.expires > Date.now()) {
|
|
2259
|
+
return cached.data;
|
|
2260
|
+
}
|
|
2261
|
+
const result = await Promise.resolve(originalMethod.apply(target, args));
|
|
2262
|
+
CacheInterceptor.cache.set(cacheKey, {
|
|
2263
|
+
data: result,
|
|
2264
|
+
expires: Date.now() + metadata.ttl
|
|
2265
|
+
});
|
|
2266
|
+
return result;
|
|
2267
|
+
}
|
|
2268
|
+
generateCacheKey(target, propertyKey, args, customKey) {
|
|
2269
|
+
if (customKey) {
|
|
2270
|
+
return customKey;
|
|
2271
|
+
}
|
|
2272
|
+
const className = typeof target === "object" && target !== null ? target.constructor?.name || "Unknown" : "Unknown";
|
|
2273
|
+
const methodName = String(propertyKey);
|
|
2274
|
+
const argsKey = JSON.stringify(args);
|
|
2275
|
+
return `${className}:${methodName}:${argsKey}`;
|
|
2276
|
+
}
|
|
2277
|
+
static clearCache() {
|
|
2278
|
+
CacheInterceptor.cache.clear();
|
|
2279
|
+
}
|
|
2280
|
+
static clearCacheKey(key) {
|
|
2281
|
+
CacheInterceptor.cache.delete(key);
|
|
2282
|
+
}
|
|
2283
|
+
static getCacheStats() {
|
|
2284
|
+
return {
|
|
2285
|
+
size: CacheInterceptor.cache.size,
|
|
2286
|
+
keys: Array.from(CacheInterceptor.cache.keys())
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
// src/interceptor/builtin/permission-interceptor.ts
|
|
2291
|
+
import"reflect-metadata";
|
|
2292
|
+
var PERMISSION_METADATA_KEY = Symbol("@dangao/bun-server:interceptor:permission");
|
|
2293
|
+
function Permission(options) {
|
|
2294
|
+
return (target, propertyKey, descriptor) => {
|
|
2295
|
+
const metadata = {
|
|
2296
|
+
resource: options.resource,
|
|
2297
|
+
action: options.action,
|
|
2298
|
+
allowAnonymous: options.allowAnonymous ?? false
|
|
2299
|
+
};
|
|
2300
|
+
Reflect.defineMetadata(PERMISSION_METADATA_KEY, metadata, target, propertyKey);
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
class PermissionInterceptor extends BaseInterceptor {
|
|
2304
|
+
static PERMISSION_SERVICE_TOKEN = Symbol("@dangao/bun-server:interceptor:permission:service");
|
|
2305
|
+
async execute(target, propertyKey, originalMethod, args, container, context) {
|
|
2306
|
+
const metadata = this.getMetadata(target, propertyKey, PERMISSION_METADATA_KEY);
|
|
2307
|
+
if (!metadata) {
|
|
2308
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
2309
|
+
}
|
|
2310
|
+
if (metadata.allowAnonymous) {
|
|
2311
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
2312
|
+
}
|
|
2313
|
+
const userId = this.getUserId(context);
|
|
2314
|
+
let permissionService;
|
|
2315
|
+
try {
|
|
2316
|
+
permissionService = this.resolveService(container, PermissionInterceptor.PERMISSION_SERVICE_TOKEN);
|
|
2317
|
+
} catch (error) {
|
|
2318
|
+
throw new Error("PermissionService not found. Please register PermissionService in DI container.");
|
|
2319
|
+
}
|
|
2320
|
+
const hasPermission = await permissionService.check(userId, metadata.resource, metadata.action);
|
|
2321
|
+
if (!hasPermission) {
|
|
2322
|
+
throw new ForbiddenException(`Permission denied: ${metadata.action} on ${metadata.resource}`);
|
|
2323
|
+
}
|
|
2324
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
2325
|
+
}
|
|
2326
|
+
getUserId(context) {
|
|
2327
|
+
if (!context) {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
const authHeader = this.getHeader(context, "Authorization");
|
|
2331
|
+
if (authHeader && authHeader.startsWith("Bearer ")) {}
|
|
2332
|
+
const userId = this.getHeader(context, "X-User-Id");
|
|
2333
|
+
if (userId) {
|
|
2334
|
+
return userId;
|
|
2335
|
+
}
|
|
2336
|
+
return null;
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
// src/interceptor/builtin/log-interceptor.ts
|
|
2340
|
+
import"reflect-metadata";
|
|
2341
|
+
|
|
2342
|
+
// src/extensions/logger-extension.ts
|
|
2343
|
+
import {
|
|
2344
|
+
LoggerManager as LoggerManager6,
|
|
2345
|
+
LogLevel,
|
|
2346
|
+
SimpleLogger
|
|
2347
|
+
} from "@dangao/logsmith";
|
|
2348
|
+
var LOGGER_TOKEN = Symbol("@dangao/bun-server:logger");
|
|
2349
|
+
|
|
2350
|
+
class LoggerExtension {
|
|
2351
|
+
options;
|
|
2352
|
+
constructor(options = {}) {
|
|
2353
|
+
this.options = {
|
|
2354
|
+
level: LogLevel.INFO,
|
|
2355
|
+
...options
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2358
|
+
register(container) {
|
|
2359
|
+
const logger = new SimpleLogger(this.options);
|
|
2360
|
+
LoggerManager6.setLogger(logger);
|
|
2361
|
+
container.registerInstance(SimpleLogger, logger);
|
|
2362
|
+
container.registerInstance(LOGGER_TOKEN, logger);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
// src/di/module.ts
|
|
2366
|
+
import"reflect-metadata";
|
|
2367
|
+
var MODULE_METADATA_KEY = Symbol("@dangao/bun-server:module");
|
|
2368
|
+
function Module(metadata) {
|
|
2369
|
+
return (target) => {
|
|
2370
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, target);
|
|
2371
|
+
};
|
|
2372
|
+
}
|
|
2373
|
+
function getModuleMetadata(moduleClass) {
|
|
2374
|
+
const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, moduleClass) ?? {};
|
|
2375
|
+
return {
|
|
2376
|
+
imports: metadata.imports ?? [],
|
|
2377
|
+
controllers: metadata.controllers ?? [],
|
|
2378
|
+
providers: metadata.providers ?? [],
|
|
2379
|
+
exports: metadata.exports ?? [],
|
|
2380
|
+
extensions: metadata.extensions ?? [],
|
|
2381
|
+
middlewares: metadata.middlewares ?? []
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// src/extensions/logger-module.ts
|
|
2386
|
+
class LoggerModule {
|
|
2387
|
+
static forRoot(options = {}) {
|
|
2388
|
+
const extensions = [];
|
|
2389
|
+
const middlewares = [];
|
|
2390
|
+
const loggerExtension = new LoggerExtension(options.logger);
|
|
2391
|
+
extensions.push(loggerExtension);
|
|
2392
|
+
if (options.enableRequestLogging !== false) {
|
|
2393
|
+
const requestLoggingMiddleware = createLoggerMiddleware({
|
|
2394
|
+
prefix: options.requestLoggingPrefix
|
|
2395
|
+
});
|
|
2396
|
+
middlewares.push(requestLoggingMiddleware);
|
|
2397
|
+
}
|
|
2398
|
+
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, LoggerModule) || {};
|
|
2399
|
+
const metadata = {
|
|
2400
|
+
...existingMetadata,
|
|
2401
|
+
extensions,
|
|
2402
|
+
middlewares
|
|
2403
|
+
};
|
|
2404
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, LoggerModule);
|
|
2405
|
+
return LoggerModule;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
LoggerModule = __legacyDecorateClassTS([
|
|
2409
|
+
Module({
|
|
2410
|
+
extensions: [],
|
|
2411
|
+
middlewares: []
|
|
2412
|
+
})
|
|
2413
|
+
], LoggerModule);
|
|
2414
|
+
|
|
2415
|
+
// src/extensions/index.ts
|
|
2416
|
+
import { LoggerManager as LoggerManager7, LogLevel as LogLevel2, SimpleLogger as SimpleLogger2 } from "@dangao/logsmith";
|
|
2417
|
+
|
|
2418
|
+
// src/interceptor/builtin/log-interceptor.ts
|
|
2419
|
+
var LOG_METADATA_KEY = Symbol("@dangao/bun-server:interceptor:log");
|
|
2420
|
+
function Log(options = {}) {
|
|
2421
|
+
return (target, propertyKey, descriptor) => {
|
|
2422
|
+
const metadata = {
|
|
2423
|
+
level: options.level || "info",
|
|
2424
|
+
message: options.message,
|
|
2425
|
+
logArgs: options.logArgs ?? false,
|
|
2426
|
+
logResult: options.logResult ?? false,
|
|
2427
|
+
logDuration: options.logDuration ?? true
|
|
2428
|
+
};
|
|
2429
|
+
Reflect.defineMetadata(LOG_METADATA_KEY, metadata, target, propertyKey);
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
class LogInterceptor extends BaseInterceptor {
|
|
2433
|
+
async execute(target, propertyKey, originalMethod, args, container, context) {
|
|
2434
|
+
const options = this.getMetadata(target, propertyKey, LOG_METADATA_KEY) || {};
|
|
2435
|
+
const level = options.level || "info";
|
|
2436
|
+
const methodName = String(propertyKey);
|
|
2437
|
+
const logMessage = options.message || `Executing ${methodName}`;
|
|
2438
|
+
const logArgs = options.logArgs ?? false;
|
|
2439
|
+
const logResult = options.logResult ?? false;
|
|
2440
|
+
const logDuration = options.logDuration ?? true;
|
|
2441
|
+
let logger;
|
|
2442
|
+
try {
|
|
2443
|
+
logger = container.resolve(LOGGER_TOKEN);
|
|
2444
|
+
} catch {}
|
|
2445
|
+
const log = (msg, data) => {
|
|
2446
|
+
if (logger) {
|
|
2447
|
+
switch (level) {
|
|
2448
|
+
case "debug":
|
|
2449
|
+
logger.debug(msg, data);
|
|
2450
|
+
break;
|
|
2451
|
+
case "info":
|
|
2452
|
+
logger.info(msg, data);
|
|
2453
|
+
break;
|
|
2454
|
+
case "warn":
|
|
2455
|
+
logger.warn(msg, data);
|
|
2456
|
+
break;
|
|
2457
|
+
case "error":
|
|
2458
|
+
logger.error(msg, data);
|
|
2459
|
+
break;
|
|
2460
|
+
}
|
|
2461
|
+
} else {
|
|
2462
|
+
console.log(`[${level.toUpperCase()}] ${msg}`, data || "");
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
const startTime = logDuration ? performance.now() : undefined;
|
|
2466
|
+
if (logArgs && args.length > 0) {
|
|
2467
|
+
log(`${logMessage} - Start`, { args });
|
|
2468
|
+
} else {
|
|
2469
|
+
log(`${logMessage} - Start`);
|
|
2470
|
+
}
|
|
2471
|
+
try {
|
|
2472
|
+
const result = await Promise.resolve(originalMethod.apply(target, args));
|
|
2473
|
+
const duration = startTime ? performance.now() - startTime : undefined;
|
|
2474
|
+
const logData = {};
|
|
2475
|
+
if (duration !== undefined) {
|
|
2476
|
+
logData.duration = `${duration.toFixed(2)}ms`;
|
|
2477
|
+
}
|
|
2478
|
+
if (logResult) {
|
|
2479
|
+
logData.result = result;
|
|
2480
|
+
}
|
|
2481
|
+
if (Object.keys(logData).length > 0) {
|
|
2482
|
+
log(`${logMessage} - Completed`, logData);
|
|
2483
|
+
} else {
|
|
2484
|
+
log(`${logMessage} - Completed`);
|
|
2485
|
+
}
|
|
2486
|
+
return result;
|
|
2487
|
+
} catch (error) {
|
|
2488
|
+
const duration = startTime ? performance.now() - startTime : undefined;
|
|
2489
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2490
|
+
const logData = { error: errorMessage };
|
|
2491
|
+
if (duration !== undefined) {
|
|
2492
|
+
logData.duration = `${duration.toFixed(2)}ms`;
|
|
2493
|
+
}
|
|
2494
|
+
log(`${logMessage} - Failed`, logData);
|
|
2495
|
+
throw error;
|
|
2242
2496
|
}
|
|
2243
|
-
|
|
2244
|
-
status: 200,
|
|
2245
|
-
headers
|
|
2246
|
-
});
|
|
2247
|
-
};
|
|
2497
|
+
}
|
|
2248
2498
|
}
|
|
2249
2499
|
// src/controller/controller.ts
|
|
2250
|
-
init_validation();
|
|
2251
2500
|
var CONTROLLER_METADATA_KEY = Symbol("controller");
|
|
2252
2501
|
function Controller(path = "") {
|
|
2253
2502
|
return function(target) {
|
|
@@ -2318,23 +2567,26 @@ class ControllerRegistry {
|
|
|
2318
2567
|
}
|
|
2319
2568
|
let method = controllerInstance[propertyKey];
|
|
2320
2569
|
if (!method || typeof method !== "function") {
|
|
2321
|
-
|
|
2322
|
-
method = prototype3[propertyKey];
|
|
2570
|
+
method = prototype2[propertyKey];
|
|
2323
2571
|
}
|
|
2324
2572
|
if (!method || typeof method !== "function") {
|
|
2325
2573
|
throw new Error(`Method ${propertyKey} not found on controller ${controllerClass.name}`);
|
|
2326
2574
|
}
|
|
2327
|
-
let
|
|
2575
|
+
let interceptorRegistry;
|
|
2328
2576
|
try {
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2577
|
+
interceptorRegistry = controllerContainer.resolve(INTERCEPTOR_REGISTRY_TOKEN);
|
|
2578
|
+
} catch (error) {
|
|
2579
|
+
interceptorRegistry = undefined;
|
|
2580
|
+
}
|
|
2581
|
+
let result;
|
|
2582
|
+
if (interceptorRegistry) {
|
|
2583
|
+
const interceptors = scanInterceptorMetadata(prototype2, propertyKey, interceptorRegistry);
|
|
2584
|
+
if (interceptors.length > 0) {
|
|
2585
|
+
result = await InterceptorChain.execute(interceptors, controllerInstance, propertyKey, method, params, controllerContainer, context);
|
|
2334
2586
|
} else {
|
|
2335
2587
|
result = method.apply(controllerInstance, params);
|
|
2336
2588
|
}
|
|
2337
|
-
}
|
|
2589
|
+
} else {
|
|
2338
2590
|
result = method.apply(controllerInstance, params);
|
|
2339
2591
|
}
|
|
2340
2592
|
const responseData = await Promise.resolve(result);
|
|
@@ -2489,50 +2741,6 @@ class WebSocketGatewayRegistry {
|
|
|
2489
2741
|
}
|
|
2490
2742
|
}
|
|
2491
2743
|
|
|
2492
|
-
// src/extensions/logger-extension.ts
|
|
2493
|
-
import {
|
|
2494
|
-
LoggerManager as LoggerManager6,
|
|
2495
|
-
LogLevel,
|
|
2496
|
-
SimpleLogger
|
|
2497
|
-
} from "@dangao/logsmith";
|
|
2498
|
-
var LOGGER_TOKEN = Symbol("@dangao/bun-server:logger");
|
|
2499
|
-
|
|
2500
|
-
class LoggerExtension {
|
|
2501
|
-
options;
|
|
2502
|
-
constructor(options = {}) {
|
|
2503
|
-
this.options = {
|
|
2504
|
-
level: LogLevel.INFO,
|
|
2505
|
-
...options
|
|
2506
|
-
};
|
|
2507
|
-
}
|
|
2508
|
-
register(container) {
|
|
2509
|
-
const logger = new SimpleLogger(this.options);
|
|
2510
|
-
LoggerManager6.setLogger(logger);
|
|
2511
|
-
container.registerInstance(SimpleLogger, logger);
|
|
2512
|
-
container.registerInstance(LOGGER_TOKEN, logger);
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
|
-
// src/di/module.ts
|
|
2517
|
-
import"reflect-metadata";
|
|
2518
|
-
var MODULE_METADATA_KEY = Symbol("@dangao/bun-server:module");
|
|
2519
|
-
function Module(metadata) {
|
|
2520
|
-
return (target) => {
|
|
2521
|
-
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, target);
|
|
2522
|
-
};
|
|
2523
|
-
}
|
|
2524
|
-
function getModuleMetadata(moduleClass) {
|
|
2525
|
-
const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, moduleClass) ?? {};
|
|
2526
|
-
return {
|
|
2527
|
-
imports: metadata.imports ?? [],
|
|
2528
|
-
controllers: metadata.controllers ?? [],
|
|
2529
|
-
providers: metadata.providers ?? [],
|
|
2530
|
-
exports: metadata.exports ?? [],
|
|
2531
|
-
extensions: metadata.extensions ?? [],
|
|
2532
|
-
middlewares: metadata.middlewares ?? []
|
|
2533
|
-
};
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
2744
|
// src/di/module-registry.ts
|
|
2537
2745
|
class ModuleRegistry {
|
|
2538
2746
|
static instance;
|
|
@@ -2692,6 +2900,9 @@ class Application {
|
|
|
2692
2900
|
RouteRegistry.getInstance().clear();
|
|
2693
2901
|
ControllerRegistry.getInstance().clear();
|
|
2694
2902
|
ModuleRegistry.getInstance().clear();
|
|
2903
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
2904
|
+
const interceptorRegistry = new InterceptorRegistry;
|
|
2905
|
+
container.registerInstance(INTERCEPTOR_REGISTRY_TOKEN, interceptorRegistry);
|
|
2695
2906
|
this.registerExtension(new LoggerExtension);
|
|
2696
2907
|
}
|
|
2697
2908
|
use(middleware) {
|
|
@@ -2890,38 +3101,6 @@ class ResponseBuilder {
|
|
|
2890
3101
|
}
|
|
2891
3102
|
// src/index.ts
|
|
2892
3103
|
init_validation();
|
|
2893
|
-
// src/extensions/logger-module.ts
|
|
2894
|
-
class LoggerModule {
|
|
2895
|
-
static forRoot(options = {}) {
|
|
2896
|
-
const extensions = [];
|
|
2897
|
-
const middlewares = [];
|
|
2898
|
-
const loggerExtension = new LoggerExtension(options.logger);
|
|
2899
|
-
extensions.push(loggerExtension);
|
|
2900
|
-
if (options.enableRequestLogging !== false) {
|
|
2901
|
-
const requestLoggingMiddleware = createLoggerMiddleware({
|
|
2902
|
-
prefix: options.requestLoggingPrefix
|
|
2903
|
-
});
|
|
2904
|
-
middlewares.push(requestLoggingMiddleware);
|
|
2905
|
-
}
|
|
2906
|
-
const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, LoggerModule) || {};
|
|
2907
|
-
const metadata = {
|
|
2908
|
-
...existingMetadata,
|
|
2909
|
-
extensions,
|
|
2910
|
-
middlewares
|
|
2911
|
-
};
|
|
2912
|
-
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, LoggerModule);
|
|
2913
|
-
return LoggerModule;
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
LoggerModule = __legacyDecorateClassTS([
|
|
2917
|
-
Module({
|
|
2918
|
-
extensions: [],
|
|
2919
|
-
middlewares: []
|
|
2920
|
-
})
|
|
2921
|
-
], LoggerModule);
|
|
2922
|
-
|
|
2923
|
-
// src/extensions/index.ts
|
|
2924
|
-
import { LoggerManager as LoggerManager7, LogLevel as LogLevel2, SimpleLogger as SimpleLogger2 } from "@dangao/logsmith";
|
|
2925
3104
|
// src/swagger/decorators.ts
|
|
2926
3105
|
import"reflect-metadata";
|
|
2927
3106
|
var API_TAG_METADATA_KEY = Symbol("swagger:api-tag");
|
|
@@ -4447,13 +4626,198 @@ function createHttpMetricsMiddleware(collector) {
|
|
|
4447
4626
|
return response;
|
|
4448
4627
|
};
|
|
4449
4628
|
}
|
|
4629
|
+
// src/database/orm/transaction-types.ts
|
|
4630
|
+
var Propagation;
|
|
4631
|
+
((Propagation2) => {
|
|
4632
|
+
Propagation2["REQUIRED"] = "REQUIRED";
|
|
4633
|
+
Propagation2["REQUIRES_NEW"] = "REQUIRES_NEW";
|
|
4634
|
+
Propagation2["SUPPORTS"] = "SUPPORTS";
|
|
4635
|
+
Propagation2["NOT_SUPPORTED"] = "NOT_SUPPORTED";
|
|
4636
|
+
Propagation2["NEVER"] = "NEVER";
|
|
4637
|
+
Propagation2["NESTED"] = "NESTED";
|
|
4638
|
+
})(Propagation ||= {});
|
|
4639
|
+
var IsolationLevel;
|
|
4640
|
+
((IsolationLevel2) => {
|
|
4641
|
+
IsolationLevel2["READ_UNCOMMITTED"] = "READ_UNCOMMITTED";
|
|
4642
|
+
IsolationLevel2["READ_COMMITTED"] = "READ_COMMITTED";
|
|
4643
|
+
IsolationLevel2["REPEATABLE_READ"] = "REPEATABLE_READ";
|
|
4644
|
+
IsolationLevel2["SERIALIZABLE"] = "SERIALIZABLE";
|
|
4645
|
+
})(IsolationLevel ||= {});
|
|
4646
|
+
var TransactionStatus;
|
|
4647
|
+
((TransactionStatus2) => {
|
|
4648
|
+
TransactionStatus2["ACTIVE"] = "ACTIVE";
|
|
4649
|
+
TransactionStatus2["COMMITTED"] = "COMMITTED";
|
|
4650
|
+
TransactionStatus2["ROLLED_BACK"] = "ROLLED_BACK";
|
|
4651
|
+
TransactionStatus2["SUSPENDED"] = "SUSPENDED";
|
|
4652
|
+
})(TransactionStatus ||= {});
|
|
4653
|
+
var TRANSACTION_SERVICE_TOKEN = Symbol("@dangao/bun-server:orm:transaction:service");
|
|
4654
|
+
|
|
4655
|
+
// src/database/orm/transaction-decorator.ts
|
|
4656
|
+
import"reflect-metadata";
|
|
4657
|
+
var TRANSACTION_METADATA_KEY = Symbol("@dangao/bun-server:orm:transaction");
|
|
4658
|
+
function Transactional(options) {
|
|
4659
|
+
return (target, propertyKey, descriptor) => {
|
|
4660
|
+
if (!descriptor || typeof descriptor.value !== "function") {
|
|
4661
|
+
throw new Error("@Transactional() can only be applied to methods");
|
|
4662
|
+
}
|
|
4663
|
+
const metadata = {
|
|
4664
|
+
propagation: options?.propagation ?? "REQUIRED" /* REQUIRED */,
|
|
4665
|
+
isolationLevel: options?.isolationLevel,
|
|
4666
|
+
timeout: options?.timeout,
|
|
4667
|
+
readOnly: options?.readOnly ?? false,
|
|
4668
|
+
rollbackFor: options?.rollbackFor ?? [],
|
|
4669
|
+
noRollbackFor: options?.noRollbackFor ?? []
|
|
4670
|
+
};
|
|
4671
|
+
Reflect.defineMetadata(TRANSACTION_METADATA_KEY, metadata, target, propertyKey);
|
|
4672
|
+
};
|
|
4673
|
+
}
|
|
4674
|
+
function getTransactionMetadata(target, propertyKey) {
|
|
4675
|
+
if (typeof target !== "object" || target === null) {
|
|
4676
|
+
return;
|
|
4677
|
+
}
|
|
4678
|
+
let metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, target, propertyKey);
|
|
4679
|
+
if (metadata !== undefined) {
|
|
4680
|
+
return metadata;
|
|
4681
|
+
}
|
|
4682
|
+
const prototype = Object.getPrototypeOf(target);
|
|
4683
|
+
if (prototype && prototype !== Object.prototype) {
|
|
4684
|
+
metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, prototype, propertyKey);
|
|
4685
|
+
if (metadata !== undefined) {
|
|
4686
|
+
return metadata;
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
const constructor = target.constructor;
|
|
4690
|
+
if (constructor && typeof constructor === "function") {
|
|
4691
|
+
if (target !== constructor.prototype) {
|
|
4692
|
+
metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, constructor.prototype, propertyKey);
|
|
4693
|
+
if (metadata !== undefined) {
|
|
4694
|
+
return metadata;
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
return;
|
|
4699
|
+
}
|
|
4700
|
+
|
|
4701
|
+
// src/database/orm/transaction-interceptor.ts
|
|
4702
|
+
class TransactionInterceptor {
|
|
4703
|
+
async execute(target, propertyKey, originalMethod, args, container, context2) {
|
|
4704
|
+
const transactionMetadata = getTransactionMetadata(target, propertyKey);
|
|
4705
|
+
if (!transactionMetadata) {
|
|
4706
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4707
|
+
}
|
|
4708
|
+
let transactionManager;
|
|
4709
|
+
try {
|
|
4710
|
+
transactionManager = container.resolve(TRANSACTION_SERVICE_TOKEN);
|
|
4711
|
+
} catch (error) {
|
|
4712
|
+
console.warn("TransactionManager not found, executing without transaction");
|
|
4713
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4714
|
+
}
|
|
4715
|
+
const propagation = transactionMetadata.propagation ?? "REQUIRED" /* REQUIRED */;
|
|
4716
|
+
const currentTransaction = transactionManager.getCurrentTransaction();
|
|
4717
|
+
switch (propagation) {
|
|
4718
|
+
case "REQUIRED" /* REQUIRED */:
|
|
4719
|
+
if (currentTransaction) {
|
|
4720
|
+
return await TransactionInterceptor.executeInExistingTransaction(originalMethod, target, args, currentTransaction.id, transactionManager);
|
|
4721
|
+
} else {
|
|
4722
|
+
return await TransactionInterceptor.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
4723
|
+
}
|
|
4724
|
+
case "REQUIRES_NEW" /* REQUIRES_NEW */:
|
|
4725
|
+
return await TransactionInterceptor.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
4726
|
+
case "SUPPORTS" /* SUPPORTS */:
|
|
4727
|
+
if (currentTransaction) {
|
|
4728
|
+
return await TransactionInterceptor.executeInExistingTransaction(originalMethod, target, args, currentTransaction.id, transactionManager);
|
|
4729
|
+
} else {
|
|
4730
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4731
|
+
}
|
|
4732
|
+
case "NOT_SUPPORTED" /* NOT_SUPPORTED */:
|
|
4733
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4734
|
+
case "NEVER" /* NEVER */:
|
|
4735
|
+
if (currentTransaction) {
|
|
4736
|
+
throw new Error("Transaction propagation NEVER requires no existing transaction");
|
|
4737
|
+
}
|
|
4738
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4739
|
+
case "NESTED" /* NESTED */:
|
|
4740
|
+
if (currentTransaction) {
|
|
4741
|
+
return await TransactionInterceptor.executeInNestedTransaction(originalMethod, target, args, currentTransaction.id, transactionMetadata, transactionManager);
|
|
4742
|
+
} else {
|
|
4743
|
+
return await TransactionInterceptor.executeInNewTransaction(originalMethod, target, args, transactionMetadata, transactionManager);
|
|
4744
|
+
}
|
|
4745
|
+
default:
|
|
4746
|
+
return await Promise.resolve(originalMethod.apply(target, args));
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
static async executeWithTransaction(target, propertyKey, originalMethod, args, container) {
|
|
4750
|
+
const interceptor = new TransactionInterceptor;
|
|
4751
|
+
return await interceptor.execute(target, propertyKey, originalMethod, args, container);
|
|
4752
|
+
}
|
|
4753
|
+
static async executeInNewTransaction(method, target, args, options, transactionManager) {
|
|
4754
|
+
const context2 = await transactionManager.beginTransaction({
|
|
4755
|
+
timeout: options.timeout
|
|
4756
|
+
});
|
|
4757
|
+
try {
|
|
4758
|
+
const result = await Promise.resolve(method.apply(target, args));
|
|
4759
|
+
await transactionManager.commitTransaction(context2.id);
|
|
4760
|
+
return result;
|
|
4761
|
+
} catch (error) {
|
|
4762
|
+
if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
|
|
4763
|
+
await transactionManager.rollbackTransaction(context2.id);
|
|
4764
|
+
} else {
|
|
4765
|
+
await transactionManager.commitTransaction(context2.id);
|
|
4766
|
+
}
|
|
4767
|
+
throw error;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
static async executeInExistingTransaction(method, target, args, transactionId, transactionManager) {
|
|
4771
|
+
return await Promise.resolve(method.apply(target, args));
|
|
4772
|
+
}
|
|
4773
|
+
static async executeInNestedTransaction(method, target, args, parentTransactionId, options, transactionManager) {
|
|
4774
|
+
const savepointName = await transactionManager.createSavepoint(parentTransactionId);
|
|
4775
|
+
try {
|
|
4776
|
+
const result = await Promise.resolve(method.apply(target, args));
|
|
4777
|
+
return result;
|
|
4778
|
+
} catch (error) {
|
|
4779
|
+
if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
|
|
4780
|
+
await transactionManager.rollbackToSavepoint(parentTransactionId, savepointName);
|
|
4781
|
+
}
|
|
4782
|
+
throw error;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
static shouldRollback(error, rollbackFor, noRollbackFor) {
|
|
4786
|
+
if (!error) {
|
|
4787
|
+
return false;
|
|
4788
|
+
}
|
|
4789
|
+
if (noRollbackFor && noRollbackFor.length > 0) {
|
|
4790
|
+
for (const ErrorClass of noRollbackFor) {
|
|
4791
|
+
if (error instanceof ErrorClass) {
|
|
4792
|
+
return false;
|
|
4793
|
+
}
|
|
4794
|
+
}
|
|
4795
|
+
}
|
|
4796
|
+
if (rollbackFor && rollbackFor.length > 0) {
|
|
4797
|
+
for (const ErrorClass of rollbackFor) {
|
|
4798
|
+
if (error instanceof ErrorClass) {
|
|
4799
|
+
return true;
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
return false;
|
|
4803
|
+
}
|
|
4804
|
+
return true;
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
|
|
4450
4808
|
// src/database/types.ts
|
|
4451
4809
|
var DATABASE_SERVICE_TOKEN = Symbol("@dangao/bun-server:database:service");
|
|
4452
4810
|
var DATABASE_OPTIONS_TOKEN = Symbol("@dangao/bun-server:database:options");
|
|
4453
4811
|
|
|
4454
4812
|
// src/database/database-extension.ts
|
|
4455
4813
|
class DatabaseExtension {
|
|
4456
|
-
register(container) {
|
|
4814
|
+
register(container) {
|
|
4815
|
+
try {
|
|
4816
|
+
const interceptorRegistry = container.resolve(INTERCEPTOR_REGISTRY_TOKEN);
|
|
4817
|
+
const transactionInterceptor = new TransactionInterceptor;
|
|
4818
|
+
interceptorRegistry.register(TRANSACTION_METADATA_KEY, transactionInterceptor, 50);
|
|
4819
|
+
} catch (error) {}
|
|
4820
|
+
}
|
|
4457
4821
|
async initialize(container) {
|
|
4458
4822
|
try {
|
|
4459
4823
|
const databaseService = container.resolve(DATABASE_SERVICE_TOKEN);
|
|
@@ -4551,7 +4915,6 @@ OrmService = __legacyDecorateClassTS([
|
|
|
4551
4915
|
], OrmService);
|
|
4552
4916
|
|
|
4553
4917
|
// src/database/orm/transaction-manager.ts
|
|
4554
|
-
init_transaction_types();
|
|
4555
4918
|
class TransactionManager {
|
|
4556
4919
|
databaseService;
|
|
4557
4920
|
transactions = new Map;
|
|
@@ -5111,7 +5474,6 @@ DatabaseService2 = __legacyDecorateClassTS([
|
|
|
5111
5474
|
var ORM_SERVICE_TOKEN = Symbol("@dangao/bun-server:orm:service");
|
|
5112
5475
|
|
|
5113
5476
|
// src/database/database-module.ts
|
|
5114
|
-
init_transaction_types();
|
|
5115
5477
|
class DatabaseModule {
|
|
5116
5478
|
static forRoot(options) {
|
|
5117
5479
|
const providers2 = [];
|
|
@@ -5241,10 +5603,6 @@ function getRepositoryMetadata(target) {
|
|
|
5241
5603
|
}
|
|
5242
5604
|
return;
|
|
5243
5605
|
}
|
|
5244
|
-
|
|
5245
|
-
// src/database/orm/index.ts
|
|
5246
|
-
init_transaction_decorator();
|
|
5247
|
-
|
|
5248
5606
|
// src/database/orm/repository.ts
|
|
5249
5607
|
class BaseRepository {
|
|
5250
5608
|
databaseService;
|
|
@@ -5312,10 +5670,6 @@ class DrizzleBaseRepository {
|
|
|
5312
5670
|
this.databaseService = databaseService;
|
|
5313
5671
|
}
|
|
5314
5672
|
}
|
|
5315
|
-
|
|
5316
|
-
// src/database/orm/index.ts
|
|
5317
|
-
init_transaction_interceptor();
|
|
5318
|
-
init_transaction_types();
|
|
5319
5673
|
// src/testing/harness.ts
|
|
5320
5674
|
import { performance as performance2 } from "perf_hooks";
|
|
5321
5675
|
|
|
@@ -6256,6 +6610,7 @@ function Session() {
|
|
|
6256
6610
|
return createParamDecorator("session" /* SESSION */);
|
|
6257
6611
|
}
|
|
6258
6612
|
export {
|
|
6613
|
+
scanInterceptorMetadata,
|
|
6259
6614
|
requiresAuth,
|
|
6260
6615
|
getTransactionMetadata,
|
|
6261
6616
|
getRepositoryMetadata,
|
|
@@ -6317,11 +6672,14 @@ export {
|
|
|
6317
6672
|
Propagation,
|
|
6318
6673
|
PrometheusFormatter,
|
|
6319
6674
|
PrimaryKey,
|
|
6675
|
+
PermissionInterceptor,
|
|
6676
|
+
Permission,
|
|
6320
6677
|
PerformanceHarness,
|
|
6321
6678
|
ParamBinder,
|
|
6322
6679
|
Param,
|
|
6323
6680
|
PUT,
|
|
6324
6681
|
POST,
|
|
6682
|
+
PERMISSION_METADATA_KEY,
|
|
6325
6683
|
PATCH,
|
|
6326
6684
|
OrmService,
|
|
6327
6685
|
OnOpen,
|
|
@@ -6347,7 +6705,10 @@ export {
|
|
|
6347
6705
|
LoggerModule,
|
|
6348
6706
|
LoggerExtension,
|
|
6349
6707
|
LogLevel2 as LogLevel,
|
|
6708
|
+
LogInterceptor,
|
|
6709
|
+
Log,
|
|
6350
6710
|
Lifecycle,
|
|
6711
|
+
LOG_METADATA_KEY,
|
|
6351
6712
|
LOGGER_TOKEN,
|
|
6352
6713
|
JwtAuthenticationProvider,
|
|
6353
6714
|
JWT_UTIL_TOKEN,
|
|
@@ -6358,8 +6719,11 @@ export {
|
|
|
6358
6719
|
IsNumber,
|
|
6359
6720
|
IsEmail,
|
|
6360
6721
|
InternalServerErrorException,
|
|
6722
|
+
InterceptorRegistry,
|
|
6723
|
+
InterceptorChain,
|
|
6361
6724
|
Injectable,
|
|
6362
6725
|
Inject,
|
|
6726
|
+
INTERCEPTOR_REGISTRY_TOKEN,
|
|
6363
6727
|
HttpException,
|
|
6364
6728
|
HealthModule,
|
|
6365
6729
|
Header,
|
|
@@ -6391,14 +6755,18 @@ export {
|
|
|
6391
6755
|
CacheService,
|
|
6392
6756
|
CachePut,
|
|
6393
6757
|
CacheModule,
|
|
6758
|
+
CacheInterceptor,
|
|
6394
6759
|
CacheEvict,
|
|
6760
|
+
Cache,
|
|
6395
6761
|
CONFIG_SERVICE_TOKEN,
|
|
6396
6762
|
CACHE_SERVICE_TOKEN,
|
|
6397
6763
|
CACHE_OPTIONS_TOKEN,
|
|
6764
|
+
CACHE_METADATA_KEY,
|
|
6398
6765
|
BunServer,
|
|
6399
6766
|
BodyParser,
|
|
6400
6767
|
Body,
|
|
6401
6768
|
BaseRepository,
|
|
6769
|
+
BaseInterceptor,
|
|
6402
6770
|
BadRequestException,
|
|
6403
6771
|
AuthenticationManager,
|
|
6404
6772
|
Auth,
|