lemon-core 4.1.15 → 4.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.
Files changed (154) hide show
  1. package/dist/common/test-helper.d.ts +2 -2
  2. package/dist/common/test-helper.js +24 -26
  3. package/dist/common/test-helper.js.map +1 -1
  4. package/dist/controllers/dummy-controller.js +39 -46
  5. package/dist/controllers/dummy-controller.js.map +1 -1
  6. package/dist/controllers/general-api-controller.js +95 -100
  7. package/dist/controllers/general-api-controller.js.map +1 -1
  8. package/dist/controllers/general-controller.js +81 -82
  9. package/dist/controllers/general-controller.js.map +1 -1
  10. package/dist/cores/api/api-service.d.ts +1 -1
  11. package/dist/cores/api/api-service.js +228 -269
  12. package/dist/cores/api/api-service.js.map +1 -1
  13. package/dist/cores/aws/aws-kms-service.d.ts +1 -2
  14. package/dist/cores/aws/aws-kms-service.js +143 -153
  15. package/dist/cores/aws/aws-kms-service.js.map +1 -1
  16. package/dist/cores/aws/aws-s3-service.d.ts +2 -4
  17. package/dist/cores/aws/aws-s3-service.js +306 -330
  18. package/dist/cores/aws/aws-s3-service.js.map +1 -1
  19. package/dist/cores/aws/aws-sns-service.js +147 -153
  20. package/dist/cores/aws/aws-sns-service.js.map +1 -1
  21. package/dist/cores/aws/aws-sqs-service.js +149 -170
  22. package/dist/cores/aws/aws-sqs-service.js.map +1 -1
  23. package/dist/cores/aws/index.js +10 -20
  24. package/dist/cores/aws/index.js.map +1 -1
  25. package/dist/cores/cache/cache-service.d.ts +2 -2
  26. package/dist/cores/cache/cache-service.js +435 -499
  27. package/dist/cores/cache/cache-service.js.map +1 -1
  28. package/dist/cores/config/config-service.d.ts +1 -1
  29. package/dist/cores/config/config-service.js +56 -63
  30. package/dist/cores/config/config-service.js.map +1 -1
  31. package/dist/cores/config/index.js +14 -24
  32. package/dist/cores/config/index.js.map +1 -1
  33. package/dist/cores/core-services.d.ts +1 -1
  34. package/dist/cores/dynamo/dynamo-query-service.js +37 -51
  35. package/dist/cores/dynamo/dynamo-query-service.js.map +1 -1
  36. package/dist/cores/dynamo/dynamo-scan-service.d.ts +2 -2
  37. package/dist/cores/dynamo/dynamo-scan-service.js +29 -40
  38. package/dist/cores/dynamo/dynamo-scan-service.js.map +1 -1
  39. package/dist/cores/dynamo/dynamo-service.d.ts +2 -2
  40. package/dist/cores/dynamo/dynamo-service.js +528 -601
  41. package/dist/cores/dynamo/dynamo-service.js.map +1 -1
  42. package/dist/cores/dynamo/tools/expressions.js +17 -7
  43. package/dist/cores/dynamo/tools/expressions.js.map +1 -1
  44. package/dist/cores/dynamo/tools/query.js +142 -127
  45. package/dist/cores/dynamo/tools/query.js.map +1 -1
  46. package/dist/cores/dynamo/tools/scan.js +111 -97
  47. package/dist/cores/dynamo/tools/scan.js.map +1 -1
  48. package/dist/cores/dynamo/tools/serializer.js +17 -7
  49. package/dist/cores/dynamo/tools/serializer.js.map +1 -1
  50. package/dist/cores/dynamo/tools/utils.d.ts +0 -2
  51. package/dist/cores/dynamo/tools/utils.js.map +1 -1
  52. package/dist/cores/elastic/elastic6-query-service.js +307 -324
  53. package/dist/cores/elastic/elastic6-query-service.js.map +1 -1
  54. package/dist/cores/elastic/elastic6-service.d.ts +3 -3
  55. package/dist/cores/elastic/elastic6-service.js +568 -647
  56. package/dist/cores/elastic/elastic6-service.js.map +1 -1
  57. package/dist/cores/elastic/hangul-service.js +52 -54
  58. package/dist/cores/elastic/hangul-service.js.map +1 -1
  59. package/dist/cores/lambda/index.js +42 -36
  60. package/dist/cores/lambda/index.js.map +1 -1
  61. package/dist/cores/lambda/lambda-alb-handler.d.ts +2 -2
  62. package/dist/cores/lambda/lambda-alb-handler.js +59 -72
  63. package/dist/cores/lambda/lambda-alb-handler.js.map +1 -1
  64. package/dist/cores/lambda/lambda-cognito-handler.js +10 -19
  65. package/dist/cores/lambda/lambda-cognito-handler.js.map +1 -1
  66. package/dist/cores/lambda/lambda-cron-handler.d.ts +1 -1
  67. package/dist/cores/lambda/lambda-cron-handler.js +14 -23
  68. package/dist/cores/lambda/lambda-cron-handler.js.map +1 -1
  69. package/dist/cores/lambda/lambda-dynamo-stream-handler.d.ts +2 -2
  70. package/dist/cores/lambda/lambda-dynamo-stream-handler.js +57 -67
  71. package/dist/cores/lambda/lambda-dynamo-stream-handler.js.map +1 -1
  72. package/dist/cores/lambda/lambda-handler.d.ts +22 -22
  73. package/dist/cores/lambda/lambda-handler.js +93 -106
  74. package/dist/cores/lambda/lambda-handler.js.map +1 -1
  75. package/dist/cores/lambda/lambda-notification-handler.d.ts +1 -1
  76. package/dist/cores/lambda/lambda-notification-handler.js +39 -50
  77. package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
  78. package/dist/cores/lambda/lambda-sns-handler.js +79 -88
  79. package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
  80. package/dist/cores/lambda/lambda-sqs-handler.js +79 -88
  81. package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
  82. package/dist/cores/lambda/lambda-web-handler.d.ts +6 -6
  83. package/dist/cores/lambda/lambda-web-handler.js +387 -412
  84. package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
  85. package/dist/cores/lambda/lambda-wss-handler.js +23 -32
  86. package/dist/cores/lambda/lambda-wss-handler.js.map +1 -1
  87. package/dist/cores/protocol/index.js +10 -20
  88. package/dist/cores/protocol/index.js.map +1 -1
  89. package/dist/cores/protocol/protocol-service.d.ts +3 -3
  90. package/dist/cores/protocol/protocol-service.js +235 -227
  91. package/dist/cores/protocol/protocol-service.js.map +1 -1
  92. package/dist/cores/storage/http-storage-service.js +65 -85
  93. package/dist/cores/storage/http-storage-service.js.map +1 -1
  94. package/dist/cores/storage/model-manager.js +66 -85
  95. package/dist/cores/storage/model-manager.js.map +1 -1
  96. package/dist/cores/storage/proxy-storage-service.d.ts +2 -2
  97. package/dist/cores/storage/proxy-storage-service.js +562 -599
  98. package/dist/cores/storage/proxy-storage-service.js.map +1 -1
  99. package/dist/cores/storage/redis-storage-service.js +163 -177
  100. package/dist/cores/storage/redis-storage-service.js.map +1 -1
  101. package/dist/cores/storage/storage-service.js +281 -322
  102. package/dist/cores/storage/storage-service.js.map +1 -1
  103. package/dist/engine/builder.d.ts +1 -1
  104. package/dist/engine/builder.js +59 -63
  105. package/dist/engine/builder.js.map +1 -1
  106. package/dist/engine/engine.d.ts +4 -4
  107. package/dist/engine/engine.js +9 -18
  108. package/dist/engine/engine.js.map +1 -1
  109. package/dist/engine/types.d.ts +1 -1
  110. package/dist/engine/utilities.d.ts +5 -5
  111. package/dist/engine/utilities.js +301 -293
  112. package/dist/engine/utilities.js.map +1 -1
  113. package/dist/environ.js +4 -6
  114. package/dist/environ.js.map +1 -1
  115. package/dist/extended/abstract-service.js +595 -645
  116. package/dist/extended/abstract-service.js.map +1 -1
  117. package/dist/extended/libs/sig-v4.js.map +1 -1
  118. package/dist/generated/field-registry.d.ts +10 -0
  119. package/dist/generated/field-registry.js +17 -0
  120. package/dist/generated/field-registry.js.map +1 -0
  121. package/dist/helpers/helpers.d.ts +17 -9
  122. package/dist/helpers/helpers.js +88 -78
  123. package/dist/helpers/helpers.js.map +1 -1
  124. package/dist/index.js +17 -7
  125. package/dist/index.js.map +1 -1
  126. package/dist/lib/dynamodb-value.js +2 -3
  127. package/dist/lib/dynamodb-value.js.map +1 -1
  128. package/dist/tools/express.js +4 -5
  129. package/dist/tools/express.js.map +1 -1
  130. package/dist/tools/tools.d.ts +3 -1
  131. package/dist/tools/tools.js +14 -21
  132. package/dist/tools/tools.js.map +1 -1
  133. package/package.json +19 -18
  134. package/dist/exec-cli.d.ts +0 -2
  135. package/dist/exec-cli.js +0 -211
  136. package/dist/exec-cli.js.map +0 -1
  137. package/dist/lib/dynamo/expressions.d.ts +0 -14
  138. package/dist/lib/dynamo/expressions.js +0 -212
  139. package/dist/lib/dynamo/expressions.js.map +0 -1
  140. package/dist/lib/dynamo/query.d.ts +0 -43
  141. package/dist/lib/dynamo/query.js +0 -246
  142. package/dist/lib/dynamo/query.js.map +0 -1
  143. package/dist/lib/dynamo/scan.d.ts +0 -33
  144. package/dist/lib/dynamo/scan.js +0 -172
  145. package/dist/lib/dynamo/scan.js.map +0 -1
  146. package/dist/lib/dynamo/serializer.d.ts +0 -12
  147. package/dist/lib/dynamo/serializer.js +0 -243
  148. package/dist/lib/dynamo/serializer.js.map +0 -1
  149. package/dist/lib/dynamo/utils.d.ts +0 -15
  150. package/dist/lib/dynamo/utils.js +0 -129
  151. package/dist/lib/dynamo/utils.js.map +0 -1
  152. package/dist/tools/shared.d.ts +0 -28
  153. package/dist/tools/shared.js +0 -143
  154. package/dist/tools/shared.js.map +0 -1
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
@@ -49,291 +59,11 @@ const COLORS = {
49
59
  * - various functions
50
60
  */
51
61
  class Utilities {
62
+ _$;
63
+ log;
64
+ err;
65
+ name;
52
66
  constructor(_$) {
53
- /**
54
- * parse float by decimal point 2
55
- */
56
- this.F2 = (x, mode = 'round') => this.FN(x, 2, mode);
57
- /**
58
- * parse float by decimal point 3
59
- */
60
- this.F3 = (x, mode = 'round') => this.FN(x, 3, mode);
61
- /**
62
- * convert and cut string like `abcd....z`
63
- */
64
- this.S = (_, h, t = 32, delim = '...') => [typeof _ == 'string' ? _ : `${this.json(_) || ''}`]
65
- .map(s => h && s.length > h + t
66
- ? s.substring(0, h) + delim + (s.length > h + t ? s.substring(s.length - t) : '')
67
- : s)
68
- .join('');
69
- /**
70
- * group as qs
71
- */
72
- this.qs = {
73
- /**
74
- * parse qs string
75
- */
76
- parse: (q) => this.qs_parse(q),
77
- /**
78
- * stringify qs object
79
- */
80
- stringify: (q) => this.qs_stringify(q),
81
- };
82
- /**
83
- * get crypto object.
84
- *
85
- * @deprecated since nodejs22, use `crypto2` instead.
86
- */
87
- this.crypto = (passwd, algorithm) => {
88
- algorithm = algorithm || 'aes-256-ctr';
89
- const MAGIC = 'LM!#';
90
- /**
91
- * Simulate OpenSSL's EVP_BytesToKey method
92
- */
93
- const evpBytesToKey = (password, keyLen, ivLen) => {
94
- let data = Buffer.alloc(0);
95
- let prev = Buffer.alloc(0);
96
- while (data.length < keyLen + ivLen) {
97
- const hash = crypto_1.default.createHash('md5');
98
- hash.update(Buffer.concat([prev, password]));
99
- prev = hash.digest();
100
- data = Buffer.concat([data, prev]);
101
- }
102
- return {
103
- key: data.slice(0, keyLen),
104
- iv: data.slice(keyLen, keyLen + ivLen),
105
- };
106
- };
107
- const getKeyIV = () => {
108
- const passwordBuf = Buffer.from(passwd, 'binary');
109
- const keyLen = 32; // for aes-256
110
- const ivLen = 16; // AES block size
111
- return evpBytesToKey(passwordBuf, keyLen, ivLen);
112
- };
113
- return new (class {
114
- constructor() {
115
- /** @deprecated since nodejs22 */
116
- this.encrypt = (val) => {
117
- val = val === undefined ? null : val;
118
- // msg = msg && typeof msg == 'object' ? JSON_TAG+JSON.stringify(msg) : msg;
119
- //* 어느 데이터 타입이든 저장하기 위해서, object로 만든다음, 암호화 시킨다.
120
- const msg = JSON.stringify({ alg: algorithm, val: val });
121
- const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8');
122
- const { key, iv } = getKeyIV();
123
- const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
124
- const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
125
- return crypted.toString(1 ? 'base64' : 'utf8');
126
- };
127
- /** @deprecated since nodejs22 */
128
- this.decrypt = (msg) => {
129
- const buffer = Buffer.from(`${msg || ''}`, 'base64');
130
- const { key, iv } = getKeyIV();
131
- const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
132
- const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]);
133
- const dec = decrypted.toString('utf8');
134
- if (!dec.startsWith(MAGIC))
135
- throw new Error('400 INVALID PASSWD - invalid magic string!');
136
- const data = dec.slice(MAGIC.length);
137
- if (data && !data.startsWith('{') && !data.endsWith('}'))
138
- throw new Error('400 INVALID PASSWD - invalid json string!');
139
- const $msg = JSON.parse(data) || {};
140
- return $msg.val;
141
- };
142
- }
143
- })();
144
- };
145
- /**
146
- * get crypto2 object (w/ Cipheriv).
147
- * - to avoid `(node:66818) Warning: Use Cipheriv for counter mode of aes-256-ctr`
148
- *
149
- * @param passwd password to crypt
150
- * @param algorithm (default as `aes-256-ctr`)
151
- * @param ivNumb iv number to populate. (default as 0, or -1 use random)
152
- * @param magic magic string to verify (default `LM!#`)
153
- */
154
- this.crypto2 = (passwd, algorithm, ivNumb, magic) => {
155
- algorithm = algorithm || 'aes-256-ctr';
156
- const MAGIC = magic === undefined ? 'LM!#' : `${magic || ''}`;
157
- const iv = Buffer.from(Array.prototype.map.call(Buffer.alloc(16), () => {
158
- return ivNumb === undefined ? 0 : ivNumb === -1 ? Math.floor(Math.random() * 256) : ivNumb;
159
- }));
160
- return new (class {
161
- constructor() {
162
- this.encrypt = (val) => {
163
- val = val === undefined ? null : val;
164
- //* use json string to support all data-type
165
- const msg = JSON.stringify({ alg: algorithm, val: val });
166
- const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8');
167
- const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
168
- const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
169
- const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
170
- return crypted.toString('base64');
171
- };
172
- this.decrypt = (msg) => {
173
- const buffer = Buffer.from(`${msg || ''}`, 'base64');
174
- const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
175
- const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
176
- const dec = Buffer.concat([decipher.update(buffer), decipher.final()]).toString('utf8');
177
- if (!dec.startsWith(MAGIC))
178
- throw new Error(`400 INVALID PASSWD - invalid magic string!`);
179
- const data = dec.substr(MAGIC.length);
180
- if (data && !data.startsWith('{') && !data.endsWith('}'))
181
- throw new Error('400 INVALID PASSWD - invalid json string!');
182
- const $msg = JSON.parse(data) || {};
183
- return $msg.val;
184
- };
185
- }
186
- })();
187
- };
188
- /**
189
- * get crypto3 object (with nonce + timestamp based IV).
190
- *
191
- * format details, see `crypto3` tests.
192
- * 1. nonce = random hex string with length 13 (like `a1b2c3d4e5f67`)
193
- * 2. head = `LM!#${VERSION}:${nonce}:${timestamp}` -> base64 encode with constant length 48 (no padding)
194
- * 3. body = JSON({d: data}) -> encrypted by AES-256-CTR with key = passwd
195
- * 4. final = [head, body].join('')
196
- *
197
- * @param passwd password to crypt
198
- * @param algorithm (default as `aes-256-ctr`)
199
- */
200
- this.crypto3 = (passwd, algorithm) => {
201
- algorithm = algorithm || 'aes-256-ctr';
202
- const MAGIC = 'LM!#';
203
- const VERSION = 'V003';
204
- const HEAD_B64_LEN = 48; // 36 bytes -> 48 base64 (no padding)
205
- const errScope = `crypto3(${algorithm}#${VERSION})`;
206
- const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
207
- const hmac = (data, sig) => this.hmac(data, sig, 'sha256', 'hex');
208
- const currentMs = () => this.current_time_ms(); // 13 digits timestamp
209
- const makeNonce = () => this.uuid().replace(/-/g, '').substring(0, 13); // 13 chars (52-bit entropy)
210
- return new (class {
211
- constructor() {
212
- this.encrypt = (val, options) => {
213
- var _a, _b;
214
- val = val === undefined ? null : val;
215
- const nonce = (_a = options === null || options === void 0 ? void 0 : options.nonce) !== null && _a !== void 0 ? _a : makeNonce();
216
- const timestamp = String((_b = options === null || options === void 0 ? void 0 : options.current) !== null && _b !== void 0 ? _b : currentMs()).padStart(13, '0');
217
- const iv = Buffer.from(hmac(`${nonce}:${timestamp}`, passwd).substring(0, 32), 'hex'); // AES IV must be 16 bytes.
218
- const buffer = Buffer.from(JSON.stringify({ d: val }), 'utf8');
219
- const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
220
- const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
221
- const headRaw = `${MAGIC}${VERSION}:${nonce}:${timestamp}`;
222
- const head = Buffer.from(headRaw).toString('base64');
223
- const body = crypted.toString('base64');
224
- return [head, body].join('');
225
- };
226
- this.decrypt = (msg, options) => {
227
- var _a;
228
- if (!msg)
229
- throw new Error(`@msg (string) is required - ${errScope}`);
230
- if (msg.length < HEAD_B64_LEN)
231
- throw new Error(`400 INVALID DATA - data too short! @${errScope}`);
232
- // 1. decode base64 header (48 chars -> 36 bytes)
233
- const headBase64 = msg.substring(0, HEAD_B64_LEN);
234
- const head = Buffer.from(headBase64, 'base64').toString('utf8');
235
- if (head.length !== 36)
236
- throw new Error(`400 INVALID DATA - invalid header! @${errScope}`);
237
- // 2. validate magic and version
238
- if (!head.startsWith(MAGIC))
239
- throw new Error(`400 INVALID MAGIC - invalid magic! @${errScope}`);
240
- const version = head.substring(4, 8);
241
- if (version !== VERSION)
242
- throw new Error(`400 INVALID VERSION - expected ${VERSION}, got ${version}! @${errScope}`);
243
- // 3. parse nonce, timestamp (format: LM!#V003:nonce:timestamp)
244
- const [, nonce, timestamp] = head.substring(8).split(':');
245
- if (!nonce || nonce.length !== 13)
246
- throw new Error(`400 INVALID DATA - invalid nonce! @${errScope}`);
247
- if (!timestamp || !/^\d+$/.test(timestamp))
248
- throw new Error(`400 INVALID DATA - invalid timestamp! @${errScope}`);
249
- if ((options === null || options === void 0 ? void 0 : options.maxAge) !== undefined) {
250
- const current = (_a = options === null || options === void 0 ? void 0 : options.current) !== null && _a !== void 0 ? _a : currentMs();
251
- if (current - parseInt(timestamp, 10) > options.maxAge)
252
- throw new Error(`400 INVALID DATA - expired timestamp! @${errScope}`);
253
- }
254
- // 4. decrypt
255
- const iv = Buffer.from(hmac(`${nonce}:${timestamp}`, passwd).substring(0, 32), 'hex');
256
- const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
257
- const dec = Buffer.concat([
258
- decipher.update(Buffer.from(msg.substring(HEAD_B64_LEN), 'base64')),
259
- decipher.final(),
260
- ]).toString('utf8');
261
- if (!dec.startsWith('{') || !dec.endsWith('}'))
262
- throw new Error(`400 INVALID PASSWD - invalid json string @${errScope}`);
263
- const $msg = JSON.parse(dec) || {};
264
- return $msg === null || $msg === void 0 ? void 0 : $msg.d;
265
- };
266
- }
267
- })();
268
- };
269
- /**
270
- * encrypt shorthand using crypto3
271
- */
272
- this.encrypt = (data, secret, options) => {
273
- return this.crypto3(secret).encrypt(data, options);
274
- };
275
- /**
276
- * decrypt shorthand using crypto3
277
- */
278
- this.decrypt = (data, secret, options) => {
279
- return this.crypto3(secret).decrypt(data, options);
280
- };
281
- /**
282
- * builder for `JWTHelper`
283
- * @param passcode string for verification.
284
- * @param current_ms (optional) current time in millisecond (required to verify `exp`)
285
- */
286
- this.jwt = (passcode, current_ms) => {
287
- const $U = this;
288
- /**
289
- * main class.
290
- */
291
- return new (class JWTHelper {
292
- constructor() {
293
- /**
294
- * use `jsonwebtoken` directly.
295
- */
296
- this.$ = jsonwebtoken_1.default;
297
- /**
298
- * encode object to token string
299
- * - Synchronous Sign with default (HS256: HMAC SHA256)
300
- *
301
- * @param data object
302
- * @param algorithm algorithm to use
303
- */
304
- this.encode = (data, algorithm = 'HS256') => {
305
- data = current_ms ? Object.assign(Object.assign({}, data), { iat: Math.floor(current_ms / 1000) }) : data;
306
- const token = jsonwebtoken_1.default.sign(data, passcode, { algorithm });
307
- return token;
308
- };
309
- /**
310
- * decode token string
311
- *
312
- * @param token string
313
- */
314
- this.decode = (token, options) => {
315
- const N = jsonwebtoken_1.default.decode(token, options);
316
- return N;
317
- };
318
- /**
319
- * verify token
320
- * - Synchronous Verify with default (HS256: HMAC SHA256)
321
- *
322
- * @param token
323
- * @param algorithm
324
- * @throws `jwt expired` if exp has expired!.
325
- */
326
- this.verify = (token, algorithm = 'HS256') => {
327
- const verified = jsonwebtoken_1.default.verify(token, passcode, { algorithms: [algorithm] });
328
- const cur = $U.N(current_ms, 0);
329
- const exp = $U.N(verified === null || verified === void 0 ? void 0 : verified.exp, 0) * 1000;
330
- if (cur > 0 && exp > 0 && exp < current_ms)
331
- throw new Error(`jwt expired at ${$U.ts(exp)}`);
332
- return verified;
333
- };
334
- }
335
- })();
336
- };
337
67
  this._$ = _$;
338
68
  this.log = _$.log;
339
69
  this.err = _$.err;
@@ -651,6 +381,22 @@ class Utilities {
651
381
  const val3 = val2 / div;
652
382
  return val3;
653
383
  }
384
+ /**
385
+ * parse float by decimal point 2
386
+ */
387
+ F2 = (x, mode = 'round') => this.FN(x, 2, mode);
388
+ /**
389
+ * parse float by decimal point 3
390
+ */
391
+ F3 = (x, mode = 'round') => this.FN(x, 3, mode);
392
+ /**
393
+ * convert and cut string like `abcd....z`
394
+ */
395
+ S = (_, h, t = 32, delim = '...') => [typeof _ == 'string' ? _ : `${this.json(_) || ''}`]
396
+ .map(s => h && s.length > h + t
397
+ ? s.substring(0, h) + delim + (s.length > h + t ? s.substring(s.length - t) : '')
398
+ : s)
399
+ .join('');
654
400
  /**
655
401
  * remove internal properties which starts with _ or $
656
402
  */
@@ -727,6 +473,7 @@ class Utilities {
727
473
  */
728
474
  isEqual(obj1, obj2) {
729
475
  const keys = Object.keys;
476
+ const toString = Object.prototype.toString;
730
477
  function tagTester(name) {
731
478
  return function (obj) {
732
479
  return toString.call(obj) === '[object ' + name + ']';
@@ -919,7 +666,8 @@ class Utilities {
919
666
  */
920
667
  md5(data, digest) {
921
668
  digest = digest === undefined ? 'hex' : digest;
922
- return crypto_1.default.createHash('md5').update(data).digest(digest);
669
+ const _digest = digest === 'latin1' ? 'binary' : digest;
670
+ return crypto_1.default.createHash('md5').update(data).digest(_digest);
923
671
  }
924
672
  /**
925
673
  * get hmac hash
@@ -928,7 +676,8 @@ class Utilities {
928
676
  KEY = KEY || 'XENI';
929
677
  encoding = encoding || 'base64';
930
678
  algorithm = algorithm || 'sha256';
931
- return crypto_1.default.createHmac(algorithm, KEY).update(data).digest(encoding);
679
+ const _encoding = encoding === 'latin1' ? 'binary' : encoding;
680
+ return crypto_1.default.createHmac(algorithm, KEY).update(data).digest(_encoding);
932
681
  }
933
682
  /**
934
683
  * parse query-string.
@@ -959,6 +708,265 @@ class Utilities {
959
708
  const param = query_string_1.default.stringify(query);
960
709
  return param;
961
710
  }
711
+ /**
712
+ * group as qs
713
+ */
714
+ qs = {
715
+ /**
716
+ * parse qs string
717
+ */
718
+ parse: (q) => this.qs_parse(q),
719
+ /**
720
+ * stringify qs object
721
+ */
722
+ stringify: (q) => this.qs_stringify(q),
723
+ };
724
+ /**
725
+ * get crypto object.
726
+ *
727
+ * @deprecated since nodejs22, use `crypto2` instead.
728
+ */
729
+ crypto = (passwd, algorithm) => {
730
+ algorithm = algorithm || 'aes-256-ctr';
731
+ const MAGIC = 'LM!#';
732
+ /**
733
+ * Simulate OpenSSL's EVP_BytesToKey method
734
+ */
735
+ const evpBytesToKey = (password, keyLen, ivLen) => {
736
+ let data = Buffer.alloc(0);
737
+ let prev = Buffer.alloc(0);
738
+ while (data.length < keyLen + ivLen) {
739
+ const hash = crypto_1.default.createHash('md5');
740
+ hash.update(Buffer.concat([prev, password]));
741
+ prev = hash.digest();
742
+ data = Buffer.concat([data, prev]);
743
+ }
744
+ return {
745
+ key: data.slice(0, keyLen),
746
+ iv: data.slice(keyLen, keyLen + ivLen),
747
+ };
748
+ };
749
+ const getKeyIV = () => {
750
+ const passwordBuf = Buffer.from(passwd, 'binary');
751
+ const keyLen = 32; // for aes-256
752
+ const ivLen = 16; // AES block size
753
+ return evpBytesToKey(passwordBuf, keyLen, ivLen);
754
+ };
755
+ return new (class {
756
+ /** @deprecated since nodejs22 */
757
+ encrypt = (val) => {
758
+ val = val === undefined ? null : val;
759
+ // msg = msg && typeof msg == 'object' ? JSON_TAG+JSON.stringify(msg) : msg;
760
+ //* 어느 데이터 타입이든 저장하기 위해서, object로 만든다음, 암호화 시킨다.
761
+ const msg = JSON.stringify({ alg: algorithm, val: val });
762
+ const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8');
763
+ const { key, iv } = getKeyIV();
764
+ const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
765
+ const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
766
+ return crypted.toString(1 ? 'base64' : 'utf8');
767
+ };
768
+ /** @deprecated since nodejs22 */
769
+ decrypt = (msg) => {
770
+ const buffer = Buffer.from(`${msg || ''}`, 'base64');
771
+ const { key, iv } = getKeyIV();
772
+ const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
773
+ const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]);
774
+ const dec = decrypted.toString('utf8');
775
+ if (!dec.startsWith(MAGIC))
776
+ throw new Error('400 INVALID PASSWD - invalid magic string!');
777
+ const data = dec.slice(MAGIC.length);
778
+ if (data && !data.startsWith('{') && !data.endsWith('}'))
779
+ throw new Error('400 INVALID PASSWD - invalid json string!');
780
+ const $msg = JSON.parse(data) || {};
781
+ return $msg.val;
782
+ };
783
+ })();
784
+ };
785
+ /**
786
+ * get crypto2 object (w/ Cipheriv).
787
+ * - to avoid `(node:66818) Warning: Use Cipheriv for counter mode of aes-256-ctr`
788
+ *
789
+ * @param passwd password to crypt
790
+ * @param algorithm (default as `aes-256-ctr`)
791
+ * @param ivNumb iv number to populate. (default as 0, or -1 use random)
792
+ * @param magic magic string to verify (default `LM!#`)
793
+ */
794
+ crypto2 = (passwd, algorithm, ivNumb, magic) => {
795
+ algorithm = algorithm || 'aes-256-ctr';
796
+ const MAGIC = magic === undefined ? 'LM!#' : `${magic || ''}`;
797
+ const iv = Buffer.from(Array.prototype.map.call(Buffer.alloc(16), () => {
798
+ return ivNumb === undefined ? 0 : ivNumb === -1 ? Math.floor(Math.random() * 256) : ivNumb;
799
+ }));
800
+ return new (class {
801
+ encrypt = (val) => {
802
+ val = val === undefined ? null : val;
803
+ //* use json string to support all data-type
804
+ const msg = JSON.stringify({ alg: algorithm, val: val });
805
+ const buffer = Buffer.from(`${MAGIC}${msg || ''}`, 'utf8');
806
+ const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
807
+ const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
808
+ const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
809
+ return crypted.toString('base64');
810
+ };
811
+ decrypt = (msg) => {
812
+ const buffer = Buffer.from(`${msg || ''}`, 'base64');
813
+ const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
814
+ const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
815
+ const dec = Buffer.concat([decipher.update(buffer), decipher.final()]).toString('utf8');
816
+ if (!dec.startsWith(MAGIC))
817
+ throw new Error(`400 INVALID PASSWD - invalid magic string!`);
818
+ const data = dec.substr(MAGIC.length);
819
+ if (data && !data.startsWith('{') && !data.endsWith('}'))
820
+ throw new Error('400 INVALID PASSWD - invalid json string!');
821
+ const $msg = JSON.parse(data) || {};
822
+ return $msg.val;
823
+ };
824
+ })();
825
+ };
826
+ /**
827
+ * get crypto3 object (with nonce + timestamp based IV).
828
+ *
829
+ * format details, see `crypto3` tests.
830
+ * 1. nonce = random hex string with length 13 (like `a1b2c3d4e5f67`)
831
+ * 2. head = `LM!#${VERSION}:${nonce}:${timestamp}` -> base64 encode with constant length 48 (no padding)
832
+ * 3. body = JSON({d: data}) -> encrypted by AES-256-CTR with key = passwd
833
+ * 4. final = [head, body].join('')
834
+ *
835
+ * @param passwd password to crypt
836
+ * @param algorithm (default as `aes-256-ctr`)
837
+ */
838
+ crypto3 = (passwd, algorithm) => {
839
+ algorithm = algorithm || 'aes-256-ctr';
840
+ const MAGIC = 'LM!#';
841
+ const VERSION = 'V003';
842
+ const HEAD_B64_LEN = 48; // 36 bytes -> 48 base64 (no padding)
843
+ const errScope = `crypto3(${algorithm}#${VERSION})`;
844
+ const key = Buffer.concat([Buffer.from(passwd)], Buffer.alloc(32).length);
845
+ const hmac = (data, sig) => this.hmac(data, sig, 'sha256', 'hex');
846
+ const currentMs = () => this.current_time_ms(); // 13 digits timestamp
847
+ const makeNonce = () => this.uuid().replace(/-/g, '').substring(0, 13); // 13 chars (52-bit entropy)
848
+ return new (class {
849
+ encrypt = (val, options) => {
850
+ val = val === undefined ? null : val;
851
+ const nonce = options?.nonce ?? makeNonce();
852
+ const timestamp = String(options?.current ?? currentMs()).padStart(13, '0');
853
+ const iv = Buffer.from(hmac(`${nonce}:${timestamp}`, passwd).substring(0, 32), 'hex'); // AES IV must be 16 bytes.
854
+ const buffer = Buffer.from(JSON.stringify({ d: val }), 'utf8');
855
+ const cipher = crypto_1.default.createCipheriv(algorithm, key, iv);
856
+ const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
857
+ const headRaw = `${MAGIC}${VERSION}:${nonce}:${timestamp}`;
858
+ const head = Buffer.from(headRaw).toString('base64');
859
+ const body = crypted.toString('base64');
860
+ return [head, body].join('');
861
+ };
862
+ decrypt = (msg, options) => {
863
+ if (!msg)
864
+ throw new Error(`@msg (string) is required - ${errScope}`);
865
+ if (msg.length < HEAD_B64_LEN)
866
+ throw new Error(`400 INVALID DATA - data too short! @${errScope}`);
867
+ // 1. decode base64 header (48 chars -> 36 bytes)
868
+ const headBase64 = msg.substring(0, HEAD_B64_LEN);
869
+ const head = Buffer.from(headBase64, 'base64').toString('utf8');
870
+ if (head.length !== 36)
871
+ throw new Error(`400 INVALID DATA - invalid header! @${errScope}`);
872
+ // 2. validate magic and version
873
+ if (!head.startsWith(MAGIC))
874
+ throw new Error(`400 INVALID MAGIC - invalid magic! @${errScope}`);
875
+ const version = head.substring(4, 8);
876
+ if (version !== VERSION)
877
+ throw new Error(`400 INVALID VERSION - expected ${VERSION}, got ${version}! @${errScope}`);
878
+ // 3. parse nonce, timestamp (format: LM!#V003:nonce:timestamp)
879
+ const [, nonce, timestamp] = head.substring(8).split(':');
880
+ if (!nonce || nonce.length !== 13)
881
+ throw new Error(`400 INVALID DATA - invalid nonce! @${errScope}`);
882
+ if (!timestamp || !/^\d+$/.test(timestamp))
883
+ throw new Error(`400 INVALID DATA - invalid timestamp! @${errScope}`);
884
+ if (options?.maxAge !== undefined) {
885
+ const current = options?.current ?? currentMs();
886
+ if (current - parseInt(timestamp, 10) > options.maxAge)
887
+ throw new Error(`400 INVALID DATA - expired timestamp! @${errScope}`);
888
+ }
889
+ // 4. decrypt
890
+ const iv = Buffer.from(hmac(`${nonce}:${timestamp}`, passwd).substring(0, 32), 'hex');
891
+ const decipher = crypto_1.default.createDecipheriv(algorithm, key, iv);
892
+ const dec = Buffer.concat([
893
+ decipher.update(Buffer.from(msg.substring(HEAD_B64_LEN), 'base64')),
894
+ decipher.final(),
895
+ ]).toString('utf8');
896
+ if (!dec.startsWith('{') || !dec.endsWith('}'))
897
+ throw new Error(`400 INVALID PASSWD - invalid json string @${errScope}`);
898
+ const $msg = JSON.parse(dec) || {};
899
+ return $msg?.d;
900
+ };
901
+ })();
902
+ };
903
+ /**
904
+ * encrypt shorthand using crypto3
905
+ */
906
+ encrypt = (data, secret, options) => {
907
+ return this.crypto3(secret).encrypt(data, options);
908
+ };
909
+ /**
910
+ * decrypt shorthand using crypto3
911
+ */
912
+ decrypt = (data, secret, options) => {
913
+ return this.crypto3(secret).decrypt(data, options);
914
+ };
915
+ /**
916
+ * builder for `JWTHelper`
917
+ * @param passcode string for verification.
918
+ * @param current_ms (optional) current time in millisecond (required to verify `exp`)
919
+ */
920
+ jwt = (passcode, current_ms) => {
921
+ const $U = this;
922
+ /**
923
+ * main class.
924
+ */
925
+ return new (class JWTHelper {
926
+ constructor() { }
927
+ /**
928
+ * use `jsonwebtoken` directly.
929
+ */
930
+ $ = jsonwebtoken_1.default;
931
+ /**
932
+ * encode object to token string
933
+ * - Synchronous Sign with default (HS256: HMAC SHA256)
934
+ *
935
+ * @param data object
936
+ * @param algorithm algorithm to use
937
+ */
938
+ encode = (data, algorithm = 'HS256') => {
939
+ data = current_ms ? { ...data, iat: Math.floor(current_ms / 1000) } : data;
940
+ const token = jsonwebtoken_1.default.sign(data, passcode, { algorithm });
941
+ return token;
942
+ };
943
+ /**
944
+ * decode token string
945
+ *
946
+ * @param token string
947
+ */
948
+ decode = (token, options) => {
949
+ const N = jsonwebtoken_1.default.decode(token, options);
950
+ return N;
951
+ };
952
+ /**
953
+ * verify token
954
+ * - Synchronous Verify with default (HS256: HMAC SHA256)
955
+ *
956
+ * @param token
957
+ * @param algorithm
958
+ * @throws `jwt expired` if exp has expired!.
959
+ */
960
+ verify = (token, algorithm = 'HS256') => {
961
+ const verified = jsonwebtoken_1.default.verify(token, passcode, { algorithms: [algorithm] });
962
+ const cur = $U.N(current_ms, 0);
963
+ const exp = $U.N(verified?.exp, 0) * 1000;
964
+ if (cur > 0 && exp > 0 && exp < current_ms)
965
+ throw new Error(`jwt expired at ${$U.ts(exp)}`);
966
+ return verified;
967
+ };
968
+ })();
969
+ };
962
970
  /**
963
971
  * get UUID as `uuid.v4()`
964
972
  */