typespeed 2.4.11 → 2.5.0
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/CHANGELOG.md +7 -2
- package/app/src/test-bind.class.ts +41 -0
- package/dist/bind.decorator.js +50 -0
- package/dist/core.decorator.js +113 -26
- package/dist/database.decorator.js +122 -16
- package/dist/decorator-utils.js +42 -0
- package/dist/route.decorator.js +175 -10
- package/dist/typespeed.d.ts +28 -24
- package/dist/typespeed.js +31 -3
- package/introduction/decorator-next/dist/main.js +116 -0
- package/introduction/decorator-next/package.json +14 -0
- package/introduction/decorator-next/src/main.ts +64 -0
- package/introduction/decorator-next/tsconfig.json +17 -0
- package/introduction/decorator-next//346/225/231/347/250/213.md +384 -0
- package/introduction/tsconfig.json +3 -0
- package/package.json +1 -1
- package/src/bind.decorator.ts +50 -0
- package/src/core.decorator.ts +111 -25
- package/src/database.decorator.ts +120 -16
- package/src/decorator-utils.ts +53 -0
- package/src/route.decorator.ts +169 -10
- package/src/typespeed.d.ts +28 -24
- package/src/typespeed.ts +31 -2
- package/test/bind.test.ts +28 -0
- package/test/decorator-utils.test.ts +42 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
typespeed 版本演进记录。本文件 2026-09-06 从 git 历史反推建立;git 最早提交 2022-09-18(ch01)。
|
|
4
4
|
|
|
5
|
-
## 2.
|
|
5
|
+
## 2.5.x
|
|
6
6
|
|
|
7
|
-
- **2.
|
|
7
|
+
- **2.5.0**:装饰器双轨——支持 TC39 标准装饰器(Stage 2.7),legacy 装饰器零破坏。单份源码运行时双签名感知(`isStd` 判据),覆盖 core/route/database 三族 + 入口 `app`/`value`;新增方法级 `@bind` 装饰器(route 参数名→来源、database 占位符→索引,替代标准模式删除的参数装饰器);`@autoware`/`@resource`/`@bean` 支持显式 token(标准模式无 `design:type`);类型声明宽松化;新增标准模式示例 `introduction/decorator-next/` + `isStd` 单测(2026-09-07)
|
|
8
|
+
|
|
9
|
+
## 2.4.x
|
|
10
|
+
|
|
11
|
+
- **2.4.11**:README 四入口、CI 修复(action 升 v4、node 22)、CHANGELOG 建立、getRootPath 兼容 Node22/mocha(2026-09-06)
|
|
12
|
+
- **2.4.10**:升级 jsonwebtoken 和 mysql2 库(2024-05-07)
|
|
8
13
|
- **2.4.9**:修复 SocketIO、MQ、Redis 等对象获取不准确问题(2024-04-22)
|
|
9
14
|
- **2.4.8**:修复 socket.io 未从 bean 工厂获取装饰类的问题;修复 select 返回值不为 null 的问题(2024-04-22)
|
|
10
15
|
- **2.4.7**:logx 日志模块(2024-04-21)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { component, getMapping, bind, resource, autoware, bean, insert } from "../../src/typespeed";
|
|
2
|
+
import UserModel from "./user-model.class";
|
|
3
|
+
|
|
4
|
+
class TestBean {
|
|
5
|
+
name = "test-bean";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@component
|
|
9
|
+
export default class TestBind {
|
|
10
|
+
|
|
11
|
+
@bean(TestBean)
|
|
12
|
+
getBeanInstance() { return new TestBean(); }
|
|
13
|
+
|
|
14
|
+
@autoware(TestBean)
|
|
15
|
+
private autowired: TestBean;
|
|
16
|
+
|
|
17
|
+
@resource(UserModel, "user")
|
|
18
|
+
private userModel: UserModel;
|
|
19
|
+
|
|
20
|
+
@getMapping("/bind/param/:id")
|
|
21
|
+
@bind({ id: "reqParam", name: "reqQuery" })
|
|
22
|
+
async bindParam(id: string, name: string) {
|
|
23
|
+
return { id, name };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@getMapping("/bind/token")
|
|
27
|
+
async bindToken(req, res) {
|
|
28
|
+
res.send(this.autowired.name + "|" + (this.userModel ? "model-ok" : "model-null"));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@getMapping("/bind/db")
|
|
32
|
+
async bindDb(req, res) {
|
|
33
|
+
const id = req.query.id || 1;
|
|
34
|
+
const newId = await this.addRowByBind("bind name " + id, id);
|
|
35
|
+
res.send("bind insert: " + newId);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@insert("Insert into `user` (id, name) values (#{id}, #{name})")
|
|
39
|
+
@bind({ name: 0, id: 1 })
|
|
40
|
+
private async addRowByBind(newName: string, id: number) { }
|
|
41
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBindMapping = exports.bind = void 0;
|
|
4
|
+
const decorator_utils_1 = require("./decorator-utils");
|
|
5
|
+
/**
|
|
6
|
+
* 方法级参数绑定注册表:key = `[className, methodName]`,value = 绑定声明。
|
|
7
|
+
* 标准装饰器删除了参数装饰器,@bind 是方法级替代(legacy 模式同样可用)。
|
|
8
|
+
* 按 value 类型区分两种语义:
|
|
9
|
+
* - database 场景:{ SQL占位符名: 参数索引 }(值为 number)
|
|
10
|
+
* - route 场景:{ 参数名: 请求来源 }(值为 string,如 "reqParam" / "reqBody" / "reqQuery")
|
|
11
|
+
*/
|
|
12
|
+
const bindParamMap = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* @bind 方法级参数绑定装饰器。
|
|
15
|
+
*
|
|
16
|
+
* database 散参绑定(对象传参已是主路径,@bind 作散参补充):
|
|
17
|
+
* ```
|
|
18
|
+
* @insert("Insert into `user` (id, name) values (#{id}, #{name})")
|
|
19
|
+
* @bind({ name: 0, id: 1 })
|
|
20
|
+
* async addRow(newName: string, id: number) { }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* route 参数绑定(替代 req/res/reqBody/reqParam/reqQuery/reqForm 参数装饰器):
|
|
24
|
+
* ```
|
|
25
|
+
* @getMapping("/user/:id")
|
|
26
|
+
* @bind({ id: "reqParam", body: "reqBody", q: "reqQuery" })
|
|
27
|
+
* async getUser(id: string, body: any, q: string) { }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
function bind(mapping) {
|
|
31
|
+
return function (...args) {
|
|
32
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
33
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
34
|
+
const methodName = String(ctx.name);
|
|
35
|
+
ctx.addInitializer(function () {
|
|
36
|
+
bindParamMap.set([this.constructor.name, methodName].toString(), mapping);
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const target = args[0];
|
|
41
|
+
const propertyKey = args[1];
|
|
42
|
+
bindParamMap.set([target.constructor.name, propertyKey].toString(), mapping);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
exports.bind = bind;
|
|
46
|
+
/** 读取某方法上的 @bind 声明(route/database 内部使用) */
|
|
47
|
+
function getBindMapping(className, methodName) {
|
|
48
|
+
return bindParamMap.get([className, methodName].toString());
|
|
49
|
+
}
|
|
50
|
+
exports.getBindMapping = getBindMapping;
|
package/dist/core.decorator.js
CHANGED
|
@@ -4,10 +4,19 @@ exports.schedule = exports.getComponent = exports.getBean = exports.autoware = e
|
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
const cron = require("cron");
|
|
6
6
|
const log_factory_class_1 = require("./factory/log-factory.class");
|
|
7
|
+
const decorator_utils_1 = require("./decorator-utils");
|
|
7
8
|
const resourceObjects = new Map();
|
|
8
9
|
const beanMapper = new Map();
|
|
9
10
|
const objectMapper = new Map();
|
|
10
|
-
function component(
|
|
11
|
+
function component(...args) {
|
|
12
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
13
|
+
const [ctor, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
14
|
+
ctx.addInitializer(function () {
|
|
15
|
+
objectMapper.set(this.name, new this());
|
|
16
|
+
});
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const constructorFunction = args[0];
|
|
11
20
|
objectMapper.set(constructorFunction.name, new constructorFunction());
|
|
12
21
|
}
|
|
13
22
|
exports.component = component;
|
|
@@ -15,45 +24,114 @@ function getComponent(constructorFunction) {
|
|
|
15
24
|
return objectMapper.get(constructorFunction.name);
|
|
16
25
|
}
|
|
17
26
|
exports.getComponent = getComponent;
|
|
18
|
-
function bean(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
function bean(...args) {
|
|
28
|
+
if (args.length >= 2) {
|
|
29
|
+
// 直接装饰器形式:@bean(无 token,走 legacy design:returntype)
|
|
30
|
+
return beanWithToken(undefined)(...args);
|
|
31
|
+
}
|
|
32
|
+
// 工厂形式:@bean(Token)(显式返回类型 token,标准模式必需)
|
|
33
|
+
return beanWithToken(args[0]);
|
|
25
34
|
}
|
|
26
35
|
exports.bean = bean;
|
|
36
|
+
function beanWithToken(token) {
|
|
37
|
+
return function (...args) {
|
|
38
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
39
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
40
|
+
const key = String(ctx.name);
|
|
41
|
+
ctx.addInitializer(function () {
|
|
42
|
+
beanMapper.set(token ? token.name : key, {
|
|
43
|
+
"target": this, "propertyKey": key,
|
|
44
|
+
"factory": this[key]()
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const target = args[0];
|
|
50
|
+
const propertyKey = args[1];
|
|
51
|
+
let returnType = token || Reflect.getMetadata("design:returntype", target, propertyKey);
|
|
52
|
+
beanMapper.set(returnType.name, {
|
|
53
|
+
"target": target, "propertyKey": propertyKey,
|
|
54
|
+
"factory": target[propertyKey]()
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
27
58
|
function getBean(mappingClass) {
|
|
28
59
|
const bean = beanMapper.get(mappingClass.name);
|
|
29
60
|
return bean["factory"];
|
|
30
61
|
}
|
|
31
62
|
exports.getBean = getBean;
|
|
32
|
-
function autoware(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
function autoware(...args) {
|
|
64
|
+
if (args.length >= 2) {
|
|
65
|
+
// 直接装饰器形式:@autoware(无 token,走 legacy design:type)
|
|
66
|
+
return autowareWithToken(undefined)(...args);
|
|
67
|
+
}
|
|
68
|
+
// 工厂形式:@autoware(Token)(显式 token,标准模式必需)
|
|
69
|
+
return autowareWithToken(args[0]);
|
|
70
|
+
}
|
|
71
|
+
exports.autoware = autoware;
|
|
72
|
+
function autowareWithToken(token) {
|
|
73
|
+
return function (...args) {
|
|
74
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
75
|
+
// 标准 field 装饰器:标准模式无 design:type,有 token 才能注入。
|
|
76
|
+
return (initialValue) => {
|
|
77
|
+
if (!token)
|
|
78
|
+
return initialValue;
|
|
79
|
+
const bean = beanMapper.get(token.name);
|
|
80
|
+
if (bean !== undefined)
|
|
81
|
+
return bean["factory"];
|
|
82
|
+
return new token();
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const target = args[0];
|
|
86
|
+
const propertyKey = args[1];
|
|
87
|
+
const type = token || Reflect.getMetadata("design:type", target, propertyKey);
|
|
88
|
+
Object.defineProperty(target, propertyKey, {
|
|
89
|
+
get: () => {
|
|
90
|
+
const targetObject = beanMapper.get(type.name);
|
|
91
|
+
if (targetObject === undefined) {
|
|
92
|
+
const resourceKey = [target.constructor.name, propertyKey, type.name].toString();
|
|
93
|
+
if (!resourceObjects[resourceKey]) {
|
|
94
|
+
resourceObjects[resourceKey] = new type();
|
|
95
|
+
}
|
|
96
|
+
return resourceObjects[resourceKey];
|
|
41
97
|
}
|
|
42
|
-
return
|
|
98
|
+
return targetObject["factory"];
|
|
43
99
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 解析 @resource(...args) 的首参:若首参是 Function(类/token),作为显式 token,
|
|
105
|
+
* 其余作为构造参数;否则全部作为构造参数(legacy 行为,如 @resource("user"))。
|
|
106
|
+
*/
|
|
107
|
+
function extractTokenAndArgs(args) {
|
|
108
|
+
if (args.length > 0 && typeof args[0] === "function") {
|
|
109
|
+
return [args[0], args.slice(1)];
|
|
110
|
+
}
|
|
111
|
+
return [undefined, args];
|
|
47
112
|
}
|
|
48
|
-
exports.autoware = autoware;
|
|
49
113
|
function resource(...args) {
|
|
50
|
-
|
|
51
|
-
|
|
114
|
+
const [token, initArgs] = extractTokenAndArgs(args);
|
|
115
|
+
return (...decoratorArgs) => {
|
|
116
|
+
if ((0, decorator_utils_1.isStd)(decoratorArgs)) {
|
|
117
|
+
// 标准 field 装饰器:标准模式无 design:type,需显式 token。
|
|
118
|
+
return (initialValue) => {
|
|
119
|
+
if (!token)
|
|
120
|
+
return initialValue;
|
|
121
|
+
const bean = beanMapper.get(token.name);
|
|
122
|
+
if (bean !== undefined)
|
|
123
|
+
return bean["factory"];
|
|
124
|
+
return new token(...initArgs);
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const target = decoratorArgs[0];
|
|
128
|
+
const propertyKey = decoratorArgs[1];
|
|
129
|
+
const type = token || Reflect.getMetadata("design:type", target, propertyKey);
|
|
52
130
|
Object.defineProperty(target, propertyKey, {
|
|
53
131
|
get: () => {
|
|
54
132
|
const resourceKey = [target.constructor.name, propertyKey, type.name].toString();
|
|
55
133
|
if (!resourceObjects[resourceKey]) {
|
|
56
|
-
resourceObjects[resourceKey] = new type(...
|
|
134
|
+
resourceObjects[resourceKey] = new type(...initArgs);
|
|
57
135
|
}
|
|
58
136
|
return resourceObjects[resourceKey];
|
|
59
137
|
}
|
|
@@ -93,7 +171,16 @@ function error(message, ...optionalParams) {
|
|
|
93
171
|
}
|
|
94
172
|
exports.error = error;
|
|
95
173
|
function schedule(cronTime) {
|
|
96
|
-
return (
|
|
174
|
+
return (...args) => {
|
|
175
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
176
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
177
|
+
ctx.addInitializer(function () {
|
|
178
|
+
new cron.CronJob(cronTime, this[String(ctx.name)]).start();
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const target = args[0];
|
|
183
|
+
const propertyKey = args[1];
|
|
97
184
|
new cron.CronJob(cronTime, target[propertyKey]).start();
|
|
98
185
|
};
|
|
99
186
|
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Model = exports.cache = exports.resultType = exports.param = exports.select = exports.remove = exports.update = exports.insert = void 0;
|
|
4
4
|
const core_decorator_1 = require("./core.decorator");
|
|
5
|
+
const decorator_utils_1 = require("./decorator-utils");
|
|
6
|
+
const bind_decorator_1 = require("./bind.decorator");
|
|
5
7
|
const cache_factory_class_1 = require("./factory/cache-factory.class");
|
|
6
8
|
const data_source_factory_class_1 = require("./factory/data-source-factory.class");
|
|
7
9
|
const paramMetadataKey = Symbol('param');
|
|
@@ -10,9 +12,24 @@ const cacheDefindMap = new Map();
|
|
|
10
12
|
const tableVersionMap = new Map();
|
|
11
13
|
let cacheBean;
|
|
12
14
|
function insert(sql) {
|
|
13
|
-
return (
|
|
14
|
-
|
|
15
|
-
const
|
|
15
|
+
return (...args) => {
|
|
16
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
17
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
18
|
+
const propertyKey = String(ctx.name);
|
|
19
|
+
return async function (...callArgs) {
|
|
20
|
+
const result = await queryForExecute(sql, callArgs, this, propertyKey);
|
|
21
|
+
if (cacheBean && result.affectedRows > 0) {
|
|
22
|
+
const [tableName, tableVersion] = getTableAndVersion("insert", sql);
|
|
23
|
+
tableVersionMap.set(tableName, tableVersion + 1);
|
|
24
|
+
}
|
|
25
|
+
return result.insertId;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const target = args[0];
|
|
29
|
+
const propertyKey = args[1];
|
|
30
|
+
const descriptor = args[2];
|
|
31
|
+
descriptor.value = async (...callArgs) => {
|
|
32
|
+
const result = await queryForExecute(sql, callArgs, target, propertyKey);
|
|
16
33
|
if (cacheBean && result.affectedRows > 0) {
|
|
17
34
|
const [tableName, tableVersion] = getTableAndVersion("insert", sql);
|
|
18
35
|
tableVersionMap.set(tableName, tableVersion + 1);
|
|
@@ -23,9 +40,24 @@ function insert(sql) {
|
|
|
23
40
|
}
|
|
24
41
|
exports.insert = insert;
|
|
25
42
|
function update(sql) {
|
|
26
|
-
return (
|
|
27
|
-
|
|
28
|
-
const
|
|
43
|
+
return (...args) => {
|
|
44
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
45
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
46
|
+
const propertyKey = String(ctx.name);
|
|
47
|
+
return async function (...callArgs) {
|
|
48
|
+
const result = await queryForExecute(sql, callArgs, this, propertyKey);
|
|
49
|
+
if (cacheBean && result.affectedRows > 0) {
|
|
50
|
+
const [tableName, tableVersion] = getTableAndVersion("update", sql);
|
|
51
|
+
tableVersionMap.set(tableName, tableVersion + 1);
|
|
52
|
+
}
|
|
53
|
+
return result.affectedRows;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const target = args[0];
|
|
57
|
+
const propertyKey = args[1];
|
|
58
|
+
const descriptor = args[2];
|
|
59
|
+
descriptor.value = async (...callArgs) => {
|
|
60
|
+
const result = await queryForExecute(sql, callArgs, target, propertyKey);
|
|
29
61
|
if (cacheBean && result.affectedRows > 0) {
|
|
30
62
|
const [tableName, tableVersion] = getTableAndVersion("update", sql);
|
|
31
63
|
tableVersionMap.set(tableName, tableVersion + 1);
|
|
@@ -36,9 +68,24 @@ function update(sql) {
|
|
|
36
68
|
}
|
|
37
69
|
exports.update = update;
|
|
38
70
|
function remove(sql) {
|
|
39
|
-
return (
|
|
40
|
-
|
|
41
|
-
const
|
|
71
|
+
return (...args) => {
|
|
72
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
73
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
74
|
+
const propertyKey = String(ctx.name);
|
|
75
|
+
return async function (...callArgs) {
|
|
76
|
+
const result = await queryForExecute(sql, callArgs, this, propertyKey);
|
|
77
|
+
if (cacheBean && result.affectedRows > 0) {
|
|
78
|
+
const [tableName, tableVersion] = getTableAndVersion("delete", sql);
|
|
79
|
+
tableVersionMap.set(tableName, tableVersion + 1);
|
|
80
|
+
}
|
|
81
|
+
return result.affectedRows;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const target = args[0];
|
|
85
|
+
const propertyKey = args[1];
|
|
86
|
+
const descriptor = args[2];
|
|
87
|
+
descriptor.value = async (...callArgs) => {
|
|
88
|
+
const result = await queryForExecute(sql, callArgs, target, propertyKey);
|
|
42
89
|
if (cacheBean && result.affectedRows > 0) {
|
|
43
90
|
const [tableName, tableVersion] = getTableAndVersion("delete", sql);
|
|
44
91
|
tableVersionMap.set(tableName, tableVersion + 1);
|
|
@@ -49,9 +96,35 @@ function remove(sql) {
|
|
|
49
96
|
}
|
|
50
97
|
exports.remove = remove;
|
|
51
98
|
function select(sql) {
|
|
52
|
-
return (
|
|
53
|
-
|
|
54
|
-
const [
|
|
99
|
+
return (...args) => {
|
|
100
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
101
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
102
|
+
const propertyKey = String(ctx.name);
|
|
103
|
+
return async function (...callArgs) {
|
|
104
|
+
const [newSql, sqlValues] = convertSQLParams(sql, this, propertyKey, callArgs);
|
|
105
|
+
const resultType = resultTypeMap.get([this.constructor.name, propertyKey].toString());
|
|
106
|
+
if (cacheBean && cacheDefindMap.has([this.constructor.name, propertyKey].toString())) {
|
|
107
|
+
const [tableName, tableVersion] = getTableAndVersion("select", newSql);
|
|
108
|
+
const cacheKey = JSON.stringify([tableName, tableVersion, newSql, sqlValues]);
|
|
109
|
+
if (cacheBean.get(cacheKey)) {
|
|
110
|
+
return cacheBean.get(cacheKey);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
const rows = await actionQuery(newSql, sqlValues, resultType);
|
|
114
|
+
cacheBean.set(cacheKey, rows, cacheDefindMap.get([this.constructor.name, propertyKey].toString()));
|
|
115
|
+
return rows;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
return await actionQuery(newSql, sqlValues, resultType);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const target = args[0];
|
|
124
|
+
const propertyKey = args[1];
|
|
125
|
+
const descriptor = args[2];
|
|
126
|
+
descriptor.value = async (...callArgs) => {
|
|
127
|
+
const [newSql, sqlValues] = convertSQLParams(sql, target, propertyKey, callArgs);
|
|
55
128
|
const resultType = resultTypeMap.get([target.constructor.name, propertyKey].toString());
|
|
56
129
|
if (cacheBean && cacheDefindMap.has([target.constructor.name, propertyKey].toString())) {
|
|
57
130
|
const [tableName, tableVersion] = getTableAndVersion("select", newSql);
|
|
@@ -73,7 +146,16 @@ function select(sql) {
|
|
|
73
146
|
}
|
|
74
147
|
exports.select = select;
|
|
75
148
|
function resultType(dataClass) {
|
|
76
|
-
return function (
|
|
149
|
+
return function (...args) {
|
|
150
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
151
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
152
|
+
ctx.addInitializer(function () {
|
|
153
|
+
resultTypeMap.set([this.constructor.name, String(ctx.name)].toString(), dataClass);
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const target = args[0];
|
|
158
|
+
const propertyKey = args[1];
|
|
77
159
|
resultTypeMap.set([target.constructor.name, propertyKey].toString(), dataClass);
|
|
78
160
|
//never return
|
|
79
161
|
};
|
|
@@ -125,8 +207,17 @@ function convertSQLParams(decoratorSQL, target, propertyKey, args) {
|
|
|
125
207
|
argsVal = new Map(Object.getOwnPropertyNames(args[0]).map((valName) => [valName, args[0][valName]]));
|
|
126
208
|
}
|
|
127
209
|
else {
|
|
128
|
-
|
|
129
|
-
|
|
210
|
+
// 优先 @param(legacy reflect-metadata),否则回退 @bind(方法级声明,标准模式)
|
|
211
|
+
let existingParameters = Reflect.getOwnMetadata(paramMetadataKey, target, propertyKey);
|
|
212
|
+
if (!existingParameters) {
|
|
213
|
+
const bindMapping = (0, bind_decorator_1.getBindMapping)(target.constructor.name, propertyKey);
|
|
214
|
+
if (bindMapping) {
|
|
215
|
+
existingParameters = Object.entries(bindMapping)
|
|
216
|
+
.filter(([, value]) => typeof value === "number")
|
|
217
|
+
.map(([name, value]) => [name, value]);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
argsVal = new Map((existingParameters || []).map(([argName, argIdx]) => [argName, args[argIdx]]));
|
|
130
221
|
}
|
|
131
222
|
const regExp = /#{(\w+)}/;
|
|
132
223
|
let match;
|
|
@@ -139,7 +230,22 @@ function convertSQLParams(decoratorSQL, target, propertyKey, args) {
|
|
|
139
230
|
return [decoratorSQL, queryValues];
|
|
140
231
|
}
|
|
141
232
|
function cache(ttl) {
|
|
142
|
-
return function (
|
|
233
|
+
return function (...args) {
|
|
234
|
+
if ((0, decorator_utils_1.isStd)(args)) {
|
|
235
|
+
const [, ctx] = (0, decorator_utils_1.getStdArgs)(args);
|
|
236
|
+
ctx.addInitializer(function () {
|
|
237
|
+
cacheDefindMap.set([this.constructor.name, String(ctx.name)].toString(), ttl);
|
|
238
|
+
if (cacheBean == null) {
|
|
239
|
+
const cacheFactory = (0, core_decorator_1.getBean)(cache_factory_class_1.default);
|
|
240
|
+
if (cacheFactory || cacheFactory["factory"]) {
|
|
241
|
+
cacheBean = cacheFactory["factory"];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const target = args[0];
|
|
248
|
+
const propertyKey = args[1];
|
|
143
249
|
cacheDefindMap.set([target.constructor.name, propertyKey].toString(), ttl);
|
|
144
250
|
if (cacheBean == null) {
|
|
145
251
|
const cacheFactory = (0, core_decorator_1.getBean)(cache_factory_class_1.default);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 装饰器双签名感知工具。
|
|
4
|
+
*
|
|
5
|
+
* typespeed 2.5.x 起,装饰器同时支持两套运行时签名:
|
|
6
|
+
* - legacy(experimentalDecorators: true,2.4.x 现状):
|
|
7
|
+
* 类装饰器 (ctor);方法/属性装饰器 (target, key);带 descriptor 的方法 (target, key, descriptor);参数 (target, key, index)
|
|
8
|
+
* - 标准(TC39 装饰器提案,当前 Stage 2.7,experimentalDecorators: false):
|
|
9
|
+
* 统一 (value, context),context 恒为带 kind 字段的对象
|
|
10
|
+
*
|
|
11
|
+
* 装饰器函数本质是普通函数,运行时收到什么签名由「调用方(用户代码)」的编译模式决定,
|
|
12
|
+
* 因此库可以单份源码、运行时按签名形态分流:legacy 分支逻辑与 2.4.x 逐字一致,标准分支为新增。
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.getStdArgs = exports.isStd = void 0;
|
|
16
|
+
/**
|
|
17
|
+
* 判断装饰器收到的运行时参数是否为标准装饰器签名 (value, context)。
|
|
18
|
+
*
|
|
19
|
+
* 判据(与 `2.5.x-新版装饰器使用方案.md` 一致):
|
|
20
|
+
* 参数个数 === 2 且第二个参数是「带 string 类型 kind 字段」的对象。
|
|
21
|
+
*
|
|
22
|
+
* 为什么这个判据可靠:
|
|
23
|
+
* - legacy 类装饰器只有 1 参 → 不满足 length === 2
|
|
24
|
+
* - legacy 方法/属性装饰器第二参是 string 的 key → 不是对象
|
|
25
|
+
* - legacy 带 descriptor 的方法 / 参数装饰器是 3 参 → 不满足 length === 2
|
|
26
|
+
* - 标准模式第二参恒是带 kind 的对象 → 命中
|
|
27
|
+
*/
|
|
28
|
+
function isStd(args) {
|
|
29
|
+
return args.length === 2
|
|
30
|
+
&& typeof args[1] === "object"
|
|
31
|
+
&& args[1] !== null
|
|
32
|
+
&& typeof args[1].kind === "string";
|
|
33
|
+
}
|
|
34
|
+
exports.isStd = isStd;
|
|
35
|
+
/**
|
|
36
|
+
* 从标准装饰器参数中解出 (value, context)。
|
|
37
|
+
* 仅应在 isStd(args) 为 true 时调用。
|
|
38
|
+
*/
|
|
39
|
+
function getStdArgs(args) {
|
|
40
|
+
return [args[0], args[1]];
|
|
41
|
+
}
|
|
42
|
+
exports.getStdArgs = getStdArgs;
|