@atproto/lex-data 0.1.3 → 0.1.4
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 +8 -0
- package/dist/blob.d.ts +2 -2
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +1 -1
- package/dist/blob.js.map +1 -1
- package/dist/lex-equals.d.ts +1 -1
- package/dist/lex-equals.d.ts.map +1 -1
- package/dist/lex-equals.js.map +1 -1
- package/dist/lex.d.ts +1 -1
- package/dist/lex.d.ts.map +1 -1
- package/dist/lex.js.map +1 -1
- package/dist/lib/nodejs-buffer.js.map +1 -1
- package/dist/uint8array-from-base64.d.ts +1 -1
- package/dist/uint8array-from-base64.d.ts.map +1 -1
- package/dist/uint8array-from-base64.js.map +1 -1
- package/dist/uint8array-to-base64.d.ts +1 -1
- package/dist/uint8array-to-base64.d.ts.map +1 -1
- package/dist/uint8array-to-base64.js.map +1 -1
- package/dist/uint8array.d.ts +1 -1
- package/dist/uint8array.d.ts.map +1 -1
- package/dist/uint8array.js.map +1 -1
- package/dist/utf8-from-base64.d.ts +1 -1
- package/dist/utf8-from-base64.d.ts.map +1 -1
- package/dist/utf8-from-base64.js.map +1 -1
- package/dist/utf8-to-base64.d.ts +1 -1
- package/dist/utf8-to-base64.d.ts.map +1 -1
- package/dist/utf8-to-base64.js.map +1 -1
- package/dist/utf8.d.ts +1 -1
- package/dist/utf8.d.ts.map +1 -1
- package/dist/utf8.js.map +1 -1
- package/package.json +4 -8
- package/src/blob.test.ts +0 -405
- package/src/blob.ts +0 -478
- package/src/cid-implementation.test.ts +0 -129
- package/src/cid.test.ts +0 -350
- package/src/cid.ts +0 -603
- package/src/core-js.d.ts +0 -2
- package/src/index.ts +0 -8
- package/src/lex-equals.test.ts +0 -183
- package/src/lex-equals.ts +0 -123
- package/src/lex-error.test.ts +0 -54
- package/src/lex-error.ts +0 -83
- package/src/lex.test.ts +0 -279
- package/src/lex.ts +0 -253
- package/src/lib/nodejs-buffer.ts +0 -46
- package/src/lib/util.test.ts +0 -49
- package/src/lib/util.ts +0 -7
- package/src/object.test.ts +0 -80
- package/src/object.ts +0 -83
- package/src/uint8array-base64.ts +0 -2
- package/src/uint8array-concat.test.ts +0 -197
- package/src/uint8array-concat.ts +0 -25
- package/src/uint8array-from-base64.test.ts +0 -130
- package/src/uint8array-from-base64.ts +0 -98
- package/src/uint8array-to-base64.test.ts +0 -170
- package/src/uint8array-to-base64.ts +0 -55
- package/src/uint8array.test.ts +0 -503
- package/src/uint8array.ts +0 -197
- package/src/utf8-from-base64.test.ts +0 -39
- package/src/utf8-from-base64.ts +0 -23
- package/src/utf8-from-bytes.test.ts +0 -43
- package/src/utf8-from-bytes.ts +0 -21
- package/src/utf8-grapheme-len.test.ts +0 -38
- package/src/utf8-grapheme-len.ts +0 -21
- package/src/utf8-len.test.ts +0 -21
- package/src/utf8-len.ts +0 -51
- package/src/utf8-to-base64.test.ts +0 -35
- package/src/utf8-to-base64.ts +0 -22
- package/src/utf8.ts +0 -128
- package/tsconfig.build.json +0 -12
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
package/src/object.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Checks whether the input is an object (not null).
|
|
3
|
-
*
|
|
4
|
-
* Returns true for any non-null value with typeof 'object', including
|
|
5
|
-
* arrays, plain objects, class instances, etc.
|
|
6
|
-
*
|
|
7
|
-
* @param input - The value to check
|
|
8
|
-
* @returns `true` if the input is an object (not null)
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* import { isObject } from '@atproto/lex-data'
|
|
13
|
-
*
|
|
14
|
-
* isObject({}) // true
|
|
15
|
-
* isObject([1, 2, 3]) // true
|
|
16
|
-
* isObject(new Date()) // true
|
|
17
|
-
* isObject(null) // false
|
|
18
|
-
* isObject('string') // false
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export function isObject(input: unknown): input is object {
|
|
22
|
-
return input != null && typeof input === 'object'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const ObjectProto = Object.prototype
|
|
26
|
-
const ObjectToString = Object.prototype.toString
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Checks whether the input is a plain object.
|
|
30
|
-
*
|
|
31
|
-
* A plain object is an object (not null) whose prototype is either null
|
|
32
|
-
* or `Object.prototype`. This excludes arrays, class instances, and other
|
|
33
|
-
* special objects.
|
|
34
|
-
*
|
|
35
|
-
* @param input - The value to check
|
|
36
|
-
* @returns `true` if the input is a plain object
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```typescript
|
|
40
|
-
* import { isPlainObject } from '@atproto/lex-data'
|
|
41
|
-
*
|
|
42
|
-
* isPlainObject({}) // true
|
|
43
|
-
* isPlainObject({ a: 1 }) // true
|
|
44
|
-
* isPlainObject(Object.create(null)) // true
|
|
45
|
-
* isPlainObject([1, 2, 3]) // false
|
|
46
|
-
* isPlainObject(new Date()) // false
|
|
47
|
-
* isPlainObject(null) // false
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
export function isPlainObject(input: unknown) {
|
|
51
|
-
return isObject(input) && isPlainProto(input)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Checks whether the prototype of an object is plain (null or Object.prototype).
|
|
56
|
-
*
|
|
57
|
-
* This is useful for checking if an object is a plain object without
|
|
58
|
-
* checking that it's non-null first (the null check is already done).
|
|
59
|
-
*
|
|
60
|
-
* @param input - The object to check (must be non-null)
|
|
61
|
-
* @returns `true` if the object's prototype is plain
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```typescript
|
|
65
|
-
* import { isPlainProto } from '@atproto/lex-data'
|
|
66
|
-
*
|
|
67
|
-
* isPlainProto({}) // true
|
|
68
|
-
* isPlainProto(Object.create(null)) // true
|
|
69
|
-
* isPlainProto([1, 2, 3]) // false (Array.prototype)
|
|
70
|
-
* isPlainProto(new Date()) // false (Date.prototype)
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export function isPlainProto(input: object): input is Record<string, unknown> {
|
|
74
|
-
const proto = Object.getPrototypeOf(input)
|
|
75
|
-
if (proto === null) return true
|
|
76
|
-
return (
|
|
77
|
-
(proto === ObjectProto ||
|
|
78
|
-
// Needed to support NodeJS's `runInNewContext` which produces objects
|
|
79
|
-
// with a different prototype
|
|
80
|
-
Object.getPrototypeOf(proto) === null) &&
|
|
81
|
-
ObjectToString.call(input) === '[object Object]'
|
|
82
|
-
)
|
|
83
|
-
}
|
package/src/uint8array-base64.ts
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
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
|
-
}
|
package/src/uint8array-concat.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { NodeJSBuffer } from './lib/nodejs-buffer.js'
|
|
2
|
-
|
|
3
|
-
const Buffer = NodeJSBuffer
|
|
4
|
-
|
|
5
|
-
export const ui8ConcatNode = Buffer
|
|
6
|
-
? function ui8ConcatNode(
|
|
7
|
-
array: readonly Uint8Array[],
|
|
8
|
-
): Uint8Array<ArrayBuffer> {
|
|
9
|
-
return Buffer.concat(array)
|
|
10
|
-
}
|
|
11
|
-
: /* v8 ignore next -- @preserve */ null
|
|
12
|
-
|
|
13
|
-
export function ui8ConcatPonyfill(
|
|
14
|
-
array: readonly Uint8Array[],
|
|
15
|
-
): Uint8Array<ArrayBuffer> {
|
|
16
|
-
let totalLength = 0
|
|
17
|
-
for (const arr of array) totalLength += arr.length
|
|
18
|
-
const result = new Uint8Array(totalLength)
|
|
19
|
-
let offset = 0
|
|
20
|
-
for (const arr of array) {
|
|
21
|
-
result.set(arr, offset)
|
|
22
|
-
offset += arr.length
|
|
23
|
-
}
|
|
24
|
-
return result
|
|
25
|
-
}
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import 'core-js/es/typed-array/from-base64.js'
|
|
2
|
-
import 'core-js/es/typed-array/to-base64.js'
|
|
3
|
-
|
|
4
|
-
import { assert, describe, expect, it } from 'vitest'
|
|
5
|
-
import {
|
|
6
|
-
fromBase64Native,
|
|
7
|
-
fromBase64Node,
|
|
8
|
-
fromBase64Ponyfill,
|
|
9
|
-
} from './uint8array-from-base64.js'
|
|
10
|
-
import { ui8Equals } from './uint8array.js'
|
|
11
|
-
|
|
12
|
-
// @NOTE This test suite relies on the NodeJS Buffer implementation to generate
|
|
13
|
-
// valid base64 strings for testing.
|
|
14
|
-
|
|
15
|
-
// @NOTE b64 needs a test suite because fromBase64 implementations differ in
|
|
16
|
-
// their behavior when encountering invalid base64 strings. This is not the case
|
|
17
|
-
// for toBase64, which is straightforward and has no edge cases.
|
|
18
|
-
|
|
19
|
-
for (const fromBase64 of [
|
|
20
|
-
fromBase64Native,
|
|
21
|
-
fromBase64Node,
|
|
22
|
-
fromBase64Ponyfill,
|
|
23
|
-
] as const) {
|
|
24
|
-
// Tests should run in NodeJS where implementations are either available or
|
|
25
|
-
// polyfilled (see core-js imports above).
|
|
26
|
-
assert(fromBase64 !== null, 'fromBase64 implementation should not be null')
|
|
27
|
-
|
|
28
|
-
describe(fromBase64.name, () => {
|
|
29
|
-
describe('valid base64 strings', () => {
|
|
30
|
-
it('decodes empty string', () => {
|
|
31
|
-
const decoded = fromBase64('')
|
|
32
|
-
expect(decoded).toBeInstanceOf(Uint8Array)
|
|
33
|
-
expect(decoded.length).toBe(0)
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('decodes 10MB', () => {
|
|
37
|
-
const bytes = Buffer.allocUnsafe(10_000_000).fill('🐩')
|
|
38
|
-
const encoded = bytes.toString('base64')
|
|
39
|
-
const decoded = fromBase64(encoded)
|
|
40
|
-
expect(decoded).toBeInstanceOf(Uint8Array)
|
|
41
|
-
expect(ui8Equals(decoded, bytes)).toBe(true)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
for (const buffer of [
|
|
45
|
-
Buffer.from(''),
|
|
46
|
-
Buffer.from('\0\0'),
|
|
47
|
-
Buffer.from('\0\0\0'),
|
|
48
|
-
Buffer.from('\0\0\0\0'),
|
|
49
|
-
Buffer.from('__'),
|
|
50
|
-
Buffer.from('é'),
|
|
51
|
-
Buffer.from('àç'),
|
|
52
|
-
Buffer.from('\0éàç'),
|
|
53
|
-
Buffer.from('```'),
|
|
54
|
-
Buffer.from('aaa'),
|
|
55
|
-
Buffer.from('Hello, World!'),
|
|
56
|
-
Buffer.from('😀😃😄😁😆😅😂🤣😊😇'),
|
|
57
|
-
Buffer.from('👩💻👨💻👩🔬👨🔬👩🚀👨🚀'),
|
|
58
|
-
Buffer.from('🌍🌎🌏🌐🪐🌟✨⚡🔥💧'),
|
|
59
|
-
Buffer.from(new Uint8Array([0xfb, 0xff, 0xbf])),
|
|
60
|
-
Buffer.from(new Uint8Array([0xfb, 0xff, 0xbf])),
|
|
61
|
-
Buffer.from(new Uint8Array([0x4d])),
|
|
62
|
-
Buffer.from(new Uint8Array([0x4d, 0x61])),
|
|
63
|
-
Buffer.from(new Uint8Array([0x4d, 0x61, 0x6e])),
|
|
64
|
-
Buffer.from(new Uint8Array([0x4d])),
|
|
65
|
-
Buffer.from(new Uint8Array([0x4d, 0x61])),
|
|
66
|
-
Buffer.from(new Uint8Array([0x00, 0x4d, 0x61, 0x6e, 0x00])),
|
|
67
|
-
]) {
|
|
68
|
-
const base64 = buffer.toString('base64')
|
|
69
|
-
const base64Unpadded = base64.replace(/=+$/, '')
|
|
70
|
-
const base64url = buffer.toString('base64url') // No padding in base64url
|
|
71
|
-
|
|
72
|
-
it(`decodes ${JSON.stringify(base64)}`, () => {
|
|
73
|
-
const decoded = fromBase64(base64)
|
|
74
|
-
expect(decoded).toBeInstanceOf(Uint8Array)
|
|
75
|
-
expect(ui8Equals(decoded, buffer)).toBe(true)
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it(`decodes ${JSON.stringify(base64url)} (base64url)`, () => {
|
|
79
|
-
const decoded = fromBase64(base64url, 'base64url')
|
|
80
|
-
expect(decoded).toBeInstanceOf(Uint8Array)
|
|
81
|
-
expect(ui8Equals(decoded, buffer)).toBe(true)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
if (base64 !== base64Unpadded) {
|
|
85
|
-
it(`decodes ${JSON.stringify(base64Unpadded)} (unpadded)`, () => {
|
|
86
|
-
const decoded = fromBase64(base64Unpadded)
|
|
87
|
-
expect(decoded).toBeInstanceOf(Uint8Array)
|
|
88
|
-
expect(ui8Equals(decoded, buffer)).toBe(true)
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
describe('invalid base64 strings', () => {
|
|
95
|
-
for (const invalidB64 of [
|
|
96
|
-
'çç',
|
|
97
|
-
'é',
|
|
98
|
-
'YWJjZGU$$$',
|
|
99
|
-
'@@@@',
|
|
100
|
-
'abcd!',
|
|
101
|
-
'ab=cd',
|
|
102
|
-
// "YWFh" is "aaa" in base64
|
|
103
|
-
'YWFh' + 'é',
|
|
104
|
-
'YWFh' + 'éé',
|
|
105
|
-
'YWFh' + 'ééé',
|
|
106
|
-
'YWFh' + 'éééé',
|
|
107
|
-
// Invalid padding
|
|
108
|
-
'YWFh' + '=',
|
|
109
|
-
'YWFh' + '==',
|
|
110
|
-
'YWFh' + '===',
|
|
111
|
-
'YWFh' + '====',
|
|
112
|
-
'YWFh' + '=====',
|
|
113
|
-
'YWFh' + '======',
|
|
114
|
-
'TWEé',
|
|
115
|
-
'TWE👍',
|
|
116
|
-
// More invalid padding
|
|
117
|
-
// 'TWE=', // 'Ma'
|
|
118
|
-
'TWE=' + '=',
|
|
119
|
-
'TWE=' + '==',
|
|
120
|
-
// 'TQ==', // 'M'
|
|
121
|
-
'TQ==' + '=',
|
|
122
|
-
'TQ==' + '==',
|
|
123
|
-
] as const) {
|
|
124
|
-
it(`throws on invalid base64 string "${invalidB64}"`, () => {
|
|
125
|
-
expect(() => fromBase64(invalidB64)).toThrow()
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import { fromString } from 'uint8arrays/from-string'
|
|
2
|
-
import { NodeJSBuffer } from './lib/nodejs-buffer.js'
|
|
3
|
-
import { Base64Alphabet } from './uint8array-base64.js'
|
|
4
|
-
|
|
5
|
-
const Buffer = NodeJSBuffer
|
|
6
|
-
|
|
7
|
-
declare global {
|
|
8
|
-
interface Uint8ArrayConstructor {
|
|
9
|
-
/**
|
|
10
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 Uint8Array.fromBase64()}
|
|
11
|
-
*/
|
|
12
|
-
fromBase64?: (
|
|
13
|
-
b64: string,
|
|
14
|
-
options?: {
|
|
15
|
-
/** @default 'base64' */
|
|
16
|
-
alphabet?: 'base64' | 'base64url'
|
|
17
|
-
lastChunkHandling?: 'loose' | 'strict' | 'stop-before-partial'
|
|
18
|
-
},
|
|
19
|
-
) => Uint8Array
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export const fromBase64Native =
|
|
24
|
-
typeof Uint8Array.fromBase64 === 'function'
|
|
25
|
-
? function fromBase64Native(
|
|
26
|
-
b64: string,
|
|
27
|
-
alphabet: Base64Alphabet = 'base64',
|
|
28
|
-
): Uint8Array {
|
|
29
|
-
return Uint8Array.fromBase64!(b64, {
|
|
30
|
-
alphabet,
|
|
31
|
-
lastChunkHandling: 'loose',
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
: /* v8 ignore next -- @preserve */ null
|
|
35
|
-
|
|
36
|
-
export const fromBase64Node = Buffer
|
|
37
|
-
? function fromBase64Node(
|
|
38
|
-
b64: string,
|
|
39
|
-
alphabet: Base64Alphabet = 'base64',
|
|
40
|
-
): Uint8Array {
|
|
41
|
-
const bytes = Buffer.from(b64, alphabet)
|
|
42
|
-
verifyBase64ForBytes(b64, bytes)
|
|
43
|
-
// Convert to Uint8Array because even though Buffer is a sub class of
|
|
44
|
-
// Uint8Array, it serializes differently to Uint8Array (e.g. in JSON) and
|
|
45
|
-
// results in unexpected behavior downstream (e.g. in tests)
|
|
46
|
-
return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
47
|
-
}
|
|
48
|
-
: /* v8 ignore next -- @preserve */ null
|
|
49
|
-
|
|
50
|
-
export function fromBase64Ponyfill(
|
|
51
|
-
b64: string,
|
|
52
|
-
alphabet: Base64Alphabet = 'base64',
|
|
53
|
-
): Uint8Array {
|
|
54
|
-
const bytes = fromString(b64, b64.endsWith('=') ? `${alphabet}pad` : alphabet)
|
|
55
|
-
verifyBase64ForBytes(b64, bytes)
|
|
56
|
-
return bytes
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// @NOTE NodeJS will silently stop decoding at the first invalid character,
|
|
60
|
-
// while "uint8arrays/from-string" will not validate that the padding is
|
|
61
|
-
// correct. The following function performs basic validation to ensure that the
|
|
62
|
-
// input was a valid base64 string. The availability of the "bytes" allows
|
|
63
|
-
// to perform checks with O[1] complexity.
|
|
64
|
-
function verifyBase64ForBytes(b64: string, bytes: Uint8Array): void {
|
|
65
|
-
const paddingCount = b64.endsWith('==') ? 2 : b64.endsWith('=') ? 1 : 0
|
|
66
|
-
const trimmedLength = b64.length - paddingCount
|
|
67
|
-
const expectedByteLength = Math.floor((trimmedLength * 3) / 4)
|
|
68
|
-
if (bytes.length !== expectedByteLength) {
|
|
69
|
-
throw new Error('Invalid base64 string')
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const expectedB64Length = (bytes.length / 3) * 4
|
|
73
|
-
const expectedPaddingCount =
|
|
74
|
-
expectedB64Length % 4 === 0 ? 0 : 4 - (expectedB64Length % 4)
|
|
75
|
-
const expectedFullB64Length = expectedB64Length + expectedPaddingCount
|
|
76
|
-
if (b64.length > expectedFullB64Length) {
|
|
77
|
-
throw new Error('Invalid base64 string')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// The previous might still allow false positive if only the last few
|
|
81
|
-
// chars are invalid.
|
|
82
|
-
for (
|
|
83
|
-
let i = Math.ceil(expectedB64Length);
|
|
84
|
-
i < b64.length - paddingCount;
|
|
85
|
-
i++
|
|
86
|
-
) {
|
|
87
|
-
const code = b64.charCodeAt(i)
|
|
88
|
-
if (
|
|
89
|
-
!(code >= 65 && code <= 90) && // A-Z
|
|
90
|
-
!(code >= 97 && code <= 122) && // a-z
|
|
91
|
-
!(code >= 48 && code <= 57) && // 0-9
|
|
92
|
-
code !== 43 && // +
|
|
93
|
-
code !== 47 // /
|
|
94
|
-
) {
|
|
95
|
-
throw new Error('Invalid base64 string')
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|