befly 0.1.26 → 0.2.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.
@@ -2,7 +2,7 @@ import { Env } from '../../config/env.js';
2
2
  import { Api } from '../../utils/api.js';
3
3
  import { RYes, RNo } from '../../utils/util.js';
4
4
 
5
- export default Api.POST('健康检查', { auth: false }, {}, [], async (bunpii, ctx) => {
5
+ export default Api.POST('健康检查', { auth: false }, {}, [], async (befly, ctx) => {
6
6
  const info = {
7
7
  status: 'ok',
8
8
  timestamp: new Date().toISOString(),
@@ -15,9 +15,9 @@ export default Api.POST('健康检查', { auth: false }, {}, [], async (bunpii,
15
15
  };
16
16
  // 检查 Redis 连接状态
17
17
  if (Env.REDIS_ENABLE === 1) {
18
- if (bunpii.redis) {
18
+ if (befly.redis) {
19
19
  try {
20
- await bunpii.redis.ping();
20
+ await befly.redis.ping();
21
21
  info.redis = '已连接';
22
22
  } catch (error) {
23
23
  info.redis = '未连接';
@@ -60,8 +60,10 @@ export default function toNumber(str, options = {}) {
60
60
  else return str;
61
61
  } else if (trimmedStr.indexOf('.') !== -1) {
62
62
  //floating number
63
- if (parsedStr === '0') return num; //0.0
64
- else if (parsedStr === numTrimmedByZeros) return num; //0.456. 0.79000
63
+ if (parsedStr === '0')
64
+ return num; //0.0
65
+ else if (parsedStr === numTrimmedByZeros)
66
+ return num; //0.456. 0.79000
65
67
  else if (parsedStr === `${sign}${numTrimmedByZeros}`) return num;
66
68
  else return str;
67
69
  }
package/libs/xml/util.js CHANGED
@@ -6,32 +6,32 @@ export const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*';
6
6
  const regexName = new RegExp('^' + nameRegexp + '$');
7
7
 
8
8
  export function getAllMatches(string, regex) {
9
- const matches = [];
10
- let match = regex.exec(string);
11
- while (match) {
12
- const allmatches = [];
13
- allmatches.startIndex = regex.lastIndex - match[0].length;
14
- const len = match.length;
15
- for (let index = 0; index < len; index++) {
16
- allmatches.push(match[index]);
9
+ const matches = [];
10
+ let match = regex.exec(string);
11
+ while (match) {
12
+ const allmatches = [];
13
+ allmatches.startIndex = regex.lastIndex - match[0].length;
14
+ const len = match.length;
15
+ for (let index = 0; index < len; index++) {
16
+ allmatches.push(match[index]);
17
+ }
18
+ matches.push(allmatches);
19
+ match = regex.exec(string);
17
20
  }
18
- matches.push(allmatches);
19
- match = regex.exec(string);
20
- }
21
- return matches;
21
+ return matches;
22
22
  }
23
23
 
24
- export const isName = function(string) {
25
- const match = regexName.exec(string);
26
- return !(match === null || typeof match === 'undefined');
27
- }
24
+ export const isName = function (string) {
25
+ const match = regexName.exec(string);
26
+ return !(match === null || typeof match === 'undefined');
27
+ };
28
28
 
29
29
  export function isExist(v) {
30
- return typeof v !== 'undefined';
30
+ return typeof v !== 'undefined';
31
31
  }
32
32
 
33
33
  export function isEmptyObject(obj) {
34
- return Object.keys(obj).length === 0;
34
+ return Object.keys(obj).length === 0;
35
35
  }
36
36
 
37
37
  /**
@@ -40,29 +40,29 @@ export function isEmptyObject(obj) {
40
40
  * @param {*} a
41
41
  */
42
42
  export function merge(target, a, arrayMode) {
43
- if (a) {
44
- const keys = Object.keys(a); // will return an array of own properties
45
- const len = keys.length; //don't make it inline
46
- for (let i = 0; i < len; i++) {
47
- if (arrayMode === 'strict') {
48
- target[keys[i]] = [ a[keys[i]] ];
49
- } else {
50
- target[keys[i]] = a[keys[i]];
51
- }
43
+ if (a) {
44
+ const keys = Object.keys(a); // will return an array of own properties
45
+ const len = keys.length; //don't make it inline
46
+ for (let i = 0; i < len; i++) {
47
+ if (arrayMode === 'strict') {
48
+ target[keys[i]] = [a[keys[i]]];
49
+ } else {
50
+ target[keys[i]] = a[keys[i]];
51
+ }
52
+ }
52
53
  }
53
- }
54
54
  }
55
55
  /* exports.merge =function (b,a){
56
56
  return Object.assign(b,a);
57
57
  } */
58
58
 
59
59
  export function getValue(v) {
60
- if (exports.isExist(v)) {
61
- return v;
62
- } else {
63
- return '';
64
- }
60
+ if (exports.isExist(v)) {
61
+ return v;
62
+ } else {
63
+ return '';
64
+ }
65
65
  }
66
66
 
67
67
  // const fakeCall = function(a) {return a;};
68
- // const fakeCallNoReturn = function() {};
68
+ // const fakeCallNoReturn = function() {};
@@ -1,197 +1,195 @@
1
1
  'use strict';
2
2
 
3
- import {getAllMatches, isName} from './util.js';
3
+ import { getAllMatches, isName } from './util.js';
4
4
 
5
5
  const defaultOptions = {
6
- allowBooleanAttributes: false, //A tag can have attributes without any value
7
- unpairedTags: []
6
+ allowBooleanAttributes: false, //A tag can have attributes without any value
7
+ unpairedTags: []
8
8
  };
9
9
 
10
10
  //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
11
11
  export function validate(xmlData, options) {
12
- options = Object.assign({}, defaultOptions, options);
13
-
14
- //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
15
- //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
16
- //xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
17
- const tags = [];
18
- let tagFound = false;
19
-
20
- //indicates that the root tag has been closed (aka. depth 0 has been reached)
21
- let reachedRoot = false;
22
-
23
- if (xmlData[0] === '\ufeff') {
24
- // check for byte order mark (BOM)
25
- xmlData = xmlData.substr(1);
26
- }
27
-
28
- for (let i = 0; i < xmlData.length; i++) {
29
-
30
- if (xmlData[i] === '<' && xmlData[i+1] === '?') {
31
- i+=2;
32
- i = readPI(xmlData,i);
33
- if (i.err) return i;
34
- }else if (xmlData[i] === '<') {
35
- //starting of tag
36
- //read until you reach to '>' avoiding any '>' in attribute value
37
- let tagStartPos = i;
38
- i++;
39
-
40
- if (xmlData[i] === '!') {
41
- i = readCommentAndCDATA(xmlData, i);
42
- continue;
43
- } else {
44
- let closingTag = false;
45
- if (xmlData[i] === '/') {
46
- //closing tag
47
- closingTag = true;
48
- i++;
49
- }
50
- //read tagname
51
- let tagName = '';
52
- for (; i < xmlData.length &&
53
- xmlData[i] !== '>' &&
54
- xmlData[i] !== ' ' &&
55
- xmlData[i] !== '\t' &&
56
- xmlData[i] !== '\n' &&
57
- xmlData[i] !== '\r'; i++
58
- ) {
59
- tagName += xmlData[i];
60
- }
61
- tagName = tagName.trim();
62
- //console.log(tagName);
63
-
64
- if (tagName[tagName.length - 1] === '/') {
65
- //self closing tag without attributes
66
- tagName = tagName.substring(0, tagName.length - 1);
67
- //continue;
68
- i--;
69
- }
70
- if (!validateTagName(tagName)) {
71
- let msg;
72
- if (tagName.trim().length === 0) {
73
- msg = "Invalid space after '<'.";
74
- } else {
75
- msg = "Tag '"+tagName+"' is an invalid name.";
76
- }
77
- return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
78
- }
12
+ options = Object.assign({}, defaultOptions, options);
79
13
 
80
- const result = readAttributeStr(xmlData, i);
81
- if (result === false) {
82
- return getErrorObject('InvalidAttr', "Attributes for '"+tagName+"' have open quote.", getLineNumberForPosition(xmlData, i));
83
- }
84
- let attrStr = result.value;
85
- i = result.index;
86
-
87
- if (attrStr[attrStr.length - 1] === '/') {
88
- //self closing tag
89
- const attrStrStart = i - attrStr.length;
90
- attrStr = attrStr.substring(0, attrStr.length - 1);
91
- const isValid = validateAttributeString(attrStr, options);
92
- if (isValid === true) {
93
- tagFound = true;
94
- //continue; //text may presents after self closing tag
95
- } else {
96
- //the result from the nested function returns the position of the error within the attribute
97
- //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
98
- //this gives us the absolute index in the entire xml, which we can use to find the line at last
99
- return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
100
- }
101
- } else if (closingTag) {
102
- if (!result.tagClosed) {
103
- return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
104
- } else if (attrStr.trim().length > 0) {
105
- return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
106
- } else if (tags.length === 0) {
107
- return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
108
- } else {
109
- const otg = tags.pop();
110
- if (tagName !== otg.tagName) {
111
- let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
112
- return getErrorObject('InvalidTag',
113
- "Expected closing tag '"+otg.tagName+"' (opened in line "+openPos.line+", col "+openPos.col+") instead of closing tag '"+tagName+"'.",
114
- getLineNumberForPosition(xmlData, tagStartPos));
115
- }
14
+ //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
15
+ //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
16
+ //xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
17
+ const tags = [];
18
+ let tagFound = false;
116
19
 
117
- //when there are no more tags, we reached the root level.
118
- if (tags.length == 0) {
119
- reachedRoot = true;
120
- }
121
- }
122
- } else {
123
- const isValid = validateAttributeString(attrStr, options);
124
- if (isValid !== true) {
125
- //the result from the nested function returns the position of the error within the attribute
126
- //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
127
- //this gives us the absolute index in the entire xml, which we can use to find the line at last
128
- return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
129
- }
130
-
131
- //if the root level has been reached before ...
132
- if (reachedRoot === true) {
133
- return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
134
- } else if(options.unpairedTags.indexOf(tagName) !== -1){
135
- //don't push into stack
136
- } else {
137
- tags.push({tagName, tagStartPos});
138
- }
139
- tagFound = true;
140
- }
20
+ //indicates that the root tag has been closed (aka. depth 0 has been reached)
21
+ let reachedRoot = false;
22
+
23
+ if (xmlData[0] === '\ufeff') {
24
+ // check for byte order mark (BOM)
25
+ xmlData = xmlData.substr(1);
26
+ }
141
27
 
142
- //skip tag text value
143
- //It may include comments and CDATA value
144
- for (i++; i < xmlData.length; i++) {
145
- if (xmlData[i] === '<') {
146
- if (xmlData[i + 1] === '!') {
147
- //comment or CADATA
148
- i++;
149
- i = readCommentAndCDATA(xmlData, i);
150
- continue;
151
- } else if (xmlData[i+1] === '?') {
152
- i = readPI(xmlData, ++i);
153
- if (i.err) return i;
154
- } else{
155
- break;
28
+ for (let i = 0; i < xmlData.length; i++) {
29
+ if (xmlData[i] === '<' && xmlData[i + 1] === '?') {
30
+ i += 2;
31
+ i = readPI(xmlData, i);
32
+ if (i.err) return i;
33
+ } else if (xmlData[i] === '<') {
34
+ //starting of tag
35
+ //read until you reach to '>' avoiding any '>' in attribute value
36
+ let tagStartPos = i;
37
+ i++;
38
+
39
+ if (xmlData[i] === '!') {
40
+ i = readCommentAndCDATA(xmlData, i);
41
+ continue;
42
+ } else {
43
+ let closingTag = false;
44
+ if (xmlData[i] === '/') {
45
+ //closing tag
46
+ closingTag = true;
47
+ i++;
48
+ }
49
+ //read tagname
50
+ let tagName = '';
51
+ for (; i < xmlData.length && xmlData[i] !== '>' && xmlData[i] !== ' ' && xmlData[i] !== '\t' && xmlData[i] !== '\n' && xmlData[i] !== '\r'; i++) {
52
+ tagName += xmlData[i];
53
+ }
54
+ tagName = tagName.trim();
55
+ //console.log(tagName);
56
+
57
+ if (tagName[tagName.length - 1] === '/') {
58
+ //self closing tag without attributes
59
+ tagName = tagName.substring(0, tagName.length - 1);
60
+ //continue;
61
+ i--;
62
+ }
63
+ if (!validateTagName(tagName)) {
64
+ let msg;
65
+ if (tagName.trim().length === 0) {
66
+ msg = "Invalid space after '<'.";
67
+ } else {
68
+ msg = "Tag '" + tagName + "' is an invalid name.";
69
+ }
70
+ return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
71
+ }
72
+
73
+ const result = readAttributeStr(xmlData, i);
74
+ if (result === false) {
75
+ return getErrorObject('InvalidAttr', "Attributes for '" + tagName + "' have open quote.", getLineNumberForPosition(xmlData, i));
76
+ }
77
+ let attrStr = result.value;
78
+ i = result.index;
79
+
80
+ if (attrStr[attrStr.length - 1] === '/') {
81
+ //self closing tag
82
+ const attrStrStart = i - attrStr.length;
83
+ attrStr = attrStr.substring(0, attrStr.length - 1);
84
+ const isValid = validateAttributeString(attrStr, options);
85
+ if (isValid === true) {
86
+ tagFound = true;
87
+ //continue; //text may presents after self closing tag
88
+ } else {
89
+ //the result from the nested function returns the position of the error within the attribute
90
+ //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
91
+ //this gives us the absolute index in the entire xml, which we can use to find the line at last
92
+ return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
93
+ }
94
+ } else if (closingTag) {
95
+ if (!result.tagClosed) {
96
+ return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
97
+ } else if (attrStr.trim().length > 0) {
98
+ return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
99
+ } else if (tags.length === 0) {
100
+ return getErrorObject('InvalidTag', "Closing tag '" + tagName + "' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
101
+ } else {
102
+ const otg = tags.pop();
103
+ if (tagName !== otg.tagName) {
104
+ let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
105
+ return getErrorObject('InvalidTag', "Expected closing tag '" + otg.tagName + "' (opened in line " + openPos.line + ', col ' + openPos.col + ") instead of closing tag '" + tagName + "'.", getLineNumberForPosition(xmlData, tagStartPos));
106
+ }
107
+
108
+ //when there are no more tags, we reached the root level.
109
+ if (tags.length == 0) {
110
+ reachedRoot = true;
111
+ }
112
+ }
113
+ } else {
114
+ const isValid = validateAttributeString(attrStr, options);
115
+ if (isValid !== true) {
116
+ //the result from the nested function returns the position of the error within the attribute
117
+ //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
118
+ //this gives us the absolute index in the entire xml, which we can use to find the line at last
119
+ return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
120
+ }
121
+
122
+ //if the root level has been reached before ...
123
+ if (reachedRoot === true) {
124
+ return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
125
+ } else if (options.unpairedTags.indexOf(tagName) !== -1) {
126
+ //don't push into stack
127
+ } else {
128
+ tags.push({ tagName, tagStartPos });
129
+ }
130
+ tagFound = true;
131
+ }
132
+
133
+ //skip tag text value
134
+ //It may include comments and CDATA value
135
+ for (i++; i < xmlData.length; i++) {
136
+ if (xmlData[i] === '<') {
137
+ if (xmlData[i + 1] === '!') {
138
+ //comment or CADATA
139
+ i++;
140
+ i = readCommentAndCDATA(xmlData, i);
141
+ continue;
142
+ } else if (xmlData[i + 1] === '?') {
143
+ i = readPI(xmlData, ++i);
144
+ if (i.err) return i;
145
+ } else {
146
+ break;
147
+ }
148
+ } else if (xmlData[i] === '&') {
149
+ const afterAmp = validateAmpersand(xmlData, i);
150
+ if (afterAmp == -1) return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
151
+ i = afterAmp;
152
+ } else {
153
+ if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
154
+ return getErrorObject('InvalidXml', 'Extra text at the end', getLineNumberForPosition(xmlData, i));
155
+ }
156
+ }
157
+ } //end of reading tag text value
158
+ if (xmlData[i] === '<') {
159
+ i--;
160
+ }
156
161
  }
157
- } else if (xmlData[i] === '&') {
158
- const afterAmp = validateAmpersand(xmlData, i);
159
- if (afterAmp == -1)
160
- return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
161
- i = afterAmp;
162
- }else{
163
- if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
164
- return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
162
+ } else {
163
+ if (isWhiteSpace(xmlData[i])) {
164
+ continue;
165
165
  }
166
- }
167
- } //end of reading tag text value
168
- if (xmlData[i] === '<') {
169
- i--;
166
+ return getErrorObject('InvalidChar', "char '" + xmlData[i] + "' is not expected.", getLineNumberForPosition(xmlData, i));
170
167
  }
171
- }
172
- } else {
173
- if ( isWhiteSpace(xmlData[i])) {
174
- continue;
175
- }
176
- return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
177
168
  }
178
- }
179
-
180
- if (!tagFound) {
181
- return getErrorObject('InvalidXml', 'Start tag expected.', 1);
182
- }else if (tags.length == 1) {
183
- return getErrorObject('InvalidTag', "Unclosed tag '"+tags[0].tagName+"'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
184
- }else if (tags.length > 0) {
185
- return getErrorObject('InvalidXml', "Invalid '"+
186
- JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\r?\n/g, '')+
187
- "' found.", {line: 1, col: 1});
188
- }
189
-
190
- return true;
191
- };
192
169
 
193
- function isWhiteSpace(char){
194
- return char === ' ' || char === '\t' || char === '\n' || char === '\r';
170
+ if (!tagFound) {
171
+ return getErrorObject('InvalidXml', 'Start tag expected.', 1);
172
+ } else if (tags.length == 1) {
173
+ return getErrorObject('InvalidTag', "Unclosed tag '" + tags[0].tagName + "'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
174
+ } else if (tags.length > 0) {
175
+ return getErrorObject(
176
+ 'InvalidXml',
177
+ "Invalid '" +
178
+ JSON.stringify(
179
+ tags.map((t) => t.tagName),
180
+ null,
181
+ 4
182
+ ).replace(/\r?\n/g, '') +
183
+ "' found.",
184
+ { line: 1, col: 1 }
185
+ );
186
+ }
187
+
188
+ return true;
189
+ }
190
+
191
+ function isWhiteSpace(char) {
192
+ return char === ' ' || char === '\t' || char === '\n' || char === '\r';
195
193
  }
196
194
  /**
197
195
  * Read Processing insstructions and skip
@@ -199,74 +197,56 @@ function isWhiteSpace(char){
199
197
  * @param {*} i
200
198
  */
201
199
  function readPI(xmlData, i) {
202
- const start = i;
203
- for (; i < xmlData.length; i++) {
204
- if (xmlData[i] == '?' || xmlData[i] == ' ') {
205
- //tagname
206
- const tagname = xmlData.substr(start, i - start);
207
- if (i > 5 && tagname === 'xml') {
208
- return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
209
- } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
210
- //check if valid attribut string
211
- i++;
212
- break;
213
- } else {
214
- continue;
215
- }
200
+ const start = i;
201
+ for (; i < xmlData.length; i++) {
202
+ if (xmlData[i] == '?' || xmlData[i] == ' ') {
203
+ //tagname
204
+ const tagname = xmlData.substr(start, i - start);
205
+ if (i > 5 && tagname === 'xml') {
206
+ return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
207
+ } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
208
+ //check if valid attribut string
209
+ i++;
210
+ break;
211
+ } else {
212
+ continue;
213
+ }
214
+ }
216
215
  }
217
- }
218
- return i;
216
+ return i;
219
217
  }
220
218
 
221
219
  function readCommentAndCDATA(xmlData, i) {
222
- if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
223
- //comment
224
- for (i += 3; i < xmlData.length; i++) {
225
- if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
226
- i += 2;
227
- break;
228
- }
229
- }
230
- } else if (
231
- xmlData.length > i + 8 &&
232
- xmlData[i + 1] === 'D' &&
233
- xmlData[i + 2] === 'O' &&
234
- xmlData[i + 3] === 'C' &&
235
- xmlData[i + 4] === 'T' &&
236
- xmlData[i + 5] === 'Y' &&
237
- xmlData[i + 6] === 'P' &&
238
- xmlData[i + 7] === 'E'
239
- ) {
240
- let angleBracketsCount = 1;
241
- for (i += 8; i < xmlData.length; i++) {
242
- if (xmlData[i] === '<') {
243
- angleBracketsCount++;
244
- } else if (xmlData[i] === '>') {
245
- angleBracketsCount--;
246
- if (angleBracketsCount === 0) {
247
- break;
220
+ if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
221
+ //comment
222
+ for (i += 3; i < xmlData.length; i++) {
223
+ if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
224
+ i += 2;
225
+ break;
226
+ }
227
+ }
228
+ } else if (xmlData.length > i + 8 && xmlData[i + 1] === 'D' && xmlData[i + 2] === 'O' && xmlData[i + 3] === 'C' && xmlData[i + 4] === 'T' && xmlData[i + 5] === 'Y' && xmlData[i + 6] === 'P' && xmlData[i + 7] === 'E') {
229
+ let angleBracketsCount = 1;
230
+ for (i += 8; i < xmlData.length; i++) {
231
+ if (xmlData[i] === '<') {
232
+ angleBracketsCount++;
233
+ } else if (xmlData[i] === '>') {
234
+ angleBracketsCount--;
235
+ if (angleBracketsCount === 0) {
236
+ break;
237
+ }
238
+ }
239
+ }
240
+ } else if (xmlData.length > i + 9 && xmlData[i + 1] === '[' && xmlData[i + 2] === 'C' && xmlData[i + 3] === 'D' && xmlData[i + 4] === 'A' && xmlData[i + 5] === 'T' && xmlData[i + 6] === 'A' && xmlData[i + 7] === '[') {
241
+ for (i += 8; i < xmlData.length; i++) {
242
+ if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
243
+ i += 2;
244
+ break;
245
+ }
248
246
  }
249
- }
250
- }
251
- } else if (
252
- xmlData.length > i + 9 &&
253
- xmlData[i + 1] === '[' &&
254
- xmlData[i + 2] === 'C' &&
255
- xmlData[i + 3] === 'D' &&
256
- xmlData[i + 4] === 'A' &&
257
- xmlData[i + 5] === 'T' &&
258
- xmlData[i + 6] === 'A' &&
259
- xmlData[i + 7] === '['
260
- ) {
261
- for (i += 8; i < xmlData.length; i++) {
262
- if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
263
- i += 2;
264
- break;
265
- }
266
247
  }
267
- }
268
248
 
269
- return i;
249
+ return i;
270
250
  }
271
251
 
272
252
  const doubleQuote = '"';
@@ -278,35 +258,35 @@ const singleQuote = "'";
278
258
  * @param {number} i
279
259
  */
280
260
  function readAttributeStr(xmlData, i) {
281
- let attrStr = '';
282
- let startChar = '';
283
- let tagClosed = false;
284
- for (; i < xmlData.length; i++) {
285
- if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
286
- if (startChar === '') {
287
- startChar = xmlData[i];
288
- } else if (startChar !== xmlData[i]) {
289
- //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
290
- } else {
291
- startChar = '';
292
- }
293
- } else if (xmlData[i] === '>') {
294
- if (startChar === '') {
295
- tagClosed = true;
296
- break;
297
- }
261
+ let attrStr = '';
262
+ let startChar = '';
263
+ let tagClosed = false;
264
+ for (; i < xmlData.length; i++) {
265
+ if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
266
+ if (startChar === '') {
267
+ startChar = xmlData[i];
268
+ } else if (startChar !== xmlData[i]) {
269
+ //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
270
+ } else {
271
+ startChar = '';
272
+ }
273
+ } else if (xmlData[i] === '>') {
274
+ if (startChar === '') {
275
+ tagClosed = true;
276
+ break;
277
+ }
278
+ }
279
+ attrStr += xmlData[i];
280
+ }
281
+ if (startChar !== '') {
282
+ return false;
298
283
  }
299
- attrStr += xmlData[i];
300
- }
301
- if (startChar !== '') {
302
- return false;
303
- }
304
-
305
- return {
306
- value: attrStr,
307
- index: i,
308
- tagClosed: tagClosed
309
- };
284
+
285
+ return {
286
+ value: attrStr,
287
+ index: i,
288
+ tagClosed: tagClosed
289
+ };
310
290
  }
311
291
 
312
292
  /**
@@ -317,109 +297,104 @@ const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s
317
297
  //attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
318
298
 
319
299
  function validateAttributeString(attrStr, options) {
320
- //console.log("start:"+attrStr+":end");
321
-
322
- //if(attrStr.trim().length === 0) return true; //empty string
323
-
324
- const matches = getAllMatches(attrStr, validAttrStrRegxp);
325
- const attrNames = {};
326
-
327
- for (let i = 0; i < matches.length; i++) {
328
- if (matches[i][1].length === 0) {
329
- //nospace before attribute name: a="sd"b="saf"
330
- return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' has no space in starting.", getPositionFromMatch(matches[i]))
331
- } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
332
- return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' is without value.", getPositionFromMatch(matches[i]));
333
- } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
334
- //independent attribute: ab
335
- return getErrorObject('InvalidAttr', "boolean attribute '"+matches[i][2]+"' is not allowed.", getPositionFromMatch(matches[i]));
336
- }
337
- /* else if(matches[i][6] === undefined){//attribute without value: ab=
300
+ //console.log("start:"+attrStr+":end");
301
+
302
+ //if(attrStr.trim().length === 0) return true; //empty string
303
+
304
+ const matches = getAllMatches(attrStr, validAttrStrRegxp);
305
+ const attrNames = {};
306
+
307
+ for (let i = 0; i < matches.length; i++) {
308
+ if (matches[i][1].length === 0) {
309
+ //nospace before attribute name: a="sd"b="saf"
310
+ return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' has no space in starting.", getPositionFromMatch(matches[i]));
311
+ } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
312
+ return getErrorObject('InvalidAttr', "Attribute '" + matches[i][2] + "' is without value.", getPositionFromMatch(matches[i]));
313
+ } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
314
+ //independent attribute: ab
315
+ return getErrorObject('InvalidAttr', "boolean attribute '" + matches[i][2] + "' is not allowed.", getPositionFromMatch(matches[i]));
316
+ }
317
+ /* else if(matches[i][6] === undefined){//attribute without value: ab=
338
318
  return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
339
319
  } */
340
- const attrName = matches[i][2];
341
- if (!validateAttrName(attrName)) {
342
- return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is an invalid name.", getPositionFromMatch(matches[i]));
343
- }
344
- if (!attrNames.hasOwnProperty(attrName)) {
345
- //check for duplicate attribute.
346
- attrNames[attrName] = 1;
347
- } else {
348
- return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is repeated.", getPositionFromMatch(matches[i]));
320
+ const attrName = matches[i][2];
321
+ if (!validateAttrName(attrName)) {
322
+ return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is an invalid name.", getPositionFromMatch(matches[i]));
323
+ }
324
+ if (!attrNames.hasOwnProperty(attrName)) {
325
+ //check for duplicate attribute.
326
+ attrNames[attrName] = 1;
327
+ } else {
328
+ return getErrorObject('InvalidAttr', "Attribute '" + attrName + "' is repeated.", getPositionFromMatch(matches[i]));
329
+ }
349
330
  }
350
- }
351
331
 
352
- return true;
332
+ return true;
353
333
  }
354
334
 
355
335
  function validateNumberAmpersand(xmlData, i) {
356
- let re = /\d/;
357
- if (xmlData[i] === 'x') {
358
- i++;
359
- re = /[\da-fA-F]/;
360
- }
361
- for (; i < xmlData.length; i++) {
362
- if (xmlData[i] === ';')
363
- return i;
364
- if (!xmlData[i].match(re))
365
- break;
366
- }
367
- return -1;
336
+ let re = /\d/;
337
+ if (xmlData[i] === 'x') {
338
+ i++;
339
+ re = /[\da-fA-F]/;
340
+ }
341
+ for (; i < xmlData.length; i++) {
342
+ if (xmlData[i] === ';') return i;
343
+ if (!xmlData[i].match(re)) break;
344
+ }
345
+ return -1;
368
346
  }
369
347
 
370
348
  function validateAmpersand(xmlData, i) {
371
- // https://www.w3.org/TR/xml/#dt-charref
372
- i++;
373
- if (xmlData[i] === ';')
374
- return -1;
375
- if (xmlData[i] === '#') {
349
+ // https://www.w3.org/TR/xml/#dt-charref
376
350
  i++;
377
- return validateNumberAmpersand(xmlData, i);
378
- }
379
- let count = 0;
380
- for (; i < xmlData.length; i++, count++) {
381
- if (xmlData[i].match(/\w/) && count < 20)
382
- continue;
383
- if (xmlData[i] === ';')
384
- break;
385
- return -1;
386
- }
387
- return i;
351
+ if (xmlData[i] === ';') return -1;
352
+ if (xmlData[i] === '#') {
353
+ i++;
354
+ return validateNumberAmpersand(xmlData, i);
355
+ }
356
+ let count = 0;
357
+ for (; i < xmlData.length; i++, count++) {
358
+ if (xmlData[i].match(/\w/) && count < 20) continue;
359
+ if (xmlData[i] === ';') break;
360
+ return -1;
361
+ }
362
+ return i;
388
363
  }
389
364
 
390
365
  function getErrorObject(code, message, lineNumber) {
391
- return {
392
- err: {
393
- code: code,
394
- msg: message,
395
- line: lineNumber.line || lineNumber,
396
- col: lineNumber.col,
397
- },
398
- };
366
+ return {
367
+ err: {
368
+ code: code,
369
+ msg: message,
370
+ line: lineNumber.line || lineNumber,
371
+ col: lineNumber.col
372
+ }
373
+ };
399
374
  }
400
375
 
401
376
  function validateAttrName(attrName) {
402
- return isName(attrName);
377
+ return isName(attrName);
403
378
  }
404
379
 
405
380
  // const startsWithXML = /^xml/i;
406
381
 
407
382
  function validateTagName(tagname) {
408
- return isName(tagname) /* && !tagname.match(startsWithXML) */;
383
+ return isName(tagname) /* && !tagname.match(startsWithXML) */;
409
384
  }
410
385
 
411
386
  //this function returns the line number for the character at the given index
412
387
  function getLineNumberForPosition(xmlData, index) {
413
- const lines = xmlData.substring(0, index).split(/\r?\n/);
414
- return {
415
- line: lines.length,
388
+ const lines = xmlData.substring(0, index).split(/\r?\n/);
389
+ return {
390
+ line: lines.length,
416
391
 
417
- // column number is last line's length + 1, because column numbering starts at 1:
418
- col: lines[lines.length - 1].length + 1
419
- };
392
+ // column number is last line's length + 1, because column numbering starts at 1:
393
+ col: lines[lines.length - 1].length + 1
394
+ };
420
395
  }
421
396
 
422
397
  //this function returns the position of the first character of match within attrStr
423
398
  function getPositionFromMatch(match) {
424
- return match.startIndex + match[1].length;
399
+ return match.startIndex + match[1].length;
425
400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "0.1.26",
3
+ "version": "0.2.0",
4
4
  "description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -49,5 +49,5 @@
49
49
  "README.md",
50
50
  "vitest.config.js"
51
51
  ],
52
- "gitHead": "763f398127df3dc2471295745b8dfde3fdd84a92"
52
+ "gitHead": "38adc6b09eeec245f6a2ce6fae7d71f93940b617"
53
53
  }
package/plugins/cors.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  after: ['_redis', '_db'],
3
- async onGet(bunpii, ctx, req) {
3
+ async onGet(befly, ctx, req) {
4
4
  // 设置 CORS 头部
5
5
  req.headers.set('Access-Control-Allow-Origin', req.headers.get('origin'));
6
6
  req.headers.set('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
package/plugins/db.js CHANGED
@@ -4,7 +4,7 @@ import { Crud } from '../utils/curd.js';
4
4
 
5
5
  export default {
6
6
  after: ['_redis'],
7
- async onInit(bunpii) {
7
+ async onInit(befly) {
8
8
  try {
9
9
  if (Env.MYSQL_ENABLE === 1) {
10
10
  // 创建 MySQL 连接池
@@ -36,7 +36,7 @@ export default {
36
36
  const result = await sql`SELECT VERSION() AS version`.execute(db);
37
37
  if (result?.rows?.[0]?.version) {
38
38
  // 扩展数据库实例
39
- return new Crud(db, bunpii.redis, sql);
39
+ return new Crud(db, befly.redis, sql);
40
40
  } else {
41
41
  return {};
42
42
  }
package/plugins/logger.js CHANGED
@@ -3,7 +3,7 @@ import { Logger } from '../utils/logger.js';
3
3
 
4
4
  export default {
5
5
  after: [],
6
- async onInit(bunpii) {
6
+ async onInit(befly) {
7
7
  try {
8
8
  return Logger;
9
9
  } catch (error) {
package/plugins/redis.js CHANGED
@@ -3,7 +3,7 @@ import { Logger } from '../utils/logger.js';
3
3
 
4
4
  export default {
5
5
  after: ['_logger'],
6
- async onInit(bunpii) {
6
+ async onInit(befly) {
7
7
  try {
8
8
  if (Env.REDIS_ENABLE === 1) {
9
9
  const config = {
package/utils/api.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Logger } from '../utils/logger.js';
2
- import { RYes, RNo } from '../utils/util.js';
1
+ import { Logger } from './logger.js';
2
+ import { RYes, RNo } from './util.js';
3
3
  export class Api {
4
4
  // GET 方法
5
5
  static GET(name, auth = false, fields = {}, required = [], handler) {
@@ -9,7 +9,7 @@ export class Api {
9
9
  auth: auth,
10
10
  fields: fields,
11
11
  required: required,
12
- handler: this.wrapHandler(handler)
12
+ handler: await handler(bp, ctx, req)
13
13
  };
14
14
  }
15
15
 
@@ -21,29 +21,7 @@ export class Api {
21
21
  auth: auth,
22
22
  fields: fields,
23
23
  required: required,
24
- handler: this.wrapHandler(handler)
25
- };
26
- }
27
-
28
- // 包装处理器,自动处理异常和响应格式
29
- static wrapHandler(handler) {
30
- return async (bp, ctx, req) => {
31
- try {
32
- const result = await handler(bp, ctx, req);
33
-
34
- return result;
35
- } catch (error) {
36
- Logger.error({
37
- msg: '内部服务器错误',
38
- error: error.message,
39
- stack: error.stack,
40
- url: req?.url || '',
41
- user: ctx?.user || {}
42
- });
43
-
44
- // 返回错误响应
45
- return RNo('内部服务器错误');
46
- }
24
+ handler: await handler(bp, ctx, req)
47
25
  };
48
26
  }
49
27
  }
package/utils/curd.js CHANGED
@@ -64,8 +64,11 @@ export class Crud {
64
64
 
65
65
  // 增强的更新方法 - 自动添加 updated_at,支持链式调用
66
66
  updData(tableName, data) {
67
+ // 剔除 undefined 值
68
+ const filteredData = Object.fromEntries(Object.entries(data).filter(([key, value]) => value !== undefined));
69
+
67
70
  const updateData = {
68
- ...omitFields(data, ['id', 'created_at', 'deleted_at']),
71
+ ...omitFields(filteredData, ['id', 'created_at', 'deleted_at']),
69
72
  updated_at: Date.now()
70
73
  };
71
74