@memberjunction/global 6.2.0-edge.2 → 6.2.0-edge.3
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/README.md +26 -0
- package/dist/__tests__/BinaryEncoding.test.d.ts +2 -0
- package/dist/__tests__/BinaryEncoding.test.d.ts.map +1 -0
- package/dist/__tests__/BinaryEncoding.test.js +244 -0
- package/dist/__tests__/BinaryEncoding.test.js.map +1 -0
- package/dist/__tests__/BinaryEncodingRows.test.d.ts +2 -0
- package/dist/__tests__/BinaryEncodingRows.test.d.ts.map +1 -0
- package/dist/__tests__/BinaryEncodingRows.test.js +146 -0
- package/dist/__tests__/BinaryEncodingRows.test.js.map +1 -0
- package/dist/__tests__/BinaryEncodingValidation.test.d.ts +2 -0
- package/dist/__tests__/BinaryEncodingValidation.test.d.ts.map +1 -0
- package/dist/__tests__/BinaryEncodingValidation.test.js +98 -0
- package/dist/__tests__/BinaryEncodingValidation.test.js.map +1 -0
- 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/util/BinaryEncoding.d.ts +197 -0
- package/dist/util/BinaryEncoding.d.ts.map +1 -0
- package/dist/util/BinaryEncoding.js +388 -0
- package/dist/util/BinaryEncoding.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -515,6 +515,32 @@ IsValueEncrypted('plain text'); // false
|
|
|
515
515
|
IsEncryptedSentinel('[!ENCRYPTED$]'); // true
|
|
516
516
|
```
|
|
517
517
|
|
|
518
|
+
### Binary Encoding
|
|
519
|
+
|
|
520
|
+
Above the database, MemberJunction represents a binary column (`varbinary` / `binary` / `image`, PostgreSQL `bytea`) as a **base64 string** — in `BaseEntity` fields, caches, `RunView` rows and GraphQL alike. These helpers convert and validate it. The codec is picked once per process from the fastest the host offers (native `Uint8Array.fromBase64`/`toBase64`, Node `Buffer`, then `atob`/`btoa`); every codec accepts and produces the same strings. See the [Binary Fields Guide](../../guides/BINARY_FIELDS_GUIDE.md).
|
|
521
|
+
|
|
522
|
+
| Function | Description |
|
|
523
|
+
|---|---|
|
|
524
|
+
| `BytesToBase64(bytes)` | Bytes to standard, padded base64 |
|
|
525
|
+
| `Base64ToBytes(b64)` | Base64 to bytes; throws on invalid input |
|
|
526
|
+
| `TryBase64ToBytes(b64)` | Same, but returns `null` for null/undefined or invalid input |
|
|
527
|
+
| `IsValidBase64(value)` | Canonical base64 only: standard alphabet, optional padding, no whitespace, data-URI prefix or URL-safe alphabet |
|
|
528
|
+
| `HasValidBase64Shape(value)` | Length/padding shape check only |
|
|
529
|
+
| `Base64DecodedByteLength(b64)` | Decoded byte count without decoding (for size checks) |
|
|
530
|
+
| `IsByteArray(value)` | True for a `Uint8Array` (including a Node `Buffer`) |
|
|
531
|
+
| `ReplaceByteArraysWithBase64(row)` | Replaces top-level byte arrays in a row with base64; copy-on-write, never mutates the input |
|
|
532
|
+
| `Float32VectorToBytes(values)` / `BytesToFloat32Vector(bytes)` | A vector as little-endian float32 bytes (4 per value, no header); decode returns `null` for empty or misaligned input |
|
|
533
|
+
| `Float32VectorToBase64(values)` / `Base64ToFloat32Vector(b64)` | The same, as the base64 value stored in a binary vector field such as `VectorBinary` |
|
|
534
|
+
| `GetBase64Codec()` / `Base64Codecs` | The codec in use / every available codec (diagnostics and tests) |
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { Float32VectorToBase64, Base64ToFloat32Vector, TryBase64ToBytes } from '@memberjunction/global';
|
|
538
|
+
|
|
539
|
+
doc.VectorBinary = Float32VectorToBase64(embedding); // write
|
|
540
|
+
const vector = Base64ToFloat32Vector(doc.VectorBinary); // Float32Array | null
|
|
541
|
+
const bytes = TryBase64ToBytes(file.Content); // Uint8Array | null
|
|
542
|
+
```
|
|
543
|
+
|
|
518
544
|
### String and JSON Utilities
|
|
519
545
|
|
|
520
546
|
A collection of utility functions for common string and JSON operations.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncoding.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/BinaryEncoding.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import { BINARY_CHANGE_VALUE_PREFIX, FormatBinaryChangeValue, IsBinaryChangeValue, Base64Codecs, Base64ToBytes, Base64ToFloat32Vector, BytesToBase64, BytesToFloat32Vector, Float32VectorCodecs, Float32VectorToBase64, Float32VectorToBytes, GetBase64Codec, IsByteArray, IsValidBase64, TryBase64ToBytes, } from '../util/BinaryEncoding.js';
|
|
3
|
+
/** Deterministic pseudo-random bytes, so failures reproduce. */
|
|
4
|
+
function bytesOf(length, seed = 7) {
|
|
5
|
+
const out = new Uint8Array(length);
|
|
6
|
+
let s = seed;
|
|
7
|
+
for (let i = 0; i < length; i++) {
|
|
8
|
+
s = (s * 16807) % 2147483647;
|
|
9
|
+
out[i] = s & 0xff;
|
|
10
|
+
}
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
const [nativeCodec, nodeCodec, portableCodec] = Base64Codecs;
|
|
14
|
+
/**
|
|
15
|
+
* Installs a stand-in for the TC39 `Uint8Array.fromBase64` / `toBase64` API (absent in Node 22)
|
|
16
|
+
* that records how it was called, so the native implementation can be exercised.
|
|
17
|
+
*/
|
|
18
|
+
function installNativeBase64() {
|
|
19
|
+
const calls = [];
|
|
20
|
+
const statics = Uint8Array;
|
|
21
|
+
const proto = Uint8Array.prototype;
|
|
22
|
+
statics.fromBase64 = (input, options) => {
|
|
23
|
+
calls.push(`fromBase64:${options?.lastChunkHandling ?? 'none'}`);
|
|
24
|
+
return new Uint8Array(Buffer.from(input, 'base64'));
|
|
25
|
+
};
|
|
26
|
+
proto.toBase64 = function () {
|
|
27
|
+
calls.push('toBase64');
|
|
28
|
+
return Buffer.from(this.buffer, this.byteOffset, this.byteLength).toString('base64');
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
calls,
|
|
32
|
+
restore: () => {
|
|
33
|
+
delete statics.fromBase64;
|
|
34
|
+
delete proto.toBase64;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
describe('IsValidBase64', () => {
|
|
39
|
+
it.each(['', 'AA==', 'AAA=', 'AAAA', 'AAAAAA', 'AQID', '+/+/', 'aGVsbG8='])('accepts %j', value => {
|
|
40
|
+
expect(IsValidBase64(value)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it.each([
|
|
43
|
+
['a single dangling character', 'A'],
|
|
44
|
+
['five characters (one dangling)', 'AAAAA'],
|
|
45
|
+
['whitespace', 'AA AA'],
|
|
46
|
+
['a line break', 'AAAA\nAAAA'],
|
|
47
|
+
['the URL-safe alphabet', 'AA-_'],
|
|
48
|
+
['a data URI', 'data:image/png;base64,AAAA'],
|
|
49
|
+
['padding before the end', 'AA=A'],
|
|
50
|
+
['three padding characters', 'A==='],
|
|
51
|
+
['padding on a partial quantum', 'AAAAA='],
|
|
52
|
+
['non-ASCII', 'AAÀA'],
|
|
53
|
+
])('rejects %s', (_label, value) => {
|
|
54
|
+
expect(IsValidBase64(value)).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('base64 implementations', () => {
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
// Defensive: a failed test must not leave the stand-in installed for the next one.
|
|
60
|
+
delete Uint8Array.fromBase64;
|
|
61
|
+
delete Uint8Array.prototype.toBase64;
|
|
62
|
+
});
|
|
63
|
+
it('lists the native API first, then Node Buffer, then atob/btoa', () => {
|
|
64
|
+
expect(Base64Codecs.map(c => c.Name)).toEqual([
|
|
65
|
+
'Uint8Array.fromBase64 / toBase64',
|
|
66
|
+
'Node Buffer',
|
|
67
|
+
'atob / btoa',
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
it('detects the native API by duck typing', () => {
|
|
71
|
+
expect(nativeCodec.IsAvailable()).toBe(false); // Node 22 has no Uint8Array.fromBase64
|
|
72
|
+
const native = installNativeBase64();
|
|
73
|
+
expect(nativeCodec.IsAvailable()).toBe(true);
|
|
74
|
+
native.restore();
|
|
75
|
+
expect(nativeCodec.IsAvailable()).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
it('selects Node Buffer on Node, and keeps that choice for the life of the process', () => {
|
|
78
|
+
expect(GetBase64Codec()).toBe(nodeCodec);
|
|
79
|
+
const native = installNativeBase64();
|
|
80
|
+
expect(GetBase64Codec()).toBe(nodeCodec); // selection is cached, not re-evaluated
|
|
81
|
+
native.restore();
|
|
82
|
+
});
|
|
83
|
+
it('calls the native API with loose final-chunk handling, so unpadded input decodes', () => {
|
|
84
|
+
const native = installNativeBase64();
|
|
85
|
+
expect(Array.from(nativeCodec.Decode('AQI'))).toEqual([1, 2]);
|
|
86
|
+
expect(nativeCodec.Encode(new Uint8Array([1, 2]))).toBe('AQI=');
|
|
87
|
+
expect(native.calls).toEqual(['fromBase64:loose', 'toBase64']);
|
|
88
|
+
native.restore();
|
|
89
|
+
});
|
|
90
|
+
it.each([0, 1, 2, 3, 4, 5, 255, 256, 1536 * 4, 0x8000 - 1, 0x8000, 0x8000 + 1, 200_000])('produces identical strings and bytes in every implementation for %i bytes', length => {
|
|
91
|
+
const native = installNativeBase64();
|
|
92
|
+
const bytes = bytesOf(length, length + 1);
|
|
93
|
+
const expected = Buffer.from(bytes).toString('base64');
|
|
94
|
+
for (const codec of Base64Codecs) {
|
|
95
|
+
expect(codec.Encode(bytes), codec.Name).toBe(expected);
|
|
96
|
+
expect(Array.from(codec.Decode(expected)), codec.Name).toEqual(Array.from(bytes));
|
|
97
|
+
}
|
|
98
|
+
native.restore();
|
|
99
|
+
});
|
|
100
|
+
it('encodes only the viewed range of a subarray in every implementation', () => {
|
|
101
|
+
const native = installNativeBase64();
|
|
102
|
+
const backing = bytesOf(64);
|
|
103
|
+
const view = backing.subarray(5, 21);
|
|
104
|
+
const expected = Buffer.from(view).toString('base64');
|
|
105
|
+
for (const codec of Base64Codecs)
|
|
106
|
+
expect(codec.Encode(view), codec.Name).toBe(expected);
|
|
107
|
+
native.restore();
|
|
108
|
+
});
|
|
109
|
+
it('returns bytes that own their buffer from the Node implementation, never a pooled view', () => {
|
|
110
|
+
const decoded = nodeCodec.Decode('AQID');
|
|
111
|
+
expect(decoded.byteOffset).toBe(0);
|
|
112
|
+
expect(decoded.buffer.byteLength).toBe(3);
|
|
113
|
+
expect(Buffer.isBuffer(decoded)).toBe(false);
|
|
114
|
+
});
|
|
115
|
+
it('reports the portable implementation as available wherever atob and btoa exist', () => {
|
|
116
|
+
expect(portableCodec.IsAvailable()).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('BytesToBase64 / Base64ToBytes', () => {
|
|
120
|
+
it('round-trips every byte value', () => {
|
|
121
|
+
const all = new Uint8Array(256).map((_, i) => i);
|
|
122
|
+
expect(Array.from(Base64ToBytes(BytesToBase64(all)))).toEqual(Array.from(all));
|
|
123
|
+
});
|
|
124
|
+
it('maps zero bytes to the empty string and back', () => {
|
|
125
|
+
expect(BytesToBase64(new Uint8Array(0))).toBe('');
|
|
126
|
+
expect(Base64ToBytes('').length).toBe(0);
|
|
127
|
+
});
|
|
128
|
+
it('accepts a Node Buffer as input', () => {
|
|
129
|
+
expect(BytesToBase64(Buffer.from([1, 2, 3]))).toBe('AQID');
|
|
130
|
+
});
|
|
131
|
+
it('decodes unpadded input the same as padded input', () => {
|
|
132
|
+
expect(Array.from(Base64ToBytes('AQI'))).toEqual(Array.from(Base64ToBytes('AQI=')));
|
|
133
|
+
});
|
|
134
|
+
it('throws on invalid input instead of guessing, unlike a lenient Buffer.from', () => {
|
|
135
|
+
expect(Buffer.from('AA AA', 'base64').length).toBeGreaterThan(0); // Node alone would accept this
|
|
136
|
+
expect(() => Base64ToBytes('AA AA')).toThrow('Invalid base64 value (length 5)');
|
|
137
|
+
expect(() => Base64ToBytes('A')).toThrow('Invalid base64');
|
|
138
|
+
});
|
|
139
|
+
it('returns null from TryBase64ToBytes for missing or invalid input', () => {
|
|
140
|
+
expect(TryBase64ToBytes(null)).toBeNull();
|
|
141
|
+
expect(TryBase64ToBytes(undefined)).toBeNull();
|
|
142
|
+
expect(TryBase64ToBytes('not base64!')).toBeNull();
|
|
143
|
+
expect(TryBase64ToBytes('')?.length).toBe(0);
|
|
144
|
+
expect(Array.from(TryBase64ToBytes('AQID') ?? [])).toEqual([1, 2, 3]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('IsByteArray', () => {
|
|
148
|
+
it('recognises Uint8Array and Node Buffer, and nothing else', () => {
|
|
149
|
+
expect(IsByteArray(new Uint8Array(1))).toBe(true);
|
|
150
|
+
expect(IsByteArray(Buffer.from('x'))).toBe(true);
|
|
151
|
+
expect(IsByteArray('AQID')).toBe(false);
|
|
152
|
+
expect(IsByteArray(new Uint16Array(1))).toBe(false);
|
|
153
|
+
expect(IsByteArray(new ArrayBuffer(4))).toBe(false);
|
|
154
|
+
expect(IsByteArray(null)).toBe(false);
|
|
155
|
+
expect(IsByteArray({ type: 'Buffer', data: [1] })).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('float32 vectors', () => {
|
|
159
|
+
const sample = [0, 1, -1, 0.5, -2.25, 3.4028234663852886e38, 1e-45, Math.PI];
|
|
160
|
+
it('writes little-endian IEEE-754 single precision, 4 bytes per value, no header', () => {
|
|
161
|
+
const bytes = Float32VectorToBytes([1, -2]);
|
|
162
|
+
// 1.0f = 0x3F800000, -2.0f = 0xC0000000, least significant byte first
|
|
163
|
+
expect(Array.from(bytes)).toEqual([0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0xc0]);
|
|
164
|
+
});
|
|
165
|
+
it('produces identical bytes and values in both implementations', () => {
|
|
166
|
+
const [typedArray, dataView] = Float32VectorCodecs;
|
|
167
|
+
const expected = typedArray.Encode(sample);
|
|
168
|
+
expect(Array.from(dataView.Encode(sample))).toEqual(Array.from(expected));
|
|
169
|
+
expect(Array.from(dataView.Decode(expected))).toEqual(Array.from(typedArray.Decode(expected)));
|
|
170
|
+
});
|
|
171
|
+
it('round-trips through bytes and through base64, rounding to single precision', () => {
|
|
172
|
+
const expected = Array.from(Float32Array.from(sample));
|
|
173
|
+
expect(Array.from(BytesToFloat32Vector(Float32VectorToBytes(sample)) ?? [])).toEqual(expected);
|
|
174
|
+
expect(Array.from(Base64ToFloat32Vector(Float32VectorToBase64(sample)) ?? [])).toEqual(expected);
|
|
175
|
+
expect(Base64ToFloat32Vector(Float32VectorToBase64([0.1]))?.[0]).toBe(Math.fround(0.1));
|
|
176
|
+
});
|
|
177
|
+
it('accepts a Float32Array as input', () => {
|
|
178
|
+
const source = Float32Array.from([1.5, 2.5]);
|
|
179
|
+
expect(Array.from(BytesToFloat32Vector(Float32VectorToBytes(source)) ?? [])).toEqual([1.5, 2.5]);
|
|
180
|
+
});
|
|
181
|
+
it('preserves NaN and infinities, leaving validation to the caller', () => {
|
|
182
|
+
const decoded = Base64ToFloat32Vector(Float32VectorToBase64([NaN, Infinity, -Infinity]));
|
|
183
|
+
expect(Number.isNaN(decoded?.[0])).toBe(true);
|
|
184
|
+
expect(decoded?.[1]).toBe(Infinity);
|
|
185
|
+
expect(decoded?.[2]).toBe(-Infinity);
|
|
186
|
+
});
|
|
187
|
+
it('decodes from a misaligned view, such as a pooled Node Buffer, in both implementations', () => {
|
|
188
|
+
const encoded = Float32VectorToBytes([1, 2, 3]);
|
|
189
|
+
const backing = new Uint8Array(encoded.length + 1);
|
|
190
|
+
backing.set(encoded, 1);
|
|
191
|
+
const misaligned = backing.subarray(1); // byteOffset 1: a Float32Array view over it would throw
|
|
192
|
+
for (const codec of Float32VectorCodecs) {
|
|
193
|
+
expect(Array.from(codec.Decode(misaligned)), codec.Name).toEqual([1, 2, 3]);
|
|
194
|
+
}
|
|
195
|
+
expect(Array.from(BytesToFloat32Vector(misaligned) ?? [])).toEqual([1, 2, 3]);
|
|
196
|
+
});
|
|
197
|
+
it('returns a vector that owns its memory, so later writes to the source do not leak in', () => {
|
|
198
|
+
const bytes = Float32VectorToBytes([1, 2]);
|
|
199
|
+
const decoded = BytesToFloat32Vector(bytes);
|
|
200
|
+
bytes.fill(0);
|
|
201
|
+
expect(Array.from(decoded ?? [])).toEqual([1, 2]);
|
|
202
|
+
});
|
|
203
|
+
it.each([
|
|
204
|
+
['null', null],
|
|
205
|
+
['undefined', undefined],
|
|
206
|
+
['zero bytes', new Uint8Array(0)],
|
|
207
|
+
['6 bytes (not a whole number of values)', new Uint8Array(6)],
|
|
208
|
+
['1 byte', new Uint8Array(1)],
|
|
209
|
+
])('returns null for %s', (_label, bytes) => {
|
|
210
|
+
expect(BytesToFloat32Vector(bytes)).toBeNull();
|
|
211
|
+
});
|
|
212
|
+
it('returns null for a missing, invalid or truncated base64 vector', () => {
|
|
213
|
+
expect(Base64ToFloat32Vector(null)).toBeNull();
|
|
214
|
+
expect(Base64ToFloat32Vector('')).toBeNull();
|
|
215
|
+
expect(Base64ToFloat32Vector('%%%%')).toBeNull();
|
|
216
|
+
expect(Base64ToFloat32Vector(Buffer.from([1, 2, 3, 4, 5, 6]).toString('base64'))).toBeNull();
|
|
217
|
+
});
|
|
218
|
+
it('encodes an empty vector as zero bytes', () => {
|
|
219
|
+
expect(Float32VectorToBytes([]).length).toBe(0);
|
|
220
|
+
expect(Float32VectorToBase64([])).toBe('');
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
describe('FormatBinaryChangeValue / IsBinaryChangeValue (change-log text for a binary value)', () => {
|
|
224
|
+
it('reports the decoded size with a thousands separator, never the content', () => {
|
|
225
|
+
expect(FormatBinaryChangeValue('AQID')).toBe('[binary: 3 bytes]');
|
|
226
|
+
expect(FormatBinaryChangeValue('AQIDBA==')).toBe('[binary: 4 bytes]');
|
|
227
|
+
expect(FormatBinaryChangeValue('A'.repeat(8192))).toBe('[binary: 6,144 bytes]');
|
|
228
|
+
expect(FormatBinaryChangeValue('')).toBe('[binary: 0 bytes]');
|
|
229
|
+
});
|
|
230
|
+
it('reports invalid base64 as such instead of throwing', () => {
|
|
231
|
+
expect(FormatBinaryChangeValue('not base64!')).toBe('[binary: invalid base64]');
|
|
232
|
+
expect(FormatBinaryChangeValue('data:image/png;base64,AAAA')).toBe('[binary: invalid base64]');
|
|
233
|
+
});
|
|
234
|
+
it('recognises its own output and nothing else', () => {
|
|
235
|
+
expect(IsBinaryChangeValue(FormatBinaryChangeValue('AQID'))).toBe(true);
|
|
236
|
+
expect(IsBinaryChangeValue('[binary: invalid base64]')).toBe(true);
|
|
237
|
+
expect(IsBinaryChangeValue('AQID')).toBe(false);
|
|
238
|
+
expect(IsBinaryChangeValue('[binary')).toBe(false);
|
|
239
|
+
expect(IsBinaryChangeValue(null)).toBe(false);
|
|
240
|
+
expect(IsBinaryChangeValue(42)).toBe(false);
|
|
241
|
+
expect(FormatBinaryChangeValue('AQID').startsWith(BINARY_CHANGE_VALUE_PREFIX)).toBe(true);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
//# sourceMappingURL=BinaryEncoding.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncoding.test.js","sourceRoot":"","sources":["../../src/__tests__/BinaryEncoding.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,mBAAmB,EAC7E,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,aAAa,EACb,gBAAgB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,gEAAgE;AAChE,SAAS,OAAO,CAAC,MAAc,EAAE,IAAI,GAAG,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,IAAI,CAAC,GAAG,IAAI,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,UAAU,CAAC;QAC7B,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACtB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,YAAY,CAAC;AAE7D;;;GAGG;AACH,SAAS,mBAAmB;IACxB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,UAAgD,CAAC;IACjE,MAAM,KAAK,GAAG,UAAU,CAAC,SAA+C,CAAC;IACzE,OAAO,CAAC,UAAU,GAAG,CAAC,KAAa,EAAE,OAAwC,EAAE,EAAE;QAC7E,KAAK,CAAC,IAAI,CAAC,cAAc,OAAO,EAAE,iBAAiB,IAAI,MAAM,EAAE,CAAC,CAAC;QACjE,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;IACF,KAAK,CAAC,QAAQ,GAAG;QACb,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzF,CAAC,CAAC;IACF,OAAO;QACH,KAAK;QACL,OAAO,EAAE,GAAG,EAAE;YACV,OAAO,OAAO,CAAC,UAAU,CAAC;YAC1B,OAAO,KAAK,CAAC,QAAQ,CAAC;QAC1B,CAAC;KACJ,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY,EAAE,KAAK,CAAC,EAAE;QAC9F,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACJ,CAAC,6BAA6B,EAAE,GAAG,CAAC;QACpC,CAAC,gCAAgC,EAAE,OAAO,CAAC;QAC3C,CAAC,YAAY,EAAE,OAAO,CAAC;QACvB,CAAC,cAAc,EAAE,YAAY,CAAC;QAC9B,CAAC,uBAAuB,EAAE,MAAM,CAAC;QACjC,CAAC,YAAY,EAAE,4BAA4B,CAAC;QAC5C,CAAC,wBAAwB,EAAE,MAAM,CAAC;QAClC,CAAC,0BAA0B,EAAE,MAAM,CAAC;QACpC,CAAC,8BAA8B,EAAE,QAAQ,CAAC;QAC1C,CAAC,WAAW,EAAE,MAAM,CAAC;KACxB,CAAC,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,SAAS,CAAC,GAAG,EAAE;QACX,mFAAmF;QACnF,OAAQ,UAAiD,CAAC,UAAU,CAAC;QACrE,OAAQ,UAAU,CAAC,SAAgD,CAAC,QAAQ,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACpE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;YAC1C,kCAAkC;YAClC,aAAa;YACb,aAAa;SAChB,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,uCAAuC;QACtF,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACtF,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,wCAAwC;QAClF,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACvF,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC,CAAC;QAC/D,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CACpF,2EAA2E,EAC3E,MAAM,CAAC,EAAE;QACL,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACvD,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CACJ,CAAC;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC3E,MAAM,MAAM,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACtD,KAAK,MAAM,KAAK,IAAI,YAAY;YAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxF,MAAM,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC7F,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACrF,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACpC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,aAAa,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B;QACjG,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/C,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACnD,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC7B,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAE7E,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACpF,MAAM,KAAK,GAAG,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,sEAAsE;QACtE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,mBAAmB,CAAC;QACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QAClF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/F,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACjG,MAAM,CAAC,qBAAqB,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACtE,MAAM,OAAO,GAAG,qBAAqB,CAAC,qBAAqB,CAAC,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;QAC7F,MAAM,OAAO,GAAG,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACxB,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,wDAAwD;QAChG,KAAK,MAAM,KAAK,IAAI,mBAAmB,EAAE,CAAC;YACtC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC3F,MAAM,KAAK,GAAG,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC;QACJ,CAAC,MAAM,EAAE,IAAI,CAAC;QACd,CAAC,WAAW,EAAE,SAAS,CAAC;QACxB,CAAC,YAAY,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC,wCAAwC,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7D,CAAC,QAAQ,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;KAChC,CAAC,CAAC,qBAAqB,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/C,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC7C,MAAM,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjD,MAAM,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChD,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oFAAoF,EAAE,GAAG,EAAE;IAChG,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClE,MAAM,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACtE,MAAM,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAChF,MAAM,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,uBAAuB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAChF,MAAM,CAAC,uBAAuB,CAAC,4BAA4B,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,mBAAmB,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxE,MAAM,CAAC,mBAAmB,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncodingRows.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/BinaryEncodingRows.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach, vi } from 'vitest';
|
|
2
|
+
import { Base64DecodedByteLength, Base64ToBytes, BytesToBase64, Float32VectorCodecs, FLOAT32_VECTOR_BYTES_PER_VALUE, ReplaceByteArraysWithBase64, } from '../util/BinaryEncoding.js';
|
|
3
|
+
describe('Base64DecodedByteLength', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
['', 0],
|
|
6
|
+
['AA==', 1],
|
|
7
|
+
['AAA=', 2],
|
|
8
|
+
['AAAA', 3],
|
|
9
|
+
['AQID', 3],
|
|
10
|
+
['AAAAAA==', 4],
|
|
11
|
+
['aGVsbG8=', 5],
|
|
12
|
+
])('reports %j as %i bytes', (value, expected) => {
|
|
13
|
+
expect(Base64DecodedByteLength(value)).toBe(expected);
|
|
14
|
+
});
|
|
15
|
+
it.each([
|
|
16
|
+
['AA', 1],
|
|
17
|
+
['AAA', 2],
|
|
18
|
+
['AQI', 2],
|
|
19
|
+
['AAAAAA', 4],
|
|
20
|
+
])('handles unpadded %j as %i bytes', (value, expected) => {
|
|
21
|
+
expect(Base64DecodedByteLength(value)).toBe(expected);
|
|
22
|
+
});
|
|
23
|
+
it('agrees with the actual decoded length for every size 0..64, padded and unpadded', () => {
|
|
24
|
+
for (let length = 0; length <= 64; length++) {
|
|
25
|
+
const bytes = new Uint8Array(length).map((_, i) => (i * 31) & 0xff);
|
|
26
|
+
const padded = BytesToBase64(bytes);
|
|
27
|
+
const unpadded = padded.replace(/=+$/, '');
|
|
28
|
+
expect(Base64DecodedByteLength(padded), `padded ${length}`).toBe(length);
|
|
29
|
+
expect(Base64DecodedByteLength(unpadded), `unpadded ${length}`).toBe(length);
|
|
30
|
+
expect(Base64ToBytes(unpadded).length).toBe(length);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
it('agrees with the decoded length of a large value without decoding it', () => {
|
|
34
|
+
const bytes = new Uint8Array(100_001);
|
|
35
|
+
expect(Base64DecodedByteLength(BytesToBase64(bytes))).toBe(100_001);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('ReplaceByteArraysWithBase64', () => {
|
|
39
|
+
it('returns the same object when the row has no byte arrays', () => {
|
|
40
|
+
const row = { ID: 'a', Name: 'x', Count: 3, Data: 'AQID', Missing: null, Nested: { inner: new Uint8Array([1]) } };
|
|
41
|
+
expect(ReplaceByteArraysWithBase64(row)).toBe(row);
|
|
42
|
+
});
|
|
43
|
+
it('returns the same object for an empty row', () => {
|
|
44
|
+
const row = {};
|
|
45
|
+
expect(ReplaceByteArraysWithBase64(row)).toBe(row);
|
|
46
|
+
});
|
|
47
|
+
it('replaces a Uint8Array value with base64 in a shallow copy', () => {
|
|
48
|
+
const bytes = new Uint8Array([1, 2, 3]);
|
|
49
|
+
const row = { ID: 'a', Data: bytes };
|
|
50
|
+
const result = ReplaceByteArraysWithBase64(row);
|
|
51
|
+
expect(result).not.toBe(row);
|
|
52
|
+
expect(result).toEqual({ ID: 'a', Data: 'AQID' });
|
|
53
|
+
expect(row.Data).toBe(bytes); // input untouched
|
|
54
|
+
});
|
|
55
|
+
it('replaces a Node Buffer value', () => {
|
|
56
|
+
const row = { Data: Buffer.from([1, 2, 3]) };
|
|
57
|
+
expect(ReplaceByteArraysWithBase64(row)).toEqual({ Data: 'AQID' });
|
|
58
|
+
});
|
|
59
|
+
it('replaces every byte array, copying only once, and keeps other values by reference', () => {
|
|
60
|
+
const nested = { a: 1 };
|
|
61
|
+
const row = {
|
|
62
|
+
First: new Uint8Array([255]),
|
|
63
|
+
Text: 'hello',
|
|
64
|
+
Nested: nested,
|
|
65
|
+
Second: Buffer.from([0, 0, 0]),
|
|
66
|
+
Empty: new Uint8Array(0),
|
|
67
|
+
};
|
|
68
|
+
const result = ReplaceByteArraysWithBase64(row);
|
|
69
|
+
expect(result).toEqual({ First: '/w==', Text: 'hello', Nested: nested, Second: 'AAAA', Empty: '' });
|
|
70
|
+
expect(result.Nested).toBe(nested);
|
|
71
|
+
expect(IsUint8(row.First)).toBe(true);
|
|
72
|
+
expect(IsUint8(row.Second)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
it('does not convert other typed arrays or ArrayBuffers', () => {
|
|
75
|
+
const u16 = new Uint16Array([1]);
|
|
76
|
+
const ab = new ArrayBuffer(2);
|
|
77
|
+
const row = { A: u16, B: ab };
|
|
78
|
+
expect(ReplaceByteArraysWithBase64(row)).toBe(row);
|
|
79
|
+
});
|
|
80
|
+
it('never mutates a frozen cache entry', () => {
|
|
81
|
+
const row = Object.freeze({ ID: 'x', Data: new Uint8Array([1, 2]) });
|
|
82
|
+
const result = ReplaceByteArraysWithBase64(row);
|
|
83
|
+
expect(result).toEqual({ ID: 'x', Data: 'AQI=' });
|
|
84
|
+
expect(Object.isFrozen(row)).toBe(true);
|
|
85
|
+
expect(row.Data).toBeInstanceOf(Uint8Array);
|
|
86
|
+
expect(Object.isFrozen(result)).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
it('encodes only the viewed range of a subarray value', () => {
|
|
89
|
+
const backing = new Uint8Array([9, 1, 2, 3, 9]);
|
|
90
|
+
const row = { Data: backing.subarray(1, 4) };
|
|
91
|
+
expect(ReplaceByteArraysWithBase64(row).Data).toBe('AQID');
|
|
92
|
+
});
|
|
93
|
+
it('produces a value that survives JSON serialization as a string', () => {
|
|
94
|
+
const row = { Data: Buffer.from('hi') };
|
|
95
|
+
expect(JSON.parse(JSON.stringify(ReplaceByteArraysWithBase64(row)))).toEqual({ Data: 'aGk=' });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
function IsUint8(value) {
|
|
99
|
+
return value instanceof Uint8Array;
|
|
100
|
+
}
|
|
101
|
+
describe('float32 vector codecs', () => {
|
|
102
|
+
it('uses 4 bytes per value', () => {
|
|
103
|
+
expect(FLOAT32_VECTOR_BYTES_PER_VALUE).toBe(4);
|
|
104
|
+
});
|
|
105
|
+
it('DataView implementation writes little-endian bytes explicitly', () => {
|
|
106
|
+
const dataView = Float32VectorCodecs[1];
|
|
107
|
+
expect(dataView.Name).toBe('DataView (any host)');
|
|
108
|
+
expect(Array.from(dataView.Encode([1, -2]))).toEqual([0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0xc0]);
|
|
109
|
+
expect(Array.from(dataView.Encode([]))).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
it('both implementations decode an empty-but-aligned buffer to an empty vector', () => {
|
|
112
|
+
for (const codec of Float32VectorCodecs) {
|
|
113
|
+
expect(codec.Decode(new Uint8Array(0)).length, codec.Name).toBe(0);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('GetBase64Codec selection on a bare host', () => {
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
vi.unstubAllGlobals();
|
|
120
|
+
vi.resetModules();
|
|
121
|
+
});
|
|
122
|
+
it('falls back to atob/btoa when Buffer is absent', async () => {
|
|
123
|
+
vi.stubGlobal('Buffer', undefined);
|
|
124
|
+
vi.resetModules();
|
|
125
|
+
// A fresh module instance is needed because the codec choice is cached per process.
|
|
126
|
+
const fresh = await import('../util/BinaryEncoding.js');
|
|
127
|
+
expect(fresh.GetBase64Codec().Name).toBe('atob / btoa');
|
|
128
|
+
expect(fresh.BytesToBase64(new Uint8Array([1, 2, 3]))).toBe('AQID');
|
|
129
|
+
expect(Array.from(fresh.Base64ToBytes('AQI'))).toEqual([1, 2]);
|
|
130
|
+
expect(fresh.ReplaceByteArraysWithBase64({ D: new Uint8Array([255]) })).toEqual({ D: '/w==' });
|
|
131
|
+
});
|
|
132
|
+
it('throws when the host offers no base64 implementation', async () => {
|
|
133
|
+
vi.stubGlobal('Buffer', undefined);
|
|
134
|
+
vi.stubGlobal('atob', undefined);
|
|
135
|
+
vi.stubGlobal('btoa', undefined);
|
|
136
|
+
vi.resetModules();
|
|
137
|
+
const fresh = await import('../util/BinaryEncoding.js');
|
|
138
|
+
expect(() => fresh.GetBase64Codec()).toThrow('No base64 implementation is available on this host');
|
|
139
|
+
expect(() => fresh.BytesToBase64(new Uint8Array([1]))).toThrow('No base64 implementation');
|
|
140
|
+
// Zero bytes and invalid input never reach a codec.
|
|
141
|
+
expect(fresh.BytesToBase64(new Uint8Array(0))).toBe('');
|
|
142
|
+
expect(fresh.Base64ToBytes('').length).toBe(0);
|
|
143
|
+
expect(fresh.TryBase64ToBytes('!!')).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=BinaryEncodingRows.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncodingRows.test.js","sourceRoot":"","sources":["../../src/__tests__/BinaryEncodingRows.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncodingValidation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/BinaryEncodingValidation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Base64Codecs, BytesToBase64, HasValidBase64Shape, IsValidBase64 } from '../util/BinaryEncoding.js';
|
|
3
|
+
/**
|
|
4
|
+
* Strict validation is enforced twice: by `IsValidBase64` (a character scan, used by codecs without a
|
|
5
|
+
* fused decoder) and by the Node codec's `TryDecode` (decode, then compare lengths — about five times
|
|
6
|
+
* cheaper). The two must agree on EVERY input, or the same value would be accepted on one host and
|
|
7
|
+
* refused on another. These tests pin that agreement, mostly by fuzzing.
|
|
8
|
+
*/
|
|
9
|
+
const nodeCodec = Base64Codecs.find(c => c.Name === 'Node Buffer');
|
|
10
|
+
if (!nodeCodec?.TryDecode)
|
|
11
|
+
throw new Error('the Node Buffer codec must expose TryDecode');
|
|
12
|
+
const tryDecode = (value) => nodeCodec.TryDecode(value);
|
|
13
|
+
/** Deterministic PRNG so a failing fuzz case reproduces. */
|
|
14
|
+
function mulberry32(seed) {
|
|
15
|
+
let a = seed;
|
|
16
|
+
return () => {
|
|
17
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
18
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
19
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
20
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
24
|
+
/** Characters a corrupted or foreign value might contain — URL-safe, whitespace, punctuation, non-ASCII. */
|
|
25
|
+
const HOSTILE = ['=', '-', '_', ' ', '\n', '\t', '\r', '.', '!', '*', '"', '\\', 'é', '€', '\u0000', '😀'];
|
|
26
|
+
describe('base64 validation: Node fused decode agrees with IsValidBase64', () => {
|
|
27
|
+
it.each([
|
|
28
|
+
['', true],
|
|
29
|
+
['AA==', true],
|
|
30
|
+
['AAA=', true],
|
|
31
|
+
['AAAA', true],
|
|
32
|
+
['AAAAAA', true],
|
|
33
|
+
['AAAAAAA', true],
|
|
34
|
+
['A', false],
|
|
35
|
+
['A===', false],
|
|
36
|
+
['AA=', false],
|
|
37
|
+
['=AAA', false],
|
|
38
|
+
['AA==AAAA', false],
|
|
39
|
+
['AA-A', false],
|
|
40
|
+
['AA_A', false],
|
|
41
|
+
['AA A', false],
|
|
42
|
+
['AA\nA', false],
|
|
43
|
+
['AAAA\n', false],
|
|
44
|
+
['AAéA', false],
|
|
45
|
+
['data:AAAA', false],
|
|
46
|
+
])('%j → %s', (value, expected) => {
|
|
47
|
+
expect(IsValidBase64(value)).toBe(expected);
|
|
48
|
+
expect(tryDecode(value) !== null).toBe(expected);
|
|
49
|
+
});
|
|
50
|
+
it('decodes valid input to the same bytes the input encoded', () => {
|
|
51
|
+
const rand = mulberry32(7);
|
|
52
|
+
for (let n = 0; n < 300; n++) {
|
|
53
|
+
const bytes = Uint8Array.from({ length: n }, () => Math.floor(rand() * 256));
|
|
54
|
+
const encoded = BytesToBase64(bytes);
|
|
55
|
+
expect(Array.from(tryDecode(encoded) ?? [])).toEqual(Array.from(bytes));
|
|
56
|
+
expect(Array.from(tryDecode(encoded.replace(/=+$/, '')) ?? [])).toEqual(Array.from(bytes)); // unpadded
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
it('agrees on 20,000 random strings mixing valid and hostile characters', () => {
|
|
60
|
+
const rand = mulberry32(42);
|
|
61
|
+
let accepted = 0;
|
|
62
|
+
for (let n = 0; n < 20000; n++) {
|
|
63
|
+
const length = Math.floor(rand() * 24);
|
|
64
|
+
let value = '';
|
|
65
|
+
for (let i = 0; i < length; i++) {
|
|
66
|
+
value += rand() < 0.9 ? ALPHABET[Math.floor(rand() * ALPHABET.length)] : HOSTILE[Math.floor(rand() * HOSTILE.length)];
|
|
67
|
+
}
|
|
68
|
+
if (rand() < 0.3)
|
|
69
|
+
value += rand() < 0.5 ? '=' : '==';
|
|
70
|
+
const strict = IsValidBase64(value);
|
|
71
|
+
if (strict)
|
|
72
|
+
accepted++;
|
|
73
|
+
expect({ value, fused: tryDecode(value) !== null }).toEqual({ value, fused: strict });
|
|
74
|
+
}
|
|
75
|
+
expect(accepted).toBeGreaterThan(1000); // the corpus exercises the accept path too
|
|
76
|
+
});
|
|
77
|
+
it('agrees when one character of a valid string is corrupted, at every position', () => {
|
|
78
|
+
const encoded = BytesToBase64(Uint8Array.from({ length: 30 }, (_, i) => (i * 37) & 0xff));
|
|
79
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
80
|
+
for (const bad of HOSTILE) {
|
|
81
|
+
const value = encoded.slice(0, i) + bad + encoded.slice(i + 1);
|
|
82
|
+
expect({ value, fused: tryDecode(value) !== null }).toEqual({ value, fused: IsValidBase64(value) });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('HasValidBase64Shape', () => {
|
|
88
|
+
it.each([
|
|
89
|
+
['', true], ['AA', true], ['AAA', true], ['AAAA', true], ['AA==', true], ['AAA=', true],
|
|
90
|
+
['A', false], ['AAAAA', false], ['AA=', false], ['A=', false],
|
|
91
|
+
])('%j → %s (length and padding only, characters ignored)', (value, expected) => {
|
|
92
|
+
expect(HasValidBase64Shape(value)).toBe(expected);
|
|
93
|
+
});
|
|
94
|
+
it('does not inspect characters', () => {
|
|
95
|
+
expect(HasValidBase64Shape('!!!!')).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=BinaryEncodingValidation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncodingValidation.test.js","sourceRoot":"","sources":["../../src/__tests__/BinaryEncodingValidation.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './util/ResourcePatternUtils.js';
|
|
|
13
13
|
export * from './util/UUIDUtils.js';
|
|
14
14
|
export * from './util/CronUtils.js';
|
|
15
15
|
export * from './util/SerializationUtils.js';
|
|
16
|
+
export * from './util/BinaryEncoding.js';
|
|
16
17
|
export * from './ValidationTypes.js';
|
|
17
18
|
export * from './JSONValidator.js';
|
|
18
19
|
export * from './SafeExpressionEvaluator.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAChE,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,2BAA2B,CAAA;AACzC,cAAc,cAAc,CAAA;AAC5B,cAAc,0BAA0B,CAAA;AACxC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAMtC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAChE,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,2BAA2B,CAAA;AACzC,cAAc,cAAc,CAAA;AAC5B,cAAc,0BAA0B,CAAA;AACxC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAMtC,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export * from './util/ResourcePatternUtils.js';
|
|
|
14
14
|
export * from './util/UUIDUtils.js';
|
|
15
15
|
export * from './util/CronUtils.js';
|
|
16
16
|
export * from './util/SerializationUtils.js';
|
|
17
|
+
export * from './util/BinaryEncoding.js';
|
|
17
18
|
export * from './ValidationTypes.js';
|
|
18
19
|
export * from './JSONValidator.js';
|
|
19
20
|
export * from './SafeExpressionEvaluator.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAChE,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,2BAA2B,CAAA;AACzC,cAAc,cAAc,CAAA;AAC5B,cAAc,0BAA0B,CAAA;AACxC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAEtC,2DAA2D;AAC3D,oCAAoC;AAEpC,0BAA0B;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA;AAE7C,6DAA6D;AAC7D,oCAAoC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAChE,cAAc,aAAa,CAAA;AAC3B,cAAc,QAAQ,CAAA;AACtB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,2BAA2B,CAAA;AACzC,cAAc,cAAc,CAAA;AAC5B,cAAc,0BAA0B,CAAA;AACxC,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AAEtC,2DAA2D;AAC3D,oCAAoC;AAEpC,0BAA0B;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,+BAA+B,CAAA;AAE7C,6DAA6D;AAC7D,oCAAoC"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary encoding primitives shared by every MemberJunction tier.
|
|
3
|
+
*
|
|
4
|
+
* MemberJunction stores binary database columns (SQL Server `binary` / `varbinary` / `image`,
|
|
5
|
+
* PostgreSQL `bytea`) in `BaseEntity` and on the wire as **base64 strings**. A string keeps the
|
|
6
|
+
* value JSON-safe, so dirty tracking, Record Changes, the RunView caches, GraphQL and remote
|
|
7
|
+
* invalidation broadcasts all handle it without special cases. Code that needs the actual bytes
|
|
8
|
+
* converts at the edge with the functions in this module.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is dependency-free and runs unchanged in browsers, Node and workers. Each
|
|
11
|
+
* conversion picks the fastest implementation the host offers:
|
|
12
|
+
*
|
|
13
|
+
* | Host | base64 → bytes | bytes → base64 |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | Browsers with the TC39 base64 API (Chrome 140+, Firefox 133+, Safari 18.2+) | `Uint8Array.fromBase64` | `Uint8Array.prototype.toBase64` |
|
|
16
|
+
* | Node.js | `Buffer.from(s, 'base64')` | `Buffer#toString('base64')` |
|
|
17
|
+
* | Anything else | `atob` | `btoa` (chunked) |
|
|
18
|
+
*
|
|
19
|
+
* The fast paths are detected by duck typing, once per process, so older browsers keep working.
|
|
20
|
+
* All three implementations accept and produce exactly the same strings, and the decoders
|
|
21
|
+
* validate input identically, so a value never decodes on one tier and fails on another.
|
|
22
|
+
*
|
|
23
|
+
* @module BinaryEncoding
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* One way of converting between bytes and base64. Every implementation must accept and produce
|
|
27
|
+
* the same strings; {@link Base64Codecs} lists them so tests can prove that.
|
|
28
|
+
*/
|
|
29
|
+
export interface Base64Codec {
|
|
30
|
+
/** Human-readable name, used in diagnostics and tests. */
|
|
31
|
+
readonly Name: string;
|
|
32
|
+
/** True when the host supports this implementation. */
|
|
33
|
+
IsAvailable(): boolean;
|
|
34
|
+
/** Encodes bytes as standard, padded base64 (RFC 4648 §4). */
|
|
35
|
+
Encode(bytes: Uint8Array): string;
|
|
36
|
+
/**
|
|
37
|
+
* Decodes base64 that has already passed {@link IsValidBase64}. Implementations may assume
|
|
38
|
+
* valid input; the public functions validate first so every implementation fails the same way.
|
|
39
|
+
*/
|
|
40
|
+
Decode(base64: string): Uint8Array;
|
|
41
|
+
/**
|
|
42
|
+
* Optional fused validate-and-decode: returns the bytes when `base64` passes
|
|
43
|
+
* {@link IsValidBase64}, else `null` — exactly the same accept/reject decision, made more cheaply
|
|
44
|
+
* than a separate character scan. Codecs without it are validated with {@link IsValidBase64}.
|
|
45
|
+
*/
|
|
46
|
+
TryDecode?(base64: string): Uint8Array | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Every base64 implementation, fastest first. Exposed so tests can prove the implementations are
|
|
50
|
+
* interchangeable; application code should call {@link BytesToBase64} / {@link Base64ToBytes}.
|
|
51
|
+
*/
|
|
52
|
+
export declare const Base64Codecs: readonly Base64Codec[];
|
|
53
|
+
/**
|
|
54
|
+
* Returns the fastest base64 implementation this host supports. The choice is made once and
|
|
55
|
+
* cached; it cannot change during a process's life.
|
|
56
|
+
*
|
|
57
|
+
* @throws Error when the host offers none of the implementations (no JavaScript host MJ supports).
|
|
58
|
+
*/
|
|
59
|
+
export declare function GetBase64Codec(): Base64Codec;
|
|
60
|
+
/**
|
|
61
|
+
* True when `value` is canonical base64: the standard alphabet (`A–Z a–z 0–9 + /`), optional
|
|
62
|
+
* `=` padding, no whitespace, and a length that can encode whole bytes. The empty string is valid
|
|
63
|
+
* and decodes to zero bytes.
|
|
64
|
+
*
|
|
65
|
+
* @param value - The string to check.
|
|
66
|
+
*/
|
|
67
|
+
export declare function IsValidBase64(value: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The length rules of canonical base64, without looking at the characters: a length that can
|
|
70
|
+
* encode whole bytes, and padding only on a full final quantum ("xx==" or "xxx="), never on a bare
|
|
71
|
+
* remainder. Part of {@link IsValidBase64}; exposed so a codec's fused validate-and-decode
|
|
72
|
+
* ({@link Base64Codec.TryDecode}) applies the same rules.
|
|
73
|
+
*
|
|
74
|
+
* @param value - The string to check.
|
|
75
|
+
*/
|
|
76
|
+
export declare function HasValidBase64Shape(value: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Returns how many bytes a base64 string decodes to, without decoding it. Useful for size checks
|
|
79
|
+
* on large values. The input is assumed to be valid (see {@link IsValidBase64}); padding is
|
|
80
|
+
* optional.
|
|
81
|
+
*
|
|
82
|
+
* @param base64 - Canonical base64.
|
|
83
|
+
*/
|
|
84
|
+
export declare function Base64DecodedByteLength(base64: string): number;
|
|
85
|
+
/** Prefix of the text recorded in a change log in place of a binary value. See {@link FormatBinaryChangeValue}. */
|
|
86
|
+
export declare const BINARY_CHANGE_VALUE_PREFIX = "[binary:";
|
|
87
|
+
/**
|
|
88
|
+
* The text a change log records, and change history shows, in place of a binary field's base64
|
|
89
|
+
* value: `[binary: 6,144 bytes]`. The bytes themselves stay in the record snapshot
|
|
90
|
+
* (`FullRecordJSON`), which restore reads; only the per-field diff (`ChangesJSON`) and its
|
|
91
|
+
* on-screen rendering use this, so a change to an embedding or a file is visible without carrying
|
|
92
|
+
* the content twice more per save. Invalid base64 is reported as such rather than thrown on.
|
|
93
|
+
*
|
|
94
|
+
* @param base64 - The field's value as held above the database.
|
|
95
|
+
*/
|
|
96
|
+
export declare function FormatBinaryChangeValue(base64: string): string;
|
|
97
|
+
/** Whether `value` is text produced by {@link FormatBinaryChangeValue}, so it is shown as-is and never decoded. */
|
|
98
|
+
export declare function IsBinaryChangeValue(value: unknown): value is string;
|
|
99
|
+
/**
|
|
100
|
+
* Encodes bytes as standard, padded base64 using the fastest implementation the host offers.
|
|
101
|
+
*
|
|
102
|
+
* @param bytes - The bytes to encode. Any `Uint8Array` works, including a Node `Buffer` or a view
|
|
103
|
+
* into a larger buffer; only the viewed range is encoded.
|
|
104
|
+
* @returns The base64 string; `''` for zero bytes.
|
|
105
|
+
*/
|
|
106
|
+
export declare function BytesToBase64(bytes: Uint8Array): string;
|
|
107
|
+
/**
|
|
108
|
+
* Decodes base64 into a new `Uint8Array` that owns its own `ArrayBuffer` (offset 0, never a view
|
|
109
|
+
* into a shared pool), using the fastest implementation the host offers.
|
|
110
|
+
*
|
|
111
|
+
* @param base64 - Canonical base64 (see {@link IsValidBase64}); padding may be omitted.
|
|
112
|
+
* @returns The decoded bytes.
|
|
113
|
+
* @throws Error when `base64` is not valid base64. Use {@link TryBase64ToBytes} to get `null` instead.
|
|
114
|
+
*/
|
|
115
|
+
export declare function Base64ToBytes(base64: string): Uint8Array;
|
|
116
|
+
/**
|
|
117
|
+
* Like {@link Base64ToBytes}, but returns `null` instead of throwing when the input is not a
|
|
118
|
+
* string or not valid base64. Convenient for reading a nullable binary entity field.
|
|
119
|
+
*
|
|
120
|
+
* @param base64 - The value to decode, typically a binary entity field (`string | null`).
|
|
121
|
+
*/
|
|
122
|
+
export declare function TryBase64ToBytes(base64: string | null | undefined): Uint8Array | null;
|
|
123
|
+
/**
|
|
124
|
+
* True when `value` is a byte array: a `Uint8Array`, which includes a Node `Buffer`. Database
|
|
125
|
+
* drivers return binary columns this way; use it at the provider boundary to recognise values
|
|
126
|
+
* that must be converted to base64 before they reach a `BaseEntity`.
|
|
127
|
+
*
|
|
128
|
+
* @param value - Any value.
|
|
129
|
+
*/
|
|
130
|
+
export declare function IsByteArray(value: unknown): value is Uint8Array;
|
|
131
|
+
/**
|
|
132
|
+
* Returns `row` with every top-level byte-array value replaced by its base64 string.
|
|
133
|
+
*
|
|
134
|
+
* Copy-on-write: when the row holds no byte arrays (the normal case, since the database providers
|
|
135
|
+
* already convert binary columns) the same object is returned untouched; otherwise a shallow copy
|
|
136
|
+
* is returned and the original — which may be a frozen cache entry — is never modified.
|
|
137
|
+
*
|
|
138
|
+
* Use it as a safety net wherever rows are serialized for a transport: `JSON.stringify` turns a
|
|
139
|
+
* Node `Buffer` into `{"type":"Buffer","data":[...]}` and GraphQL's `String` scalar rejects it.
|
|
140
|
+
*
|
|
141
|
+
* @param row - A data row.
|
|
142
|
+
*/
|
|
143
|
+
export declare function ReplaceByteArraysWithBase64<T extends Record<string, unknown>>(row: T): T;
|
|
144
|
+
/**
|
|
145
|
+
* The byte layout MemberJunction uses for persisted embedding vectors: consecutive
|
|
146
|
+
* IEEE-754 single-precision values, little-endian, 4 bytes each, no header. The dimension count
|
|
147
|
+
* is `byteLength / 4`. This is the layout of a `Float32Array` on every little-endian host, which
|
|
148
|
+
* is every platform MemberJunction runs on; big-endian hosts are still handled correctly.
|
|
149
|
+
*/
|
|
150
|
+
export declare const FLOAT32_VECTOR_BYTES_PER_VALUE = 4;
|
|
151
|
+
/**
|
|
152
|
+
* Converts between numbers and the {@link FLOAT32_VECTOR_BYTES_PER_VALUE little-endian float32}
|
|
153
|
+
* layout. Two implementations exist so the result is correct on any host:
|
|
154
|
+
* a typed-array copy (little-endian hosts) and an explicit `DataView` (any host).
|
|
155
|
+
*/
|
|
156
|
+
export interface Float32VectorCodec {
|
|
157
|
+
/** Human-readable name, used in tests. */
|
|
158
|
+
readonly Name: string;
|
|
159
|
+
/** Writes `values` as little-endian float32 bytes into a new array. */
|
|
160
|
+
Encode(values: ArrayLike<number>): Uint8Array;
|
|
161
|
+
/** Reads little-endian float32 bytes (length already checked to be a positive multiple of 4). */
|
|
162
|
+
Decode(bytes: Uint8Array): Float32Array;
|
|
163
|
+
}
|
|
164
|
+
/** Both float32 vector implementations. Exposed for tests; use the functions below instead. */
|
|
165
|
+
export declare const Float32VectorCodecs: readonly Float32VectorCodec[];
|
|
166
|
+
/**
|
|
167
|
+
* Encodes a vector as little-endian float32 bytes. Values are rounded to single precision, which
|
|
168
|
+
* is the precision embedding models produce; `NaN` and infinities are stored as-is.
|
|
169
|
+
*
|
|
170
|
+
* @param values - The vector, e.g. a `number[]` or a `Float32Array`.
|
|
171
|
+
* @returns A new byte array of `values.length × 4` bytes.
|
|
172
|
+
*/
|
|
173
|
+
export declare function Float32VectorToBytes(values: ArrayLike<number>): Uint8Array;
|
|
174
|
+
/**
|
|
175
|
+
* Decodes little-endian float32 bytes into a new `Float32Array` that owns its memory.
|
|
176
|
+
*
|
|
177
|
+
* @param bytes - Bytes in the {@link FLOAT32_VECTOR_BYTES_PER_VALUE persisted vector layout}.
|
|
178
|
+
* @returns The vector, or `null` when `bytes` is empty or not a whole number of float32 values,
|
|
179
|
+
* so a truncated or foreign value is treated as missing rather than misread.
|
|
180
|
+
*/
|
|
181
|
+
export declare function BytesToFloat32Vector(bytes: Uint8Array | null | undefined): Float32Array | null;
|
|
182
|
+
/**
|
|
183
|
+
* Encodes a vector as base64 of its little-endian float32 bytes: the value stored in a binary
|
|
184
|
+
* vector entity field such as `VectorBinary`.
|
|
185
|
+
*
|
|
186
|
+
* @param values - The vector.
|
|
187
|
+
*/
|
|
188
|
+
export declare function Float32VectorToBase64(values: ArrayLike<number>): string;
|
|
189
|
+
/**
|
|
190
|
+
* Decodes a binary vector entity field (base64 of little-endian float32 bytes).
|
|
191
|
+
*
|
|
192
|
+
* @param base64 - The field value; `null` / `undefined` are allowed.
|
|
193
|
+
* @returns The vector, or `null` when the value is missing, not valid base64, empty, or not a
|
|
194
|
+
* whole number of float32 values. Callers usually fall back to a JSON copy of the vector.
|
|
195
|
+
*/
|
|
196
|
+
export declare function Base64ToFloat32Vector(base64: string | null | undefined): Float32Array | null;
|
|
197
|
+
//# sourceMappingURL=BinaryEncoding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncoding.d.ts","sourceRoot":"","sources":["../../src/util/BinaryEncoding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary encoding primitives shared by every MemberJunction tier.
|
|
3
|
+
*
|
|
4
|
+
* MemberJunction stores binary database columns (SQL Server `binary` / `varbinary` / `image`,
|
|
5
|
+
* PostgreSQL `bytea`) in `BaseEntity` and on the wire as **base64 strings**. A string keeps the
|
|
6
|
+
* value JSON-safe, so dirty tracking, Record Changes, the RunView caches, GraphQL and remote
|
|
7
|
+
* invalidation broadcasts all handle it without special cases. Code that needs the actual bytes
|
|
8
|
+
* converts at the edge with the functions in this module.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is dependency-free and runs unchanged in browsers, Node and workers. Each
|
|
11
|
+
* conversion picks the fastest implementation the host offers:
|
|
12
|
+
*
|
|
13
|
+
* | Host | base64 → bytes | bytes → base64 |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | Browsers with the TC39 base64 API (Chrome 140+, Firefox 133+, Safari 18.2+) | `Uint8Array.fromBase64` | `Uint8Array.prototype.toBase64` |
|
|
16
|
+
* | Node.js | `Buffer.from(s, 'base64')` | `Buffer#toString('base64')` |
|
|
17
|
+
* | Anything else | `atob` | `btoa` (chunked) |
|
|
18
|
+
*
|
|
19
|
+
* The fast paths are detected by duck typing, once per process, so older browsers keep working.
|
|
20
|
+
* All three implementations accept and produce exactly the same strings, and the decoders
|
|
21
|
+
* validate input identically, so a value never decodes on one tier and fails on another.
|
|
22
|
+
*
|
|
23
|
+
* @module BinaryEncoding
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Lookup table over every UTF-16 code unit (64 KB, so the scan needs no range check): 1 for the standard base64 alphabet (`A–Z a–z 0–9 + /`),
|
|
27
|
+
* 0 otherwise. Data URIs, URL-safe base64 and MIME line breaks are deliberately rejected: binary
|
|
28
|
+
* columns only ever carry canonical base64. A table scan is used instead of a regular expression
|
|
29
|
+
* because validation runs on every decode, and on an 8 KB vector a regex costs about five times
|
|
30
|
+
* as much as the decode itself.
|
|
31
|
+
*/
|
|
32
|
+
/** Char code of the base64 padding character `=`. */
|
|
33
|
+
const EQUALS_CODE = 0x3d;
|
|
34
|
+
const BASE64_ALPHABET = (() => {
|
|
35
|
+
const table = new Uint8Array(0x10000);
|
|
36
|
+
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
37
|
+
for (let i = 0; i < alphabet.length; i++)
|
|
38
|
+
table[alphabet.charCodeAt(i)] = 1;
|
|
39
|
+
return table;
|
|
40
|
+
})();
|
|
41
|
+
/** `String.fromCharCode` argument count per call when building a binary string for `btoa`. */
|
|
42
|
+
const BTOA_CHUNK = 0x8000;
|
|
43
|
+
/** Reads a global by name without assuming any particular host typings. */
|
|
44
|
+
function readGlobal(name) {
|
|
45
|
+
return Reflect.get(globalThis, name);
|
|
46
|
+
}
|
|
47
|
+
/** Native TC39 base64 support on `Uint8Array`, detected by duck typing. */
|
|
48
|
+
const NativeCodec = {
|
|
49
|
+
Name: 'Uint8Array.fromBase64 / toBase64',
|
|
50
|
+
IsAvailable() {
|
|
51
|
+
return typeof Reflect.get(Uint8Array, 'fromBase64') === 'function'
|
|
52
|
+
&& typeof Reflect.get(Uint8Array.prototype, 'toBase64') === 'function';
|
|
53
|
+
},
|
|
54
|
+
Encode(bytes) {
|
|
55
|
+
return bytes.toBase64();
|
|
56
|
+
},
|
|
57
|
+
Decode(base64) {
|
|
58
|
+
const fromBase64 = Reflect.get(Uint8Array, 'fromBase64');
|
|
59
|
+
return fromBase64.call(Uint8Array, base64, { lastChunkHandling: 'loose' });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
/** Node's `Buffer`, which encodes and decodes in native code. */
|
|
63
|
+
const NodeBufferCodec = {
|
|
64
|
+
Name: 'Node Buffer',
|
|
65
|
+
IsAvailable() {
|
|
66
|
+
return typeof readGlobal('Buffer')?.from === 'function';
|
|
67
|
+
},
|
|
68
|
+
Encode(bytes) {
|
|
69
|
+
const nodeBuffer = readGlobal('Buffer');
|
|
70
|
+
return nodeBuffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64');
|
|
71
|
+
},
|
|
72
|
+
Decode(base64) {
|
|
73
|
+
const nodeBuffer = readGlobal('Buffer');
|
|
74
|
+
const decoded = nodeBuffer.from(base64, 'base64');
|
|
75
|
+
// Copy out of Node's shared allocation pool: a pooled Buffer views a larger ArrayBuffer at
|
|
76
|
+
// an arbitrary offset, which typed-array views and transfers would otherwise trip over.
|
|
77
|
+
return new Uint8Array(decoded);
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Validates by decoding. Node's decoder silently SKIPS characters outside the alphabet
|
|
81
|
+
* (whitespace, punctuation, non-ASCII) and stops at an early `=`, so any such character makes the
|
|
82
|
+
* output shorter than a canonical string of the same length decodes to — the length check rejects
|
|
83
|
+
* it. The one thing it accepts that canonical base64 does not is the URL-safe alphabet (`-`, `_`),
|
|
84
|
+
* rejected separately with a native `indexOf`. About five times cheaper than a JavaScript
|
|
85
|
+
* character scan on vector-sized values; fuzz-tested against {@link IsValidBase64}.
|
|
86
|
+
*/
|
|
87
|
+
TryDecode(base64) {
|
|
88
|
+
if (!HasValidBase64Shape(base64))
|
|
89
|
+
return null;
|
|
90
|
+
if (base64.indexOf('-') !== -1 || base64.indexOf('_') !== -1)
|
|
91
|
+
return null;
|
|
92
|
+
if (base64.length === 0)
|
|
93
|
+
return new Uint8Array(0);
|
|
94
|
+
const decoded = this.Decode(base64);
|
|
95
|
+
return decoded.length === Base64DecodedByteLength(base64) ? decoded : null;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
/** `atob` / `btoa`, available in every browser, worker and modern Node. */
|
|
99
|
+
const PortableCodec = {
|
|
100
|
+
Name: 'atob / btoa',
|
|
101
|
+
IsAvailable() {
|
|
102
|
+
return typeof readGlobal('atob') === 'function' && typeof readGlobal('btoa') === 'function';
|
|
103
|
+
},
|
|
104
|
+
Encode(bytes) {
|
|
105
|
+
let binary = '';
|
|
106
|
+
for (let start = 0; start < bytes.length; start += BTOA_CHUNK) {
|
|
107
|
+
const chunk = bytes.subarray(start, Math.min(start + BTOA_CHUNK, bytes.length));
|
|
108
|
+
binary += String.fromCharCode(...chunk);
|
|
109
|
+
}
|
|
110
|
+
return btoa(binary);
|
|
111
|
+
},
|
|
112
|
+
Decode(base64) {
|
|
113
|
+
const binary = atob(base64);
|
|
114
|
+
const bytes = new Uint8Array(binary.length);
|
|
115
|
+
for (let i = 0; i < binary.length; i++)
|
|
116
|
+
bytes[i] = binary.charCodeAt(i);
|
|
117
|
+
return bytes;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Every base64 implementation, fastest first. Exposed so tests can prove the implementations are
|
|
122
|
+
* interchangeable; application code should call {@link BytesToBase64} / {@link Base64ToBytes}.
|
|
123
|
+
*/
|
|
124
|
+
export const Base64Codecs = [NativeCodec, NodeBufferCodec, PortableCodec];
|
|
125
|
+
let selectedCodec;
|
|
126
|
+
/**
|
|
127
|
+
* Returns the fastest base64 implementation this host supports. The choice is made once and
|
|
128
|
+
* cached; it cannot change during a process's life.
|
|
129
|
+
*
|
|
130
|
+
* @throws Error when the host offers none of the implementations (no JavaScript host MJ supports).
|
|
131
|
+
*/
|
|
132
|
+
export function GetBase64Codec() {
|
|
133
|
+
const codec = findBase64Codec();
|
|
134
|
+
if (!codec)
|
|
135
|
+
throw new Error('No base64 implementation is available on this host');
|
|
136
|
+
return codec;
|
|
137
|
+
}
|
|
138
|
+
/** The selected codec, choosing it on first use; `undefined` when the host offers none. */
|
|
139
|
+
function findBase64Codec() {
|
|
140
|
+
selectedCodec ??= Base64Codecs.find(codec => codec.IsAvailable());
|
|
141
|
+
return selectedCodec;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* True when `value` is canonical base64: the standard alphabet (`A–Z a–z 0–9 + /`), optional
|
|
145
|
+
* `=` padding, no whitespace, and a length that can encode whole bytes. The empty string is valid
|
|
146
|
+
* and decodes to zero bytes.
|
|
147
|
+
*
|
|
148
|
+
* @param value - The string to check.
|
|
149
|
+
*/
|
|
150
|
+
export function IsValidBase64(value) {
|
|
151
|
+
if (!HasValidBase64Shape(value))
|
|
152
|
+
return false;
|
|
153
|
+
const dataLength = value.length - trailingPadding(value);
|
|
154
|
+
for (let i = 0; i < dataLength; i++) {
|
|
155
|
+
if (BASE64_ALPHABET[value.charCodeAt(i)] === 0)
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
/** Number of trailing `=` characters, counting at most two. */
|
|
161
|
+
function trailingPadding(value) {
|
|
162
|
+
const length = value.length;
|
|
163
|
+
if (length === 0 || value.charCodeAt(length - 1) !== EQUALS_CODE)
|
|
164
|
+
return 0;
|
|
165
|
+
return length > 1 && value.charCodeAt(length - 2) === EQUALS_CODE ? 2 : 1;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The length rules of canonical base64, without looking at the characters: a length that can
|
|
169
|
+
* encode whole bytes, and padding only on a full final quantum ("xx==" or "xxx="), never on a bare
|
|
170
|
+
* remainder. Part of {@link IsValidBase64}; exposed so a codec's fused validate-and-decode
|
|
171
|
+
* ({@link Base64Codec.TryDecode}) applies the same rules.
|
|
172
|
+
*
|
|
173
|
+
* @param value - The string to check.
|
|
174
|
+
*/
|
|
175
|
+
export function HasValidBase64Shape(value) {
|
|
176
|
+
const length = value.length;
|
|
177
|
+
if (length % 4 === 1)
|
|
178
|
+
return false;
|
|
179
|
+
return trailingPadding(value) === 0 || length % 4 === 0;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Returns how many bytes a base64 string decodes to, without decoding it. Useful for size checks
|
|
183
|
+
* on large values. The input is assumed to be valid (see {@link IsValidBase64}); padding is
|
|
184
|
+
* optional.
|
|
185
|
+
*
|
|
186
|
+
* @param base64 - Canonical base64.
|
|
187
|
+
*/
|
|
188
|
+
export function Base64DecodedByteLength(base64) {
|
|
189
|
+
const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;
|
|
190
|
+
const dataChars = base64.length - padding;
|
|
191
|
+
return Math.floor((dataChars * 3) / 4);
|
|
192
|
+
}
|
|
193
|
+
/** Prefix of the text recorded in a change log in place of a binary value. See {@link FormatBinaryChangeValue}. */
|
|
194
|
+
export const BINARY_CHANGE_VALUE_PREFIX = '[binary:';
|
|
195
|
+
/**
|
|
196
|
+
* The text a change log records, and change history shows, in place of a binary field's base64
|
|
197
|
+
* value: `[binary: 6,144 bytes]`. The bytes themselves stay in the record snapshot
|
|
198
|
+
* (`FullRecordJSON`), which restore reads; only the per-field diff (`ChangesJSON`) and its
|
|
199
|
+
* on-screen rendering use this, so a change to an embedding or a file is visible without carrying
|
|
200
|
+
* the content twice more per save. Invalid base64 is reported as such rather than thrown on.
|
|
201
|
+
*
|
|
202
|
+
* @param base64 - The field's value as held above the database.
|
|
203
|
+
*/
|
|
204
|
+
export function FormatBinaryChangeValue(base64) {
|
|
205
|
+
if (!IsValidBase64(base64))
|
|
206
|
+
return `${BINARY_CHANGE_VALUE_PREFIX} invalid base64]`;
|
|
207
|
+
return `${BINARY_CHANGE_VALUE_PREFIX} ${Base64DecodedByteLength(base64).toLocaleString('en-US')} bytes]`;
|
|
208
|
+
}
|
|
209
|
+
/** Whether `value` is text produced by {@link FormatBinaryChangeValue}, so it is shown as-is and never decoded. */
|
|
210
|
+
export function IsBinaryChangeValue(value) {
|
|
211
|
+
return typeof value === 'string' && value.startsWith(BINARY_CHANGE_VALUE_PREFIX) && value.endsWith(']');
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Encodes bytes as standard, padded base64 using the fastest implementation the host offers.
|
|
215
|
+
*
|
|
216
|
+
* @param bytes - The bytes to encode. Any `Uint8Array` works, including a Node `Buffer` or a view
|
|
217
|
+
* into a larger buffer; only the viewed range is encoded.
|
|
218
|
+
* @returns The base64 string; `''` for zero bytes.
|
|
219
|
+
*/
|
|
220
|
+
export function BytesToBase64(bytes) {
|
|
221
|
+
if (bytes.length === 0)
|
|
222
|
+
return '';
|
|
223
|
+
return GetBase64Codec().Encode(bytes);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Decodes base64 into a new `Uint8Array` that owns its own `ArrayBuffer` (offset 0, never a view
|
|
227
|
+
* into a shared pool), using the fastest implementation the host offers.
|
|
228
|
+
*
|
|
229
|
+
* @param base64 - Canonical base64 (see {@link IsValidBase64}); padding may be omitted.
|
|
230
|
+
* @returns The decoded bytes.
|
|
231
|
+
* @throws Error when `base64` is not valid base64. Use {@link TryBase64ToBytes} to get `null` instead.
|
|
232
|
+
*/
|
|
233
|
+
export function Base64ToBytes(base64) {
|
|
234
|
+
const bytes = decodeValidated(base64);
|
|
235
|
+
if (!bytes) {
|
|
236
|
+
throw new Error(`Invalid base64 value (length ${base64.length})`);
|
|
237
|
+
}
|
|
238
|
+
return bytes;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Validates and decodes in one step: the codec's fused {@link Base64Codec.TryDecode} when it has
|
|
242
|
+
* one, else {@link IsValidBase64} followed by a plain decode. `null` when invalid.
|
|
243
|
+
*/
|
|
244
|
+
function decodeValidated(base64) {
|
|
245
|
+
if (base64.length === 0)
|
|
246
|
+
return new Uint8Array(0);
|
|
247
|
+
const codec = findBase64Codec();
|
|
248
|
+
if (codec?.TryDecode)
|
|
249
|
+
return codec.TryDecode(base64);
|
|
250
|
+
// No fused decoder — or no codec at all, in which case invalid input still answers null and
|
|
251
|
+
// only a valid value reaches GetBase64Codec() and its "no implementation" error.
|
|
252
|
+
if (!IsValidBase64(base64))
|
|
253
|
+
return null;
|
|
254
|
+
return (codec ?? GetBase64Codec()).Decode(base64);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Like {@link Base64ToBytes}, but returns `null` instead of throwing when the input is not a
|
|
258
|
+
* string or not valid base64. Convenient for reading a nullable binary entity field.
|
|
259
|
+
*
|
|
260
|
+
* @param base64 - The value to decode, typically a binary entity field (`string | null`).
|
|
261
|
+
*/
|
|
262
|
+
export function TryBase64ToBytes(base64) {
|
|
263
|
+
return typeof base64 === 'string' ? decodeValidated(base64) : null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* True when `value` is a byte array: a `Uint8Array`, which includes a Node `Buffer`. Database
|
|
267
|
+
* drivers return binary columns this way; use it at the provider boundary to recognise values
|
|
268
|
+
* that must be converted to base64 before they reach a `BaseEntity`.
|
|
269
|
+
*
|
|
270
|
+
* @param value - Any value.
|
|
271
|
+
*/
|
|
272
|
+
export function IsByteArray(value) {
|
|
273
|
+
return value instanceof Uint8Array;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Returns `row` with every top-level byte-array value replaced by its base64 string.
|
|
277
|
+
*
|
|
278
|
+
* Copy-on-write: when the row holds no byte arrays (the normal case, since the database providers
|
|
279
|
+
* already convert binary columns) the same object is returned untouched; otherwise a shallow copy
|
|
280
|
+
* is returned and the original — which may be a frozen cache entry — is never modified.
|
|
281
|
+
*
|
|
282
|
+
* Use it as a safety net wherever rows are serialized for a transport: `JSON.stringify` turns a
|
|
283
|
+
* Node `Buffer` into `{"type":"Buffer","data":[...]}` and GraphQL's `String` scalar rejects it.
|
|
284
|
+
*
|
|
285
|
+
* @param row - A data row.
|
|
286
|
+
*/
|
|
287
|
+
export function ReplaceByteArraysWithBase64(row) {
|
|
288
|
+
let copy = null;
|
|
289
|
+
for (const key of Object.keys(row)) {
|
|
290
|
+
const value = row[key];
|
|
291
|
+
if (IsByteArray(value)) {
|
|
292
|
+
if (!copy)
|
|
293
|
+
copy = { ...row };
|
|
294
|
+
copy[key] = BytesToBase64(value);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return (copy ?? row);
|
|
298
|
+
}
|
|
299
|
+
/* ------------------------------------------------------------------------------------------ */
|
|
300
|
+
/* Float32 vectors */
|
|
301
|
+
/* ------------------------------------------------------------------------------------------ */
|
|
302
|
+
/**
|
|
303
|
+
* The byte layout MemberJunction uses for persisted embedding vectors: consecutive
|
|
304
|
+
* IEEE-754 single-precision values, little-endian, 4 bytes each, no header. The dimension count
|
|
305
|
+
* is `byteLength / 4`. This is the layout of a `Float32Array` on every little-endian host, which
|
|
306
|
+
* is every platform MemberJunction runs on; big-endian hosts are still handled correctly.
|
|
307
|
+
*/
|
|
308
|
+
export const FLOAT32_VECTOR_BYTES_PER_VALUE = 4;
|
|
309
|
+
/** True when this host stores multi-byte numbers little-endian. */
|
|
310
|
+
function hostIsLittleEndian() {
|
|
311
|
+
return new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
312
|
+
}
|
|
313
|
+
/** Typed-array copy; correct only on little-endian hosts. */
|
|
314
|
+
const TypedArrayFloat32Codec = {
|
|
315
|
+
Name: 'typed array (little-endian host)',
|
|
316
|
+
Encode(values) {
|
|
317
|
+
return new Uint8Array(Float32Array.from(values).buffer);
|
|
318
|
+
},
|
|
319
|
+
Decode(bytes) {
|
|
320
|
+
// Copy first: the source may be a misaligned view (e.g. a pooled Node Buffer), and a
|
|
321
|
+
// Float32Array over it would also alias memory the caller does not own.
|
|
322
|
+
return new Float32Array(bytes.slice().buffer);
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
/** Explicit little-endian reads and writes; correct on every host. */
|
|
326
|
+
const DataViewFloat32Codec = {
|
|
327
|
+
Name: 'DataView (any host)',
|
|
328
|
+
Encode(values) {
|
|
329
|
+
const bytes = new Uint8Array(values.length * FLOAT32_VECTOR_BYTES_PER_VALUE);
|
|
330
|
+
const view = new DataView(bytes.buffer);
|
|
331
|
+
for (let i = 0; i < values.length; i++)
|
|
332
|
+
view.setFloat32(i * FLOAT32_VECTOR_BYTES_PER_VALUE, values[i], true);
|
|
333
|
+
return bytes;
|
|
334
|
+
},
|
|
335
|
+
Decode(bytes) {
|
|
336
|
+
const count = bytes.byteLength / FLOAT32_VECTOR_BYTES_PER_VALUE;
|
|
337
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
338
|
+
const values = new Float32Array(count);
|
|
339
|
+
for (let i = 0; i < count; i++)
|
|
340
|
+
values[i] = view.getFloat32(i * FLOAT32_VECTOR_BYTES_PER_VALUE, true);
|
|
341
|
+
return values;
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
/** Both float32 vector implementations. Exposed for tests; use the functions below instead. */
|
|
345
|
+
export const Float32VectorCodecs = [TypedArrayFloat32Codec, DataViewFloat32Codec];
|
|
346
|
+
const float32Codec = hostIsLittleEndian() ? TypedArrayFloat32Codec : DataViewFloat32Codec;
|
|
347
|
+
/**
|
|
348
|
+
* Encodes a vector as little-endian float32 bytes. Values are rounded to single precision, which
|
|
349
|
+
* is the precision embedding models produce; `NaN` and infinities are stored as-is.
|
|
350
|
+
*
|
|
351
|
+
* @param values - The vector, e.g. a `number[]` or a `Float32Array`.
|
|
352
|
+
* @returns A new byte array of `values.length × 4` bytes.
|
|
353
|
+
*/
|
|
354
|
+
export function Float32VectorToBytes(values) {
|
|
355
|
+
return float32Codec.Encode(values);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Decodes little-endian float32 bytes into a new `Float32Array` that owns its memory.
|
|
359
|
+
*
|
|
360
|
+
* @param bytes - Bytes in the {@link FLOAT32_VECTOR_BYTES_PER_VALUE persisted vector layout}.
|
|
361
|
+
* @returns The vector, or `null` when `bytes` is empty or not a whole number of float32 values,
|
|
362
|
+
* so a truncated or foreign value is treated as missing rather than misread.
|
|
363
|
+
*/
|
|
364
|
+
export function BytesToFloat32Vector(bytes) {
|
|
365
|
+
if (!bytes || bytes.byteLength === 0 || bytes.byteLength % FLOAT32_VECTOR_BYTES_PER_VALUE !== 0)
|
|
366
|
+
return null;
|
|
367
|
+
return float32Codec.Decode(bytes);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Encodes a vector as base64 of its little-endian float32 bytes: the value stored in a binary
|
|
371
|
+
* vector entity field such as `VectorBinary`.
|
|
372
|
+
*
|
|
373
|
+
* @param values - The vector.
|
|
374
|
+
*/
|
|
375
|
+
export function Float32VectorToBase64(values) {
|
|
376
|
+
return BytesToBase64(Float32VectorToBytes(values));
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Decodes a binary vector entity field (base64 of little-endian float32 bytes).
|
|
380
|
+
*
|
|
381
|
+
* @param base64 - The field value; `null` / `undefined` are allowed.
|
|
382
|
+
* @returns The vector, or `null` when the value is missing, not valid base64, empty, or not a
|
|
383
|
+
* whole number of float32 values. Callers usually fall back to a JSON copy of the vector.
|
|
384
|
+
*/
|
|
385
|
+
export function Base64ToFloat32Vector(base64) {
|
|
386
|
+
return BytesToFloat32Vector(TryBase64ToBytes(base64));
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=BinaryEncoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BinaryEncoding.js","sourceRoot":"","sources":["../../src/util/BinaryEncoding.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED