@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.
Files changed (160) hide show
  1. package/README.md +3 -1
  2. package/dist/cjs/index.cjs +2 -0
  3. package/dist/cjs/index.cjs.map +1 -0
  4. package/dist/cjs/server.cjs +2 -0
  5. package/dist/cjs/server.cjs.map +1 -0
  6. package/dist/esm/index.js +1 -1
  7. package/dist/esm/index.js.map +1 -1
  8. package/dist/esm/server.js +1 -1
  9. package/dist/esm/server.js.map +1 -1
  10. package/dist/system/index.js +1 -1
  11. package/dist/system/index.js.map +1 -1
  12. package/dist/system/server.js +1 -1
  13. package/dist/system/server.js.map +1 -1
  14. package/lib/arrays/MaybeArray.js.map +1 -1
  15. package/lib/asyncs/createLazyPromise.js +12 -3
  16. package/lib/asyncs/createLazyPromise.js.map +1 -1
  17. package/lib/asyncs/timeout.js +1 -1
  18. package/lib/asyncs/timeout.js.map +1 -1
  19. package/lib/browsers/copy.js +2 -3
  20. package/lib/browsers/copy.js.map +1 -1
  21. package/lib/browsers/loaders.js +6 -11
  22. package/lib/browsers/loaders.js.map +1 -1
  23. package/lib/crypto/{hex.js → base.js} +1 -1
  24. package/lib/crypto/base.js.map +1 -0
  25. package/lib/crypto/getRandomValues.js +37 -0
  26. package/lib/crypto/getRandomValues.js.map +1 -0
  27. package/lib/crypto/hashing.js +4 -4
  28. package/lib/crypto/hashing.js.map +1 -1
  29. package/lib/crypto/randomUUID.js.map +1 -1
  30. package/lib/crypto/ulid.js +139 -0
  31. package/lib/crypto/ulid.js.map +1 -0
  32. package/lib/i18n/createTranslate.js +17 -1
  33. package/lib/i18n/createTranslate.js.map +1 -1
  34. package/lib/index.js +10 -5
  35. package/lib/index.js.map +1 -1
  36. package/lib/io/ArrayBuffers.js +170 -0
  37. package/lib/io/ArrayBuffers.js.map +1 -0
  38. package/lib/io/Buffer.js +21 -0
  39. package/lib/io/Buffer.js.map +1 -0
  40. package/lib/io/isBuffer.js +1 -1
  41. package/lib/io/isBuffer.js.map +1 -1
  42. package/lib/io/isTransferable.js.map +1 -1
  43. package/lib/isomorphics/structuredClone.js.map +1 -1
  44. package/lib/langs/deepEqual.js.map +1 -1
  45. package/lib/langs/shallowClone.js +15 -0
  46. package/lib/langs/shallowClone.js.map +1 -0
  47. package/lib/langs/shallowEqual.js.map +1 -1
  48. package/lib/logging/createChildLogger.js.map +1 -1
  49. package/lib/logging/createNoopLogger.js.map +1 -1
  50. package/lib/logging/createWriteLogger.js.map +1 -1
  51. package/lib/modules/isModule.js.map +1 -1
  52. package/lib/modules/parseModuleId.js +7 -5
  53. package/lib/modules/parseModuleId.js.map +1 -1
  54. package/lib/objects/get.js.map +1 -1
  55. package/lib/objects/parseObjectPath.js.map +1 -1
  56. package/lib/objects/set.js.map +1 -1
  57. package/lib/server/polyfillBrowser.js.map +1 -1
  58. package/lib/server/polyfillFetch.js.map +1 -1
  59. package/lib/server/polyfillJsDom.js +1 -1
  60. package/lib/server/polyfillJsDom.js.map +1 -1
  61. package/lib/strings/formatBytes.js +1 -1
  62. package/lib/strings/formatBytes.js.map +1 -1
  63. package/lib/strings/renderTemplate.js +1 -1
  64. package/lib/strings/renderTemplate.js.map +1 -1
  65. package/lib/validations/isEmptyObject.js +3 -4
  66. package/lib/validations/isEmptyObject.js.map +1 -1
  67. package/lib/validations/isPlainObject.js +11 -0
  68. package/lib/validations/isPlainObject.js.map +1 -0
  69. package/package.json +14 -6
  70. package/src/arrays/MaybeArray.ts +1 -1
  71. package/src/asyncs/createLazyPromise.test.ts +19 -4
  72. package/src/asyncs/createLazyPromise.ts +19 -4
  73. package/src/asyncs/generatorOfStream.ts +1 -0
  74. package/src/asyncs/timeout.ts +1 -1
  75. package/src/browsers/copy.ts +6 -5
  76. package/src/browsers/loaders.ts +6 -11
  77. package/src/crypto/{hex.ts → base.ts} +3 -0
  78. package/src/crypto/getRandomValues.ts +40 -0
  79. package/src/crypto/hashing.test.ts +1 -1
  80. package/src/crypto/hashing.ts +4 -4
  81. package/src/crypto/randomUUID.ts +3 -0
  82. package/src/crypto/ulid.test.ts +22 -0
  83. package/src/crypto/ulid.ts +182 -0
  84. package/src/i18n/createTranslate.test.ts +33 -18
  85. package/src/i18n/createTranslate.ts +20 -4
  86. package/src/index.ts +14 -7
  87. package/src/io/AbstractEncoding.ts +21 -0
  88. package/src/io/ArrayBuffer.test-d.ts +4 -0
  89. package/src/io/ArrayBuffers.base64.test.ts +61 -0
  90. package/src/io/ArrayBuffers.test.ts +23 -0
  91. package/src/io/ArrayBuffers.ts +288 -0
  92. package/src/io/Buffer.test.ts +23 -0
  93. package/src/io/Buffer.ts +30 -0
  94. package/src/io/isBuffer.test.ts +2 -0
  95. package/src/io/isBuffer.ts +3 -8
  96. package/src/io/isTransferable.ts +1 -1
  97. package/src/isomorphics/structuredClone.test.ts +1 -1
  98. package/src/isomorphics/structuredClone.ts +9 -6
  99. package/src/langs/deepEqual.ts +1 -0
  100. package/src/langs/shallowClone.ts +13 -0
  101. package/src/langs/shallowEqual.ts +1 -1
  102. package/src/logging/Logger.ts +6 -0
  103. package/src/logging/createChildLogger.ts +1 -1
  104. package/src/logging/createNoopLogger.ts +2 -2
  105. package/src/logging/createWriteLogger.ts +1 -1
  106. package/src/logging/logger.test.ts +3 -3
  107. package/src/modules/isModule.ts +3 -0
  108. package/src/modules/parseModuleId.test.ts +7 -2
  109. package/src/modules/parseModuleId.ts +15 -9
  110. package/src/objects/get.test-d.ts +51 -0
  111. package/src/objects/get.test.ts +2 -55
  112. package/src/objects/get.ts +2 -1
  113. package/src/objects/parseObjectPath.ts +4 -3
  114. package/src/objects/set.test.ts +32 -31
  115. package/src/objects/set.ts +4 -3
  116. package/src/server/polyfillBrowser.ts +8 -0
  117. package/src/server/polyfillFetch.ts +0 -1
  118. package/src/server/polyfillJsDom.ts +3 -2
  119. package/src/strings/formatBytes.ts +1 -1
  120. package/src/strings/renderTemplate.test.ts +1 -0
  121. package/src/strings/renderTemplate.ts +8 -5
  122. package/src/typedoc.ts +2 -0
  123. package/src/validations/isEmptyObject.ts +3 -4
  124. package/src/validations/isFunction.ts +3 -0
  125. package/src/validations/isPlainObject.ts +10 -0
  126. package/tsconfig.json +2 -1
  127. package/dist/cjs/_commonjsHelpers-dfec268f.js +0 -2
  128. package/dist/cjs/_commonjsHelpers-dfec268f.js.map +0 -1
  129. package/dist/cjs/api-7db97ae3.js +0 -1085
  130. package/dist/cjs/api-7db97ae3.js.map +0 -1
  131. package/dist/cjs/index-a6d1d653.js +0 -14
  132. package/dist/cjs/index-a6d1d653.js.map +0 -1
  133. package/dist/cjs/index.js +0 -2
  134. package/dist/cjs/index.js.map +0 -1
  135. package/dist/cjs/multipart-parser-141ed517.js +0 -3
  136. package/dist/cjs/multipart-parser-141ed517.js.map +0 -1
  137. package/dist/cjs/server.js +0 -2
  138. package/dist/cjs/server.js.map +0 -1
  139. package/dist/esm/_commonjsHelpers-28e086c5.js +0 -2
  140. package/dist/esm/_commonjsHelpers-28e086c5.js.map +0 -1
  141. package/dist/esm/api-3f555472.js +0 -1085
  142. package/dist/esm/api-3f555472.js.map +0 -1
  143. package/dist/esm/index-b50fef91.js +0 -14
  144. package/dist/esm/index-b50fef91.js.map +0 -1
  145. package/dist/esm/multipart-parser-5c1d6ee9.js +0 -3
  146. package/dist/esm/multipart-parser-5c1d6ee9.js.map +0 -1
  147. package/dist/system/_commonjsHelpers-07f370a7.js +0 -2
  148. package/dist/system/_commonjsHelpers-07f370a7.js.map +0 -1
  149. package/dist/system/api-dc50ebac.js +0 -1085
  150. package/dist/system/api-dc50ebac.js.map +0 -1
  151. package/dist/system/index-8f1807ba.js +0 -14
  152. package/dist/system/index-8f1807ba.js.map +0 -1
  153. package/dist/system/multipart-parser-53518ee9.js +0 -3
  154. package/dist/system/multipart-parser-53518ee9.js.map +0 -1
  155. package/lib/crypto/hex.js.map +0 -1
  156. package/lib/shim/urljoin.js +0 -51
  157. package/lib/shim/urljoin.js.map +0 -1
  158. package/src/shim/urljoin.test.ts +0 -6
  159. package/src/shim/urljoin.ts +0 -75
  160. package/src/types.d.ts +0 -7
@@ -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 new Promise(async (resolve, reject) => {
29
+ return Promise.resolve().then(async () => {
30
30
  const all = [];
31
- try {
32
- for (let s of src) {
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
- resolve(all);
34
+ return all;
40
35
  });
41
36
  }
42
37
  // todo quote ?
43
- let $ele = document.querySelector(`script[src="${src}"]`) as HTMLScriptElement;
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
- let $ele = document.querySelector(`link[href="${href}"]`) as HTMLLinkElement;
50
+ const $ele = document.querySelector(`link[href="${href}"]`) as HTMLLinkElement;
56
51
  if ($ele) {
57
52
  return Promise.resolve($ele);
58
53
  }
@@ -1,3 +1,6 @@
1
+ /**
2
+ * hex string
3
+ */
1
4
  export function hex(s: Uint8Array | ArrayBuffer) {
2
5
  return Array.from(new Uint8Array(s))
3
6
  .map((v) => v.toString(16).padStart(2, '0'))
@@ -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 './hex';
5
+ import { hex } from './base';
6
6
  import { _randomUUID } from './randomUUID';
7
7
 
8
8
  test.before(async () => {
@@ -1,16 +1,16 @@
1
- export async function sha1(s: string | BufferSource) {
1
+ export function sha1(s: string | BufferSource) {
2
2
  return digestOf('SHA-1', s);
3
3
  }
4
4
 
5
- export async function sha256(s: string | BufferSource) {
5
+ export function sha256(s: string | BufferSource) {
6
6
  return digestOf('SHA-256', s);
7
7
  }
8
8
 
9
- export async function sha384(s: string | BufferSource) {
9
+ export function sha384(s: string | BufferSource) {
10
10
  return digestOf('SHA-384', s);
11
11
  }
12
12
 
13
- export async function sha512(s: string | BufferSource) {
13
+ export function sha512(s: string | BufferSource) {
14
14
  return digestOf('SHA-512', s);
15
15
  }
16
16
 
@@ -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
- let out = createTranslate();
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
- let ctx = createTranslate({
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.is(ctx.dict('foobar'), undefined);
21
+ t.true(ctx.dict('foobar') === undefined);
22
22
 
23
- let foo = ctx.t('hello');
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
- let bar = ctx.t('hello');
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
- let baz = ctx.t('hello', { name: 'world' });
34
+ const baz = ctx.t('hello', { name: 'world' });
35
35
  t.is(baz, 'Hello, world!', '~> interpolations successful');
36
36
 
37
- let bat = ctx.t('hello', { name: 'world' }, 'es');
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
- let quz = ctx.t('hello', { name: 'world' }, 'pt');
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
- let qut = ctx.t('foo', { name: 'bar' }, 'pt');
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
- let qux = ctx.t('hello', { name: 'default' });
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
- let qar = ctx.t('hello', { name: 'world' }, 'de');
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
- let ctx = createTranslate({
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
- let foo = ctx.t('hello');
85
+ const foo = ctx.t('hello');
86
86
  t.is(foo, 'hello stranger~!', '~> called function w/o param');
87
87
 
88
- let bar = ctx.t('hello', 'world' as any);
88
+ const bar = ctx.t('hello', 'world' as any);
89
89
  t.is(bar, 'hello world~!', '~> called function w/ param (string)');
90
90
 
91
- let baz = ctx.t('hello', [1, 2, 3]);
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
- let ctx = createTranslate({
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
- let ctx = createTranslate({
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
- let ctx = createTranslate({
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
- const val = get(tree[lang || locale], key, '') as any;
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!, 'common');
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
- // validations
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/hex';
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,4 @@
1
+ import { expectType } from 'tsd';
2
+ import { ArrayBuffers } from './ArrayBuffers';
3
+
4
+ expectType<Buffer>(ArrayBuffers.asView(Buffer, new Uint8Array()));
@@ -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
+ });