@wener/utils 1.1.6 → 1.1.8
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/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/server.cjs +2 -0
- package/dist/cjs/server.cjs.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/server.js +1 -1
- package/dist/esm/server.js.map +1 -1
- package/dist/system/index.js +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/server.js +1 -1
- package/dist/system/server.js.map +1 -1
- package/lib/arrays/MaybeArray.js.map +1 -1
- package/lib/asyncs/createLazyPromise.js +12 -3
- package/lib/asyncs/createLazyPromise.js.map +1 -1
- package/lib/asyncs/timeout.js +1 -1
- package/lib/asyncs/timeout.js.map +1 -1
- package/lib/browsers/copy.js +2 -3
- package/lib/browsers/copy.js.map +1 -1
- package/lib/browsers/loaders.js +6 -11
- package/lib/browsers/loaders.js.map +1 -1
- package/lib/crypto/{hex.js → base.js} +1 -1
- package/lib/crypto/base.js.map +1 -0
- package/lib/crypto/getRandomValues.js +37 -0
- package/lib/crypto/getRandomValues.js.map +1 -0
- package/lib/crypto/hashing.js +4 -4
- package/lib/crypto/hashing.js.map +1 -1
- package/lib/crypto/randomUUID.js.map +1 -1
- package/lib/crypto/ulid.js +139 -0
- package/lib/crypto/ulid.js.map +1 -0
- package/lib/i18n/createTranslate.js +17 -1
- package/lib/i18n/createTranslate.js.map +1 -1
- package/lib/index.js +10 -5
- package/lib/index.js.map +1 -1
- package/lib/io/ArrayBuffers.js +170 -0
- package/lib/io/ArrayBuffers.js.map +1 -0
- package/lib/io/Buffer.js +21 -0
- package/lib/io/Buffer.js.map +1 -0
- package/lib/io/isBuffer.js +1 -1
- package/lib/io/isBuffer.js.map +1 -1
- package/lib/io/isTransferable.js.map +1 -1
- package/lib/isomorphics/structuredClone.js.map +1 -1
- package/lib/langs/deepEqual.js.map +1 -1
- package/lib/langs/shallowClone.js +15 -0
- package/lib/langs/shallowClone.js.map +1 -0
- package/lib/langs/shallowEqual.js.map +1 -1
- package/lib/logging/createChildLogger.js.map +1 -1
- package/lib/logging/createNoopLogger.js.map +1 -1
- package/lib/logging/createWriteLogger.js.map +1 -1
- package/lib/modules/isModule.js.map +1 -1
- package/lib/modules/parseModuleId.js +7 -5
- package/lib/modules/parseModuleId.js.map +1 -1
- package/lib/objects/get.js.map +1 -1
- package/lib/objects/parseObjectPath.js.map +1 -1
- package/lib/objects/set.js.map +1 -1
- package/lib/server/polyfillBrowser.js.map +1 -1
- package/lib/server/polyfillFetch.js.map +1 -1
- package/lib/server/polyfillJsDom.js +1 -1
- package/lib/server/polyfillJsDom.js.map +1 -1
- package/lib/strings/formatBytes.js +1 -1
- package/lib/strings/formatBytes.js.map +1 -1
- package/lib/strings/renderTemplate.js +1 -1
- package/lib/strings/renderTemplate.js.map +1 -1
- package/lib/validations/isEmptyObject.js +3 -4
- package/lib/validations/isEmptyObject.js.map +1 -1
- package/lib/validations/isPlainObject.js +11 -0
- package/lib/validations/isPlainObject.js.map +1 -0
- package/package.json +14 -6
- package/src/arrays/MaybeArray.ts +1 -1
- package/src/asyncs/createLazyPromise.test.ts +19 -4
- package/src/asyncs/createLazyPromise.ts +19 -4
- package/src/asyncs/generatorOfStream.ts +1 -0
- package/src/asyncs/timeout.ts +1 -1
- package/src/browsers/copy.ts +6 -5
- package/src/browsers/loaders.ts +6 -11
- package/src/crypto/{hex.ts → base.ts} +3 -0
- package/src/crypto/getRandomValues.ts +40 -0
- package/src/crypto/hashing.test.ts +1 -1
- package/src/crypto/hashing.ts +4 -4
- package/src/crypto/randomUUID.ts +3 -0
- package/src/crypto/ulid.test.ts +22 -0
- package/src/crypto/ulid.ts +182 -0
- package/src/i18n/createTranslate.test.ts +33 -18
- package/src/i18n/createTranslate.ts +20 -4
- package/src/index.ts +14 -7
- package/src/io/AbstractEncoding.ts +21 -0
- package/src/io/ArrayBuffer.test-d.ts +4 -0
- package/src/io/ArrayBuffers.base64.test.ts +61 -0
- package/src/io/ArrayBuffers.test.ts +23 -0
- package/src/io/ArrayBuffers.ts +288 -0
- package/src/io/Buffer.test.ts +23 -0
- package/src/io/Buffer.ts +30 -0
- package/src/io/isBuffer.test.ts +2 -0
- package/src/io/isBuffer.ts +3 -8
- package/src/io/isTransferable.ts +1 -1
- package/src/isomorphics/structuredClone.test.ts +1 -1
- package/src/isomorphics/structuredClone.ts +9 -6
- package/src/langs/deepEqual.ts +1 -0
- package/src/langs/shallowClone.ts +13 -0
- package/src/langs/shallowEqual.ts +1 -1
- package/src/logging/Logger.ts +6 -0
- package/src/logging/createChildLogger.ts +1 -1
- package/src/logging/createNoopLogger.ts +2 -2
- package/src/logging/createWriteLogger.ts +1 -1
- package/src/logging/logger.test.ts +3 -3
- package/src/modules/isModule.ts +3 -0
- package/src/modules/parseModuleId.test.ts +7 -2
- package/src/modules/parseModuleId.ts +15 -9
- package/src/objects/get.test-d.ts +51 -0
- package/src/objects/get.test.ts +2 -55
- package/src/objects/get.ts +2 -1
- package/src/objects/parseObjectPath.ts +4 -3
- package/src/objects/set.test.ts +32 -31
- package/src/objects/set.ts +4 -3
- package/src/server/polyfillBrowser.ts +8 -0
- package/src/server/polyfillFetch.ts +0 -1
- package/src/server/polyfillJsDom.ts +3 -2
- package/src/strings/formatBytes.ts +1 -1
- package/src/strings/renderTemplate.test.ts +1 -0
- package/src/strings/renderTemplate.ts +8 -5
- package/src/typedoc.ts +2 -0
- package/src/validations/isEmptyObject.ts +3 -4
- package/src/validations/isFunction.ts +3 -0
- package/src/validations/isPlainObject.ts +10 -0
- package/tsconfig.json +2 -1
- package/dist/cjs/_commonjsHelpers-dfec268f.js +0 -2
- package/dist/cjs/_commonjsHelpers-dfec268f.js.map +0 -1
- package/dist/cjs/api-7db97ae3.js +0 -1085
- package/dist/cjs/api-7db97ae3.js.map +0 -1
- package/dist/cjs/index-a6d1d653.js +0 -14
- package/dist/cjs/index-a6d1d653.js.map +0 -1
- package/dist/cjs/index.js +0 -2
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/multipart-parser-141ed517.js +0 -3
- package/dist/cjs/multipart-parser-141ed517.js.map +0 -1
- package/dist/cjs/server.js +0 -2
- package/dist/cjs/server.js.map +0 -1
- package/dist/esm/_commonjsHelpers-28e086c5.js +0 -2
- package/dist/esm/_commonjsHelpers-28e086c5.js.map +0 -1
- package/dist/esm/api-3f555472.js +0 -1085
- package/dist/esm/api-3f555472.js.map +0 -1
- package/dist/esm/index-b50fef91.js +0 -14
- package/dist/esm/index-b50fef91.js.map +0 -1
- package/dist/esm/multipart-parser-5c1d6ee9.js +0 -3
- package/dist/esm/multipart-parser-5c1d6ee9.js.map +0 -1
- package/dist/system/_commonjsHelpers-07f370a7.js +0 -2
- package/dist/system/_commonjsHelpers-07f370a7.js.map +0 -1
- package/dist/system/api-dc50ebac.js +0 -1085
- package/dist/system/api-dc50ebac.js.map +0 -1
- package/dist/system/index-8f1807ba.js +0 -14
- package/dist/system/index-8f1807ba.js.map +0 -1
- package/dist/system/multipart-parser-53518ee9.js +0 -3
- package/dist/system/multipart-parser-53518ee9.js.map +0 -1
- package/lib/crypto/hex.js.map +0 -1
- package/lib/shim/urljoin.js +0 -51
- package/lib/shim/urljoin.js.map +0 -1
- package/src/shim/urljoin.test.ts +0 -6
- package/src/shim/urljoin.ts +0 -75
- package/src/types.d.ts +0 -7
package/src/browsers/loaders.ts
CHANGED
|
@@ -26,21 +26,16 @@ export function loadScripts(
|
|
|
26
26
|
options?: { attributes: Record<string, string> },
|
|
27
27
|
): Promise<HTMLScriptElement | HTMLScriptElement[]> {
|
|
28
28
|
if (Array.isArray(src)) {
|
|
29
|
-
return
|
|
29
|
+
return Promise.resolve().then(async () => {
|
|
30
30
|
const all = [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
all.push(await loadScripts(s));
|
|
34
|
-
}
|
|
35
|
-
} catch (e) {
|
|
36
|
-
reject(e);
|
|
37
|
-
return;
|
|
31
|
+
for (const s of src) {
|
|
32
|
+
all.push(await loadScripts(s));
|
|
38
33
|
}
|
|
39
|
-
|
|
34
|
+
return all;
|
|
40
35
|
});
|
|
41
36
|
}
|
|
42
37
|
// todo quote ?
|
|
43
|
-
|
|
38
|
+
const $ele = document.querySelector(`script[src="${src}"]`) as HTMLScriptElement;
|
|
44
39
|
if ($ele) {
|
|
45
40
|
return Promise.resolve($ele);
|
|
46
41
|
}
|
|
@@ -52,7 +47,7 @@ export function loadScripts(
|
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
export function loadStyles(href: string, options?: { attributes: Record<string, string> }): Promise<HTMLLinkElement> {
|
|
55
|
-
|
|
50
|
+
const $ele = document.querySelector(`link[href="${href}"]`) as HTMLLinkElement;
|
|
56
51
|
if ($ele) {
|
|
57
52
|
return Promise.resolve($ele);
|
|
58
53
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
|
2
|
+
let nodeCrypto: Awaited<typeof import('node:crypto')>;
|
|
3
|
+
// globalThis.process?.release?.name
|
|
4
|
+
|
|
5
|
+
// typedoc error
|
|
6
|
+
if (!(process as any).browser) {
|
|
7
|
+
try {
|
|
8
|
+
if (typeof require === 'undefined') {
|
|
9
|
+
void import('node:crypto').then((v) => (nodeCrypto = v.default));
|
|
10
|
+
} else {
|
|
11
|
+
nodeCrypto = require('node:crypto');
|
|
12
|
+
}
|
|
13
|
+
} catch (e) {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export let getRandomValues: <T extends Exclude<NodeJS.TypedArray, Float32Array | Float64Array>>(typedArray: T) => T =
|
|
17
|
+
globalThis.crypto?.getRandomValues || (globalThis as any).msCrypto?.getRandomValues || _getRandomValues;
|
|
18
|
+
|
|
19
|
+
function _getRandomValues<T extends Exclude<NodeJS.TypedArray, Float32Array | Float64Array>>(buf: T) {
|
|
20
|
+
if (nodeCrypto?.webcrypto?.getRandomValues) {
|
|
21
|
+
getRandomValues = nodeCrypto?.webcrypto?.getRandomValues;
|
|
22
|
+
return nodeCrypto.webcrypto.getRandomValues(buf);
|
|
23
|
+
}
|
|
24
|
+
if (nodeCrypto?.randomBytes) {
|
|
25
|
+
if (!(buf instanceof Uint8Array)) {
|
|
26
|
+
throw new TypeError('expected Uint8Array');
|
|
27
|
+
}
|
|
28
|
+
if (buf.length > 65536) {
|
|
29
|
+
const e: any = new Error();
|
|
30
|
+
e.code = 22;
|
|
31
|
+
e.message = `Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length (${buf.length}) exceeds the number of bytes of entropy available via this API (65536).`;
|
|
32
|
+
e.name = 'QuotaExceededError';
|
|
33
|
+
throw e;
|
|
34
|
+
}
|
|
35
|
+
const bytes = nodeCrypto.randomBytes(buf.length);
|
|
36
|
+
buf.set(bytes);
|
|
37
|
+
return buf;
|
|
38
|
+
}
|
|
39
|
+
throw new Error('No secure random number generator available.');
|
|
40
|
+
}
|
|
@@ -2,7 +2,7 @@ import test from 'ava';
|
|
|
2
2
|
import { polyfillCrypto } from '../server/polyfillCrypto';
|
|
3
3
|
import { isUUID } from '../validations/isUUID';
|
|
4
4
|
import { sha1, sha256, sha384, sha512 } from './hashing';
|
|
5
|
-
import { hex } from './
|
|
5
|
+
import { hex } from './base';
|
|
6
6
|
import { _randomUUID } from './randomUUID';
|
|
7
7
|
|
|
8
8
|
test.before(async () => {
|
package/src/crypto/hashing.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export
|
|
1
|
+
export function sha1(s: string | BufferSource) {
|
|
2
2
|
return digestOf('SHA-1', s);
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
-
export
|
|
5
|
+
export function sha256(s: string | BufferSource) {
|
|
6
6
|
return digestOf('SHA-256', s);
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export
|
|
9
|
+
export function sha384(s: string | BufferSource) {
|
|
10
10
|
return digestOf('SHA-384', s);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export
|
|
13
|
+
export function sha512(s: string | BufferSource) {
|
|
14
14
|
return digestOf('SHA-512', s);
|
|
15
15
|
}
|
|
16
16
|
|
package/src/crypto/randomUUID.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const randomUUID: () => string = globalThis.crypto?.randomUUID || _randomUUID;
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
6
9
|
export function _randomUUID() {
|
|
7
10
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
8
11
|
const r = (Math.random() * 16) | 0;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { createULID, parseULID } from './ulid';
|
|
3
|
+
|
|
4
|
+
test('ulid', (t) => {
|
|
5
|
+
// monotonic
|
|
6
|
+
{
|
|
7
|
+
let lastTime = 0;
|
|
8
|
+
const ulid = createULID({
|
|
9
|
+
now: () => {
|
|
10
|
+
lastTime ||= Date.now();
|
|
11
|
+
return lastTime;
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const ulid1 = ulid();
|
|
16
|
+
t.is(parseULID(ulid1).time, lastTime);
|
|
17
|
+
|
|
18
|
+
const ulid2 = ulid();
|
|
19
|
+
t.true(ulid1 < ulid2);
|
|
20
|
+
t.is(parseULID(ulid2).time, lastTime);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { getRandomValues } from './getRandomValues';
|
|
2
|
+
|
|
3
|
+
type PRNG = () => number;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Universally Unique Lexicographically Sortable Identifier
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/ulid/spec ulid/spec
|
|
9
|
+
*/
|
|
10
|
+
export type ULID = (seedTime?: number) => string;
|
|
11
|
+
|
|
12
|
+
export interface ULIDError extends Error {
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function createError(message: string): ULIDError {
|
|
17
|
+
const err = new Error(message) as ULIDError;
|
|
18
|
+
err.source = 'ulid';
|
|
19
|
+
return err;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// These values should NEVER change. If
|
|
23
|
+
// they do, we're no longer making ulids!
|
|
24
|
+
const ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; // Crockford's Base32
|
|
25
|
+
const ENCODING_LEN = ENCODING.length;
|
|
26
|
+
const TIME_MAX = Math.pow(2, 48) - 1;
|
|
27
|
+
const TIME_LEN = 10;
|
|
28
|
+
const RANDOM_LEN = 16;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* check give {@link str} is a valid ulid
|
|
32
|
+
*/
|
|
33
|
+
export function isULID(str: string): boolean {
|
|
34
|
+
return str?.length === 26 && /^[0-9A-HJKMNP-TV-Z]{26}$/.test(str);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function replaceCharAt(str: string, index: number, char: string) {
|
|
38
|
+
if (index > str.length - 1) {
|
|
39
|
+
return str;
|
|
40
|
+
}
|
|
41
|
+
return str.substr(0, index) + char + str.substr(index + 1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function incrementBase32(str: string): string {
|
|
45
|
+
let done;
|
|
46
|
+
let index = str.length;
|
|
47
|
+
let char;
|
|
48
|
+
let charIndex;
|
|
49
|
+
const maxCharIndex = ENCODING_LEN - 1;
|
|
50
|
+
while (!done && index-- >= 0) {
|
|
51
|
+
char = str[index];
|
|
52
|
+
charIndex = ENCODING.indexOf(char);
|
|
53
|
+
if (charIndex === -1) {
|
|
54
|
+
throw createError('incorrectly encoded string');
|
|
55
|
+
}
|
|
56
|
+
if (charIndex === maxCharIndex) {
|
|
57
|
+
str = replaceCharAt(str, index, ENCODING[0]);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
done = replaceCharAt(str, index, ENCODING[charIndex + 1]);
|
|
61
|
+
}
|
|
62
|
+
if (typeof done === 'string') {
|
|
63
|
+
return done;
|
|
64
|
+
}
|
|
65
|
+
throw createError('cannot increment this string');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function randomChar(prng: PRNG): string {
|
|
69
|
+
let rand = Math.floor(prng() * ENCODING_LEN);
|
|
70
|
+
if (rand === ENCODING_LEN) {
|
|
71
|
+
rand = ENCODING_LEN - 1;
|
|
72
|
+
}
|
|
73
|
+
return ENCODING.charAt(rand);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function encodeTime(now: number, len: number): string {
|
|
77
|
+
if (isNaN(now)) {
|
|
78
|
+
throw new Error(`${now} must be a number`);
|
|
79
|
+
}
|
|
80
|
+
if (now > TIME_MAX) {
|
|
81
|
+
throw createError(`cannot encode time greater than ${TIME_MAX}`);
|
|
82
|
+
}
|
|
83
|
+
if (now < 0) {
|
|
84
|
+
throw createError('time must be positive');
|
|
85
|
+
}
|
|
86
|
+
if (!Number.isInteger(now)) {
|
|
87
|
+
throw createError('time must be an integer');
|
|
88
|
+
}
|
|
89
|
+
let mod;
|
|
90
|
+
let str = '';
|
|
91
|
+
for (; len > 0; len--) {
|
|
92
|
+
mod = now % ENCODING_LEN;
|
|
93
|
+
str = ENCODING.charAt(mod) + str;
|
|
94
|
+
now = (now - mod) / ENCODING_LEN;
|
|
95
|
+
}
|
|
96
|
+
return str;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function encodeRandom(len: number, prng: PRNG): string {
|
|
100
|
+
let str = '';
|
|
101
|
+
for (; len > 0; len--) {
|
|
102
|
+
str = randomChar(prng) + str;
|
|
103
|
+
}
|
|
104
|
+
return str;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* extract time & random from ulid
|
|
109
|
+
*
|
|
110
|
+
* @throws ULIDError
|
|
111
|
+
*/
|
|
112
|
+
export function parseULID(id: string): { time: number; random: string } {
|
|
113
|
+
if (id.length !== TIME_LEN + RANDOM_LEN) {
|
|
114
|
+
throw createError('malformed ulid');
|
|
115
|
+
}
|
|
116
|
+
const time = id
|
|
117
|
+
.substr(0, TIME_LEN)
|
|
118
|
+
.split('')
|
|
119
|
+
.reverse()
|
|
120
|
+
.reduce((carry, char, index) => {
|
|
121
|
+
const encodingIndex = ENCODING.indexOf(char);
|
|
122
|
+
if (encodingIndex === -1) {
|
|
123
|
+
throw createError('invalid character found: ' + char);
|
|
124
|
+
}
|
|
125
|
+
return (carry += encodingIndex * Math.pow(ENCODING_LEN, index));
|
|
126
|
+
}, 0);
|
|
127
|
+
if (time > TIME_MAX) {
|
|
128
|
+
throw createError('malformed ulid, timestamp too large');
|
|
129
|
+
}
|
|
130
|
+
return { time, random: id.substring(TIME_LEN) };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function createPrng(): PRNG {
|
|
134
|
+
return () => {
|
|
135
|
+
const buffer = new Uint8Array(1);
|
|
136
|
+
getRandomValues(buffer);
|
|
137
|
+
return buffer[0] / 0xff;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* create a ulid generator
|
|
143
|
+
*/
|
|
144
|
+
export function createULID({
|
|
145
|
+
monotonic = true,
|
|
146
|
+
random = createPrng(),
|
|
147
|
+
now = Date.now,
|
|
148
|
+
}: { monotonic?: boolean; now?: () => number; random?: () => number } = {}) {
|
|
149
|
+
if (!monotonic) {
|
|
150
|
+
return function ulid(seedTime?: number): string {
|
|
151
|
+
seedTime ||= now();
|
|
152
|
+
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN, random);
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let lastTime: number = 0;
|
|
157
|
+
let lastRandom: string;
|
|
158
|
+
return function ulid(seedTime?: number): string {
|
|
159
|
+
seedTime ||= now();
|
|
160
|
+
if (seedTime <= lastTime) {
|
|
161
|
+
const incrementedRandom = (lastRandom = incrementBase32(lastRandom));
|
|
162
|
+
return encodeTime(lastTime, TIME_LEN) + incrementedRandom;
|
|
163
|
+
}
|
|
164
|
+
lastTime = seedTime;
|
|
165
|
+
const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, random));
|
|
166
|
+
return encodeTime(seedTime, TIME_LEN) + newRandom;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* default monotonic ulid generator
|
|
172
|
+
*/
|
|
173
|
+
export let ulid: ULID = (...args) => {
|
|
174
|
+
if (_real) {
|
|
175
|
+
return _real(...args);
|
|
176
|
+
}
|
|
177
|
+
// delay initialize crypto
|
|
178
|
+
_real = createULID();
|
|
179
|
+
ulid = _real;
|
|
180
|
+
return _real(...args);
|
|
181
|
+
};
|
|
182
|
+
let _real: ULID;
|
|
@@ -2,7 +2,7 @@ import test from 'ava';
|
|
|
2
2
|
import { createTranslate } from './createTranslate';
|
|
3
3
|
|
|
4
4
|
test('exports', (t) => {
|
|
5
|
-
|
|
5
|
+
const out = createTranslate();
|
|
6
6
|
t.is(typeof out, 'object', 'returns an object');
|
|
7
7
|
t.is(typeof out.t, 'function', '~> has "t" function');
|
|
8
8
|
t.is(typeof out.dict, 'function', '~> has "set" function');
|
|
@@ -10,7 +10,7 @@ test('exports', (t) => {
|
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
test('usage', (t) => {
|
|
13
|
-
|
|
13
|
+
const ctx = createTranslate({
|
|
14
14
|
en: { hello: 'Hello, {{name}}!' },
|
|
15
15
|
es: { hello: 'Hola {{name}}!' },
|
|
16
16
|
pt: { foo: 'foo {{name}}~!' },
|
|
@@ -18,23 +18,23 @@ test('usage', (t) => {
|
|
|
18
18
|
|
|
19
19
|
t.deepEqual(ctx.dict('en'), { hello: 'Hello, {{name}}!' });
|
|
20
20
|
|
|
21
|
-
t.
|
|
21
|
+
t.true(ctx.dict('foobar') === undefined);
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
const foo = ctx.t('hello');
|
|
24
24
|
t.is(foo, '', '~> "" w/o locale');
|
|
25
25
|
|
|
26
26
|
t.is(ctx.locale('en'), 'en', '>>> ctx.locale()');
|
|
27
27
|
|
|
28
28
|
t.is(ctx.locale(), 'en');
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
const bar = ctx.t('hello');
|
|
31
31
|
t.not(bar, '', '(en) found "hello" key');
|
|
32
32
|
t.is(bar, 'Hello, !', '~> interpolations empty if missing param');
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const baz = ctx.t('hello', { name: 'world' });
|
|
35
35
|
t.is(baz, 'Hello, world!', '~> interpolations successful');
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const bat = ctx.t('hello', { name: 'world' }, 'es');
|
|
38
38
|
t.not(bat, '', '(es) found "hello" key');
|
|
39
39
|
t.is(bat, 'Hola world!', '~> success');
|
|
40
40
|
|
|
@@ -42,11 +42,11 @@ test('usage', (t) => {
|
|
|
42
42
|
|
|
43
43
|
t.is(ctx.dict('pt', { hello: 'Oí {{name}}!' }), undefined, '>>> ctx.dict()');
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
const quz = ctx.t('hello', { name: 'world' }, 'pt');
|
|
46
46
|
t.not(quz, '', '(pt) found "hello" key');
|
|
47
47
|
t.is(quz, 'Oí world!', '~> success');
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
const qut = ctx.t('foo', { name: 'bar' }, 'pt');
|
|
50
50
|
t.not(qut, '', '(pt) found "foo" key');
|
|
51
51
|
t.is(qut, 'foo bar~!', '~> success');
|
|
52
52
|
|
|
@@ -58,7 +58,7 @@ test('usage', (t) => {
|
|
|
58
58
|
t.is(ctx.locale(null as any), 'es');
|
|
59
59
|
t.is(ctx.locale(0 as any), 'es');
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
const qux = ctx.t('hello', { name: 'default' });
|
|
62
62
|
t.not(qux, '', '(es) found "hello" key');
|
|
63
63
|
t.is(qux, 'Hola default!', '~> success');
|
|
64
64
|
|
|
@@ -66,13 +66,13 @@ test('usage', (t) => {
|
|
|
66
66
|
|
|
67
67
|
t.is(ctx.dict('de', { hello: 'Hallo {{name}}!' }), undefined, '>>> ctx.dict(de)');
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
const qar = ctx.t('hello', { name: 'world' }, 'de');
|
|
70
70
|
t.not(qar, '', '(de) found "hello" key');
|
|
71
71
|
t.is(qar, 'Hallo world!', '~> success');
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test('functional', (t) => {
|
|
75
|
-
|
|
75
|
+
const ctx = createTranslate({
|
|
76
76
|
en: {
|
|
77
77
|
hello(value: any) {
|
|
78
78
|
return `hello ${value || 'stranger'}~!`;
|
|
@@ -82,18 +82,18 @@ test('functional', (t) => {
|
|
|
82
82
|
|
|
83
83
|
ctx.locale('en');
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
const foo = ctx.t('hello');
|
|
86
86
|
t.is(foo, 'hello stranger~!', '~> called function w/o param');
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
const bar = ctx.t('hello', 'world' as any);
|
|
89
89
|
t.is(bar, 'hello world~!', '~> called function w/ param (string)');
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
const baz = ctx.t('hello', [1, 2, 3]);
|
|
92
92
|
t.is(baz, 'hello 1,2,3~!', '~> called function w/ param (array)');
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
test('nested', (t) => {
|
|
96
|
-
|
|
96
|
+
const ctx = createTranslate({
|
|
97
97
|
en: {
|
|
98
98
|
fruits: {
|
|
99
99
|
apple: 'apple',
|
|
@@ -126,7 +126,7 @@ test('nested', (t) => {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
test('arrays', (t) => {
|
|
129
|
-
|
|
129
|
+
const ctx = createTranslate({
|
|
130
130
|
en: {
|
|
131
131
|
foo: '{{0}} + {{1}} = {{2}}',
|
|
132
132
|
bar: [
|
|
@@ -145,7 +145,7 @@ test('arrays', (t) => {
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
test('invalid value', (t) => {
|
|
148
|
-
|
|
148
|
+
const ctx = createTranslate({
|
|
149
149
|
en: {
|
|
150
150
|
foo: ['bar'],
|
|
151
151
|
},
|
|
@@ -153,3 +153,18 @@ test('invalid value', (t) => {
|
|
|
153
153
|
|
|
154
154
|
t.deepEqual(ctx.t('foo', null as any, 'en'), ['bar']);
|
|
155
155
|
});
|
|
156
|
+
|
|
157
|
+
test('fallback', (t) => {
|
|
158
|
+
const ctx = createTranslate({
|
|
159
|
+
en: {
|
|
160
|
+
a: 'a',
|
|
161
|
+
},
|
|
162
|
+
'en-US': {
|
|
163
|
+
a: 'a-US',
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// t.deepEqual(ctx.t('a', undefined, 'en'), 'a');
|
|
168
|
+
// t.deepEqual(ctx.t('a', undefined, 'en-US'), 'a-US');
|
|
169
|
+
t.deepEqual(ctx.t('a', undefined, 'en-UK'), 'a');
|
|
170
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { get } from '../objects/get';
|
|
2
|
-
import { ObjectPathLike } from '../objects/parseObjectPath';
|
|
2
|
+
import type { ObjectPathLike } from '../objects/parseObjectPath';
|
|
3
3
|
import { renderTemplate } from '../strings/renderTemplate';
|
|
4
4
|
|
|
5
5
|
export interface Translate<T extends object> {
|
|
@@ -19,7 +19,17 @@ export interface Translate<T extends object> {
|
|
|
19
19
|
export function createTranslate<T extends object>(obj?: Record<string, T>): Translate<T> {
|
|
20
20
|
let locale = '';
|
|
21
21
|
const tree = obj || {};
|
|
22
|
-
|
|
22
|
+
// en-US -> en-US,en
|
|
23
|
+
const keyOfDict = (s: string | string[]) => {
|
|
24
|
+
if (Array.isArray(s)) {
|
|
25
|
+
return s;
|
|
26
|
+
}
|
|
27
|
+
const sp = s.split(/[_-]/);
|
|
28
|
+
if (sp.length > 1) {
|
|
29
|
+
return [s, sp[0]];
|
|
30
|
+
}
|
|
31
|
+
return [s];
|
|
32
|
+
};
|
|
23
33
|
return {
|
|
24
34
|
locale(lang) {
|
|
25
35
|
return (locale = lang || locale);
|
|
@@ -34,7 +44,13 @@ export function createTranslate<T extends object>(obj?: Record<string, T>): Tran
|
|
|
34
44
|
}) as Translate<T>['dict'],
|
|
35
45
|
|
|
36
46
|
t(key, params, lang) {
|
|
37
|
-
|
|
47
|
+
let val: any;
|
|
48
|
+
for (const k of keyOfDict(lang || locale)) {
|
|
49
|
+
val = get(tree[k], key, '');
|
|
50
|
+
if (val) {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
38
54
|
if (process.env.NODE_ENV === 'development') {
|
|
39
55
|
if (val == null) {
|
|
40
56
|
return console.error(
|
|
@@ -45,7 +61,7 @@ export function createTranslate<T extends object>(obj?: Record<string, T>): Tran
|
|
|
45
61
|
}
|
|
46
62
|
}
|
|
47
63
|
if (typeof val === 'function') return val(params);
|
|
48
|
-
if (typeof val === 'string') return renderTemplate(val, params
|
|
64
|
+
if (typeof val === 'string') return renderTemplate(val, params, 'common');
|
|
49
65
|
return val;
|
|
50
66
|
},
|
|
51
67
|
};
|
package/src/index.ts
CHANGED
|
@@ -22,15 +22,18 @@ export { timeout, TimeoutError } from './asyncs/timeout';
|
|
|
22
22
|
export { isPromise } from './asyncs/isPromise';
|
|
23
23
|
// export * from './async/promiseOfCallback';
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// langs
|
|
26
|
+
export { shallowEqual } from './langs/shallowEqual';
|
|
27
|
+
export { deepEqual } from './langs/deepEqual';
|
|
28
|
+
export { classOf } from './langs/classOf';
|
|
29
|
+
export { shallowClone } from './langs/shallowClone';
|
|
30
|
+
|
|
31
|
+
// assertions
|
|
26
32
|
export { isClass } from './validations/isClass';
|
|
27
33
|
export { isDefined } from './validations/isDefined';
|
|
28
34
|
export { isEmptyObject } from './validations/isEmptyObject';
|
|
29
|
-
export { shallowEqual } from './langs/shallowEqual';
|
|
30
|
-
export { deepEqual } from './langs/deepEqual';
|
|
31
35
|
export { isUUID } from './validations/isUUID';
|
|
32
|
-
|
|
33
|
-
export { classOf } from './langs/classOf';
|
|
36
|
+
export { isPlainObject } from './validations/isPlainObject';
|
|
34
37
|
|
|
35
38
|
// modules
|
|
36
39
|
export { parseModuleId, type ParsedModuleId } from './modules/parseModuleId';
|
|
@@ -53,6 +56,9 @@ export { createTranslate } from './i18n/createTranslate';
|
|
|
53
56
|
// io
|
|
54
57
|
export { isBuffer } from './io/isBuffer';
|
|
55
58
|
export { isTransferable } from './io/isTransferable';
|
|
59
|
+
export { ArrayBuffers } from './io/ArrayBuffers';
|
|
60
|
+
export { Buffer } from './io/Buffer';
|
|
61
|
+
export type { AbstractEncoding } from './io/AbstractEncoding';
|
|
56
62
|
|
|
57
63
|
// browser
|
|
58
64
|
export { copy } from './browsers/copy';
|
|
@@ -66,9 +72,10 @@ export { structuredClone } from './isomorphics/structuredClone';
|
|
|
66
72
|
|
|
67
73
|
// crypto
|
|
68
74
|
export { randomUUID } from './crypto/randomUUID';
|
|
75
|
+
export { getRandomValues } from './crypto/getRandomValues';
|
|
69
76
|
export { sha1, sha256, sha384, sha512 } from './crypto/hashing';
|
|
70
|
-
export { hex } from './crypto/
|
|
77
|
+
export { hex } from './crypto/base';
|
|
78
|
+
export { isULID, createULID, ulid, parseULID } from './crypto/ulid';
|
|
71
79
|
|
|
72
80
|
// misc
|
|
73
81
|
export { createRandom } from './maths/random';
|
|
74
|
-
export { urljoin } from './shim/urljoin';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AbstractEncoding contract
|
|
3
|
+
*
|
|
4
|
+
* @see https://github.com/mafintosh/abstract-encoding
|
|
5
|
+
*/
|
|
6
|
+
export interface AbstractEncoding<T> {
|
|
7
|
+
/**
|
|
8
|
+
* encode a value to a buffer
|
|
9
|
+
*/
|
|
10
|
+
encode(data: T, buffer?: ArrayBuffer, offset?: number): BufferSource;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* decode data from buffer
|
|
14
|
+
*/
|
|
15
|
+
decode(buffer: BufferSource, start?: number, end?: number): T;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* byteLength of data
|
|
19
|
+
*/
|
|
20
|
+
byteLength?: (data: T) => number;
|
|
21
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { ArrayBuffers } from './ArrayBuffers';
|
|
3
|
+
|
|
4
|
+
test.before(() => {
|
|
5
|
+
ArrayBuffers.setAllowedNativeBuffer(false);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
test('base64: ignore whitespace', function (t) {
|
|
9
|
+
const text = '\n YW9ldQ== ';
|
|
10
|
+
const buf = ArrayBuffers.from(text, 'base64');
|
|
11
|
+
t.is(ArrayBuffers.toString(buf), 'aoeu');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('base64: strings without padding', function (t) {
|
|
15
|
+
t.is(ArrayBuffers.toString(ArrayBuffers.from('YW9ldQ', 'base64')), 'aoeu');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('base64: newline in utf8 -- should not be an issue', function (t) {
|
|
19
|
+
t.is(
|
|
20
|
+
ArrayBuffers.toString(
|
|
21
|
+
ArrayBuffers.from('LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK', 'base64'),
|
|
22
|
+
'utf8',
|
|
23
|
+
),
|
|
24
|
+
'---\ntitle: Three dashes marks the spot\ntags:\n',
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('base64: newline in base64 -- should get stripped', function (t) {
|
|
29
|
+
t.is(
|
|
30
|
+
ArrayBuffers.toString(
|
|
31
|
+
ArrayBuffers.from(
|
|
32
|
+
'LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK\nICAtIHlhbWwKICAtIGZyb250LW1hdHRlcgogIC0gZGFzaGVzCmV4cGFuZWQt',
|
|
33
|
+
'base64',
|
|
34
|
+
),
|
|
35
|
+
'utf8',
|
|
36
|
+
),
|
|
37
|
+
'---\ntitle: Three dashes marks the spot\ntags:\n - yaml\n - front-matter\n - dashes\nexpaned-',
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('base64: tab characters in base64 - should get stripped', function (t) {
|
|
42
|
+
t.is(
|
|
43
|
+
ArrayBuffers.toString(
|
|
44
|
+
ArrayBuffers.from(
|
|
45
|
+
'LS0tCnRpdGxlOiBUaHJlZSBkYXNoZXMgbWFya3MgdGhlIHNwb3QKdGFnczoK\t\t\t\tICAtIHlhbWwKICAtIGZyb250LW1hdHRlcgogIC0gZGFzaGVzCmV4cGFuZWQt',
|
|
46
|
+
'base64',
|
|
47
|
+
),
|
|
48
|
+
'utf8',
|
|
49
|
+
),
|
|
50
|
+
'---\ntitle: Three dashes marks the spot\ntags:\n - yaml\n - front-matter\n - dashes\nexpaned-',
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('base64: invalid non-alphanumeric characters -- should be stripped', function (t) {
|
|
55
|
+
t.is(ArrayBuffers.toString(ArrayBuffers.from('!"#$%&\'()*,.:;<=>?@[\\]^`{|}~', 'base64'), 'utf8'), '');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('base64: high byte', function (t) {
|
|
59
|
+
const highByte = ArrayBuffers.from([128]);
|
|
60
|
+
t.deepEqual(ArrayBuffers.alloc(1, ArrayBuffers.toString(highByte, 'base64'), 'base64'), highByte);
|
|
61
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { ArrayBuffers } from './ArrayBuffers';
|
|
3
|
+
|
|
4
|
+
test('concat', (t) => {
|
|
5
|
+
const check = (Type: any = Uint8Array, va = [1, 2, 3], vb = [4, 5, 6]) => {
|
|
6
|
+
const a = new Type(va);
|
|
7
|
+
const b = new Type(vb);
|
|
8
|
+
t.deepEqual(ArrayBuffers.concat([a, b]), new Type([...va, ...vb]).buffer, `should concat ${Type}`);
|
|
9
|
+
};
|
|
10
|
+
check(Uint8Array);
|
|
11
|
+
check(Uint16Array, [0xff, 0xffff]);
|
|
12
|
+
check(Uint32Array, [0xfffffff]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('asView for Buffer', (t) => {
|
|
16
|
+
const buf = Buffer.from([1, 2, 3, 4, 5]);
|
|
17
|
+
t.is(ArrayBuffers.asView(Buffer, buf), buf);
|
|
18
|
+
const uint8 = ArrayBuffers.asView(Uint8Array, buf);
|
|
19
|
+
t.is(ArrayBuffers.asView(Buffer, uint8).buffer, buf.buffer);
|
|
20
|
+
|
|
21
|
+
t.deepEqual(uint8, buf);
|
|
22
|
+
t.deepEqual(ArrayBuffers.asView(Buffer, uint8), buf);
|
|
23
|
+
});
|