@wener/utils 1.1.5 → 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 +3 -1
- 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 +11 -6
- package/lib/index.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/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 +3 -1
- package/lib/strings/renderTemplate.js.map +1 -1
- package/lib/validations/isUUID.js +6 -0
- package/lib/validations/isUUID.js.map +1 -0
- package/package.json +5 -1
- 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 +20 -8
- package/src/io/isBuffer.test.ts +7 -0
- 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/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.ts +4 -1
- package/src/validations/isUUID.ts +3 -0
- 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/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
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { createTranslate } from './createTranslate';
|
|
3
|
+
|
|
4
|
+
test('exports', (t) => {
|
|
5
|
+
let out = createTranslate();
|
|
6
|
+
t.is(typeof out, 'object', 'returns an object');
|
|
7
|
+
t.is(typeof out.t, 'function', '~> has "t" function');
|
|
8
|
+
t.is(typeof out.dict, 'function', '~> has "set" function');
|
|
9
|
+
t.is(typeof out.locale, 'function', '~> has "locale" function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('usage', (t) => {
|
|
13
|
+
let ctx = createTranslate({
|
|
14
|
+
en: { hello: 'Hello, {{name}}!' },
|
|
15
|
+
es: { hello: 'Hola {{name}}!' },
|
|
16
|
+
pt: { foo: 'foo {{name}}~!' },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
t.deepEqual(ctx.dict('en'), { hello: 'Hello, {{name}}!' });
|
|
20
|
+
|
|
21
|
+
t.is(ctx.dict('foobar'), undefined);
|
|
22
|
+
|
|
23
|
+
let foo = ctx.t('hello');
|
|
24
|
+
t.is(foo, '', '~> "" w/o locale');
|
|
25
|
+
|
|
26
|
+
t.is(ctx.locale('en'), 'en', '>>> ctx.locale()');
|
|
27
|
+
|
|
28
|
+
t.is(ctx.locale(), 'en');
|
|
29
|
+
|
|
30
|
+
let bar = ctx.t('hello');
|
|
31
|
+
t.not(bar, '', '(en) found "hello" key');
|
|
32
|
+
t.is(bar, 'Hello, !', '~> interpolations empty if missing param');
|
|
33
|
+
|
|
34
|
+
let baz = ctx.t('hello', { name: 'world' });
|
|
35
|
+
t.is(baz, 'Hello, world!', '~> interpolations successful');
|
|
36
|
+
|
|
37
|
+
let bat = ctx.t('hello', { name: 'world' }, 'es');
|
|
38
|
+
t.not(bat, '', '(es) found "hello" key');
|
|
39
|
+
t.is(bat, 'Hola world!', '~> success');
|
|
40
|
+
|
|
41
|
+
t.is(ctx.t('hello', { name: 'world' }, 'pt'), '', '(pt) did NOT find "hello" key');
|
|
42
|
+
|
|
43
|
+
t.is(ctx.dict('pt', { hello: 'Oí {{name}}!' }), undefined, '>>> ctx.dict()');
|
|
44
|
+
|
|
45
|
+
let quz = ctx.t('hello', { name: 'world' }, 'pt');
|
|
46
|
+
t.not(quz, '', '(pt) found "hello" key');
|
|
47
|
+
t.is(quz, 'Oí world!', '~> success');
|
|
48
|
+
|
|
49
|
+
let qut = ctx.t('foo', { name: 'bar' }, 'pt');
|
|
50
|
+
t.not(qut, '', '(pt) found "foo" key');
|
|
51
|
+
t.is(qut, 'foo bar~!', '~> success');
|
|
52
|
+
|
|
53
|
+
t.is(ctx.locale('es'), 'es', '>>> ctx.locale()');
|
|
54
|
+
|
|
55
|
+
t.is(ctx.locale(), 'es');
|
|
56
|
+
t.is(ctx.locale(''), 'es');
|
|
57
|
+
t.is(ctx.locale(false as any), 'es');
|
|
58
|
+
t.is(ctx.locale(null as any), 'es');
|
|
59
|
+
t.is(ctx.locale(0 as any), 'es');
|
|
60
|
+
|
|
61
|
+
let qux = ctx.t('hello', { name: 'default' });
|
|
62
|
+
t.not(qux, '', '(es) found "hello" key');
|
|
63
|
+
t.is(qux, 'Hola default!', '~> success');
|
|
64
|
+
|
|
65
|
+
t.is(ctx.t('hello', { name: 'world' }, 'de'), '', '(de) did NOT find "hello" key');
|
|
66
|
+
|
|
67
|
+
t.is(ctx.dict('de', { hello: 'Hallo {{name}}!' }), undefined, '>>> ctx.dict(de)');
|
|
68
|
+
|
|
69
|
+
let qar = ctx.t('hello', { name: 'world' }, 'de');
|
|
70
|
+
t.not(qar, '', '(de) found "hello" key');
|
|
71
|
+
t.is(qar, 'Hallo world!', '~> success');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('functional', (t) => {
|
|
75
|
+
let ctx = createTranslate({
|
|
76
|
+
en: {
|
|
77
|
+
hello(value: any) {
|
|
78
|
+
return `hello ${value || 'stranger'}~!`;
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
ctx.locale('en');
|
|
84
|
+
|
|
85
|
+
let foo = ctx.t('hello');
|
|
86
|
+
t.is(foo, 'hello stranger~!', '~> called function w/o param');
|
|
87
|
+
|
|
88
|
+
let bar = ctx.t('hello', 'world' as any);
|
|
89
|
+
t.is(bar, 'hello world~!', '~> called function w/ param (string)');
|
|
90
|
+
|
|
91
|
+
let baz = ctx.t('hello', [1, 2, 3]);
|
|
92
|
+
t.is(baz, 'hello 1,2,3~!', '~> called function w/ param (array)');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('nested', (t) => {
|
|
96
|
+
let ctx = createTranslate({
|
|
97
|
+
en: {
|
|
98
|
+
fruits: {
|
|
99
|
+
apple: 'apple',
|
|
100
|
+
orange: 'orange',
|
|
101
|
+
grape: 'grape',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
es: {
|
|
105
|
+
fruits: {
|
|
106
|
+
apple: 'manzana',
|
|
107
|
+
orange: 'naranja',
|
|
108
|
+
grape: 'uva',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
ctx.locale('en');
|
|
114
|
+
t.is(ctx.t('fruits.apple'), 'apple', '(en) fruits.apple');
|
|
115
|
+
t.is(ctx.t('fruits.orange'), 'orange', '(en) fruits.orange');
|
|
116
|
+
t.is(ctx.t(['fruits', 'grape']), 'grape', '(en) ["fruits","grape"]');
|
|
117
|
+
t.is(ctx.t('fruits.404'), '', '(en) fruits.404 ~> ""');
|
|
118
|
+
t.is(ctx.t('error.404'), '', '(en) error.404 ~> ""');
|
|
119
|
+
|
|
120
|
+
ctx.locale('es');
|
|
121
|
+
t.is(ctx.t('fruits.apple'), 'manzana', '(es) fruits.apple');
|
|
122
|
+
t.is(ctx.t('fruits.orange'), 'naranja', '(es) fruits.orange');
|
|
123
|
+
t.is(ctx.t(['fruits', 'grape']), 'uva', '(es) ["fruits","grape"]');
|
|
124
|
+
t.is(ctx.t('fruits.404'), '', '(es) fruits.404 ~> ""');
|
|
125
|
+
t.is(ctx.t('error.404'), '', '(es) error.404 ~> ""');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('arrays', (t) => {
|
|
129
|
+
let ctx = createTranslate({
|
|
130
|
+
en: {
|
|
131
|
+
foo: '{{0}} + {{1}} = {{2}}',
|
|
132
|
+
bar: [
|
|
133
|
+
{
|
|
134
|
+
baz: 'roses are {{colors.0}}, violets are {{colors.1}}',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
ctx.locale('en');
|
|
141
|
+
|
|
142
|
+
t.is(ctx.t('foo', [1, 2, 3]), '1 + 2 = 3', '~> foo');
|
|
143
|
+
|
|
144
|
+
t.is(ctx.t('bar.0.baz', { colors: ['red', 'blue'] }), 'roses are red, violets are blue', '~> bar.0.baz');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('invalid value', (t) => {
|
|
148
|
+
let ctx = createTranslate({
|
|
149
|
+
en: {
|
|
150
|
+
foo: ['bar'],
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
t.deepEqual(ctx.t('foo', null as any, 'en'), ['bar']);
|
|
155
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { get } from '../objects/get';
|
|
2
|
+
import { ObjectPathLike } from '../objects/parseObjectPath';
|
|
3
|
+
import { renderTemplate } from '../strings/renderTemplate';
|
|
4
|
+
|
|
5
|
+
export interface Translate<T extends object> {
|
|
6
|
+
/** Get/Set the language key */
|
|
7
|
+
locale(lang?: string): string;
|
|
8
|
+
|
|
9
|
+
/** Define the dict of translations for a language */
|
|
10
|
+
dict(lang: string, dict: T): void;
|
|
11
|
+
|
|
12
|
+
/** Get the dict of translations for a language */
|
|
13
|
+
dict(lang: string): T;
|
|
14
|
+
|
|
15
|
+
/** Retrieve a translation segment for the current language */
|
|
16
|
+
t<X extends Record<string, any> | any[]>(key: ObjectPathLike, params?: X, lang?: string): string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createTranslate<T extends object>(obj?: Record<string, T>): Translate<T> {
|
|
20
|
+
let locale = '';
|
|
21
|
+
const tree = obj || {};
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
locale(lang) {
|
|
25
|
+
return (locale = lang || locale);
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
dict: ((lang, dict?) => {
|
|
29
|
+
if (dict) {
|
|
30
|
+
tree[lang] = Object.assign(tree[lang] || {}, dict);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
return tree[lang];
|
|
34
|
+
}) as Translate<T>['dict'],
|
|
35
|
+
|
|
36
|
+
t(key, params, lang) {
|
|
37
|
+
const val = get(tree[lang || locale], key, '') as any;
|
|
38
|
+
if (process.env.NODE_ENV === 'development') {
|
|
39
|
+
if (val == null) {
|
|
40
|
+
return console.error(
|
|
41
|
+
`[Translate] Missing the "${[].concat(key as any).join('.')}" key within the "${
|
|
42
|
+
lang || locale
|
|
43
|
+
}" dictionary`,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (typeof val === 'function') return val(params);
|
|
48
|
+
if (typeof val === 'string') return renderTemplate(val, params!, 'common');
|
|
49
|
+
return val;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { set } from './objects/set';
|
|
|
13
13
|
export { parseObjectPath } from './objects/parseObjectPath';
|
|
14
14
|
|
|
15
15
|
// async
|
|
16
|
-
export { createLazyPromise, type LazyPromise } from './asyncs/
|
|
16
|
+
export { createLazyPromise, type LazyPromise } from './asyncs/createLazyPromise';
|
|
17
17
|
export { setAsyncInterval, clearAsyncInterval } from './asyncs/AsyncInterval';
|
|
18
18
|
export { type MaybePromise } from './asyncs/MaybePromise';
|
|
19
19
|
|
|
@@ -26,12 +26,15 @@ export { isPromise } from './asyncs/isPromise';
|
|
|
26
26
|
export { isClass } from './validations/isClass';
|
|
27
27
|
export { isDefined } from './validations/isDefined';
|
|
28
28
|
export { isEmptyObject } from './validations/isEmptyObject';
|
|
29
|
-
export {
|
|
30
|
-
export {
|
|
29
|
+
export { shallowEqual } from './langs/shallowEqual';
|
|
30
|
+
export { deepEqual } from './langs/deepEqual';
|
|
31
|
+
export { isUUID } from './validations/isUUID';
|
|
32
|
+
|
|
33
|
+
export { classOf } from './langs/classOf';
|
|
31
34
|
|
|
32
35
|
// modules
|
|
33
36
|
export { parseModuleId, type ParsedModuleId } from './modules/parseModuleId';
|
|
34
|
-
export { isModule } from './modules/isModule';
|
|
37
|
+
export { isModule, type Module } from './modules/isModule';
|
|
35
38
|
|
|
36
39
|
// logging
|
|
37
40
|
export { type Logger, type LogLevel } from './logging/Logger';
|
|
@@ -42,21 +45,30 @@ export { createChildLogger } from './logging/createChildLogger';
|
|
|
42
45
|
// strings
|
|
43
46
|
export { pascalCase, camelCase } from './strings/camelCase';
|
|
44
47
|
export { renderTemplate } from './strings/renderTemplate';
|
|
48
|
+
export { formatBytes } from './strings/formatBytes';
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
// i18n
|
|
51
|
+
export { createTranslate } from './i18n/createTranslate';
|
|
52
|
+
|
|
53
|
+
// io
|
|
47
54
|
export { isBuffer } from './io/isBuffer';
|
|
55
|
+
export { isTransferable } from './io/isTransferable';
|
|
48
56
|
|
|
57
|
+
// browser
|
|
49
58
|
export { copy } from './browsers/copy';
|
|
50
59
|
export { download } from './browsers/download';
|
|
51
60
|
export { loadScripts, loadStyles } from './browsers/loaders';
|
|
52
61
|
export { getFileFromDataTransfer } from './browsers/getFileFromDataTransfer';
|
|
53
62
|
|
|
63
|
+
// polyfills
|
|
54
64
|
export { getGlobalThis } from './isomorphics/getGlobalThis';
|
|
55
|
-
|
|
56
|
-
export { formatBytes } from './formats/formatBytes';
|
|
57
|
-
export { urljoin } from './shim/urljoin';
|
|
65
|
+
export { structuredClone } from './isomorphics/structuredClone';
|
|
58
66
|
|
|
59
67
|
// crypto
|
|
60
68
|
export { randomUUID } from './crypto/randomUUID';
|
|
61
69
|
export { sha1, sha256, sha384, sha512 } from './crypto/hashing';
|
|
62
70
|
export { hex } from './crypto/hex';
|
|
71
|
+
|
|
72
|
+
// misc
|
|
73
|
+
export { createRandom } from './maths/random';
|
|
74
|
+
export { urljoin } from './shim/urljoin';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { Buffer } from 'node:buffer';
|
|
3
|
+
import { isTransferable } from './isTransferable';
|
|
4
|
+
|
|
5
|
+
test('isTransferable', (t) => {
|
|
6
|
+
t.false(isTransferable(0));
|
|
7
|
+
t.false(isTransferable(Buffer.from('')));
|
|
8
|
+
t.true(new ArrayBuffer(0) instanceof ArrayBuffer);
|
|
9
|
+
t.true(isTransferable(new ArrayBuffer(0)));
|
|
10
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transferable object pass between workers, can work with structuredClone
|
|
3
|
+
*
|
|
4
|
+
* - Chrome 87, FF 103, Safari X, NodeJS X
|
|
5
|
+
*
|
|
6
|
+
* {@link https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects | Transferable objects}
|
|
7
|
+
*/
|
|
8
|
+
export function isTransferable(v: any): v is TransferableObject {
|
|
9
|
+
_ctors ||= ctors();
|
|
10
|
+
return _ctors.some((ctor) => v instanceof ctor);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let _ctors: any[];
|
|
14
|
+
|
|
15
|
+
function ctors() {
|
|
16
|
+
const o: any = globalThis.window || globalThis || global;
|
|
17
|
+
return [
|
|
18
|
+
o.ArrayBuffer,
|
|
19
|
+
o.MessagePort,
|
|
20
|
+
o.ReadableStream,
|
|
21
|
+
o.WritableStream,
|
|
22
|
+
o.TransformStream,
|
|
23
|
+
o.AudioData,
|
|
24
|
+
o.ImageBitmap,
|
|
25
|
+
o.VideoFrame,
|
|
26
|
+
o.OffscreenCanvas,
|
|
27
|
+
o.RTCDataChannel,
|
|
28
|
+
].filter(Boolean);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type TransferableObject =
|
|
32
|
+
| Transferable
|
|
33
|
+
| ArrayBuffer
|
|
34
|
+
| MessagePort
|
|
35
|
+
| ReadableStream
|
|
36
|
+
| WritableStream
|
|
37
|
+
| TransformStream
|
|
38
|
+
| AudioData
|
|
39
|
+
| ImageBitmap
|
|
40
|
+
| VideoFrame
|
|
41
|
+
| OffscreenCanvas
|
|
42
|
+
| RTCDataChannel;
|
|
43
|
+
|
|
44
|
+
declare global {
|
|
45
|
+
interface OffscreenCanvas {}
|
|
46
|
+
|
|
47
|
+
interface VideoFrame {}
|
|
48
|
+
|
|
49
|
+
interface AudioData {}
|
|
50
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { classOf } from '../langs/classOf';
|
|
3
|
+
import { _clone } from './structuredClone';
|
|
4
|
+
|
|
5
|
+
test('structuredClone', (t) => {
|
|
6
|
+
for (const [k, v] of [
|
|
7
|
+
['', ''],
|
|
8
|
+
[Number(1), 1],
|
|
9
|
+
]) {
|
|
10
|
+
let c = _clone(k);
|
|
11
|
+
t.deepEqual(c, v);
|
|
12
|
+
t.is(classOf(c), classOf(v));
|
|
13
|
+
}
|
|
14
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome 98, Safari 15.4
|
|
3
|
+
*
|
|
4
|
+
* {@link https://developer.mozilla.org/en-US/docs/Web/API/structuredClone structuredClone}
|
|
5
|
+
* {@link https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/web.structured-clone.js core-js}
|
|
6
|
+
*/
|
|
7
|
+
import { classOf } from '../langs/classOf';
|
|
8
|
+
|
|
9
|
+
export const structuredClone: <T>(value: T, options?: StructuredSerializeOptions) => T =
|
|
10
|
+
globalThis.structuredClone || _clone;
|
|
11
|
+
|
|
12
|
+
function set(obj: any, key: any, val: any) {
|
|
13
|
+
if (typeof val.value === 'object') val.value = _clone(val.value);
|
|
14
|
+
if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === '__proto__') {
|
|
15
|
+
Object.defineProperty(obj, key, val);
|
|
16
|
+
} else obj[key] = val.value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* {@link https://github.com/lukeed/klona/blob/master/src/full.js klona}
|
|
21
|
+
*/
|
|
22
|
+
export function _clone(x: any): any {
|
|
23
|
+
// too complex
|
|
24
|
+
// https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/web.structured-clone.js
|
|
25
|
+
|
|
26
|
+
if (typeof x !== 'object') return x;
|
|
27
|
+
|
|
28
|
+
let i = 0;
|
|
29
|
+
let k;
|
|
30
|
+
let list;
|
|
31
|
+
let tmp: any;
|
|
32
|
+
const str = classOf(x);
|
|
33
|
+
switch (str) {
|
|
34
|
+
case 'Array':
|
|
35
|
+
tmp = Array(x.length);
|
|
36
|
+
break;
|
|
37
|
+
case 'Object':
|
|
38
|
+
tmp = Object.create(x.__proto__ || null);
|
|
39
|
+
break;
|
|
40
|
+
case 'Set':
|
|
41
|
+
tmp = new Set();
|
|
42
|
+
x.forEach(function (val: any) {
|
|
43
|
+
tmp.add(_clone(val));
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
46
|
+
case 'Map':
|
|
47
|
+
tmp = new Map();
|
|
48
|
+
x.forEach(function (val: any, key: any) {
|
|
49
|
+
tmp.set(_clone(key), _clone(val));
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
case 'Date':
|
|
53
|
+
tmp = new Date(+x);
|
|
54
|
+
break;
|
|
55
|
+
case 'RegExp':
|
|
56
|
+
tmp = new RegExp(x.source, x.flags);
|
|
57
|
+
break;
|
|
58
|
+
case 'DataView':
|
|
59
|
+
tmp = new x.constructor(_clone(x.buffer));
|
|
60
|
+
break;
|
|
61
|
+
case 'ArrayBuffer':
|
|
62
|
+
tmp = x.slice(0);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
// typed arrays
|
|
66
|
+
if (str.endsWith('Array')) {
|
|
67
|
+
// ArrayBuffer.isView(x)
|
|
68
|
+
// ~> `new` bcuz `Buffer.slice` => ref
|
|
69
|
+
tmp = new x.constructor(x);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (tmp) {
|
|
74
|
+
for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) {
|
|
75
|
+
set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i]));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) {
|
|
79
|
+
if (Object.hasOwnProperty.call(tmp, (k = list[i])) && tmp[k] === x[k]) continue;
|
|
80
|
+
set(tmp, k, Object.getOwnPropertyDescriptor(x, k));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return tmp || x;
|
|
85
|
+
}
|
|
@@ -3,7 +3,7 @@ const hasElementType = typeof Element !== 'undefined';
|
|
|
3
3
|
|
|
4
4
|
function find(iter: any, tar: any, key?: any) {
|
|
5
5
|
for (key of iter.keys()) {
|
|
6
|
-
if (
|
|
6
|
+
if (deepEqual(key, tar)) return key;
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ function find(iter: any, tar: any, key?: any) {
|
|
|
12
12
|
* @see {@link https://github.com/lukeed/dequal/blob/master/src/lite.js dequal/src/lite.js}
|
|
13
13
|
* @see {@link https://github.com/FormidableLabs/react-fast-compare/blob/master/index.js react-fast-compare/index.js}
|
|
14
14
|
*/
|
|
15
|
-
export function
|
|
15
|
+
export function deepEqual(foo: any, bar: any) {
|
|
16
16
|
let ctor, len, tmp;
|
|
17
17
|
if (foo === bar) return true;
|
|
18
18
|
|
|
@@ -22,7 +22,7 @@ export function dequal(foo: any, bar: any) {
|
|
|
22
22
|
|
|
23
23
|
if (ctor === Array) {
|
|
24
24
|
if ((len = foo.length) === bar.length) {
|
|
25
|
-
while (len-- &&
|
|
25
|
+
while (len-- && deepEqual(foo[len], bar[len]));
|
|
26
26
|
}
|
|
27
27
|
return len === -1;
|
|
28
28
|
}
|
|
@@ -52,7 +52,7 @@ export function dequal(foo: any, bar: any) {
|
|
|
52
52
|
tmp = find(bar, tmp);
|
|
53
53
|
if (!tmp) return false;
|
|
54
54
|
}
|
|
55
|
-
if (!
|
|
55
|
+
if (!deepEqual(len[1], bar.get(tmp))) {
|
|
56
56
|
return false;
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -95,7 +95,7 @@ export function dequal(foo: any, bar: any) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
if (has.call(foo, ctor) && ++len && !has.call(bar, ctor)) return false;
|
|
98
|
-
if (!(ctor in bar) || !
|
|
98
|
+
if (!(ctor in bar) || !deepEqual(foo[ctor], bar[ctor])) return false;
|
|
99
99
|
}
|
|
100
100
|
return Object.keys(bar).length === len;
|
|
101
101
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { classOf } from './classOf';
|
|
3
|
+
|
|
4
|
+
test('classOf', (t) => {
|
|
5
|
+
for (const [k, v] of [
|
|
6
|
+
[0, 'Number'],
|
|
7
|
+
['', 'String'],
|
|
8
|
+
[true, 'Boolean'],
|
|
9
|
+
[null, 'Null'],
|
|
10
|
+
[undefined, 'Undefined'],
|
|
11
|
+
[{}, 'Object'],
|
|
12
|
+
[[], 'Array'],
|
|
13
|
+
[new ArrayBuffer(0), 'ArrayBuffer'],
|
|
14
|
+
[new Map(), 'Map'],
|
|
15
|
+
[new Set(), 'Set'],
|
|
16
|
+
[new Date(), 'Date'],
|
|
17
|
+
[new RegExp(''), 'RegExp'],
|
|
18
|
+
[new DataView(new ArrayBuffer(0)), 'DataView'],
|
|
19
|
+
[new Int8Array(0), 'Int8Array'],
|
|
20
|
+
]) {
|
|
21
|
+
t.is(classOf(k), v as any);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -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
|
+
}
|