@wener/utils 1.1.3 → 1.1.5

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 (49) hide show
  1. package/README.md +9 -0
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/esm/index.js +1 -1
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/system/index.js +1 -1
  7. package/dist/system/index.js.map +1 -1
  8. package/lib/index.js +8 -1
  9. package/lib/index.js.map +1 -1
  10. package/lib/io/isBuffer.js.map +1 -1
  11. package/lib/logging/createChildLogger.js +18 -0
  12. package/lib/logging/createChildLogger.js.map +1 -0
  13. package/lib/logging/createNoopLogger.js +14 -0
  14. package/lib/logging/createNoopLogger.js.map +1 -0
  15. package/lib/logging/createWriteLogger.js +13 -0
  16. package/lib/logging/createWriteLogger.js.map +1 -0
  17. package/lib/modules/isModule.js +6 -0
  18. package/lib/modules/isModule.js.map +1 -0
  19. package/lib/modules/parseModuleId.js.map +1 -1
  20. package/lib/objects/get.js +14 -0
  21. package/lib/objects/get.js.map +1 -0
  22. package/lib/objects/parseObjectPath.js +27 -0
  23. package/lib/objects/parseObjectPath.js.map +1 -0
  24. package/lib/objects/set.js +36 -0
  25. package/lib/objects/set.js.map +1 -0
  26. package/lib/strings/renderTemplate.js +23 -0
  27. package/lib/strings/renderTemplate.js.map +1 -0
  28. package/package.json +13 -6
  29. package/src/index.ts +13 -1
  30. package/src/io/isBuffer.ts +5 -1
  31. package/src/logging/Logger.ts +19 -0
  32. package/src/logging/createChildLogger.ts +16 -0
  33. package/src/logging/createNoopLogger.ts +13 -0
  34. package/src/logging/createWriteLogger.ts +15 -0
  35. package/src/logging/logger.test.ts +34 -0
  36. package/src/modules/isModule.ts +10 -0
  37. package/src/modules/parseModuleId.ts +5 -0
  38. package/src/objects/get.test.ts +116 -0
  39. package/src/objects/get.ts +49 -0
  40. package/src/objects/parseObjectPath.test.ts +18 -0
  41. package/src/objects/parseObjectPath.ts +40 -0
  42. package/src/objects/set.test.ts +405 -0
  43. package/src/objects/set.ts +48 -0
  44. package/src/strings/renderTemplate.test.ts +25 -0
  45. package/src/strings/renderTemplate.ts +31 -0
  46. package/tsconfig.json +0 -4
  47. package/lib/strings/templates.js +0 -8
  48. package/lib/strings/templates.js.map +0 -1
  49. package/src/strings/templates.ts +0 -13
@@ -0,0 +1,405 @@
1
+ import test from 'ava';
2
+ import { set } from './set';
3
+
4
+ test('set basics', (t) => {
5
+ t.is(set({}, 'c', 3), undefined, 'should not give return value');
6
+ {
7
+ let item = { foo: 1 };
8
+ set(item, 'bar', 123);
9
+ t.is(item, item);
10
+ t.deepEqual(
11
+ item,
12
+ {
13
+ foo: 1,
14
+ bar: 123,
15
+ },
16
+ 'should mutate original object',
17
+ );
18
+ }
19
+ });
20
+
21
+ test('set objects', (t) => {
22
+ const prepare = (x: any) => ({ input: x, copy: JSON.parse(JSON.stringify(x)) });
23
+ const objects = (s: string, f: Function) => {
24
+ t.log(s);
25
+ f();
26
+ };
27
+ const orig = set;
28
+
29
+ function run(isMerge: boolean) {
30
+ const set = (a: any, b: any, c: any) => orig(a, b, c, isMerge);
31
+ const verb = isMerge ? 'merge' : 'overwrite';
32
+ objects(`should ${verb} existing object value :: simple`, () => {
33
+ let { input } = prepare({
34
+ hello: { a: 1 },
35
+ });
36
+
37
+ set(input, 'hello', { foo: 123 });
38
+
39
+ if (isMerge) {
40
+ t.deepEqual(input, {
41
+ hello: {
42
+ a: 1,
43
+ foo: 123,
44
+ },
45
+ });
46
+ } else {
47
+ t.deepEqual(input, {
48
+ hello: { foo: 123 },
49
+ });
50
+ }
51
+ });
52
+
53
+ objects(`should ${verb} existing object value :: nested`, () => {
54
+ let { input, copy } = prepare({
55
+ a: {
56
+ b: {
57
+ c: 123,
58
+ },
59
+ },
60
+ });
61
+
62
+ set(input, 'a.b', { foo: 123 });
63
+
64
+ if (isMerge) {
65
+ Object.assign(copy.a.b, { foo: 123 });
66
+ } else {
67
+ copy.a.b = { foo: 123 };
68
+ }
69
+
70
+ t.deepEqual(input, copy);
71
+ });
72
+
73
+ objects(`should ${verb} existing array value :: simple`, () => {
74
+ let { input } = prepare([{ foo: 1 }]);
75
+
76
+ set(input, '0', { bar: 2 });
77
+
78
+ if (isMerge) {
79
+ t.deepEqual(input, [{ foo: 1, bar: 2 }]);
80
+ } else {
81
+ t.deepEqual(input, [{ bar: 2 }]);
82
+ }
83
+ });
84
+
85
+ objects(`should ${verb} existing array value :: nested`, () => {
86
+ let { input } = prepare([
87
+ { name: 'bob', age: 56, friends: ['foobar'] },
88
+ { name: 'alice', age: 47, friends: ['mary'] },
89
+ ]);
90
+
91
+ set(input, '0', { age: 57, friends: ['alice', 'mary'] });
92
+ set(input, '1', { friends: ['bob'] });
93
+ set(input, '2', { name: 'mary', age: 49, friends: ['bob'] });
94
+
95
+ if (isMerge) {
96
+ t.deepEqual(input, [
97
+ { name: 'bob', age: 57, friends: ['alice', 'mary'] },
98
+ { name: 'alice', age: 47, friends: ['bob'] },
99
+ { name: 'mary', age: 49, friends: ['bob'] },
100
+ ]);
101
+ } else {
102
+ t.deepEqual(input, [
103
+ { age: 57, friends: ['alice', 'mary'] },
104
+ { friends: ['bob'] },
105
+ { name: 'mary', age: 49, friends: ['bob'] },
106
+ ]);
107
+ }
108
+ });
109
+ }
110
+
111
+ run(true);
112
+ run(false);
113
+ });
114
+
115
+ test('set arrays', (t) => {
116
+ const arrays = (s: string, f: Function) => {
117
+ t.log(s);
118
+ f();
119
+ };
120
+ arrays('should create array instead of object via numeric key :: simple', () => {
121
+ let input: any = { a: 1 };
122
+ set(input, 'e.0', 2);
123
+ t.true(Array.isArray(input.e));
124
+ t.is(input.e[0], 2);
125
+ t.deepEqual(input, {
126
+ a: 1,
127
+ e: [2],
128
+ });
129
+ });
130
+
131
+ arrays('should create array instead of object via numeric key :: nested', () => {
132
+ let input: any = { a: 1 };
133
+ set(input, 'e.0.0', 123);
134
+ t.true(input.e instanceof Array);
135
+ t.is(input.e[0][0], 123);
136
+ t.deepEqual(input, {
137
+ a: 1,
138
+ e: [[123]],
139
+ });
140
+ });
141
+
142
+ arrays('should be able to create object inside of array', () => {
143
+ let input: any = {};
144
+ set(input, ['x', '0', 'z'], 123);
145
+ t.true(input.x instanceof Array);
146
+ t.deepEqual(input, {
147
+ x: [{ z: 123 }],
148
+ });
149
+ });
150
+
151
+ arrays('should create arrays with hole(s) if needed', () => {
152
+ let input: any = {};
153
+ set(input, ['x', '1', 'z'], 123);
154
+ t.true(input.x instanceof Array);
155
+ t.deepEqual(input, {
156
+ x: [, { z: 123 }],
157
+ });
158
+ });
159
+
160
+ arrays('should create object from decimal-like key :: array :: zero :: string', () => {
161
+ let input: any = {};
162
+ set(input, ['x', '10.0', 'z'], 123);
163
+ t.false(input.x instanceof Array);
164
+ t.deepEqual(input, {
165
+ x: {
166
+ '10.0': {
167
+ z: 123,
168
+ },
169
+ },
170
+ });
171
+ });
172
+
173
+ arrays('should create array from decimal-like key :: array :: zero :: number', () => {
174
+ let input: any = {};
175
+ set(input, ['x', 10.0, 'z'], 123);
176
+ t.true(input.x instanceof Array);
177
+
178
+ let x = Array(10);
179
+ x.push({ z: 123 });
180
+ t.deepEqual(input, { x });
181
+ });
182
+
183
+ arrays('should create object from decimal-like key :: array :: nonzero', () => {
184
+ let input: any = {};
185
+ set(input, ['x', '10.2', 'z'], 123);
186
+ t.false(input.x instanceof Array);
187
+ t.deepEqual(input, {
188
+ x: {
189
+ '10.2': {
190
+ z: 123,
191
+ },
192
+ },
193
+ });
194
+ });
195
+ });
196
+
197
+ test('set pollution', (t) => {
198
+ const pollution = (s: string, f: Function) => {
199
+ t.log(s);
200
+ f();
201
+ };
202
+ pollution('should protect against "__proto__" assignment', () => {
203
+ let input: any = { abc: 123 };
204
+ let before = input.__proto__;
205
+ set(input, '__proto__.hello', 123);
206
+
207
+ t.deepEqual(input.__proto__, before);
208
+ t.deepEqual(input, {
209
+ abc: 123,
210
+ });
211
+ });
212
+
213
+ pollution('should protect against "__proto__" assignment :: nested', () => {
214
+ let input: any = { abc: 123 };
215
+ let before = input.__proto__;
216
+ set(input, ['xyz', '__proto__', 'hello'], 123);
217
+
218
+ t.deepEqual(input.__proto__, before);
219
+ t.deepEqual(input, {
220
+ abc: 123,
221
+ xyz: {
222
+ // empty
223
+ },
224
+ });
225
+
226
+ t.is(input.hello, undefined);
227
+ });
228
+
229
+ pollution('should ignore "prototype" assignment', () => {
230
+ let input: any = { a: 123 };
231
+ set(input, 'a.prototype.hello', 'world');
232
+
233
+ t.is(input.a.prototype, undefined);
234
+ t.is(input.a.hello, undefined);
235
+
236
+ t.deepEqual(input, {
237
+ a: {
238
+ // converted, then aborted
239
+ },
240
+ });
241
+
242
+ t.is(JSON.stringify(input), '{"a":{}}');
243
+ });
244
+
245
+ pollution('should ignore "constructor" assignment :: direct', () => {
246
+ let input: any = { a: 123 };
247
+
248
+ function Custom() {
249
+ //
250
+ }
251
+
252
+ set(input, 'a.constructor', Custom);
253
+ t.not(input.a.constructor, Custom);
254
+ t.false(input.a instanceof Custom);
255
+
256
+ t.true(input.a.constructor instanceof Object, '~> 123 -> {}');
257
+ t.is(input.a.hasOwnProperty('constructor'), false);
258
+ t.deepEqual(input, { a: {} });
259
+ });
260
+
261
+ pollution('should ignore "constructor" assignment :: nested', () => {
262
+ let input: any = {};
263
+
264
+ set(input, 'constructor.prototype.hello', 'world');
265
+ t.is(input.hasOwnProperty('constructor'), false);
266
+ t.is(input.hasOwnProperty('hello'), false);
267
+
268
+ t.deepEqual(input, {
269
+ // empty
270
+ });
271
+ });
272
+
273
+ // Test for CVE-2022-25645 - CWE-1321
274
+ pollution('should ignore JSON.parse crafted object with "__proto__" key', () => {
275
+ let a: any = { b: { c: 1 } };
276
+ t.is(a.polluted, undefined);
277
+ set(a, 'b', JSON.parse('{"__proto__":{"polluted":"Yes!"}}'));
278
+ t.is(a.polluted, undefined);
279
+ });
280
+ });
281
+ test('set assigns', (t) => {
282
+ const assigns = (s: string, f: Function) => {
283
+ t.log(s);
284
+ f();
285
+ };
286
+ assigns('should add value to key path :: shallow :: string', () => {
287
+ let input: any = {};
288
+ set(input, 'abc', 123);
289
+ t.deepEqual(input, { abc: 123 });
290
+ });
291
+
292
+ assigns('should add value to key path :: shallow :: array', () => {
293
+ let input: any = {};
294
+ set(input, ['abc'], 123);
295
+ t.deepEqual(input, { abc: 123 });
296
+ });
297
+
298
+ assigns('should add value to key path :: nested :: string', () => {
299
+ let input: any = {};
300
+ set(input, 'a.b.c', 123);
301
+ t.deepEqual(input, {
302
+ a: {
303
+ b: {
304
+ c: 123,
305
+ },
306
+ },
307
+ });
308
+ });
309
+
310
+ assigns('should add value to key path :: nested :: array', () => {
311
+ let input: any = {};
312
+ set(input, ['a', 'b', 'c'], 123);
313
+ t.deepEqual(input, {
314
+ a: {
315
+ b: {
316
+ c: 123,
317
+ },
318
+ },
319
+ });
320
+ });
321
+
322
+ assigns('should create Array via integer key :: string', () => {
323
+ let input: any = {};
324
+ set(input, ['foo', '0'], 123);
325
+ t.true(input.foo instanceof Array);
326
+ t.deepEqual(input, {
327
+ foo: [123],
328
+ });
329
+ });
330
+
331
+ assigns('should create Array via integer key :: number', () => {
332
+ let input: any = {};
333
+ set(input, ['foo', 0], 123);
334
+ t.true(input.foo instanceof Array);
335
+ t.deepEqual(input, {
336
+ foo: [123],
337
+ });
338
+ });
339
+ });
340
+ test('set preserves', (t) => {
341
+ const preserves = (s: string, f: Function) => {
342
+ t.log(s);
343
+ f();
344
+ };
345
+ preserves('should preserve existing object structure', () => {
346
+ let input = {
347
+ a: {
348
+ b: {
349
+ c: 123,
350
+ },
351
+ },
352
+ };
353
+
354
+ set(input, 'a.b.x.y', 456);
355
+
356
+ t.deepEqual(input, {
357
+ a: {
358
+ b: {
359
+ c: 123,
360
+ x: {
361
+ y: 456,
362
+ },
363
+ },
364
+ },
365
+ });
366
+ });
367
+
368
+ preserves('should overwrite existing non-object values as object', () => {
369
+ let input = {
370
+ a: {
371
+ b: 123,
372
+ },
373
+ };
374
+
375
+ set(input, 'a.b.c', 'hello');
376
+
377
+ t.deepEqual(input, {
378
+ a: {
379
+ b: {
380
+ c: 'hello',
381
+ },
382
+ },
383
+ });
384
+ });
385
+
386
+ preserves('should preserve existing object tree w/ array value', () => {
387
+ let input = {
388
+ a: {
389
+ b: {
390
+ c: 123,
391
+ d: {
392
+ e: 5,
393
+ },
394
+ },
395
+ },
396
+ };
397
+
398
+ set(input, 'a.b.d.z', [1, 2, 3, 4]);
399
+
400
+ t.deepEqual(input.a.b.d, {
401
+ e: 5,
402
+ z: [1, 2, 3, 4],
403
+ });
404
+ });
405
+ });
@@ -0,0 +1,48 @@
1
+ import { ObjectKey, ObjectPath, parseObjectPath } from './parseObjectPath';
2
+
3
+ /**
4
+ * Deep set
5
+ *
6
+ * {@link https://github.com/lukeed/dset dset}
7
+ */
8
+ export function set<T extends object, V>(obj: T, key: ObjectKey | ObjectPath, val: V, merging = true) {
9
+ const path = parseObjectPath(key);
10
+ let i = 0;
11
+ const len = path.length;
12
+ let current: any = obj;
13
+ let x, k;
14
+ while (i < len) {
15
+ k = path[i++];
16
+ if (k === '__proto__' || k === 'constructor' || k === 'prototype') break;
17
+ // noinspection PointlessArithmeticExpressionJS
18
+ current = current[k] =
19
+ i === len
20
+ ? merging
21
+ ? merge(current[k], val)
22
+ : val
23
+ : typeof (x = current[k]) === typeof path
24
+ ? x
25
+ : // @ts-ignore hacky type check
26
+ path[i] * 0 !== 0 || !!~('' + path[i]).indexOf('.')
27
+ ? {}
28
+ : [];
29
+ }
30
+ }
31
+
32
+ export function merge(a: any, b: any) {
33
+ let k;
34
+ if (typeof a === 'object' && typeof b === 'object') {
35
+ if (Array.isArray(a) && Array.isArray(b)) {
36
+ for (k = 0; k < b.length; k++) {
37
+ a[k] = merge(a[k], b[k]);
38
+ }
39
+ } else {
40
+ for (k in b) {
41
+ if (k === '__proto__' || k === 'constructor' || k === 'prototype') break;
42
+ a[k] = merge(a[k], b[k]);
43
+ }
44
+ }
45
+ return a;
46
+ }
47
+ return b;
48
+ }
@@ -0,0 +1,25 @@
1
+ import test from 'ava';
2
+ import { renderTemplate } from './renderTemplate';
3
+
4
+ test('renderTemplate', (t) => {
5
+ const obj = {
6
+ name: 'wener',
7
+ authors: [
8
+ {
9
+ name: 'wener',
10
+ },
11
+ ],
12
+ };
13
+ for (const [k, v] of Object.entries({
14
+ 'My name is ${name}': 'My name is wener',
15
+ 'My name is ${ authors[0].name }': 'My name is wener',
16
+ })) {
17
+ t.is(renderTemplate(k, obj), v);
18
+ }
19
+ t.is(
20
+ renderTemplate('My name is ${name}', (v) => v),
21
+ 'My name is name',
22
+ );
23
+ t.is(renderTemplate('My name is ${name}', obj, 'common'), 'My name is ${name}');
24
+ t.is(renderTemplate('My name is {{name}}', obj, 'common'), 'My name is wener');
25
+ });
@@ -0,0 +1,31 @@
1
+ import { get } from '../objects/get';
2
+
3
+ /**
4
+ * 替换类似于 JS 的模板字符串
5
+ *
6
+ * templateString('My name is ${name}',{name:'wener'}) // => 'My name is wener'
7
+ *
8
+ */
9
+ export function renderTemplate(
10
+ template: string,
11
+ data: ((v: string) => any) | object,
12
+ match: 'js' | 'common' | RegExp = 'js',
13
+ ) {
14
+ let getter: Function;
15
+ if (typeof data === 'function') {
16
+ getter = data;
17
+ } else {
18
+ getter = (v: string) => get(data, v);
19
+ }
20
+ if (typeof match === 'string') {
21
+ match = Matches[match] || Matches['js'];
22
+ }
23
+ return template.replace(match, (_, g) => {
24
+ return getter(g.trim());
25
+ });
26
+ }
27
+
28
+ const Matches = {
29
+ js: /\${(.*?)}/g,
30
+ common: /{{(.*?)}}/g,
31
+ };
package/tsconfig.json CHANGED
@@ -11,12 +11,8 @@
11
11
  "strict": true,
12
12
  "sourceMap": true,
13
13
  "skipLibCheck": true,
14
- "declaration": false,
15
14
  "esModuleInterop": true,
16
- "importHelpers": true,
17
15
  "allowSyntheticDefaultImports": true,
18
- "experimentalDecorators": true,
19
- "emitDecoratorMetadata": true,
20
16
  "noImplicitAny": true,
21
17
  "noImplicitReturns": true,
22
18
  "noUnusedLocals": true,
@@ -1,8 +0,0 @@
1
- function templateString(template, getter) {
2
- return template.replace(/\${(.*?)}/g, (_, g) => {
3
- return getter(g.trim());
4
- });
5
- }
6
-
7
- export { templateString };
8
- //# sourceMappingURL=templates.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"templates.js","sources":["../../src/strings/templates.ts"],"sourcesContent":["/**\n * 替换类似于 JS 的模板字符串\n *\n * @example\n * templateString('My name is ${name}',{name:'wener'})\n */\nexport function templateString(template: string, getter: (v: string) => any) {\n return template.replace(/\\${(.*?)}/g, (_, g) => {\n // variables[g.trim()]\n // 支持路径 - 例如 a.b[0]\n return getter(g.trim());\n });\n}\n"],"names":[],"mappings":"AAMgB,SAAA,cAAA,CAAe,UAAkB,MAA4B,EAAA;AAC3E,EAAA,OAAO,QAAS,CAAA,OAAA,CAAQ,YAAc,EAAA,CAAC,GAAG,CAAM,KAAA;AAG9C,IAAO,OAAA,MAAA,CAAO,CAAE,CAAA,IAAA,EAAM,CAAA,CAAA;AAAA,GACvB,CAAA,CAAA;AACH;;;;"}
@@ -1,13 +0,0 @@
1
- /**
2
- * 替换类似于 JS 的模板字符串
3
- *
4
- * @example
5
- * templateString('My name is ${name}',{name:'wener'})
6
- */
7
- export function templateString(template: string, getter: (v: string) => any) {
8
- return template.replace(/\${(.*?)}/g, (_, g) => {
9
- // variables[g.trim()]
10
- // 支持路径 - 例如 a.b[0]
11
- return getter(g.trim());
12
- });
13
- }