@wener/utils 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/cjs/_commonjsHelpers-dfec268f.js +2 -0
- package/dist/cjs/_commonjsHelpers-dfec268f.js.map +1 -0
- package/dist/cjs/api-7db97ae3.js +1085 -0
- package/dist/cjs/api-7db97ae3.js.map +1 -0
- package/dist/cjs/index-a6d1d653.js +14 -0
- package/dist/cjs/index-a6d1d653.js.map +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/multipart-parser-141ed517.js +3 -0
- package/dist/cjs/multipart-parser-141ed517.js.map +1 -0
- package/dist/cjs/server.js +1 -1
- package/dist/cjs/server.js.map +1 -1
- package/dist/esm/_commonjsHelpers-28e086c5.js +2 -0
- package/dist/esm/_commonjsHelpers-28e086c5.js.map +1 -0
- package/dist/esm/api-3f555472.js +1085 -0
- package/dist/esm/api-3f555472.js.map +1 -0
- package/dist/esm/index-b50fef91.js +14 -0
- package/dist/esm/index-b50fef91.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/multipart-parser-5c1d6ee9.js +3 -0
- package/dist/esm/multipart-parser-5c1d6ee9.js.map +1 -0
- package/dist/esm/server.js +1 -1
- package/dist/esm/server.js.map +1 -1
- package/dist/system/_commonjsHelpers-07f370a7.js +2 -0
- package/dist/system/_commonjsHelpers-07f370a7.js.map +1 -0
- package/dist/system/api-dc50ebac.js +1085 -0
- package/dist/system/api-dc50ebac.js.map +1 -0
- package/dist/system/index-8f1807ba.js +14 -0
- package/dist/system/index-8f1807ba.js.map +1 -0
- package/dist/system/index.js +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/multipart-parser-53518ee9.js +3 -0
- package/dist/system/multipart-parser-53518ee9.js.map +1 -0
- package/dist/system/server.js +1 -1
- package/dist/system/server.js.map +1 -1
- package/lib/asyncs/createLazyPromise.js +55 -0
- package/lib/asyncs/createLazyPromise.js.map +1 -0
- package/lib/crypto/randomUUID.js +3 -5
- package/lib/crypto/randomUUID.js.map +1 -1
- package/lib/i18n/createTranslate.js +37 -0
- package/lib/i18n/createTranslate.js.map +1 -0
- package/lib/index.js +15 -7
- package/lib/index.js.map +1 -1
- package/lib/io/isBuffer.js.map +1 -1
- package/lib/io/isTransferable.js +23 -0
- package/lib/io/isTransferable.js.map +1 -0
- package/lib/isomorphics/structuredClone.js +70 -0
- package/lib/isomorphics/structuredClone.js.map +1 -0
- package/lib/langs/classOf.js +6 -0
- package/lib/langs/classOf.js.map +1 -0
- package/lib/{validations/dequal.js → langs/deepEqual.js} +7 -7
- package/lib/langs/deepEqual.js.map +1 -0
- package/lib/{validations/shallow.js → langs/shallowEqual.js} +3 -3
- package/lib/langs/shallowEqual.js.map +1 -0
- package/lib/modules/isModule.js.map +1 -1
- package/lib/modules/parseModuleId.js.map +1 -1
- package/lib/objects/get.js +14 -0
- package/lib/objects/get.js.map +1 -0
- package/lib/objects/parseObjectPath.js +27 -0
- package/lib/objects/parseObjectPath.js.map +1 -0
- package/lib/objects/set.js +36 -0
- package/lib/objects/set.js.map +1 -0
- package/lib/server/polyfillBrowser.js +12 -0
- package/lib/server/polyfillBrowser.js.map +1 -0
- package/lib/server/polyfillCrypto.js +10 -0
- package/lib/server/polyfillCrypto.js.map +1 -0
- package/lib/server/polyfillFetch.js +31 -0
- package/lib/server/polyfillFetch.js.map +1 -0
- package/lib/server/polyfillJsDom.js +49 -0
- package/lib/server/polyfillJsDom.js.map +1 -0
- package/lib/server.js +4 -1
- package/lib/server.js.map +1 -1
- package/lib/{formats → strings}/formatBytes.js +0 -0
- package/lib/{formats → strings}/formatBytes.js.map +1 -1
- package/lib/strings/renderTemplate.js +25 -0
- package/lib/strings/renderTemplate.js.map +1 -0
- package/lib/validations/isUUID.js +6 -0
- package/lib/validations/isUUID.js.map +1 -0
- package/package.json +14 -4
- package/src/asyncs/createLazyPromise.test.ts +39 -0
- package/src/asyncs/createLazyPromise.ts +63 -0
- package/src/crypto/hashing.test.ts +11 -7
- package/src/crypto/randomUUID.ts +3 -4
- package/src/i18n/createTranslate.test.ts +155 -0
- package/src/i18n/createTranslate.ts +52 -0
- package/src/index.ts +26 -9
- package/src/io/isBuffer.test.ts +7 -0
- package/src/io/isBuffer.ts +5 -1
- package/src/io/isTransferable.test.ts +10 -0
- package/src/io/isTransferable.ts +50 -0
- package/src/isomorphics/structuredClone.test.ts +14 -0
- package/src/isomorphics/structuredClone.ts +85 -0
- package/src/langs/classOf.ts +3 -0
- package/src/{validations/dequal.test.ts → langs/deepEqual.test.ts} +2 -2
- package/src/{validations/dequal.ts → langs/deepEqual.ts} +5 -5
- package/src/langs/langs.test.ts +23 -0
- package/src/{validations/shallow.ts → langs/shallowEqual.ts} +1 -1
- package/src/logging/logger.test.ts +18 -0
- package/src/modules/isModule.ts +8 -1
- package/src/modules/parseModuleId.ts +5 -0
- package/src/objects/get.test.ts +116 -0
- package/src/objects/get.ts +49 -0
- package/src/objects/parseObjectPath.test.ts +18 -0
- package/src/objects/parseObjectPath.ts +40 -0
- package/src/objects/set.test.ts +405 -0
- package/src/objects/set.ts +48 -0
- package/src/server/polyfillBrowser.test.ts +15 -0
- package/src/server/polyfillBrowser.ts +9 -0
- package/src/server/polyfillCrypto.ts +7 -0
- package/src/server/polyfillFetch.ts +29 -0
- package/src/server/polyfillJsDom.ts +84 -0
- package/src/server.ts +4 -1
- package/src/{formats → strings}/formatBytes.ts +0 -0
- package/src/strings/renderTemplate.test.ts +25 -0
- package/src/strings/renderTemplate.ts +34 -0
- package/src/validations/isUUID.ts +3 -0
- package/tsconfig.json +0 -4
- package/lib/asyncs/LazyPromise.js +0 -27
- package/lib/asyncs/LazyPromise.js.map +0 -1
- package/lib/server/polyfill.js +0 -8
- package/lib/server/polyfill.js.map +0 -1
- package/lib/strings/templates.js +0 -8
- package/lib/strings/templates.js.map +0 -1
- package/lib/validations/dequal.js.map +0 -1
- package/lib/validations/shallow.js.map +0 -1
- package/src/asyncs/LazyPromise.ts +0 -29
- package/src/server/polyfill.ts +0 -5
- 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,15 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { polyfillBrowser } from './polyfillBrowser';
|
|
3
|
+
|
|
4
|
+
test.before(async () => {
|
|
5
|
+
await polyfillBrowser();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
test('polyfillBrowser', (t) => {
|
|
9
|
+
t.truthy(fetch);
|
|
10
|
+
t.truthy(window);
|
|
11
|
+
t.truthy(document);
|
|
12
|
+
t.truthy(crypto);
|
|
13
|
+
// not the same
|
|
14
|
+
t.not(window, globalThis);
|
|
15
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { polyfillCrypto } from './polyfillCrypto';
|
|
2
|
+
import { polyfillFetch } from './polyfillFetch';
|
|
3
|
+
import { polyfillJsDom } from './polyfillJsDom';
|
|
4
|
+
|
|
5
|
+
export async function polyfillBrowser() {
|
|
6
|
+
await polyfillCrypto();
|
|
7
|
+
await polyfillFetch();
|
|
8
|
+
await polyfillJsDom();
|
|
9
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export async function polyfillFetch() {
|
|
2
|
+
if ('fetch' in globalThis) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const {
|
|
6
|
+
default: fetch,
|
|
7
|
+
Response,
|
|
8
|
+
Headers,
|
|
9
|
+
Request,
|
|
10
|
+
AbortError,
|
|
11
|
+
FetchError,
|
|
12
|
+
FormData,
|
|
13
|
+
Blob,
|
|
14
|
+
File,
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
} = await import('node-fetch');
|
|
17
|
+
Object.assign(globalThis, {
|
|
18
|
+
fetch,
|
|
19
|
+
Response,
|
|
20
|
+
Headers,
|
|
21
|
+
Request,
|
|
22
|
+
AbortError,
|
|
23
|
+
FetchError,
|
|
24
|
+
FormData,
|
|
25
|
+
Blob,
|
|
26
|
+
File,
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ConstructorOptions, ResourceLoaderConstructorOptions } from 'jsdom';
|
|
2
|
+
|
|
3
|
+
export async function polyfillJsDom() {
|
|
4
|
+
if (typeof window !== 'undefined') {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { ResourceLoader, JSDOM } = await import('jsdom');
|
|
9
|
+
|
|
10
|
+
// https://github.com/lukechilds/window/blob/master/src/index.js
|
|
11
|
+
class Window {
|
|
12
|
+
constructor(opts: ResourceLoaderConstructorOptions & ConstructorOptions = {}) {
|
|
13
|
+
const { proxy, strictSSL, userAgent, ...jsdomOpts } = opts;
|
|
14
|
+
const resources = new ResourceLoader({
|
|
15
|
+
proxy,
|
|
16
|
+
strictSSL,
|
|
17
|
+
userAgent,
|
|
18
|
+
});
|
|
19
|
+
return new JSDOM(
|
|
20
|
+
'',
|
|
21
|
+
Object.assign(jsdomOpts, {
|
|
22
|
+
resources,
|
|
23
|
+
}),
|
|
24
|
+
).window;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// https://github.com/lukechilds/browser-env/blob/master/src/index.js
|
|
29
|
+
// Default jsdom config.
|
|
30
|
+
// These settings must override any custom settings to make sure we can iterate
|
|
31
|
+
// over the window object.
|
|
32
|
+
const defaultJsdomConfig = {
|
|
33
|
+
// features: {
|
|
34
|
+
// FetchExternalResources: false,
|
|
35
|
+
// ProcessExternalResources: false,
|
|
36
|
+
// },
|
|
37
|
+
};
|
|
38
|
+
// IIFE executed on import to return an array of global Node.js properties that
|
|
39
|
+
// conflict with global browser properties.
|
|
40
|
+
const protectedProperties = (() =>
|
|
41
|
+
Object.getOwnPropertyNames(new Window(defaultJsdomConfig)).filter(
|
|
42
|
+
(prop) => typeof globalThis[prop as keyof typeof globalThis] !== 'undefined',
|
|
43
|
+
))();
|
|
44
|
+
|
|
45
|
+
function installEnv(...args: any[]) {
|
|
46
|
+
// Sets up global browser environment
|
|
47
|
+
// Extract options from args
|
|
48
|
+
const properties = args.filter((arg: any) => Array.isArray(arg))[0];
|
|
49
|
+
const userJsdomConfig = args.filter((arg: any) => !Array.isArray(arg))[0];
|
|
50
|
+
|
|
51
|
+
// Create window object
|
|
52
|
+
const window = new Window(Object.assign({}, userJsdomConfig, defaultJsdomConfig));
|
|
53
|
+
|
|
54
|
+
// Get all global browser properties
|
|
55
|
+
Object.getOwnPropertyNames(window)
|
|
56
|
+
|
|
57
|
+
// Remove protected properties
|
|
58
|
+
.filter((prop) => protectedProperties.indexOf(prop) === -1)
|
|
59
|
+
|
|
60
|
+
// If we're only applying specific required properties remove everything else
|
|
61
|
+
.filter((prop) => !(properties && properties.indexOf(prop) === -1))
|
|
62
|
+
.filter((prop) => {
|
|
63
|
+
switch (prop) {
|
|
64
|
+
case 'undefined':
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Copy what's left to the Node.js global scope
|
|
71
|
+
.forEach((prop) => {
|
|
72
|
+
// console.debug(`define globalThis.${prop}`);
|
|
73
|
+
Object.defineProperty(globalThis, prop, {
|
|
74
|
+
configurable: true,
|
|
75
|
+
get: () => window[prop as keyof Window] as any,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return window;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
installEnv({ url: 'http://localhost' });
|
|
83
|
+
return true;
|
|
84
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { polyfillCrypto } from './server/polyfillCrypto';
|
|
2
|
+
export { polyfillFetch } from './server/polyfillFetch';
|
|
3
|
+
export { polyfillJsDom } from './server/polyfillJsDom';
|
|
4
|
+
export { polyfillBrowser } from './server/polyfillBrowser';
|
|
File without changes
|
|
@@ -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,34 @@
|
|
|
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 (!data) {
|
|
16
|
+
// todo warning in dev
|
|
17
|
+
getter = () => '';
|
|
18
|
+
} else if (typeof data === 'function') {
|
|
19
|
+
getter = data;
|
|
20
|
+
} else {
|
|
21
|
+
getter = (v: string) => get(data, v);
|
|
22
|
+
}
|
|
23
|
+
if (typeof match === 'string') {
|
|
24
|
+
match = Matches[match] || Matches['js'];
|
|
25
|
+
}
|
|
26
|
+
return template.replace(match, (_, g) => {
|
|
27
|
+
return getter(g.trim());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const Matches = {
|
|
32
|
+
js: /\${(.*?)}/g,
|
|
33
|
+
common: /{{(.*?)}}/g,
|
|
34
|
+
};
|
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,
|