oak-domain 5.1.31 → 5.1.33

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.
@@ -1,16 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CHECKER_PRIORITY_MAP = exports.CHECKER_MAX_PRIORITY = exports.TRIGGER_MAX_PRIORITY = exports.TRIGGER_DEFAULT_PRIORITY = exports.TRIGGER_MIN_PRIORITY = void 0;
4
+ /* ============================================================================
5
+ * 优先级常量定义
6
+ *
7
+ * 触发器按优先级从小到大依次执行,数值越小越先执行。
8
+ * 合理设置优先级可以控制触发器的执行顺序,确保数据处理的正确性。
9
+ * ============================================================================ */
4
10
  /**
5
- * 优先级越小,越早执行。定义在1~99之间
11
+ * 触发器最小优先级(最先执行)
12
+ * 用于需要最早执行的触发器,如数据预处理
6
13
  */
7
14
  exports.TRIGGER_MIN_PRIORITY = 1;
15
+ /**
16
+ * 触发器默认优先级
17
+ * 未指定优先级时使用此值
18
+ */
8
19
  exports.TRIGGER_DEFAULT_PRIORITY = 25;
20
+ /**
21
+ * 触发器最大优先级(在普通触发器中最后执行)
22
+ * 用于依赖其他触发器处理结果的场景
23
+ */
9
24
  exports.TRIGGER_MAX_PRIORITY = 50;
25
+ /**
26
+ * Checker(检查器)的最大优先级
27
+ * Checker 优先级范围是 51-99,在所有普通触发器之后执行
28
+ * 这确保了数据校验在数据处理完成后进行
29
+ */
10
30
  exports.CHECKER_MAX_PRIORITY = 99;
11
31
  /**
12
- * logical可能会更改row和data的值,应当最先执行,data和row不能修改相关的值
13
- * 允许logicalData去改data中的值
32
+ * 不同类型 Checker 的默认优先级映射
33
+ *
34
+ * 执行顺序(从先到后):
35
+ * 1. logicalData (31) - 逻辑数据检查,可修改 data 中的值
36
+ * 2. logical (33) - 纯逻辑检查
37
+ * 3. row (51) - 行级检查,检查数据库中已存在的行
38
+ * 4. relation (56) - 关系检查,检查关联数据
39
+ * 5. data (61) - 数据完整性检查
40
+ *
41
+ * @example
42
+ * // logicalData 类型可以自动填充默认值
43
+ * // logical 类型进行业务逻辑校验
44
+ * // row 类型检查当前行状态是否允许操作
14
45
  */
15
46
  exports.CHECKER_PRIORITY_MAP = {
16
47
  logicalData: 31,
@@ -33,19 +33,19 @@ export default class SimpleConnector<ED extends EntityDict & BaseEntityDict, Fro
33
33
  protected parseAspectResult(response: Response): Promise<{
34
34
  result: any;
35
35
  opRecords: any;
36
- message: string | null;
36
+ message: string | undefined;
37
37
  } | {
38
38
  result: ReadableStream<Uint8Array<ArrayBuffer>> | null;
39
- message: string | null;
39
+ message: string | undefined;
40
40
  opRecords?: undefined;
41
41
  }>;
42
42
  callAspect(name: string, params: any, context?: FrontCxt): Promise<{
43
43
  result: any;
44
44
  opRecords: any;
45
- message: string | null;
45
+ message: string | undefined;
46
46
  } | {
47
47
  result: ReadableStream<Uint8Array<ArrayBuffer>> | null;
48
- message: string | null;
48
+ message: string | undefined;
49
49
  opRecords?: undefined;
50
50
  }>;
51
51
  getRouter(): string;
@@ -62,7 +62,7 @@ export default class SimpleConnector<ED extends EntityDict & BaseEntityDict, Fro
62
62
  aspectName: string;
63
63
  data: any;
64
64
  };
65
- serializeResult(result: any, opRecords: OpRecord<ED>[], headers: IncomingHttpHeaders, body: any, message?: string): Promise<{
65
+ serializeResult(result: any, opRecords: OpRecord<ED>[], headers: IncomingHttpHeaders, body: any, rawMessage?: string): Promise<{
66
66
  body: any;
67
67
  headers?: Record<string, any> | undefined;
68
68
  }>;
@@ -80,7 +80,8 @@ class SimpleConnector {
80
80
  if (response.status > 299) {
81
81
  throw new types_1.OakServerProxyException(`网络请求返回status是${response.status}`);
82
82
  }
83
- const message = response.headers.get('oak-message');
83
+ const rawMessage = response.headers.get('oak-message');
84
+ const message = rawMessage ? decodeURIComponent(rawMessage) : undefined;
84
85
  const responseType = response.headers.get('Content-Type') || response.headers.get('content-type');
85
86
  if (responseType?.toLocaleLowerCase().match(/application\/json/i)) {
86
87
  const { exception, result, opRecords } = await response.json();
@@ -188,7 +189,8 @@ class SimpleConnector {
188
189
  data: files ? Object.assign({}, body, files) : body,
189
190
  };
190
191
  }
191
- async serializeResult(result, opRecords, headers, body, message) {
192
+ async serializeResult(result, opRecords, headers, body, rawMessage) {
193
+ const message = rawMessage ? encodeURIComponent(rawMessage) : undefined;
192
194
  if (result instanceof stream_1.Stream || result instanceof Buffer) {
193
195
  return {
194
196
  body: result,
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 匹配 glob 模式
3
+ * @param filePath 文件路径(已标准化)
4
+ * @param pattern glob 模式
5
+ */
6
+ export declare const matchGlobPattern: (filePath: string, pattern: string) => boolean;
7
+ /**
8
+ * 展开花括号模式 {a,b,c}
9
+ */
10
+ export declare const expandBraces: (pattern: string) => string[];
11
+ /**
12
+ * 分割花括号选项(处理嵌套)
13
+ */
14
+ export declare const splitBraceOptions: (content: string) => string[];
15
+ /**
16
+ * 递归匹配路径段
17
+ */
18
+ export declare const matchSegments: (fileSegs: string[], fileIdx: number, patternSegs: string[], patternIdx: number) => boolean;
19
+ /**
20
+ * 匹配单个路径段(处理 *, ?, [] 通配符)
21
+ */
22
+ export declare const matchSegment: (fileSeg: string, patternSeg: string) => boolean;
23
+ /**
24
+ * 查找匹配的右括号
25
+ */
26
+ export declare const findClosingBracket: (pattern: string, startIdx: number) => number;
27
+ /**
28
+ * 匹配字符类 [abc] [a-z] [!abc]
29
+ */
30
+ export declare const matchCharClass: (char: string, charClass: string) => boolean;
@@ -0,0 +1,286 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchCharClass = exports.findClosingBracket = exports.matchSegment = exports.matchSegments = exports.splitBraceOptions = exports.expandBraces = exports.matchGlobPattern = void 0;
4
+ /**
5
+ * 匹配 glob 模式
6
+ * @param filePath 文件路径(已标准化)
7
+ * @param pattern glob 模式
8
+ */
9
+ const matchGlobPattern = (filePath, pattern) => {
10
+ // 处理花括号展开 {a,b,c}
11
+ const expandedPatterns = (0, exports.expandBraces)(pattern);
12
+ // 只要有一个展开的模式匹配就返回 true
13
+ for (const expandedPattern of expandedPatterns) {
14
+ const fileSegments = filePath.split('/');
15
+ const patternSegments = expandedPattern.split('/');
16
+ if ((0, exports.matchSegments)(fileSegments, 0, patternSegments, 0)) {
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ };
22
+ exports.matchGlobPattern = matchGlobPattern;
23
+ /**
24
+ * 展开花括号模式 {a,b,c}
25
+ */
26
+ const expandBraces = (pattern) => {
27
+ const result = [];
28
+ let current = '';
29
+ let depth = 0;
30
+ let braceStart = -1;
31
+ for (let i = 0; i < pattern.length; i++) {
32
+ const char = pattern[i];
33
+ const prevChar = i > 0 ? pattern[i - 1] : '';
34
+ // 处理转义
35
+ if (prevChar === '\\') {
36
+ current += char;
37
+ continue;
38
+ }
39
+ if (char === '{') {
40
+ if (depth === 0) {
41
+ braceStart = current.length;
42
+ }
43
+ depth++;
44
+ current += char;
45
+ }
46
+ else if (char === '}' && depth > 0) {
47
+ depth--;
48
+ current += char;
49
+ if (depth === 0) {
50
+ // 提取花括号内容
51
+ const braceContent = current.substring(braceStart + 1, current.length - 1);
52
+ const options = (0, exports.splitBraceOptions)(braceContent);
53
+ const prefix = current.substring(0, braceStart);
54
+ const suffix = pattern.substring(i + 1);
55
+ // 递归展开每个选项
56
+ for (const option of options) {
57
+ const expanded = (0, exports.expandBraces)(prefix + option + suffix);
58
+ result.push(...expanded);
59
+ }
60
+ return result;
61
+ }
62
+ }
63
+ else {
64
+ current += char;
65
+ }
66
+ }
67
+ return [pattern];
68
+ };
69
+ exports.expandBraces = expandBraces;
70
+ /**
71
+ * 分割花括号选项(处理嵌套)
72
+ */
73
+ const splitBraceOptions = (content) => {
74
+ const options = [];
75
+ let current = '';
76
+ let depth = 0;
77
+ for (let i = 0; i < content.length; i++) {
78
+ const char = content[i];
79
+ const prevChar = i > 0 ? content[i - 1] : '';
80
+ if (prevChar === '\\') {
81
+ current += char;
82
+ continue;
83
+ }
84
+ if (char === '{') {
85
+ depth++;
86
+ current += char;
87
+ }
88
+ else if (char === '}') {
89
+ depth--;
90
+ current += char;
91
+ }
92
+ else if (char === ',' && depth === 0) {
93
+ options.push(current);
94
+ current = '';
95
+ }
96
+ else {
97
+ current += char;
98
+ }
99
+ }
100
+ if (current) {
101
+ options.push(current);
102
+ }
103
+ return options;
104
+ };
105
+ exports.splitBraceOptions = splitBraceOptions;
106
+ /**
107
+ * 递归匹配路径段
108
+ */
109
+ const matchSegments = (fileSegs, fileIdx, patternSegs, patternIdx) => {
110
+ // 都匹配完了,成功
111
+ if (fileIdx === fileSegs.length && patternIdx === patternSegs.length) {
112
+ return true;
113
+ }
114
+ // pattern 匹配完了但文件路径还有,失败
115
+ if (patternIdx === patternSegs.length) {
116
+ return false;
117
+ }
118
+ const patternSeg = patternSegs[patternIdx];
119
+ // 处理 **
120
+ if (patternSeg === '**') {
121
+ // ** 可以匹配 0 到多个目录段
122
+ // 尝试匹配 0 个(跳过 **)
123
+ if ((0, exports.matchSegments)(fileSegs, fileIdx, patternSegs, patternIdx + 1)) {
124
+ return true;
125
+ }
126
+ // 尝试匹配 1 个或多个(消耗一个文件段,** 保持)
127
+ if (fileIdx < fileSegs.length) {
128
+ return (0, exports.matchSegments)(fileSegs, fileIdx + 1, patternSegs, patternIdx);
129
+ }
130
+ return false;
131
+ }
132
+ // 文件路径匹配完了但 pattern 还有(且不是 **),失败
133
+ if (fileIdx === fileSegs.length) {
134
+ return false;
135
+ }
136
+ const fileSeg = fileSegs[fileIdx];
137
+ // 处理普通段(可能包含 *, ?, [])
138
+ if ((0, exports.matchSegment)(fileSeg, patternSeg)) {
139
+ return (0, exports.matchSegments)(fileSegs, fileIdx + 1, patternSegs, patternIdx + 1);
140
+ }
141
+ return false;
142
+ };
143
+ exports.matchSegments = matchSegments;
144
+ /**
145
+ * 匹配单个路径段(处理 *, ?, [] 通配符)
146
+ */
147
+ const matchSegment = (fileSeg, patternSeg) => {
148
+ let fileIdx = 0;
149
+ let patternIdx = 0;
150
+ let starIdx = -1;
151
+ let matchIdx = 0;
152
+ while (fileIdx < fileSeg.length) {
153
+ if (patternIdx < patternSeg.length) {
154
+ const patternChar = patternSeg[patternIdx];
155
+ const prevChar = patternIdx > 0 ? patternSeg[patternIdx - 1] : '';
156
+ // 处理转义字符
157
+ if (prevChar === '\\') {
158
+ if (fileSeg[fileIdx] === patternChar) {
159
+ fileIdx++;
160
+ patternIdx++;
161
+ continue;
162
+ }
163
+ else {
164
+ if (starIdx !== -1) {
165
+ patternIdx = starIdx + 1;
166
+ matchIdx++;
167
+ fileIdx = matchIdx;
168
+ continue;
169
+ }
170
+ return false;
171
+ }
172
+ }
173
+ // 跳过转义符本身
174
+ if (patternChar === '\\') {
175
+ patternIdx++;
176
+ continue;
177
+ }
178
+ // 处理 ?
179
+ if (patternChar === '?') {
180
+ fileIdx++;
181
+ patternIdx++;
182
+ continue;
183
+ }
184
+ // 处理 []
185
+ if (patternChar === '[') {
186
+ const closeIdx = (0, exports.findClosingBracket)(patternSeg, patternIdx);
187
+ if (closeIdx !== -1) {
188
+ const charClass = patternSeg.substring(patternIdx + 1, closeIdx);
189
+ if ((0, exports.matchCharClass)(fileSeg[fileIdx], charClass)) {
190
+ fileIdx++;
191
+ patternIdx = closeIdx + 1;
192
+ continue;
193
+ }
194
+ else {
195
+ if (starIdx !== -1) {
196
+ patternIdx = starIdx + 1;
197
+ matchIdx++;
198
+ fileIdx = matchIdx;
199
+ continue;
200
+ }
201
+ return false;
202
+ }
203
+ }
204
+ }
205
+ // 处理 *
206
+ if (patternChar === '*') {
207
+ starIdx = patternIdx;
208
+ matchIdx = fileIdx;
209
+ patternIdx++;
210
+ continue;
211
+ }
212
+ // 普通字符匹配
213
+ if (fileSeg[fileIdx] === patternChar) {
214
+ fileIdx++;
215
+ patternIdx++;
216
+ continue;
217
+ }
218
+ }
219
+ // 不匹配,回溯到上一个 *
220
+ if (starIdx !== -1) {
221
+ patternIdx = starIdx + 1;
222
+ matchIdx++;
223
+ fileIdx = matchIdx;
224
+ continue;
225
+ }
226
+ return false;
227
+ }
228
+ // 处理 pattern 末尾的 * 和转义符
229
+ while (patternIdx < patternSeg.length) {
230
+ const char = patternSeg[patternIdx];
231
+ if (char === '*' || char === '\\') {
232
+ patternIdx++;
233
+ }
234
+ else {
235
+ break;
236
+ }
237
+ }
238
+ return patternIdx === patternSeg.length;
239
+ };
240
+ exports.matchSegment = matchSegment;
241
+ /**
242
+ * 查找匹配的右括号
243
+ */
244
+ const findClosingBracket = (pattern, startIdx) => {
245
+ for (let i = startIdx + 1; i < pattern.length; i++) {
246
+ if (pattern[i] === ']' && pattern[i - 1] !== '\\') {
247
+ return i;
248
+ }
249
+ }
250
+ return -1;
251
+ };
252
+ exports.findClosingBracket = findClosingBracket;
253
+ /**
254
+ * 匹配字符类 [abc] [a-z] [!abc]
255
+ */
256
+ const matchCharClass = (char, charClass) => {
257
+ // 处理否定 [!...] 或 [^...]
258
+ const isNegated = charClass[0] === '!' || charClass[0] === '^';
259
+ const classContent = isNegated ? charClass.substring(1) : charClass;
260
+ let matched = false;
261
+ let i = 0;
262
+ while (i < classContent.length) {
263
+ const currentChar = classContent[i];
264
+ // 处理范围 a-z
265
+ if (i + 2 < classContent.length && classContent[i + 1] === '-') {
266
+ const start = currentChar.charCodeAt(0);
267
+ const end = classContent[i + 2].charCodeAt(0);
268
+ const charCode = char.charCodeAt(0);
269
+ if (charCode >= start && charCode <= end) {
270
+ matched = true;
271
+ break;
272
+ }
273
+ i += 3;
274
+ }
275
+ else {
276
+ // 单个字符
277
+ if (char === currentChar) {
278
+ matched = true;
279
+ break;
280
+ }
281
+ i++;
282
+ }
283
+ }
284
+ return isNegated ? !matched : matched;
285
+ };
286
+ exports.matchCharClass = matchCharClass;
@@ -2,5 +2,5 @@ declare const ToCent: (float: number) => number;
2
2
  declare const ToYuan: (int: number) => number;
3
3
  declare const StringToCent: (value: string, allowNegative?: true) => number | undefined;
4
4
  declare const CentToString: (value: number, fixed?: number) => string | undefined;
5
- declare const ThousandCont: (value: number | string, decimalPlaces?: number) => string | undefined;
5
+ declare const ThousandCont: (value: number | string, decimalPlaces?: number) => string;
6
6
  export { ToCent, ToYuan, StringToCent, CentToString, ThousandCont };
@@ -23,29 +23,40 @@ const CentToString = (value, fixed) => {
23
23
  };
24
24
  exports.CentToString = CentToString;
25
25
  const ThousandCont = (value, decimalPlaces) => {
26
- let value1 = typeof value === 'number' ? `${value}` : value;
27
- const numArr = value1.split('.');
28
- value1 = numArr[0];
29
- let result = '';
30
- while (value1.length > 3) {
31
- result = ',' + value1.slice(-3) + result;
32
- value1 = value1.slice(0, value1.length - 3);
33
- }
34
- if (value1) {
35
- result = value1 + result;
36
- }
37
- if (decimalPlaces && decimalPlaces > 0) {
38
- if (numArr[1]) {
39
- const decimalPart = numArr[1].padEnd(decimalPlaces, '0').slice(0, decimalPlaces);
40
- result = result + '.' + decimalPart;
41
- }
42
- else {
43
- result = result + '.' + '0'.repeat(decimalPlaces);
44
- }
26
+ let valueStr = typeof value === 'number' ? value.toString() : value;
27
+ // 处理负号
28
+ let isNegative = false;
29
+ if (valueStr.startsWith('-')) {
30
+ isNegative = true;
31
+ valueStr = valueStr.slice(1); // 移除负号
32
+ }
33
+ // 分离整数部分和小数部分
34
+ const [integerPart, decimalPart = ''] = valueStr.split('.');
35
+ // 千分位格式化整数部分
36
+ let formattedInteger = '';
37
+ let remainingInteger = integerPart;
38
+ while (remainingInteger.length > 3) {
39
+ formattedInteger = ',' + remainingInteger.slice(-3) + formattedInteger;
40
+ remainingInteger = remainingInteger.slice(0, remainingInteger.length - 3);
41
+ }
42
+ if (remainingInteger) {
43
+ formattedInteger = remainingInteger + formattedInteger;
44
+ }
45
+ // 恢复负号
46
+ if (isNegative) {
47
+ formattedInteger = '-' + formattedInteger;
48
+ }
49
+ // 处理小数部分
50
+ let formattedDecimal = '';
51
+ if (decimalPlaces !== undefined && decimalPlaces > 0) {
52
+ formattedDecimal = decimalPart.padEnd(decimalPlaces, '0').slice(0, decimalPlaces);
53
+ return formattedInteger + '.' + formattedDecimal;
54
+ }
55
+ else if (decimalPart) {
56
+ return formattedInteger + '.' + decimalPart;
45
57
  }
46
58
  else {
47
- result = numArr[1] ? result + '.' + numArr[1] : result;
59
+ return formattedInteger;
48
60
  }
49
- return result;
50
61
  };
51
62
  exports.ThousandCont = ThousandCont;
@@ -1,4 +1,4 @@
1
- import { Buffer } from 'buffer';
1
+ import { Buffer, WithImplicitCoercion } from 'buffer';
2
2
  declare function percentEncode(c: number): string;
3
3
  declare function percentDecode(input: Buffer): Buffer<ArrayBuffer>;
4
4
  declare function serializeUrlencoded(tuples: any[], encodingOverride?: undefined): string;
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "oak-domain",
3
- "version": "5.1.31",
3
+ "version": "5.1.33",
4
4
  "author": {
5
5
  "name": "XuChang"
6
6
  },
7
7
  "scripts": {
8
8
  "make:domain": "ts-node scripts/make.ts",
9
- "build": "tsc",
9
+ "build": "node --stack-size=65500 ./scripts/build.js -p ./tsconfig.json",
10
+ "test:build": "node --stack-size=65500 ./scripts/build.js -p ./tsconfig.test.json --noEmit",
10
11
  "prebuild": "npm run make:domain",
11
- "test": "ts-node test/test.ts"
12
+ "test": "mocha --require ts-node/register test/**.ts"
12
13
  },
13
14
  "files": [
14
15
  "lib/**/*",