@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.
- package/lib/asyncs/createLazyPromise.js +1 -3
- package/lib/asyncs/createLazyPromise.js.map +1 -1
- package/lib/browsers/download.js +20 -11
- package/lib/browsers/download.js.map +1 -1
- package/lib/cn/division/DivisionCode.js +204 -0
- package/lib/cn/division/DivisionCode.js.map +1 -0
- package/lib/cn/id/Mod11Checksum.js +46 -0
- package/lib/cn/id/Mod11Checksum.js.map +1 -0
- package/lib/cn/id/ResidentIdNumber.js +89 -0
- package/lib/cn/id/ResidentIdNumber.js.map +1 -0
- package/lib/cn/id/types.js +8 -0
- package/lib/cn/id/types.js.map +1 -0
- package/lib/cn/index.js +9 -1
- package/lib/cn/index.js.map +1 -1
- package/lib/cn/uscc/Mod31Checksum.js +52 -0
- package/lib/cn/uscc/Mod31Checksum.js.map +1 -0
- package/lib/cn/uscc/USCC.js +97 -0
- package/lib/cn/uscc/USCC.js.map +1 -0
- package/lib/cn/uscc/isUSCC.js +8 -0
- package/lib/cn/uscc/isUSCC.js.map +1 -0
- package/lib/crypto/getRandomValues.js +1 -1
- package/lib/crypto/getRandomValues.js.map +1 -1
- package/lib/crypto/randomUUID.js +1 -1
- package/lib/crypto/randomUUID.js.map +1 -1
- package/lib/crypto/randomUUIDv7.js +3 -0
- package/lib/crypto/randomUUIDv7.js.map +1 -0
- package/lib/errors/Errors.js +1 -1
- package/lib/errors/Errors.js.map +1 -1
- package/lib/fetch/HttpStatus.js.map +1 -0
- package/lib/fetch/createFetchWith.js +1 -1
- package/lib/fetch/createFetchWith.js.map +1 -1
- package/lib/fetch/createFetchWithRetry.js +1 -1
- package/lib/fetch/createFetchWithRetry.js.map +1 -1
- package/lib/index.js +7 -5
- package/lib/index.js.map +1 -1
- package/lib/io/ArrayBuffers.js +58 -4
- package/lib/io/ArrayBuffers.js.map +1 -1
- package/lib/io/isTransferable.js +1 -1
- package/lib/io/isTransferable.js.map +1 -1
- package/lib/io/parseDataUri.js +57 -0
- package/lib/io/parseDataUri.js.map +1 -0
- package/lib/objects/computeIfAbsent.js +14 -0
- package/lib/objects/computeIfAbsent.js.map +1 -0
- package/lib/runtime/AsyncCloser.js.map +1 -0
- package/lib/runtime/Closer.js.map +1 -0
- package/lib/runtime/getGlobalThis.js.map +1 -0
- package/lib/runtime/structuredClone.js.map +1 -0
- package/lib/server/fetch/createFetchWithProxyByNodeFetch.js +1 -1
- package/lib/server/fetch/createFetchWithProxyByNodeFetch.js.map +1 -1
- package/lib/server/fetch/createFetchWithProxyByUndici.js +1 -1
- package/lib/server/fetch/createFetchWithProxyByUndici.js.map +1 -1
- package/lib/server/getPackageDir.js +5 -4
- package/lib/server/getPackageDir.js.map +1 -1
- package/lib/server/polyfill/polyfillCrypto.js +1 -1
- package/lib/server/polyfill/polyfillCrypto.js.map +1 -1
- package/lib/server/polyfill/polyfillJsDom.js +1 -1
- package/lib/server/polyfill/polyfillJsDom.js.map +1 -1
- package/lib/server/polyfill/polyfillWebSocket.js +1 -1
- package/lib/server/polyfill/polyfillWebSocket.js.map +1 -1
- package/package.json +18 -15
- package/src/asyncs/createLazyPromise.ts +0 -2
- package/src/browsers/download.ts +20 -13
- package/src/cn/division/DivisionCode.ts +109 -0
- package/src/cn/id/Mod11Checksum.ts +29 -0
- package/src/cn/id/ResidentIdNumber.ts +102 -0
- package/src/cn/id/id.test.ts +20 -0
- package/src/cn/id/types.ts +26 -0
- package/src/cn/index.ts +13 -0
- package/src/cn/uscc/Mod31Checksum.ts +42 -0
- package/src/cn/uscc/USCC.ts +98 -0
- package/src/cn/uscc/isUSCC.ts +8 -0
- package/src/cn/uscc/uscc.test.ts +16 -0
- package/src/crypto/getRandomValues.ts +1 -1
- package/src/crypto/randomUUID.ts +1 -1
- package/src/crypto/randomUUIDv7.ts +1 -0
- package/src/errors/Errors.ts +1 -1
- package/src/fetch/createFetchWith.ts +1 -1
- package/src/fetch/createFetchWithRetry.ts +1 -1
- package/src/index.ts +7 -5
- package/src/io/ArrayBuffers.base64.test.ts +24 -0
- package/src/io/ArrayBuffers.ts +106 -4
- package/src/io/isTransferable.ts +1 -1
- package/src/io/parseDataUri.test.ts +34 -0
- package/src/io/parseDataUri.ts +66 -0
- package/src/objects/computeIfAbsent.ts +15 -0
- package/src/schema/typebox/gen/README.md +2 -0
- package/src/server/fetch/createFetchWithProxyByNodeFetch.ts +1 -1
- package/src/server/fetch/createFetchWithProxyByUndici.ts +1 -1
- package/src/server/getPackageDir.ts +6 -4
- package/src/server/polyfill/polyfillCrypto.ts +1 -1
- package/src/server/polyfill/polyfillJsDom.ts +1 -1
- package/src/server/polyfill/polyfillWebSocket.ts +1 -1
- package/lib/http/HttpStatus.js.map +0 -1
- package/lib/io/Bytes.js +0 -51
- package/lib/io/Bytes.js.map +0 -1
- package/lib/isomorphics/getGlobalThis.js.map +0 -1
- package/lib/isomorphics/structuredClone.js.map +0 -1
- package/lib/langs/AsyncCloser.js.map +0 -1
- package/lib/langs/Closer.js.map +0 -1
- package/src/io/Bytes.ts +0 -64
- /package/lib/{http → fetch}/HttpStatus.js +0 -0
- /package/lib/{langs → runtime}/AsyncCloser.js +0 -0
- /package/lib/{langs → runtime}/Closer.js +0 -0
- /package/lib/{isomorphics → runtime}/getGlobalThis.js +0 -0
- /package/lib/{isomorphics → runtime}/structuredClone.js +0 -0
- /package/src/{http → fetch}/HttpStatus.ts +0 -0
- /package/src/{langs → runtime}/AsyncCloser.ts +0 -0
- /package/src/{langs → runtime}/Closer.ts +0 -0
- /package/src/{isomorphics → runtime}/getGlobalThis.ts +0 -0
- /package/src/{isomorphics → runtime}/structuredClone.test.ts +0 -0
- /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,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 '../
|
|
3
|
+
import { getGlobalThis } from '../runtime/getGlobalThis';
|
|
4
4
|
import { getNodeCrypto } from './getNodeCrypto';
|
|
5
5
|
|
|
6
6
|
const globalThis = getGlobalThis();
|
package/src/crypto/randomUUID.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// https://github.com/LiosK/uuidv7/blob/main/src/index.ts
|
package/src/errors/Errors.ts
CHANGED
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 './
|
|
46
|
-
export { Closer } from './
|
|
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 './
|
|
84
|
-
export { structuredClone } from './
|
|
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 './
|
|
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
|
+
});
|
package/src/io/ArrayBuffers.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
+
}
|
package/src/io/isTransferable.ts
CHANGED
|
@@ -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
|
+
});
|