@wener/utils 1.1.36 → 1.1.38

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 (111) hide show
  1. package/lib/asyncs/createLazyPromise.js +1 -3
  2. package/lib/asyncs/createLazyPromise.js.map +1 -1
  3. package/lib/browsers/download.js +20 -11
  4. package/lib/browsers/download.js.map +1 -1
  5. package/lib/cn/division/DivisionCode.js +204 -0
  6. package/lib/cn/division/DivisionCode.js.map +1 -0
  7. package/lib/cn/id/Mod11Checksum.js +46 -0
  8. package/lib/cn/id/Mod11Checksum.js.map +1 -0
  9. package/lib/cn/id/ResidentIdNumber.js +89 -0
  10. package/lib/cn/id/ResidentIdNumber.js.map +1 -0
  11. package/lib/cn/id/types.js +8 -0
  12. package/lib/cn/id/types.js.map +1 -0
  13. package/lib/cn/index.js +9 -1
  14. package/lib/cn/index.js.map +1 -1
  15. package/lib/cn/uscc/Mod31Checksum.js +52 -0
  16. package/lib/cn/uscc/Mod31Checksum.js.map +1 -0
  17. package/lib/cn/uscc/USCC.js +97 -0
  18. package/lib/cn/uscc/USCC.js.map +1 -0
  19. package/lib/cn/uscc/isUSCC.js +8 -0
  20. package/lib/cn/uscc/isUSCC.js.map +1 -0
  21. package/lib/crypto/getRandomValues.js +1 -1
  22. package/lib/crypto/getRandomValues.js.map +1 -1
  23. package/lib/crypto/randomUUID.js +1 -1
  24. package/lib/crypto/randomUUID.js.map +1 -1
  25. package/lib/crypto/randomUUIDv7.js +3 -0
  26. package/lib/crypto/randomUUIDv7.js.map +1 -0
  27. package/lib/errors/Errors.js +1 -1
  28. package/lib/errors/Errors.js.map +1 -1
  29. package/lib/fetch/HttpStatus.js.map +1 -0
  30. package/lib/fetch/createFetchWith.js +1 -1
  31. package/lib/fetch/createFetchWith.js.map +1 -1
  32. package/lib/fetch/createFetchWithRetry.js +1 -1
  33. package/lib/fetch/createFetchWithRetry.js.map +1 -1
  34. package/lib/index.js +7 -5
  35. package/lib/index.js.map +1 -1
  36. package/lib/io/ArrayBuffers.js +58 -4
  37. package/lib/io/ArrayBuffers.js.map +1 -1
  38. package/lib/io/isTransferable.js +1 -1
  39. package/lib/io/isTransferable.js.map +1 -1
  40. package/lib/io/parseDataUri.js +57 -0
  41. package/lib/io/parseDataUri.js.map +1 -0
  42. package/lib/objects/computeIfAbsent.js +14 -0
  43. package/lib/objects/computeIfAbsent.js.map +1 -0
  44. package/lib/runtime/AsyncCloser.js.map +1 -0
  45. package/lib/runtime/Closer.js.map +1 -0
  46. package/lib/runtime/getGlobalThis.js.map +1 -0
  47. package/lib/runtime/structuredClone.js.map +1 -0
  48. package/lib/server/fetch/createFetchWithProxyByNodeFetch.js +1 -1
  49. package/lib/server/fetch/createFetchWithProxyByNodeFetch.js.map +1 -1
  50. package/lib/server/fetch/createFetchWithProxyByUndici.js +1 -1
  51. package/lib/server/fetch/createFetchWithProxyByUndici.js.map +1 -1
  52. package/lib/server/getPackageDir.js +5 -4
  53. package/lib/server/getPackageDir.js.map +1 -1
  54. package/lib/server/polyfill/polyfillCrypto.js +1 -1
  55. package/lib/server/polyfill/polyfillCrypto.js.map +1 -1
  56. package/lib/server/polyfill/polyfillJsDom.js +1 -1
  57. package/lib/server/polyfill/polyfillJsDom.js.map +1 -1
  58. package/lib/server/polyfill/polyfillWebSocket.js +1 -1
  59. package/lib/server/polyfill/polyfillWebSocket.js.map +1 -1
  60. package/package.json +18 -15
  61. package/src/asyncs/createLazyPromise.ts +0 -2
  62. package/src/browsers/download.ts +20 -13
  63. package/src/cn/division/DivisionCode.ts +109 -0
  64. package/src/cn/id/Mod11Checksum.ts +29 -0
  65. package/src/cn/id/ResidentIdNumber.ts +102 -0
  66. package/src/cn/id/id.test.ts +20 -0
  67. package/src/cn/id/types.ts +26 -0
  68. package/src/cn/index.ts +13 -0
  69. package/src/cn/uscc/Mod31Checksum.ts +42 -0
  70. package/src/cn/uscc/USCC.ts +98 -0
  71. package/src/cn/uscc/isUSCC.ts +8 -0
  72. package/src/cn/uscc/uscc.test.ts +16 -0
  73. package/src/crypto/getRandomValues.ts +1 -1
  74. package/src/crypto/randomUUID.ts +1 -1
  75. package/src/crypto/randomUUIDv7.ts +1 -0
  76. package/src/errors/Errors.ts +1 -1
  77. package/src/fetch/createFetchWith.ts +1 -1
  78. package/src/fetch/createFetchWithRetry.ts +1 -1
  79. package/src/index.ts +7 -5
  80. package/src/io/ArrayBuffers.base64.test.ts +24 -0
  81. package/src/io/ArrayBuffers.ts +106 -4
  82. package/src/io/isTransferable.ts +1 -1
  83. package/src/io/parseDataUri.test.ts +34 -0
  84. package/src/io/parseDataUri.ts +66 -0
  85. package/src/objects/computeIfAbsent.ts +15 -0
  86. package/src/schema/typebox/gen/README.md +2 -0
  87. package/src/server/fetch/createFetchWithProxyByNodeFetch.ts +1 -1
  88. package/src/server/fetch/createFetchWithProxyByUndici.ts +1 -1
  89. package/src/server/getPackageDir.ts +6 -4
  90. package/src/server/polyfill/polyfillCrypto.ts +1 -1
  91. package/src/server/polyfill/polyfillJsDom.ts +1 -1
  92. package/src/server/polyfill/polyfillWebSocket.ts +1 -1
  93. package/lib/http/HttpStatus.js.map +0 -1
  94. package/lib/io/Bytes.js +0 -51
  95. package/lib/io/Bytes.js.map +0 -1
  96. package/lib/isomorphics/getGlobalThis.js.map +0 -1
  97. package/lib/isomorphics/structuredClone.js.map +0 -1
  98. package/lib/langs/AsyncCloser.js.map +0 -1
  99. package/lib/langs/Closer.js.map +0 -1
  100. package/src/io/Bytes.ts +0 -64
  101. /package/lib/{http → fetch}/HttpStatus.js +0 -0
  102. /package/lib/{langs → runtime}/AsyncCloser.js +0 -0
  103. /package/lib/{langs → runtime}/Closer.js +0 -0
  104. /package/lib/{isomorphics → runtime}/getGlobalThis.js +0 -0
  105. /package/lib/{isomorphics → runtime}/structuredClone.js +0 -0
  106. /package/src/{http → fetch}/HttpStatus.ts +0 -0
  107. /package/src/{langs → runtime}/AsyncCloser.ts +0 -0
  108. /package/src/{langs → runtime}/Closer.ts +0 -0
  109. /package/src/{isomorphics → runtime}/getGlobalThis.ts +0 -0
  110. /package/src/{isomorphics → runtime}/structuredClone.test.ts +0 -0
  111. /package/src/{isomorphics → runtime}/structuredClone.ts +0 -0
@@ -0,0 +1,102 @@
1
+ import { Mod11Checksum } from './Mod11Checksum';
2
+
3
+ export class ResidentIdNumber {
4
+ checksum = Mod11Checksum.get();
5
+
6
+ // 9 https://zh.wikipedia.org/wiki/中华人民共和国外国人永久居留身份证
7
+ // 9 标识码
8
+ // 申领地代码 - 2 位行政区划
9
+ // 国籍国代码 - 3 位数字 https://zh.wikipedia.org/wiki/ISO_3166-1数字代码
10
+
11
+ regex =
12
+ /^(?<division>[1-9]\d{5})(?<year>18|19|20)\d{2}(?<month>0[1-9]|1[0-2])(?<day>0[1-9]|[12]\d|3[01])(?<sequence>\d{3})(?<checksum>[0-9Xx])$/;
13
+
14
+ private static instance: ResidentIdNumber;
15
+
16
+ static get() {
17
+ return (this.instance ||= new ResidentIdNumber());
18
+ }
19
+
20
+ verify(s: string) {
21
+ if (!s) return false;
22
+ return this.regex.test(s) && this.checksum.verify(s.toUpperCase());
23
+ }
24
+
25
+ parse(s: string): IdNumber | undefined {
26
+ if (!this.verify(s)) {
27
+ return;
28
+ }
29
+
30
+ let date = s.slice(6, 14);
31
+ let y = parseInt(date.slice(0, 4));
32
+ let m = parseInt(date.slice(4, 6));
33
+ let d = parseInt(date.slice(6));
34
+ return new IdNumber(
35
+ s.slice(0, 6),
36
+ new Date(`${y}-${m}-${d}`), // YYYYMMDD
37
+ // new Date(y, m - 1, d, 0, 0, 0, 0), // 有时区问题
38
+ parseInt(s.slice(14, 17)),
39
+ s.slice(17, 18).toUpperCase(),
40
+ );
41
+ }
42
+ }
43
+
44
+ class IdNumber {
45
+ constructor(
46
+ public division: string,
47
+ public date: Date,
48
+ public sequence: number,
49
+ public checksum: string,
50
+ ) {}
51
+
52
+ toString() {
53
+ return this.subject + this.checksum;
54
+ }
55
+
56
+ toJSON() {
57
+ return this.toString();
58
+ }
59
+
60
+ toObject() {
61
+ const { division, date, sequence, checksum } = this;
62
+ return {
63
+ division,
64
+ date,
65
+ sequence,
66
+ checksum,
67
+ };
68
+ }
69
+
70
+ get male() {
71
+ return this.sequence % 2 === 1;
72
+ }
73
+
74
+ get female() {
75
+ return this.sequence % 2 === 0;
76
+ }
77
+
78
+ get age() {
79
+ return new Date().getFullYear() - this.date.getFullYear();
80
+ }
81
+
82
+ get gender(): 'male' | 'female' {
83
+ return this.sequence % 2 === 1 ? 'male' : 'female';
84
+ }
85
+
86
+ get subject() {
87
+ return [this.division, formatDate(this.date, 'YYYYMMDD'), this.sequence.toString().padStart(3, '0')].join('');
88
+ }
89
+ }
90
+
91
+ function formatDate(date: Date, format = 'YYYYMMDD') {
92
+ switch (format) {
93
+ case 'YYYYMMDD': {
94
+ const year = date.getFullYear();
95
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
96
+ const day = date.getDate().toString().padStart(2, '0');
97
+ return `${year}${month}${day}`;
98
+ }
99
+ default:
100
+ throw new Error(`Invalid format`);
101
+ }
102
+ }
@@ -0,0 +1,20 @@
1
+ import { test, assert, expect } from 'vitest';
2
+ import { Mod11Checksum } from './Mod11Checksum';
3
+ import { ResidentIdNumber } from './ResidentIdNumber';
4
+
5
+ test('id', (t) => {
6
+ const ids = ['11010519491231002X', '11010219840406970X'];
7
+
8
+ let cs = Mod11Checksum.get();
9
+ for (const id of ids) {
10
+ assert.equal(cs.generate(id.slice(0, -1)), id.at(-1));
11
+ }
12
+
13
+ let parse = ResidentIdNumber.get().parse(ids[0]);
14
+ expect(parse?.toObject()).toEqual({
15
+ division: '110105',
16
+ date: new Date('1949-12-31'),
17
+ sequence: 2,
18
+ checksum: 'X',
19
+ });
20
+ });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 居民身份证
3
+ *
4
+ * @see https://en.wikipedia.org/wiki/Resident_Identity_Card
5
+ * @see https://en.wikipedia.org/wiki/Foreign_Permanent_Resident_ID_Card
6
+ */
7
+ export interface ResidentIdentityCard {
8
+ // 姓名
9
+ name: string;
10
+ // 性别
11
+ gender: string;
12
+ // 民族
13
+ ethnicity: string;
14
+ // 出生日期
15
+ birthDate: Date;
16
+ // 地址
17
+ domicile: string;
18
+ // 身份证号
19
+ idNumber: string;
20
+ // 签发机关
21
+ issuingAuthority: string;
22
+ // 有效期开始日期
23
+ validStartDate: Date;
24
+ // 有效期结束日期
25
+ validEndDate?: Date;
26
+ }
package/src/cn/index.ts CHANGED
@@ -1 +1,14 @@
1
+ /**
2
+ * China related utilities.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+
1
7
  export { parseChineseNumber } from './parseChineseNumber';
8
+
9
+ export { isUSCC } from './uscc/isUSCC';
10
+ export { USCC } from './uscc/USCC';
11
+
12
+ export { ResidentIdNumber } from './id/ResidentIdNumber';
13
+
14
+ export { DivisionCode } from './division/DivisionCode';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * GB/T 17710(采ISO 7064)的模31校验码
3
+ *
4
+ * Mod31-3
5
+ */
6
+ export class Mod31Checksum {
7
+ weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];
8
+ chars = '0123456789ABCDEFGHJKLMNPQRTUWXY';
9
+ numbers: Record<string, number> = this.chars.split('').reduce(
10
+ (acc, cur, i) => {
11
+ acc[cur] = i;
12
+ return acc;
13
+ },
14
+ {} as Record<string, number>,
15
+ );
16
+
17
+ private static instance: Mod31Checksum;
18
+
19
+ static get() {
20
+ return this.instance || (this.instance = new Mod31Checksum());
21
+ }
22
+
23
+ toChar(n: number) {
24
+ return this.chars[n];
25
+ }
26
+
27
+ toNumber(c: string) {
28
+ return this.numbers[c];
29
+ }
30
+
31
+ verify(s: string) {
32
+ return s.at(-1) === this.generate(s.slice(0, s.length - 1));
33
+ }
34
+
35
+ generate(s: string) {
36
+ let sum = 0;
37
+ for (let i = 0; i < s.length; i++) {
38
+ sum += this.numbers[s[i]] * this.weights[i];
39
+ }
40
+ return this.toChar(31 - (sum % 31));
41
+ }
42
+ }
@@ -0,0 +1,98 @@
1
+ import { Mod31Checksum } from './Mod31Checksum';
2
+
3
+ /**
4
+ * - GB 32100-2015 法人和其他组织统一社会信用代码编码规则
5
+ * - USCI -> UnifiedSocialCreditIdentifier
6
+ * - USCC -> Unified Social Credit Code - 官方叫法
7
+ * - Business License
8
+ *
9
+ * @see https://en.wikipedia.org/wiki/Unified_Social_Credit_Identifier
10
+ */
11
+ export class USCC {
12
+ registryCodeLabels: Record<string, Code> = {
13
+ 1: {
14
+ label: '机构编制',
15
+ children: {
16
+ 1: { label: '机关' },
17
+ 2: { label: '事业单位' },
18
+ 3: { label: '中央编办直接管理机构编制的群众团体' },
19
+ 9: { label: '其他' },
20
+ },
21
+ },
22
+ 5: {
23
+ label: '民政',
24
+ children: {
25
+ 1: { label: '社会团体' },
26
+ 2: { label: '民办非企业单位' },
27
+ 3: { label: '基金会' },
28
+ 9: { label: '其他' },
29
+ },
30
+ },
31
+ 9: {
32
+ label: '工商',
33
+ children: {
34
+ 1: { label: '企业' },
35
+ 2: { label: '个体工商户' },
36
+ 3: { label: '农民专业合作社' },
37
+ 9: { label: '其他' },
38
+ },
39
+ },
40
+ Y: {
41
+ label: '其他',
42
+ children: {
43
+ 1: { label: '其他' },
44
+ },
45
+ },
46
+ };
47
+
48
+ /* 16 位 */
49
+ regex = /^([159][1239]|Y1)[0-9]{6}[0-9A-HJ-NP-RTUWXY]{10}$/; // 无 I O Z S V
50
+ checksum = Mod31Checksum.get();
51
+
52
+ static instance: USCC;
53
+
54
+ static get() {
55
+ return (this.instance ||= new USCC());
56
+ }
57
+
58
+ parse(s: string): ParsedUSCC {
59
+ let registryCode = s[0];
60
+ let l1 = this.registryCodeLabels[registryCode];
61
+ let organizationTypeCode = s[1];
62
+ return {
63
+ registryAuthorityCode: registryCode,
64
+ registryAuthorityCodeLabel: l1?.label,
65
+ organizationTypeCode,
66
+ organizationTypeLabel: l1?.children?.[organizationTypeCode]?.label,
67
+ registryDivisionCode: s.slice(3, 6),
68
+ // GB 11714-1997 全国组织机构代码编制规则
69
+ subject: s.slice(6, 15),
70
+ checksum: s.slice(17, 18),
71
+ };
72
+ }
73
+
74
+ verify(s: string) {
75
+ return this.regex.test(s) && this.checksum.verify(s);
76
+ }
77
+ }
78
+
79
+ interface Code {
80
+ label: string;
81
+ children?: Record<string, Code>;
82
+ }
83
+
84
+ interface ParsedUSCC {
85
+ // 登记管理部门码
86
+ registryAuthorityCode: string;
87
+ registryAuthorityCodeLabel?: string;
88
+ // 机构类别代码
89
+ organizationTypeCode: string;
90
+ organizationTypeLabel?: string;
91
+ // 登记管理机关行政区划代码
92
+ registryDivisionCode: string;
93
+ registryDivisionLabel?: string;
94
+ // 主体标识码
95
+ subject: string;
96
+ // 校验码
97
+ checksum: string;
98
+ }
@@ -0,0 +1,8 @@
1
+ import { USCC } from './USCC';
2
+
3
+ /**
4
+ * USCC - 统一社会信用代码
5
+ */
6
+ export function isUSCC(s: string) {
7
+ return USCC.get().verify(s);
8
+ }
@@ -0,0 +1,16 @@
1
+ import { test, expect } from 'vitest';
2
+ import { Mod31Checksum } from './Mod31Checksum';
3
+ import { USCC } from './USCC';
4
+ import { isUSCC } from './isUSCC';
5
+
6
+ test('uscc', () => {
7
+ let cs = Mod31Checksum.get();
8
+
9
+ // 阿里云计算
10
+ expect(cs.verify('91330106673959654P')).toBeTruthy();
11
+ expect(cs.generate('91330106673959654')).toBe('P');
12
+
13
+ expect(isUSCC('91330106673959654P')).toBeTruthy();
14
+
15
+ console.log(USCC.get().parse('91330106673959654P'));
16
+ });
@@ -1,6 +1,6 @@
1
1
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
2
2
  import type { TypedArray } from '../io/ArrayBuffers';
3
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
3
+ import { getGlobalThis } from '../runtime/getGlobalThis';
4
4
  import { getNodeCrypto } from './getNodeCrypto';
5
5
 
6
6
  const globalThis = getGlobalThis();
@@ -1,4 +1,4 @@
1
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
1
+ import { getGlobalThis } from '../runtime/getGlobalThis';
2
2
 
3
3
  const globalThis = getGlobalThis();
4
4
 
@@ -0,0 +1 @@
1
+ // https://github.com/LiosK/uuidv7/blob/main/src/index.ts
@@ -1,4 +1,4 @@
1
- import { getHttpStatusText } from '../http/HttpStatus';
1
+ import { getHttpStatusText } from '../fetch/HttpStatus';
2
2
 
3
3
  export interface ErrorDetailInit {
4
4
  message?: string;
@@ -1,5 +1,5 @@
1
1
  import { MaybePromise } from '../asyncs/MaybePromise';
2
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
2
+ import { getGlobalThis } from '../runtime/getGlobalThis';
3
3
  import { type FetchLike } from './types';
4
4
 
5
5
  export function createFetchWith({
@@ -1,4 +1,4 @@
1
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
1
+ import { getGlobalThis } from '../runtime/getGlobalThis';
2
2
  import { FetchLike } from './types';
3
3
 
4
4
  type RequestDelayFunction = (attempt: number, error: Error | null, response: Response | null) => number;
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export { arrayFromAsync } from './arrays/arrayFromAsync';
12
12
  export { get } from './objects/get';
13
13
  export { set } from './objects/set';
14
14
  export { parseObjectPath } from './objects/parseObjectPath';
15
+ export { computeIfAbsent } from './objects/computeIfAbsent';
15
16
  export { merge, type MergeOptions } from './objects/merge';
16
17
 
17
18
  // async
@@ -42,8 +43,8 @@ export { isPlainObject } from './langs/isPlainObject';
42
43
  export { parseBoolean } from './langs/parseBoolean';
43
44
  export { maybeFunction, type MaybeFunction } from './langs/MaybeFunction';
44
45
  export { memoize } from './langs/memoize';
45
- export { AsyncCloser } from './langs/AsyncCloser';
46
- export { Closer } from './langs/Closer';
46
+ export { AsyncCloser } from './runtime/AsyncCloser';
47
+ export { Closer } from './runtime/Closer';
47
48
 
48
49
  export { isUUID } from './validations/isUUID';
49
50
  export { parseTimestamp } from './validations/parseTimestamp';
@@ -71,6 +72,7 @@ export { isBuffer } from './io/isBuffer';
71
72
  export { isTransferable } from './io/isTransferable';
72
73
  export { ArrayBuffers } from './io/ArrayBuffers';
73
74
  export { Buffer } from './io/Buffer';
75
+ export { parseDataUri, type ParsedDataUri } from './io/parseDataUri';
74
76
  export type { AbstractEncoding } from './io/AbstractEncoding';
75
77
 
76
78
  // browser
@@ -80,8 +82,8 @@ export { loadScripts, loadStyles } from './browsers/loaders';
80
82
  export { getFileFromDataTransfer } from './browsers/getFileFromDataTransfer';
81
83
 
82
84
  // polyfills
83
- export { getGlobalThis } from './isomorphics/getGlobalThis';
84
- export { structuredClone } from './isomorphics/structuredClone';
85
+ export { getGlobalThis } from './runtime/getGlobalThis';
86
+ export { structuredClone } from './runtime/structuredClone';
85
87
 
86
88
  // crypto
87
89
  export { randomUUID } from './crypto/randomUUID';
@@ -113,6 +115,6 @@ export { default as ms } from './libs/ms';
113
115
  // error
114
116
  export { Errors, DetailError, type ErrorDetail, type ErrorDetailInit } from './errors/Errors';
115
117
  // http
116
- export { getHttpStatusText, isRetryableHttpStatus } from './http/HttpStatus';
118
+ export { getHttpStatusText, isRetryableHttpStatus } from './fetch/HttpStatus';
117
119
 
118
120
  export type * from './types';
@@ -60,3 +60,27 @@ test('base64: high byte', function () {
60
60
  const highByte = ArrayBuffers.from([128]);
61
61
  assert.deepEqual(ArrayBuffers.alloc(1, ArrayBuffers.toString(highByte, 'base64'), 'base64'), highByte);
62
62
  });
63
+
64
+ test('fromBase64', () => {
65
+ const text = 'aoeu';
66
+ const buf = ArrayBuffers.from(text, 'utf8', Uint8Array);
67
+ expect(ArrayBuffers.fromBase64(ArrayBuffers.toBase64(buf))).toEqual(buf);
68
+ });
69
+
70
+ test('fromHex', () => {
71
+ const text = 'aoeu';
72
+ const buf = ArrayBuffers.from(text, 'utf8', Uint8Array);
73
+ expect(ArrayBuffers.fromHex(ArrayBuffers.toHex(buf))).toEqual(buf);
74
+ });
75
+
76
+ test('toHex', () => {
77
+ const text = 'aoeu';
78
+ const buf = ArrayBuffers.from(text, 'utf8', Uint8Array);
79
+ expect(ArrayBuffers.toHex(buf)).toEqual('616f6575');
80
+ });
81
+
82
+ test('toBase64', () => {
83
+ const text = 'aoeu';
84
+ const buf = ArrayBuffers.from(text, 'utf8', Uint8Array);
85
+ expect(ArrayBuffers.toBase64(buf)).toEqual('YW9ldQ==');
86
+ });
@@ -1,10 +1,13 @@
1
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
2
1
  import { classOf } from '../langs/classOf';
2
+ import { getGlobalThis } from '../runtime/getGlobalThis';
3
3
  import { decodeBase64ToArrayBuffer, encodeArrayBufferToBase64 } from './base64';
4
4
  import { isBuffer } from './isBuffer';
5
5
 
6
6
  /**
7
7
  * Various utils to work with {@link ArrayBuffer}
8
+ *
9
+ * @see https://github.com/tc39/proposal-arraybuffer-base64
10
+ * @see https://github.com/tc39/proposal-resizablearraybuffer
8
11
  */
9
12
  export interface ArrayBuffers {
10
13
  /**
@@ -48,10 +51,26 @@ export interface ArrayBuffers {
48
51
  */
49
52
  from(v: string | BufferSource, encoding?: ToStringEncoding): ArrayBuffer;
50
53
 
54
+ from<C extends ArrayBufferViewConstructor<unknown>>(
55
+ v: string | BufferSource,
56
+ encoding: ToStringEncoding,
57
+ TypedArray: C,
58
+ ): C;
59
+
51
60
  /**
52
61
  * concat the given {@link BufferSource} to a new {@link ArrayBuffer}
53
62
  */
54
63
  concat(buffers: Array<BufferSource>, result?: ArrayBuffer, offset?: number): ArrayBuffer;
64
+
65
+ fromBase64(v: string): Uint8Array;
66
+
67
+ fromHex(v: string): Uint8Array;
68
+
69
+ toBase64(v: BufferSource): string;
70
+
71
+ toHex(v: BufferSource): string;
72
+
73
+ resize(v: ArrayBuffer, newByteLength: number): ArrayBuffer;
55
74
  }
56
75
 
57
76
  type ToStringEncoding =
@@ -89,6 +108,67 @@ export class ArrayBuffers {
89
108
  return v instanceof ArrayBuffer;
90
109
  };
91
110
 
111
+ static toArrayBuffer(v: BufferSource): ArrayBuffer {
112
+ return v instanceof ArrayBuffer ? v : (v.buffer as ArrayBuffer);
113
+ }
114
+
115
+ static toUint8Array(v: BufferSource) {
116
+ return ArrayBuffers.asView(Uint8Array, v);
117
+ }
118
+
119
+ static toBase64 = (v: BufferSource) => {
120
+ if ('toBase64' in Uint8Array.prototype) {
121
+ return this.toUint8Array(v).toBase64();
122
+ }
123
+
124
+ if (ArrayBuffers.isNativeBufferAllowed()) {
125
+ return Buffer.from(ArrayBuffers.asView(Uint8Array, v)).toString('base64');
126
+ }
127
+
128
+ return encodeArrayBufferToBase64(this.toArrayBuffer(v));
129
+ };
130
+
131
+ static toHex = (v: BufferSource) => {
132
+ if ('toHex' in Uint8Array.prototype) {
133
+ return this.toUint8Array(v).toHex();
134
+ }
135
+ if (ArrayBuffers.isNativeBufferAllowed()) {
136
+ return Buffer.from(ArrayBuffers.asView(Uint8Array, v)).toString('hex');
137
+ }
138
+ return ArrayBuffers.toString(v, 'hex');
139
+ };
140
+
141
+ static fromBase64 = (v: string) => {
142
+ if ('fromBase64' in Uint8Array) {
143
+ return Uint8Array.fromBase64(v);
144
+ }
145
+ if (ArrayBuffers.isNativeBufferAllowed()) {
146
+ return Buffer.from(v, 'base64');
147
+ }
148
+ return this.toUint8Array(decodeBase64ToArrayBuffer(v));
149
+ };
150
+
151
+ static fromHex = (v: string) => {
152
+ if ('fromHex' in Uint8Array) {
153
+ return Uint8Array.fromHex(v);
154
+ }
155
+ return this.toUint8Array(ArrayBuffers.from(v, 'hex'));
156
+ };
157
+
158
+ static resize = (v: ArrayBuffer, newByteLength: number): ArrayBuffer => {
159
+ if ('resize' in v) {
160
+ (v as any).resize(newByteLength);
161
+ return v;
162
+ }
163
+
164
+ const old = v;
165
+ const newBuf = new ArrayBuffer(newByteLength);
166
+ const oldView = new Uint8Array(old);
167
+ const newView = new Uint8Array(newBuf);
168
+ newView.set(oldView);
169
+ return newBuf;
170
+ };
171
+
92
172
  static slice = (o: TypedArray, start?: number, end?: number) => {
93
173
  // NodeJS Buffer slice is not the same as UInt8Array slice
94
174
  // https://nodejs.org/api/buffer.html#bufslicestart-end
@@ -195,7 +275,11 @@ export class ArrayBuffers {
195
275
  static from = (
196
276
  v: string | BufferSource | ArrayLike<number> | Iterable<number>,
197
277
  encoding: ToStringEncoding = 'utf8',
198
- ): BufferSource => {
278
+ view?: any,
279
+ ): any => {
280
+ if (view) {
281
+ return this.asView(view, this.from(v, encoding));
282
+ }
199
283
  if (!v) {
200
284
  return new ArrayBuffer(0);
201
285
  }
@@ -212,8 +296,8 @@ export class ArrayBuffers {
212
296
  case 'base64':
213
297
  // replaceAll need higher version of nodejs
214
298
  return decodeBase64ToArrayBuffer(v.replace(/[^0-9a-zA-Z=+/_]/g, ''));
215
- // error in nodejs 18
216
- // return Uint8Array.from(atob(v.replace(/[^0-9a-zA-Z=+/_ \r\n]/g, '')), (c) => c.charCodeAt(0));
299
+ case 'hex':
300
+ return new Uint8Array(v.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))).buffer;
217
301
  default:
218
302
  throw new Error(`[ArrayBuffers.from] Unknown encoding: ${encoding}`);
219
303
  }
@@ -303,3 +387,21 @@ const hexLookupTable = (function () {
303
387
  }
304
388
  return table;
305
389
  })();
390
+
391
+ declare global {
392
+ interface ArrayBuffer {
393
+ // resize(newByteLength: number): void;
394
+ }
395
+
396
+ interface Uint8Array {
397
+ toBase64(): string;
398
+
399
+ toHex(): string;
400
+ }
401
+
402
+ interface Uint8ArrayConstructor {
403
+ fromBase64(v: string): Uint8Array;
404
+
405
+ fromHex(v: string): Uint8Array;
406
+ }
407
+ }
@@ -1,4 +1,4 @@
1
- import { getGlobalThis } from '../isomorphics/getGlobalThis';
1
+ import { getGlobalThis } from '../runtime/getGlobalThis';
2
2
 
3
3
  const globalThis = getGlobalThis();
4
4
 
@@ -0,0 +1,34 @@
1
+ import { assert, test } from 'vitest';
2
+ import { ArrayBuffers } from './ArrayBuffers';
3
+ import { parseDataUri } from './parseDataUri';
4
+
5
+ test('parseDataUri', async () => {
6
+ {
7
+ let uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==';
8
+ const { getData, ...out } = parseDataUri(uri)!;
9
+ assert.deepEqual(out, {
10
+ type: 'text/plain',
11
+ params: { base64: true },
12
+ content: 'SGVsbG8sIFdvcmxkIQ==',
13
+ base64: true,
14
+ });
15
+ assert.equal(await getData('utf-8'), await fetch(uri).then((v) => v.text()));
16
+ assert.equal(await getData('base64'), 'SGVsbG8sIFdvcmxkIQ==');
17
+ }
18
+ {
19
+ // text
20
+ let uri = 'data:text/plain,Hello%2C%20World!';
21
+ const { getData, ...out } = parseDataUri(uri)!;
22
+ assert.deepEqual(out, {
23
+ type: 'text/plain',
24
+ params: {},
25
+ content: 'Hello%2C%20World!',
26
+ base64: false,
27
+ });
28
+
29
+ assert.equal(await getData('utf-8'), 'Hello, World!');
30
+ assert.deepEqual(ArrayBuffers.asView(Uint8Array, await getData()), new TextEncoder().encode('Hello, World!'));
31
+ assert.equal(await getData('utf-8'), await fetch(uri).then((v) => v.text()));
32
+ assert.equal(await getData('base64'), 'SGVsbG8sIFdvcmxkIQ==');
33
+ }
34
+ });