@atproto/lex-data 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/blob.d.ts +28 -2
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +43 -6
- package/dist/blob.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lex-error.d.ts +17 -0
- package/dist/lex-error.d.ts.map +1 -0
- package/dist/lex-error.js +26 -0
- package/dist/lex-error.js.map +1 -0
- package/dist/lib/nodejs-buffer.d.ts +1 -0
- package/dist/lib/nodejs-buffer.d.ts.map +1 -1
- package/dist/lib/nodejs-buffer.js +1 -1
- package/dist/lib/nodejs-buffer.js.map +1 -1
- package/dist/uint8array-concat.d.ts +3 -0
- package/dist/uint8array-concat.d.ts.map +1 -0
- package/dist/uint8array-concat.js +24 -0
- package/dist/uint8array-concat.js.map +1 -0
- package/dist/uint8array-from-base64.d.ts.map +1 -1
- package/dist/uint8array-from-base64.js +2 -2
- package/dist/uint8array-from-base64.js.map +1 -1
- package/dist/uint8array-to-base64.d.ts.map +1 -1
- package/dist/uint8array-to-base64.js +2 -2
- package/dist/uint8array-to-base64.js.map +1 -1
- package/dist/uint8array.d.ts +1 -0
- package/dist/uint8array.d.ts.map +1 -1
- package/dist/uint8array.js +14 -3
- package/dist/uint8array.js.map +1 -1
- package/dist/utf8-from-base64.d.ts +4 -0
- package/dist/utf8-from-base64.d.ts.map +1 -0
- package/dist/utf8-from-base64.js +18 -0
- package/dist/utf8-from-base64.js.map +1 -0
- package/dist/utf8-grapheme-len.d.ts.map +1 -1
- package/dist/utf8-grapheme-len.js +2 -2
- package/dist/utf8-grapheme-len.js.map +1 -1
- package/dist/utf8-len.d.ts.map +1 -1
- package/dist/utf8-len.js +1 -1
- package/dist/utf8-len.js.map +1 -1
- package/dist/utf8-to-base64.d.ts +4 -0
- package/dist/utf8-to-base64.d.ts.map +1 -0
- package/dist/utf8-to-base64.js +20 -0
- package/dist/utf8-to-base64.js.map +1 -0
- package/dist/utf8.d.ts +3 -0
- package/dist/utf8.d.ts.map +1 -1
- package/dist/utf8.js +16 -3
- package/dist/utf8.js.map +1 -1
- package/package.json +5 -5
- package/src/blob.test.ts +223 -20
- package/src/blob.ts +82 -10
- package/src/cid.test.ts +126 -0
- package/src/index.ts +1 -0
- package/src/language.test.ts +1 -0
- package/src/lex-equals.test.ts +30 -0
- package/src/lex-error.ts +34 -0
- package/src/lex.test.ts +65 -13
- package/src/lib/nodejs-buffer.ts +2 -1
- package/src/object.test.ts +2 -0
- package/src/uint8array-concat.test.ts +197 -0
- package/src/uint8array-concat.ts +21 -0
- package/src/uint8array-from-base64.test.ts +4 -1
- package/src/uint8array-from-base64.ts +2 -2
- package/src/uint8array-to-base64.test.ts +3 -3
- package/src/uint8array-to-base64.ts +2 -2
- package/src/uint8array.test.ts +484 -0
- package/src/uint8array.ts +14 -2
- package/src/utf8-from-base64.test.ts +39 -0
- package/src/utf8-from-base64.ts +23 -0
- package/src/utf8-grapheme-len.test.ts +3 -2
- package/src/utf8-grapheme-len.ts +2 -2
- package/src/utf8-len.test.ts +3 -2
- package/src/utf8-len.ts +1 -1
- package/src/utf8-to-base64.test.ts +35 -0
- package/src/utf8-to-base64.ts +22 -0
- package/src/utf8.ts +23 -2
- package/tsconfig.tests.json +1 -1
package/src/cid.test.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import { sha256, sha512 } from 'multiformats/hashes/sha2'
|
|
3
|
+
import { describe, expect, it } from 'vitest'
|
|
4
|
+
import {
|
|
5
|
+
RAW_BIN_MULTICODEC,
|
|
6
|
+
createCid,
|
|
7
|
+
decodeCid,
|
|
8
|
+
ensureValidCidString,
|
|
9
|
+
isCid,
|
|
10
|
+
parseCid,
|
|
11
|
+
parseCidString,
|
|
12
|
+
} from './cid.js'
|
|
13
|
+
|
|
14
|
+
describe(isCid, () => {
|
|
15
|
+
describe('non-strict mode', () => {
|
|
16
|
+
it('returns true for parsed CIDs', () => {
|
|
17
|
+
const cid = parseCid(
|
|
18
|
+
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
19
|
+
)
|
|
20
|
+
expect(isCid(cid)).toBe(true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('returns true for CID v0 and v1', async () => {
|
|
24
|
+
const digest = await sha256.digest(Buffer.from('hello world'))
|
|
25
|
+
const cidV0 = CID.createV0(digest)
|
|
26
|
+
const cidV1 = CID.createV1(RAW_BIN_MULTICODEC, digest)
|
|
27
|
+
expect(isCid(cidV0)).toBe(true)
|
|
28
|
+
expect(isCid(cidV1)).toBe(true)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('returns false for invalid CIDs', () => {
|
|
32
|
+
expect(isCid(new Date())).toBe(false)
|
|
33
|
+
expect(isCid({})).toBe(false)
|
|
34
|
+
expect(isCid('not a cid')).toBe(false)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('strict mode', () => {
|
|
39
|
+
it('returns true for valid CIDs in strict mode', async () => {
|
|
40
|
+
const digest = await sha256.digest(Buffer.from('hello world'))
|
|
41
|
+
const cid = CID.createV1(RAW_BIN_MULTICODEC, digest)
|
|
42
|
+
expect(isCid(cid, { strict: true })).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('rejects CID v0 when strict option is set', async () => {
|
|
46
|
+
const digest = await sha256.digest(Buffer.from('hello world'))
|
|
47
|
+
const cid = CID.createV0(digest)
|
|
48
|
+
expect(isCid(cid, { strict: true })).toBe(false)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('rejects CIDs with invalid hash algorithm', async () => {
|
|
52
|
+
const digest = await sha512.digest(Buffer.from('hello world'))
|
|
53
|
+
const cid = CID.createV1(RAW_BIN_MULTICODEC, digest)
|
|
54
|
+
expect(isCid(cid, { strict: true })).toBe(false)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('rejects CIDs with invalid code', async () => {
|
|
58
|
+
const digest = await sha256.digest(Buffer.from('hello world'))
|
|
59
|
+
const cid = CID.createV1(3333, digest)
|
|
60
|
+
expect(isCid(cid, { strict: true })).toBe(false)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
describe(createCid, () => {
|
|
66
|
+
it('creates a valid CID v1', async () => {
|
|
67
|
+
const digest = await sha256.digest(Buffer.from('hello world'))
|
|
68
|
+
const cid = createCid(RAW_BIN_MULTICODEC, digest)
|
|
69
|
+
expect(cid.version).toBe(1)
|
|
70
|
+
expect(cid.code).toBe(RAW_BIN_MULTICODEC)
|
|
71
|
+
expect(cid.multihash.code).toBe(sha256.code)
|
|
72
|
+
expect(cid.multihash.digest).toEqual(digest.digest)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe(decodeCid, () => {
|
|
77
|
+
it('decodes CID from bytes', () => {
|
|
78
|
+
const cidStr = 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'
|
|
79
|
+
const cid = parseCid(cidStr)
|
|
80
|
+
const bytes = cid.bytes
|
|
81
|
+
const decodedCid = decodeCid(bytes)
|
|
82
|
+
expect(decodedCid.toString()).toBe(cidStr)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe(parseCid, () => {
|
|
87
|
+
it('parses valid CIDs', () => {
|
|
88
|
+
const cidStr = 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'
|
|
89
|
+
const cid = parseCid(cidStr)
|
|
90
|
+
expect(cid.toString()).toBe(cidStr)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('throws for invalid CIDs', () => {
|
|
94
|
+
const invalidCidStr = 'invalidcidstring'
|
|
95
|
+
expect(() => parseCid(invalidCidStr)).toThrow()
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe(parseCidString, () => {
|
|
100
|
+
it('parses valid CIDs', () => {
|
|
101
|
+
const cidStr = 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'
|
|
102
|
+
const cid = parseCidString(cidStr)
|
|
103
|
+
expect(cid).toBeDefined()
|
|
104
|
+
expect(cid!.toString()).toBe(cidStr)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('returns undefined for invalid CIDs', () => {
|
|
108
|
+
const invalidCidStr = 'invalidcidstring'
|
|
109
|
+
const cid = parseCidString(invalidCidStr)
|
|
110
|
+
expect(cid).toBeUndefined()
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
describe(ensureValidCidString, () => {
|
|
115
|
+
it('does not throw for valid CIDs', () => {
|
|
116
|
+
const cidStr = 'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a'
|
|
117
|
+
expect(() => ensureValidCidString(cidStr)).not.toThrow()
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('throws for invalid CIDs', () => {
|
|
121
|
+
const invalidCidStr = 'invalidcidstring'
|
|
122
|
+
expect(() => ensureValidCidString(invalidCidStr)).toThrow(
|
|
123
|
+
'Invalid CID string',
|
|
124
|
+
)
|
|
125
|
+
})
|
|
126
|
+
})
|
package/src/index.ts
CHANGED
package/src/language.test.ts
CHANGED
package/src/lex-equals.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
1
2
|
import { parseCid } from './cid.js'
|
|
2
3
|
import { lexEquals } from './lex-equals.js'
|
|
3
4
|
import { LexValue } from './lex.js'
|
|
@@ -38,6 +39,7 @@ describe('lexEquals', () => {
|
|
|
38
39
|
expectLexEqual([1, 2, 3], [1, 2, 4], false)
|
|
39
40
|
expectLexEqual([1, 2, 3], [1, 2], false)
|
|
40
41
|
expectLexEqual([1, 2, 3], 'not an array', false)
|
|
42
|
+
expectLexEqual([1, 2, 3], { 0: 1, 1: 2, 2: 3 }, false)
|
|
41
43
|
})
|
|
42
44
|
|
|
43
45
|
it('compares Uint8Arrays', () => {
|
|
@@ -59,10 +61,23 @@ describe('lexEquals', () => {
|
|
|
59
61
|
expectLexEqual(cid2, cid3, true)
|
|
60
62
|
|
|
61
63
|
expectLexEqual(cid1, cid1.toString(), false)
|
|
64
|
+
expectLexEqual(cid1, { not: 'a cid' }, false)
|
|
65
|
+
expectLexEqual(cid1, [], false)
|
|
66
|
+
expectLexEqual(cid1, cid1.bytes, false)
|
|
62
67
|
})
|
|
63
68
|
|
|
64
69
|
it('compares objects', () => {
|
|
65
70
|
expectLexEqual({ a: 1, b: 2 }, { a: 1, b: 2 }, true)
|
|
71
|
+
expectLexEqual(
|
|
72
|
+
{ a: 1, b: 2, c: undefined },
|
|
73
|
+
{ a: 1, b: 2, c: undefined },
|
|
74
|
+
true,
|
|
75
|
+
)
|
|
76
|
+
expectLexEqual(
|
|
77
|
+
{ a: 1, b: 2, c: { e: 1, d: undefined } },
|
|
78
|
+
{ a: 1, b: 2, c: { d: undefined, e: 1 } },
|
|
79
|
+
true,
|
|
80
|
+
)
|
|
66
81
|
expectLexEqual(
|
|
67
82
|
{ a: 1, b: { unicode: 'a~öñ©⽘☎𓋓😀👨👩👧👧' } },
|
|
68
83
|
{ a: 1, b: { unicode: 'a~öñ©⽘☎𓋓😀👨👩👧👧' } },
|
|
@@ -75,6 +90,21 @@ describe('lexEquals', () => {
|
|
|
75
90
|
expectLexEqual({ a: 1, b: 2 }, null, false)
|
|
76
91
|
})
|
|
77
92
|
|
|
93
|
+
it('accounts for undefined (but present) properties in objects', () => {
|
|
94
|
+
expectLexEqual({ a: 1, b: undefined }, { a: 1 }, false)
|
|
95
|
+
expectLexEqual(
|
|
96
|
+
{ a: 1, b: { c: undefined, d: 2 } },
|
|
97
|
+
{ a: 1, b: { d: 2 } },
|
|
98
|
+
false,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
expectLexEqual(
|
|
102
|
+
{ a: 1, b: { c: undefined, d: 2 } },
|
|
103
|
+
{ a: 1, b: { c: 3, d: 2 } },
|
|
104
|
+
false,
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
78
108
|
it('compares nested structures', () => {
|
|
79
109
|
const lex1 = {
|
|
80
110
|
foo: [1, 2, { bar: new Uint8Array([3, 4, 5]) }],
|
package/src/lex-error.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type LexErrorCode = string
|
|
2
|
+
|
|
3
|
+
export type LexErrorData<N extends LexErrorCode = LexErrorCode> = {
|
|
4
|
+
error: N
|
|
5
|
+
message?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class LexError<N extends LexErrorCode = LexErrorCode> extends Error {
|
|
9
|
+
name = 'LexError'
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
readonly error: N,
|
|
13
|
+
message?: string,
|
|
14
|
+
options?: ErrorOptions,
|
|
15
|
+
) {
|
|
16
|
+
super(message, options)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
toString(): string {
|
|
20
|
+
return `${this.name}: [${this.error}] ${this.message}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toJSON(): LexErrorData<N> {
|
|
24
|
+
const { error, message } = this
|
|
25
|
+
return { error, message: message ?? undefined }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Translate into an HTTP response for downstream clients.
|
|
30
|
+
*/
|
|
31
|
+
toResponse(): Response {
|
|
32
|
+
return Response.json(this.toJSON(), { status: 400 })
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/lex.test.ts
CHANGED
|
@@ -1,4 +1,59 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { parseCid } from './cid.js'
|
|
3
|
+
import { isLexArray, isLexScalar, isLexValue, isTypedLexMap } from './lex.js'
|
|
4
|
+
|
|
5
|
+
describe('isLexScalar', () => {
|
|
6
|
+
for (const { note, value, expected } of [
|
|
7
|
+
{ note: 'string', value: 'hello', expected: true },
|
|
8
|
+
{ note: 'boolean', value: true, expected: true },
|
|
9
|
+
{ note: 'null', value: null, expected: true },
|
|
10
|
+
{ note: 'Uint8Array', value: new Uint8Array([1, 2, 3]), expected: true },
|
|
11
|
+
{
|
|
12
|
+
note: 'Cid',
|
|
13
|
+
value: parseCid(
|
|
14
|
+
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
15
|
+
),
|
|
16
|
+
expected: true,
|
|
17
|
+
},
|
|
18
|
+
{ note: 'number (integer)', value: 42, expected: true },
|
|
19
|
+
{ note: 'number (float)', value: 3.14, expected: false },
|
|
20
|
+
{ note: 'object', value: { a: 1 }, expected: false },
|
|
21
|
+
{ note: 'array', value: [1, 2, 3], expected: false },
|
|
22
|
+
{ note: 'undefined', value: undefined, expected: false },
|
|
23
|
+
{ note: 'function', value: () => {}, expected: false },
|
|
24
|
+
]) {
|
|
25
|
+
it(note, () => {
|
|
26
|
+
const result = isLexScalar(value)
|
|
27
|
+
expect(result).toBe(expected)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('isLexArray', () => {
|
|
33
|
+
it('returns true for valid LexArray', () => {
|
|
34
|
+
const list = [123, 'blah', true, null, new Uint8Array([1, 2, 3]), { a: 1 }]
|
|
35
|
+
expect(isLexArray(list)).toBe(true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('returns false for non-arrays', () => {
|
|
39
|
+
const values = [
|
|
40
|
+
123,
|
|
41
|
+
'blah',
|
|
42
|
+
true,
|
|
43
|
+
null,
|
|
44
|
+
new Uint8Array([1, 2, 3]),
|
|
45
|
+
{ a: 1 },
|
|
46
|
+
]
|
|
47
|
+
for (const value of values) {
|
|
48
|
+
expect(isLexArray(value)).toBe(false)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('returns false for arrays with non-Lex values', () => {
|
|
53
|
+
expect(isLexArray([123, 'blah', () => {}])).toBe(false)
|
|
54
|
+
expect(isLexArray([123, 'blah', undefined])).toBe(false)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
2
57
|
|
|
3
58
|
describe('isLexValue', () => {
|
|
4
59
|
describe('valid values', () => {
|
|
@@ -9,7 +64,9 @@ describe('isLexValue', () => {
|
|
|
9
64
|
{ note: 'Uint8Array', value: new Uint8Array([1, 2, 3]) },
|
|
10
65
|
{
|
|
11
66
|
note: 'Cid',
|
|
12
|
-
value:
|
|
67
|
+
value: parseCid(
|
|
68
|
+
'bafyreidfayvfuwqa7qlnopdjiqrxzs6blmoeu4rujcjtnci5beludirz2a',
|
|
69
|
+
),
|
|
13
70
|
},
|
|
14
71
|
{
|
|
15
72
|
note: 'record with Lex values',
|
|
@@ -51,17 +108,12 @@ describe('isLexValue', () => {
|
|
|
51
108
|
{ note: 'float', value: 123.456 },
|
|
52
109
|
{ note: 'undefined', value: undefined },
|
|
53
110
|
{ note: 'function', value: () => {} },
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
note: 'list with non-Lex value',
|
|
63
|
-
value: [123, 'blah', () => {}],
|
|
64
|
-
},
|
|
111
|
+
{ note: 'obj with fn', value: { a: 123, b: () => {} } },
|
|
112
|
+
{ note: 'list with non-Lex value', value: [123, 'blah', () => {}] },
|
|
113
|
+
{ note: 'Date object', value: new Date() },
|
|
114
|
+
{ note: 'Map object', value: new Map() },
|
|
115
|
+
{ note: 'Set object', value: new Set() },
|
|
116
|
+
{ note: 'class instance', value: new (class A {})() },
|
|
65
117
|
]) {
|
|
66
118
|
it(note, () => {
|
|
67
119
|
expect(isLexValue(value)).toBe(false)
|
package/src/lib/nodejs-buffer.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface NodeJSBufferConstructor {
|
|
|
12
12
|
input: Uint8Array | ArrayBuffer | ArrayBufferView,
|
|
13
13
|
): NodeJSBuffer<ArrayBuffer>
|
|
14
14
|
from(input: string, encoding?: Encoding): NodeJSBuffer<ArrayBuffer>
|
|
15
|
+
concat(list: readonly Uint8Array[], totalLength?: number): NodeJSBuffer
|
|
15
16
|
byteLength(input: string, encoding?: Encoding): number
|
|
16
17
|
prototype: NodeJSBuffer
|
|
17
18
|
}
|
|
@@ -24,4 +25,4 @@ export const NodeJSBuffer: NodeJSBufferConstructor | null =
|
|
|
24
25
|
(globalThis as any)?.[BUFFER]?.prototype instanceof Uint8Array &&
|
|
25
26
|
'byteLength' in (globalThis as any)[BUFFER]
|
|
26
27
|
? ((globalThis as any)[BUFFER] as NodeJSBufferConstructor)
|
|
27
|
-
: null
|
|
28
|
+
: /* v8 ignore next -- @preserve */ null
|
package/src/object.test.ts
CHANGED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { assert, describe, expect, it } from 'vitest'
|
|
2
|
+
import { ui8ConcatNode, ui8ConcatPonyfill } from './uint8array-concat.js'
|
|
3
|
+
import { ui8Equals } from './uint8array.js'
|
|
4
|
+
|
|
5
|
+
for (const ui8Concat of [ui8ConcatNode, ui8ConcatPonyfill] as const) {
|
|
6
|
+
// Tests should run in NodeJS where implementations are available.
|
|
7
|
+
assert(ui8Concat, 'ui8Concat implementation should not be undefined')
|
|
8
|
+
|
|
9
|
+
describe(ui8Concat.name, () => {
|
|
10
|
+
describe('empty array', () => {
|
|
11
|
+
it('returns empty Uint8Array when given empty array', () => {
|
|
12
|
+
const result = ui8Concat([])
|
|
13
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
14
|
+
expect(result.length).toBe(0)
|
|
15
|
+
})
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('single array', () => {
|
|
19
|
+
it('returns copy of single Uint8Array', () => {
|
|
20
|
+
const input = new Uint8Array([1, 2, 3])
|
|
21
|
+
const result = ui8Concat([input])
|
|
22
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
23
|
+
expect(ui8Equals(result, input)).toBe(true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('returns copy of single empty Uint8Array', () => {
|
|
27
|
+
const input = new Uint8Array(0)
|
|
28
|
+
const result = ui8Concat([input])
|
|
29
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
30
|
+
expect(result.length).toBe(0)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('multiple arrays', () => {
|
|
35
|
+
it('concatenates two arrays', () => {
|
|
36
|
+
const a = new Uint8Array([1, 2, 3])
|
|
37
|
+
const b = new Uint8Array([4, 5, 6])
|
|
38
|
+
const result = ui8Concat([a, b])
|
|
39
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
40
|
+
expect(ui8Equals(result, new Uint8Array([1, 2, 3, 4, 5, 6]))).toBe(true)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('concatenates three arrays', () => {
|
|
44
|
+
const a = new Uint8Array([1, 2])
|
|
45
|
+
const b = new Uint8Array([3, 4])
|
|
46
|
+
const c = new Uint8Array([5, 6])
|
|
47
|
+
const result = ui8Concat([a, b, c])
|
|
48
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
49
|
+
expect(ui8Equals(result, new Uint8Array([1, 2, 3, 4, 5, 6]))).toBe(true)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('concatenates many arrays', () => {
|
|
53
|
+
const arrays = [
|
|
54
|
+
new Uint8Array([0x00]),
|
|
55
|
+
new Uint8Array([0x01, 0x02]),
|
|
56
|
+
new Uint8Array([0x03, 0x04, 0x05]),
|
|
57
|
+
new Uint8Array([0x06, 0x07, 0x08, 0x09]),
|
|
58
|
+
new Uint8Array([0x0a]),
|
|
59
|
+
]
|
|
60
|
+
const result = ui8Concat(arrays)
|
|
61
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
62
|
+
expect(
|
|
63
|
+
ui8Equals(
|
|
64
|
+
result,
|
|
65
|
+
new Uint8Array([
|
|
66
|
+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
|
|
67
|
+
]),
|
|
68
|
+
),
|
|
69
|
+
).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('arrays with different lengths', () => {
|
|
74
|
+
it('concatenates arrays of varying lengths', () => {
|
|
75
|
+
const a = new Uint8Array([1])
|
|
76
|
+
const b = new Uint8Array([2, 3, 4, 5, 6])
|
|
77
|
+
const c = new Uint8Array([7, 8])
|
|
78
|
+
const result = ui8Concat([a, b, c])
|
|
79
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
80
|
+
expect(
|
|
81
|
+
ui8Equals(result, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])),
|
|
82
|
+
).toBe(true)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('handles large arrays', () => {
|
|
86
|
+
const size = 10000
|
|
87
|
+
const a = new Uint8Array(size).fill(0xaa)
|
|
88
|
+
const b = new Uint8Array(size).fill(0xbb)
|
|
89
|
+
const result = ui8Concat([a, b])
|
|
90
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
91
|
+
expect(result.length).toBe(size * 2)
|
|
92
|
+
// Check first array portion
|
|
93
|
+
for (let i = 0; i < size; i++) {
|
|
94
|
+
expect(result[i]).toBe(0xaa)
|
|
95
|
+
}
|
|
96
|
+
// Check second array portion
|
|
97
|
+
for (let i = size; i < size * 2; i++) {
|
|
98
|
+
expect(result[i]).toBe(0xbb)
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('empty Uint8Arrays in input', () => {
|
|
104
|
+
it('handles empty array at the beginning', () => {
|
|
105
|
+
const a = new Uint8Array(0)
|
|
106
|
+
const b = new Uint8Array([1, 2, 3])
|
|
107
|
+
const result = ui8Concat([a, b])
|
|
108
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
109
|
+
expect(ui8Equals(result, new Uint8Array([1, 2, 3]))).toBe(true)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('handles empty array in the middle', () => {
|
|
113
|
+
const a = new Uint8Array([1, 2])
|
|
114
|
+
const b = new Uint8Array(0)
|
|
115
|
+
const c = new Uint8Array([3, 4])
|
|
116
|
+
const result = ui8Concat([a, b, c])
|
|
117
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
118
|
+
expect(ui8Equals(result, new Uint8Array([1, 2, 3, 4]))).toBe(true)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('handles empty array at the end', () => {
|
|
122
|
+
const a = new Uint8Array([1, 2, 3])
|
|
123
|
+
const b = new Uint8Array(0)
|
|
124
|
+
const result = ui8Concat([a, b])
|
|
125
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
126
|
+
expect(ui8Equals(result, new Uint8Array([1, 2, 3]))).toBe(true)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('handles multiple empty arrays', () => {
|
|
130
|
+
const a = new Uint8Array(0)
|
|
131
|
+
const b = new Uint8Array([1])
|
|
132
|
+
const c = new Uint8Array(0)
|
|
133
|
+
const d = new Uint8Array([2])
|
|
134
|
+
const e = new Uint8Array(0)
|
|
135
|
+
const result = ui8Concat([a, b, c, d, e])
|
|
136
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
137
|
+
expect(ui8Equals(result, new Uint8Array([1, 2]))).toBe(true)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('handles all empty arrays', () => {
|
|
141
|
+
const a = new Uint8Array(0)
|
|
142
|
+
const b = new Uint8Array(0)
|
|
143
|
+
const c = new Uint8Array(0)
|
|
144
|
+
const result = ui8Concat([a, b, c])
|
|
145
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
146
|
+
expect(result.length).toBe(0)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe('byte value preservation', () => {
|
|
151
|
+
it('preserves all byte values from 0x00 to 0xff', () => {
|
|
152
|
+
const allBytes = new Uint8Array(256)
|
|
153
|
+
for (let i = 0; i < 256; i++) {
|
|
154
|
+
allBytes[i] = i
|
|
155
|
+
}
|
|
156
|
+
const result = ui8Concat([allBytes])
|
|
157
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
158
|
+
expect(ui8Equals(result, allBytes)).toBe(true)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('preserves boundary byte values in concatenation', () => {
|
|
162
|
+
const a = new Uint8Array([0x00, 0x01, 0x7f])
|
|
163
|
+
const b = new Uint8Array([0x80, 0xfe, 0xff])
|
|
164
|
+
const result = ui8Concat([a, b])
|
|
165
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
166
|
+
expect(
|
|
167
|
+
ui8Equals(
|
|
168
|
+
result,
|
|
169
|
+
new Uint8Array([0x00, 0x01, 0x7f, 0x80, 0xfe, 0xff]),
|
|
170
|
+
),
|
|
171
|
+
).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('subarray handling', () => {
|
|
176
|
+
it('correctly concatenates subarrays', () => {
|
|
177
|
+
const fullArray = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
178
|
+
const sub1 = fullArray.subarray(0, 3) // [0, 1, 2]
|
|
179
|
+
const sub2 = fullArray.subarray(5, 8) // [5, 6, 7]
|
|
180
|
+
const result = ui8Concat([sub1, sub2])
|
|
181
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
182
|
+
expect(ui8Equals(result, new Uint8Array([0, 1, 2, 5, 6, 7]))).toBe(true)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('correctly concatenates subarray with regular array', () => {
|
|
186
|
+
const fullArray = new Uint8Array([10, 20, 30, 40, 50])
|
|
187
|
+
const sub = fullArray.subarray(1, 4) // [20, 30, 40]
|
|
188
|
+
const regular = new Uint8Array([100, 200])
|
|
189
|
+
const result = ui8Concat([sub, regular])
|
|
190
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
191
|
+
expect(ui8Equals(result, new Uint8Array([20, 30, 40, 100, 200]))).toBe(
|
|
192
|
+
true,
|
|
193
|
+
)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NodeJSBuffer } from './lib/nodejs-buffer.js'
|
|
2
|
+
|
|
3
|
+
const Buffer = NodeJSBuffer
|
|
4
|
+
|
|
5
|
+
export const ui8ConcatNode = Buffer
|
|
6
|
+
? function ui8ConcatNode(array: readonly Uint8Array[]): Uint8Array {
|
|
7
|
+
return Buffer.concat(array)
|
|
8
|
+
}
|
|
9
|
+
: /* v8 ignore next -- @preserve */ null
|
|
10
|
+
|
|
11
|
+
export function ui8ConcatPonyfill(array: readonly Uint8Array[]): Uint8Array {
|
|
12
|
+
let totalLength = 0
|
|
13
|
+
for (const arr of array) totalLength += arr.length
|
|
14
|
+
const result = new Uint8Array(totalLength)
|
|
15
|
+
let offset = 0
|
|
16
|
+
for (const arr of array) {
|
|
17
|
+
result.set(arr, offset)
|
|
18
|
+
offset += arr.length
|
|
19
|
+
}
|
|
20
|
+
return result
|
|
21
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 'core-js/modules/es.uint8-array.from-base64.js'
|
|
2
2
|
import 'core-js/modules/es.uint8-array.to-base64.js'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { assert, describe, expect, it } from 'vitest'
|
|
4
5
|
import {
|
|
5
6
|
fromBase64Native,
|
|
6
7
|
fromBase64Node,
|
|
@@ -110,6 +111,8 @@ for (const fromBase64 of [
|
|
|
110
111
|
'YWFh' + '====',
|
|
111
112
|
'YWFh' + '=====',
|
|
112
113
|
'YWFh' + '======',
|
|
114
|
+
'TWEé',
|
|
115
|
+
'TWE👍',
|
|
113
116
|
// More invalid padding
|
|
114
117
|
// 'TWE=', // 'Ma'
|
|
115
118
|
'TWE=' + '=',
|
|
@@ -31,7 +31,7 @@ export const fromBase64Native =
|
|
|
31
31
|
lastChunkHandling: 'loose',
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
|
-
: null
|
|
34
|
+
: /* v8 ignore next -- @preserve */ null
|
|
35
35
|
|
|
36
36
|
export const fromBase64Node = Buffer
|
|
37
37
|
? function fromBase64Node(
|
|
@@ -45,7 +45,7 @@ export const fromBase64Node = Buffer
|
|
|
45
45
|
// results in unexpected behavior downstream (e.g. in tests)
|
|
46
46
|
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
47
47
|
}
|
|
48
|
-
: null
|
|
48
|
+
: /* v8 ignore next -- @preserve */ null
|
|
49
49
|
|
|
50
50
|
export function fromBase64Ponyfill(
|
|
51
51
|
b64: string,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'core-js/modules/es.uint8-array.from-base64.js'
|
|
2
2
|
import 'core-js/modules/es.uint8-array.to-base64.js'
|
|
3
|
-
import assert from '
|
|
3
|
+
import { assert, describe, expect, it } from 'vitest'
|
|
4
4
|
import {
|
|
5
5
|
toBase64Native,
|
|
6
6
|
toBase64Node,
|
|
@@ -14,9 +14,9 @@ for (const toBase64 of [
|
|
|
14
14
|
] as const) {
|
|
15
15
|
// Tests should run in NodeJS where implementations are either available or
|
|
16
16
|
// polyfilled (see core-js imports above).
|
|
17
|
-
assert(toBase64
|
|
17
|
+
assert(toBase64, 'toBase64 implementation should not be null')
|
|
18
18
|
|
|
19
|
-
describe(toBase64
|
|
19
|
+
describe(toBase64, () => {
|
|
20
20
|
describe('basic encoding', () => {
|
|
21
21
|
it('encodes empty Uint8Array', () => {
|
|
22
22
|
const encoded = toBase64(new Uint8Array(0))
|
|
@@ -25,7 +25,7 @@ export const toBase64Native =
|
|
|
25
25
|
): string {
|
|
26
26
|
return bytes.toBase64!({ alphabet, omitPadding: true })
|
|
27
27
|
}
|
|
28
|
-
: null
|
|
28
|
+
: /* v8 ignore next -- @preserve */ null
|
|
29
29
|
|
|
30
30
|
export const toBase64Node = Buffer
|
|
31
31
|
? function toBase64Node(
|
|
@@ -45,7 +45,7 @@ export const toBase64Node = Buffer
|
|
|
45
45
|
: b64.slice(0, -1) // '='
|
|
46
46
|
: b64
|
|
47
47
|
}
|
|
48
|
-
: null
|
|
48
|
+
: /* v8 ignore next -- @preserve */ null
|
|
49
49
|
|
|
50
50
|
export function toBase64Ponyfill(
|
|
51
51
|
bytes: Uint8Array,
|