From b53a3a4ec01c4818edde73a989f46919f478e447 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 12:28:57 +0200 Subject: [PATCH 01/41] Added test suites: Array Buffer, Bytes, constructor-endings, constructor, text --- apps/test-suite/TestModules.js | 1 + apps/test-suite/tests/Blob.ts | 760 ++++++++++++++++++ .../results.bin | 1 + .../classes/classes_dex/classes.dex | Bin 0 -> 45756 bytes .../results.bin | 1 + .../classes/classes_dex/classes.dex | Bin 0 -> 45772 bytes .../main/java/expo/modules/blob/BlobModule.kt | 4 +- packages/expo-blob/build/BlobModule.d.ts | 23 +- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 29 +- packages/expo-blob/build/BlobModule.js.map | 2 +- .../expo-blob/build/BlobModule.types.d.ts | 3 +- .../expo-blob/build/BlobModule.types.d.ts.map | 2 +- .../expo-blob/build/BlobModule.types.js.map | 2 +- packages/expo-blob/build/utils.d.ts | 17 + packages/expo-blob/build/utils.d.ts.map | 1 + packages/expo-blob/build/utils.js | 24 + packages/expo-blob/build/utils.js.map | 1 + packages/expo-blob/src/BlobModule.ts | 10 +- 19 files changed, 858 insertions(+), 25 deletions(-) create mode 100644 apps/test-suite/tests/Blob.ts create mode 100644 packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin create mode 100644 packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex create mode 100644 packages/expo-blob/android/build/.transforms/8f8a6c2346678f20ad35dd488b88f140/results.bin create mode 100644 packages/expo-blob/android/build/.transforms/8f8a6c2346678f20ad35dd488b88f140/transformed/classes/classes_dex/classes.dex create mode 100644 packages/expo-blob/build/utils.d.ts create mode 100644 packages/expo-blob/build/utils.d.ts.map create mode 100644 packages/expo-blob/build/utils.js create mode 100644 packages/expo-blob/build/utils.js.map diff --git a/apps/test-suite/TestModules.js b/apps/test-suite/TestModules.js index 2b622665646961..4db10c6088cd3b 100644 --- a/apps/test-suite/TestModules.js +++ b/apps/test-suite/TestModules.js @@ -76,6 +76,7 @@ export function getTestModules() { if (['android', 'ios'].includes(Platform.OS)) { modules.push(require('./tests/FileSystemNext')); + modules.push(require('./tests/Blob')); } if (Platform.OS === 'android') { diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts new file mode 100644 index 00000000000000..69665a7c0b0520 --- /dev/null +++ b/apps/test-suite/tests/Blob.ts @@ -0,0 +1,760 @@ +import {ExpoBlob as Blob} from "expo-blob" +import { Platform } from 'expo-modules-core'; + +export const name = 'Blob'; + +export async function test({ describe, it, expect, jasmine }) { + const test_blob = (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + + it(desc, async (t) => { + var blob = fn(); + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + expect(blob.type).toEqual(type); + expect(blob.size).toBe(expected.length); + + const text = await blob.text(); + const text1 = blob.syncText(); + expect(text).toEqual(expected); + expect(text).toEqual(text1); + }); + } + const test_blob_binary = async (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + it(desc, async () => { + var blob = fn(); + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + expect(blob.type).toBe(type); + expect(blob.size).toBe(expected.length); + + const ab = await blob.arrayBuffer(); + expect(ab instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(ab)).toEqual(expected); + }) + + } + describe('Blob', async () => { + describe('Blob creation', () => { + it('Empty blob', () => { + const blob = new Blob([]); + expect(blob).toBeTruthy(); + }); + it('String blob', () => { + const blob = new Blob(["ab", "cd"]); + expect(blob).toBeTruthy(); + }); + it('TypedArray blob', () => { + const blob = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); + expect(blob).toBeTruthy(); + }); + it('Blob blob', () => { + const blob = new Blob([new Blob([]), new Blob(["a", "b"])]); + expect(blob).toBeTruthy(); + }) + it('Mixed blob', () => { + const blob0 = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); + const blob1 = new Blob(["ab", "cd"]); + const blob2 = new Blob(["a", new Blob([new Blob(["b"])])]) + const blob = new Blob([blob0, blob1, blob2]); + expect(blob).toBeTruthy(); + }) + it('Blob flatten', () => { + const blob = new Blob(["aa", ["ab", "cd"], "ef"]) + expect(blob).toBeTruthy(); + }) + }); + + describe('Array buffer', () => { + it('simple', async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }); + it('empty Blob data', async () => { + const input_arr = new TextEncoder().encode(""); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }) + it('non-ascii input', async () => { + const input_arr = new TextEncoder().encode("\u08B8\u000a"); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }) + it('non-unicode input', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + // const array_buffer = await blob.arrayBuffer(); + const array_buffer = await blob.bytes(); + // expect(blob.syncText()).toEqual("\u0008\u00F1\u0030\u007B\u0097") + // expect(new Uint8Array(array_buffer) == typed_arr).toBeTruthy() + // expect(new Uint8Array(array_buffer)).toEqual(typed_arr); + expect(blob.size).toBe(5) + expect(array_buffer.length).toBe(5) + console.log(array_buffer.byteOffset) + console.log(array_buffer, typed_arr) + expect(array_buffer).toEqual(typed_arr); + }) + it('concurrent reads', async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const array_buffer_results = await Promise.all([blob.arrayBuffer(), + blob.arrayBuffer(), blob.arrayBuffer()]); + for (let array_buffer of array_buffer_results) { + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + } + }) + }) + + describe('Bytes', async () => { + it('Simple', async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); + }); + it('empty Blob data', async () => { + const input_arr = new TextEncoder().encode(""); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); + }); + it('non-ascii input', async () => { + const input_arr = new TextEncoder().encode("\u08B8\u000a"); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array).toEqual(input_arr); + }); + it('non-unicode input', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const uint8array = await blob.bytes(); + expect(uint8array).toEqual(typed_arr); + }); + it('concurrent reads', async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const uint8array_results = await Promise.all([blob.bytes(), + blob.bytes(), blob.bytes()]); + for (let uint8array of uint8array_results) { + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); + } + }); + }) + + // Windows platforms use CRLF as the native line ending. All others use LF. + const crlf = Platform.OS == 'windows'; + const native_ending = crlf ? '\r\n' : '\n'; + describe('constructor-endings', async () => { + it('valid endings value', () => { + const blob0 = new Blob([], {endings: 'native'}); + const blob1 = new Blob([], {endings: 'transparent'}); + expect(blob0).toBeTruthy() + expect(blob1).toBeTruthy() + }) + it('invalud endings value', () => { + [ + null, + '', + 'invalidEnumValue', + 'Transparent', + 'NATIVE', + 0, + {}, + ].forEach((ending) => { + // @ts-expect-error + expect(() => new Blob([], {endings:ending})).toThrow() + }) + }) + it('Exception propagation from options', () => { + const test_error = {name: 'test string'}; + // @ts-expect-error + expect(() => new Blob([], { get endings() { throw test_error; }})).toThrow('test string') + }) + // TODO weird test, as it could maybe be used lazily and not in the constructor + it('The \'endings\' options property is used', () => { + let got = false; + // @ts-expect-error + new Blob([], { get endings() { got = true; } }); + expect(got).toBeTruthy() + }) + const sampleEndings = [ + {name: 'LF', input: '\n', native: native_ending}, + {name: 'CR', input: '\r', native: native_ending}, + + {name: 'CRLF', input: '\r\n', native: native_ending}, + {name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)}, + {name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)}, + {name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)}, + + {name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)}, + {name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)}, + {name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)}, + + {name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)}, + {name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)}, + + ]; + it('Newlines should not change with endings unspecified', () => { + sampleEndings.forEach(testCase => { + const blob = new Blob([testCase.input]); + expect(blob.syncText()).toBe(testCase.input) + }) + }) + it('Newlines should not change with endings "transparent"', () => { + sampleEndings.forEach(testCase => { + const blob = new Blob([testCase.input], {endings: 'transparent'}); + expect(blob.syncText()).toBe(testCase.input) + }); + }) + it('Newlines should match the platform with endings "native"', () => { + sampleEndings.forEach(testCase => { + const blob = new Blob([testCase.input], {endings: 'native'}); + expect(blob.syncText()).toBe(testCase.native) + }); + }) + it('CR/LF in adjacent input strings', () => { + const blob = new Blob(['\r', '\n'], {endings: 'native'}); + const expected = native_ending.repeat(2); + expect(blob.syncText()).toBe(expected) + }) + }) + + describe('constructor', () => { + it("globalThis should have a Blob property.", () => {expect("Blob" in globalThis).toBeTruthy()}) + it("Blob.length should be 0", () => {expect(Blob.length).toBe(0)}) + it("Blob should be a function", () => {expect(Blob instanceof Function).toBeTruthy()}) + it("Blob constructor with no arguments", () => { + var blob = new Blob() + expect(blob instanceof Blob).toBeTruthy() + expect(String(blob)).toBe('[object Blob]') + expect(blob.size).toBe(0) + expect(blob.type).toBe("") + }) + it("Blob constructor with no arguments, without 'new'", () => { + expect(() => {var blob = Blob();}).toThrow() + }) + it("Blob constructor without brackets", () => { + var blob = new Blob; + expect(blob instanceof Blob).toBeTruthy() + expect(blob.size).toBe(0) + expect(blob.type).toBe("") + }) + it("Blob constructor with undefined as first argument", () => { + var blob = new Blob(undefined) + expect(blob instanceof Blob).toBeTruthy() + expect(String(blob)).toBe('[object Blob]') + expect(blob.size).toBe(0) + expect(blob.type).toBe("") + }) + // TODO Something wrong with null ? Why should Blob() work and Blob(null) not ??? + it('Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.', () => { + var args = [ + null, + true, + false, + 0, + 1, + 1.5, + "FAIL", + new Date(), + // @ts-expect-error + new RegExp(), + {}, + { 0: "FAIL", length: 1 }, + ]; + args.forEach((arg) => { + // @ts-expect-error + expect(() => new Blob(arg)).toThrow() + }); + }) + test_blob(function() { + + return new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + }); + }, { + expected: "", + type: "", + desc: "A plain object with @@iterator should be treated as a sequence for the blobParts argument." + }); + it('A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.', () => { + const blob = new Blob({ + [Symbol.iterator]() { + var i = 0; + return {next: () => [ + {done:false, value:'ab'}, + {done:false, value:'cde'}, + {done:true} + ][i++] + }; + } + }); + expect(blob.size).toBe(5) + }) + it('A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument.', () => { + let blob = new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + 0: "PASS", + length: 1 + }); + expect(blob.syncText()).toEqual("PASS") + expect(blob.type).toBe("") + }) + it('A String object should be treated as a sequence for the blobParts argument.', () => { + let blob = new Blob(new String("xyz")); + expect(blob.syncText()).toEqual("xyz") + expect(blob.type).toBe("") + }) + test_blob(function() { + return new Blob(new String("xyz")); + }, { + expected: "xyz", + type: "", + desc: "A String object should be treated as a sequence for the blobParts argument." + }); + test_blob(function() { + return new Blob(new Uint8Array([1, 2, 3])); + }, { + expected: "123", + type: "", + desc: "A Uint8Array object should be treated as a sequence for the blobParts argument." + }); + + var test_error = { + name: "test", + message: "test error", + }; + it("The length getter should be invoked and any exceptions should be propagated.", () => { + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + get length() { throw test_error; } + }; + new Blob(obj) + }).toThrow(test_error) + }) + it("ToUint32 should be applied to the length and any exceptions should be propagated.", () => { + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { + valueOf: null, + toString: function() { throw test_error; } + } + }; + new Blob(obj); + }).toThrow(test_error) + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { valueOf: function() { throw test_error; } } + }; + new Blob(obj); + }).toThrow(test_error) + }) + it("Getters and value conversions should happen in order until an exception is thrown.", () => { + var received = []; + var obj = { + get [Symbol.iterator]() { + received.push("Symbol.iterator"); + return Array.prototype[Symbol.iterator]; + }, + get length() { + received.push("length getter"); + return { + valueOf: function() { + received.push("length valueOf"); + return 3; + } + }; + }, + get 0() { + received.push("0 getter"); + return { + toString: function() { + received.push("0 toString"); + return "a"; + } + }; + }, + get 1() { + received.push("1 getter"); + throw test_error; + }, + get 2() { + received.push("2 getter"); + expect(true).toBe(false); + return "unreachable"; + } + }; + expect(() => new Blob(obj)).toThrow(test_error); + // Somehow we don't call 0 toString but I don't know why not or why would we + expect(received).toEqual([ + "Symbol.iterator", + "length getter", + "length valueOf", + "0 getter", + "0 toString", + "length getter", + "length valueOf", + "1 getter", + ]); + }) + it("ToString should be called on elements of the blobParts array and any exceptions should be propagated.", () => { + expect(() => new Blob([{ toString: function() { throw test_error; } }])).toThrow(test_error) + expect(() => new Blob([{ toString: undefined, valueOf: function() { throw test_error; } }])).toThrow(test_error) + expect(() => new Blob([{ + toString: function() { throw test_error; }, + valueOf: function() { expect(false).toBe(true); } + }])).toThrow(test_error) + // TODO add the proper TypeError type to toThrow + expect(() => new Blob([{toString: null, valueOf: null}])).toThrow() + }) + test_blob(function() { + var arr = [ + { toString: function() { arr.pop(); return "PASS"; } }, + { toString: function() { } } + ]; + return new Blob(arr); + }, { + expected: "PASS", + type: "", + desc: "Changes to the blobParts array should be reflected in the returned Blob (pop)." + }); + test_blob(function() { + var arr = [ + { + toString: function() { + if (arr.length === 3) { + return "A"; + } + arr.unshift({ + toString: function() { + expect(true).toBe(false) + } + }); + return "P"; + } + }, + { + toString: function() { + return "SS"; + } + } + ]; + return new Blob(arr); + }, { + expected: "PASS", + type: "", + desc: "Changes to the blobParts array should be reflected in the returned Blob (unshift)." + }); + test_blob(function() { + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17652 + return new Blob([ + null, + undefined, + true, + false, + 0, + 1, + new String("stringobject"), + [], + ['x', 'y'], + {}, + { 0: "FAIL", length: 1 }, + { toString: function() { return "stringA"; } }, + { toString: undefined, valueOf: function() { return "stringB"; } }, + { valueOf: function() { expect(false).toBe(true) } } + ]); + }, { + expected: "nullundefinedtruefalse01stringobjectx,y[object Object][object Object]stringAstringB[object Object]", + type: "", + desc: "ToString should be called on elements of the blobParts array." + }); + test_blob(function() { + return new Blob([ + new ArrayBuffer(8) + ]); + }, { + expected: "\0\0\0\0\0\0\0\0", + type: "", + desc: "ArrayBuffer elements of the blobParts array should be supported." + }); + + test_blob(function() { + return new Blob([ + new Uint8Array([0x50, 0x41, 0x53, 0x53]), + new Int8Array([0x50, 0x41, 0x53, 0x53]), + new Uint16Array([0x4150, 0x5353]), + new Int16Array([0x4150, 0x5353]), + new Uint32Array([0x53534150]), + new Int32Array([0x53534150]), + new Float32Array([0xD341500000]) + ]); + }, { + expected: "PASSPASSPASSPASSPASSPASSPASS", + type: "", + desc: "Passing typed arrays as elements of the blobParts array should work." + }); + test_blob(function() { + return new Blob([ + new Float16Array([2.65625, 58.59375]) + ]); + }, { + expected: "PASS", + type: "", + desc: "Passing a Float16Array as element of the blobParts array should work." + }); + test_blob(function() { + return new Blob([ + // 0x535 3415053534150 + // 0x535 = 0b010100110101 -> Sign = +, Exponent = 1333 - 1023 = 310 + // 0x13415053534150 * 2**(-52) + // ==> 0x13415053534150 * 2**258 = 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680 + new Float64Array([2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680]) + ]); + }, { + expected: "PASSPASS", + type: "", + desc: "Passing a Float64Array as element of the blobParts array should work." + }); + test_blob(function() { + return new Blob([ + new BigInt64Array([BigInt("0x5353415053534150")]), + new BigUint64Array([BigInt("0x5353415053534150")]) + ]); + }, { + expected: "PASSPASSPASSPASS", + type: "", + desc: "Passing BigInt typed arrays as elements of the blobParts array should work." + }); + + // TODO revisit this one as its implementation is weird in the wpt + it("Passing a FrozenArray as the blobParts array should work (FrozenArray).", async () => { + var channel = new MessageChannel(); + channel.port2.onmessage = this.step_func(function(e) { + var b_ports = new Blob(e.ports); + expect(b_ports.size).toEqual("[object MessagePort]".length) + this.done(); + }); + var channel2 = new MessageChannel(); + channel.port1.postMessage('', [channel2.port1]); + }) + + test_blob(function() { + var blob = new Blob(['foo']); + return new Blob([blob, blob]); + }, { + expected: "foofoo", + type: "", + desc: "Array with two blobs" + }); + test_blob_binary(function() { + var view = new Uint8Array([0, 255, 0]); + return new Blob([view.buffer, view.buffer]); + }, { + expected: [0, 255, 0, 0, 255, 0], + type: "", + desc: "Array with two buffers" + }); + + test_blob_binary(function() { + var view = new Uint8Array([0, 255, 0, 4]); + var blob = new Blob([view, view]); + expect(blob.size).toBe(8); + var view1 = new Uint16Array(view.buffer, 2); + return new Blob([view1, view.buffer, view1]); + }, { + expected: [0, 4, 0, 255, 0, 4, 0, 4], + type: "", + desc: "Array with two bufferviews" + }); + + // TODO revisit this, why can we pass view but not the buffer? + test_blob(function() { + var view = new Uint8Array([0]); + var blob = new Blob(["fo"]); + return new Blob([view.buffer, blob, "foo"]); + }, { + expected: "\0fofoo", + type: "", + desc: "Array with mixed types" + }); + + it('options properties should be accessed in lexicographic order.', async () => { + const accessed = []; + const stringified = []; + + new Blob([], { + get type() { accessed.push('type'); }, + get endings() { accessed.push('endings'); } + }); + new Blob([], { + type: { toString: () => { stringified.push('type'); return ''; } }, + endings: { toString: () => { stringified.push('endings'); return 'transparent'; } } + }); + expect(accessed).toEqual(['endings', 'type']); + expect(stringified).toEqual(['endings', 'type']); + }) + + it('Arguments should be evaluated from left to right.', async () => { + expect( + () => new Blob( + [{ toString: function() { throw test_error } }], + { + get type() { assert_unreached("type getter should not be called."); } + }) + ).toThrow(test_error); + }) + + describe('Passing arguments for options', () => { + [ + null, + undefined, + {}, + { unrecognized: true }, + /regex/, + function() {} + ].forEach((arg : any, idx) => { + test_blob(function() { + return new Blob([], arg); + }, { + expected: "", + type: "", + desc: "Passing " + JSON.stringify(arg) + " (index " + idx + ") for options should use the defaults." + }); + test_blob(function() { + return new Blob(["\na\r\nb\n\rc\r"], arg); + }, { + expected: "\na\r\nb\n\rc\r", + type: "", + desc: "Passing " + JSON.stringify(arg) + " (index " + idx + ") for options should use the defaults (with newlines)." + }); + }); + }) + + describe('Blob constructor should throw with invalid property bag', () => { + [ + 123, + 123.4, + true, + 'abc' + ].forEach((arg : any) => { + it('Passing ' + JSON.stringify(arg) + ' for options should throw', () => { + expect(() => { + new Blob([], arg) + }).toThrow() + }) + }); + }) + + describe('Type test', () => { + var type_tests = [ + // blobParts, type, expected type + [[], '', ''], + [[], 'a', 'a'], + [[], 'A', 'a'], + [[], 'text/html', 'text/html'], + [[], 'TEXT/HTML', 'text/html'], + [[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'], + [[], '\u00E5', ''], + [[], '\uD801\uDC7E', ''], // U+1047E + [[], ' image/gif ', ' image/gif '], + [[], '\timage/gif\t', ''], + [[], 'image/gif;\u007f', ''], + [[], '\u0130mage/gif', ''], // uppercase i with dot + [[], '\u0131mage/gif', ''], // lowercase dotless i + [[], 'image/gif\u0000', ''], + // check that type isn't changed based on sniffing + [[0x3C, 0x48, 0x54, 0x4D, 0x4C, 0x3E], 'unknown/unknown', 'unknown/unknown'], // "" + [[0x00, 0xFF], 'text/plain', 'text/plain'], + [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], 'image/png', 'image/png'], // "GIF89a" + ]; + + type_tests.forEach(function(t: Array) { + it("Blob with type " + JSON.stringify(t[1]), () => { + // TODO Why this construction? It does'nt work yet, but it's not what we test for... + // var arr = new Uint8Array([t[0]]).buffer; + // var b = new Blob([arr], {type:t[1]}); + + var b = new Blob(t[0], {type:t[1]}); + expect(b.type).toEqual(t[2]); + }); + }); + }) + }) + + describe('Text', async () => { + it('simple', async () => { + const blob = new Blob(["PASS"]); + const text = await blob.text(); + expect(text).toBe("PASS"); + }); + it('empty blob data', async () => { + const blob = new Blob(); + const text = await blob.text(); + expect(text).toBe(""); + }); + it('multi-element array in constructor', async () => { + const blob = new Blob(["P", "A", "SS"]); + const text = await blob.text(); + expect(text).toBe("PASS"); + }); + it('non-unicode', async () => { + const non_unicode = "\u0061\u030A"; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr]); + const text = await blob.text(); + expect(text).toBe(non_unicode); + }); + it('different charset param in type option', async () => { + const blob = new Blob(["PASS"], { type: "text/plain;charset=utf-16le" }); + const text = await blob.text(); + expect(text).toBe("PASS"); + }) + it('Sync Text', () => { + const blob = new Blob(["PA", "SS"]); + const text = blob.syncText(); + expect(text).toBe("PASS"); + }) + it('different charset param with non-ascii input', async () => { + const non_unicode = "\u0061\u030A"; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr], { type: "text/plain;charset=utf-16le" }); + const text = await blob.text(); + expect(text).toBe(non_unicode); + }) + it('invalid utf-8 input', async () => { + const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); + const blob = new Blob([input_arr]); + const text = await blob.text(); + expect(text).toBe("\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd"); + }); + it('Promise.all multiple reads', async () => { + const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); + const blob = new Blob([input_arr]); + const text_results = await Promise.all([blob.text(), blob.text(), blob.text()]); + text_results.forEach(text => { + expect(text).toBe("\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd"); + }); + }); + }) + }); +} \ No newline at end of file diff --git a/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin b/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin new file mode 100644 index 00000000000000..0d259ddcb52852 --- /dev/null +++ b/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin @@ -0,0 +1 @@ +o/classes diff --git a/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex b/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex new file mode 100644 index 0000000000000000000000000000000000000000..906cd2e1836d6bae41bfd64f9ea55901218437c9 GIT binary patch literal 45756 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 d, options: BlobOptionsBag? -> + Constructor() { blobParts: List?, options: BlobOptionsBag? -> val type = options?.type ?: DEFAULT_TYPE val endings = options?.endings ?: EndingType.TRANSPARENT - Blob(blobParts.internal(endings == EndingType.NATIVE), type) + Blob((blobParts ?: listOf()).internal(endings == EndingType.NATIVE), type) } Property("size") { blob: Blob -> diff --git a/packages/expo-blob/build/BlobModule.d.ts b/packages/expo-blob/build/BlobModule.d.ts index a772a64fc0d806..0f9b14aa49bf37 100644 --- a/packages/expo-blob/build/BlobModule.d.ts +++ b/packages/expo-blob/build/BlobModule.d.ts @@ -1,10 +1,23 @@ -import { Blob } from "./BlobModule.types"; -declare const NativeBlobModule: any; -export declare class ExpoBlob extends NativeBlobModule.Blob implements Blob { - constructor(blobParts?: any, options?: BlobPropertyBag); - slice(start?: number, end?: number, contentType?: string): Blob; +import { NativeModule, SharedObject } from "expo"; +import { Blob, BlobPart } from "./BlobModule.types"; +declare class NativeBlob extends SharedObject { + readonly size: number; + readonly type: string; + constructor(blobParts?: BlobPart[], options?: BlobPropertyBag); + slice(start?: number, end?: number, contentType?: string): ExpoBlob; + bytes(): Promise; text(): Promise; + syncText(): string; +} +declare class ExpoBlobModule extends NativeModule { + Blob: typeof NativeBlob; +} +declare const NativeBlobModule: ExpoBlobModule; +export declare class ExpoBlob extends NativeBlobModule.Blob implements Blob { + constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag); + slice(start?: number, end?: number, contentType?: string): ExpoBlob; stream(): ReadableStream; + arrayBuffer(): Promise; } export {}; //# sourceMappingURL=BlobModule.d.ts.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 1edd9d4f667063..1a8d2dff32d89a 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAE1C,QAAA,MAAM,gBAAgB,EAAE,GAAqC,CAAC;AAE9D,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,eAAe;IAItD,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IASzD,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,MAAM,IAAI,cAAc;CAgBxB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAUxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 1d059405272487..53c193782a6a49 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -1,22 +1,26 @@ import { requireNativeModule } from "expo"; +import { normalizedContentType } from "./utils"; const NativeBlobModule = requireNativeModule("ExpoBlob"); export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { - super(blobParts, options); + if (!blobParts) { + super([], options); + } + else if (blobParts instanceof Array) { + super(blobParts.flat(Infinity), options); + } + else { + super(Array.from(blobParts).flat(Infinity), options); + } } slice(start, end, contentType) { - const slicedBlob = super.slice(start, end, contentType); - const options = { - type: slicedBlob.type, - endings: slicedBlob.endings, - }; - return new ExpoBlob(slicedBlob, options); - } - async text() { - return Promise.resolve(super.text()); + const normalizedType = contentType ?? normalizedContentType(contentType); + const slicedBlob = super.slice(start, end, normalizedType); + Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); + return slicedBlob; } stream() { - const text = super.text(); + const text = super.syncText(); const encoder = new TextEncoder(); const uint8 = encoder.encode(text); let offset = 0; @@ -32,5 +36,8 @@ export class ExpoBlob extends NativeBlobModule.Blob { }, }); } + async arrayBuffer() { + return super.bytes().then((bytes) => bytes.buffer); + } } //# sourceMappingURL=BlobModule.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 7b6a0254eb182f..92a91f40466193 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAG3C,MAAM,gBAAgB,GAAQ,mBAAmB,CAAC,UAAU,CAAC,CAAC;AAE9D,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAe,EAAE,OAAyB;QACrD,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QACxD,MAAM,OAAO,GAAoB;YAChC,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,OAAO,EAAE,UAAU,CAAC,OAAO;SAC3B,CAAC;QACF,OAAO,IAAI,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,IAAI;QACT,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;CACD","sourcesContent":["import { requireNativeModule } from \"expo\";\nimport { Blob } from \"./BlobModule.types\";\n\nconst NativeBlobModule: any = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any, options?: BlobPropertyBag) {\n\t\tsuper(blobParts, options);\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): Blob {\n\t\tconst slicedBlob = super.slice(start, end, contentType);\n\t\tconst options: BlobPropertyBag = {\n\t\t\ttype: slicedBlob.type,\n\t\t\tendings: slicedBlob.endings,\n\t\t};\n\t\treturn new ExpoBlob(slicedBlob, options);\n\t}\n\n\tasync text(): Promise {\n\t\treturn Promise.resolve(super.text());\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.text();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = contentType ?? normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.types.d.ts b/packages/expo-blob/build/BlobModule.types.d.ts index f99f2cf2abec05..67ee05e989afee 100644 --- a/packages/expo-blob/build/BlobModule.types.d.ts +++ b/packages/expo-blob/build/BlobModule.types.d.ts @@ -1,8 +1,9 @@ export declare class Blob { constructor(blobParts?: any, options?: BlobPropertyBag); slice(start?: number, end?: number, contentType?: string): Blob; - text(): Promise; stream(): ReadableStream; + text(): Promise; + arrayBuffer(): Promise; } export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob; //# sourceMappingURL=BlobModule.types.d.ts.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.types.d.ts.map b/packages/expo-blob/build/BlobModule.types.d.ts.map index c0809539567694..a1a64eee7d0228 100644 --- a/packages/expo-blob/build/BlobModule.types.d.ts.map +++ b/packages/expo-blob/build/BlobModule.types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.types.d.ts","sourceRoot":"","sources":["../src/BlobModule.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,IAAI;gBACZ,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,eAAe;IAEtD,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/D,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,MAAM,IAAI,cAAc;CACxB;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAC"} \ No newline at end of file +{"version":3,"file":"BlobModule.types.d.ts","sourceRoot":"","sources":["../src/BlobModule.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,OAAO,IAAI;gBACZ,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,eAAe;IAEtD,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/D,MAAM,IAAI,cAAc;IACxB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CACvC;AAED,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAC"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.types.js.map b/packages/expo-blob/build/BlobModule.types.js.map index 8bc4951a8137fc..4aead2898e8892 100644 --- a/packages/expo-blob/build/BlobModule.types.js.map +++ b/packages/expo-blob/build/BlobModule.types.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.types.js","sourceRoot":"","sources":["../src/BlobModule.types.ts"],"names":[],"mappings":"","sourcesContent":["export declare class Blob {\n\tconstructor(blobParts?: any, options?: BlobPropertyBag);\n\n\tslice(start?: number, end?: number, contentType?: string): Blob;\n\ttext(): Promise;\n\tstream(): ReadableStream;\n}\n\nexport type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob;\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.types.js","sourceRoot":"","sources":["../src/BlobModule.types.ts"],"names":[],"mappings":"","sourcesContent":["export declare class Blob {\n\tconstructor(blobParts?: any, options?: BlobPropertyBag);\n\n\tslice(start?: number, end?: number, contentType?: string): Blob;\n\tstream(): ReadableStream;\n\ttext(): Promise;\n\tarrayBuffer(): Promise;\n}\n\nexport type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob;\n"]} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts b/packages/expo-blob/build/utils.d.ts new file mode 100644 index 00000000000000..a7f581287101ee --- /dev/null +++ b/packages/expo-blob/build/utils.d.ts @@ -0,0 +1,17 @@ +/** + * Normalizes the content type string for a Blob. + * + * Returns the lowercased content type if it is valid, or an empty string otherwise. + * + * A valid content type: + * - Is not null, undefined, or empty + * - Contains only printable ASCII characters (0x20–0x7E) + * - Does not contain forbidden control characters: NUL (\x00), LF (\x0A), or CR (\x0D) + * + * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type. + * + * @param type The content type string to normalize. + * @returns The normalized (lowercased) content type, or an empty string if invalid. + */ +export declare function normalizedContentType(type?: string): string; +//# sourceMappingURL=utils.d.ts.map \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts.map b/packages/expo-blob/build/utils.d.ts.map new file mode 100644 index 00000000000000..b19ab9410a6b2a --- /dev/null +++ b/packages/expo-blob/build/utils.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D"} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js b/packages/expo-blob/build/utils.js new file mode 100644 index 00000000000000..35e1a30b50ac45 --- /dev/null +++ b/packages/expo-blob/build/utils.js @@ -0,0 +1,24 @@ +/** + * Normalizes the content type string for a Blob. + * + * Returns the lowercased content type if it is valid, or an empty string otherwise. + * + * A valid content type: + * - Is not null, undefined, or empty + * - Contains only printable ASCII characters (0x20–0x7E) + * - Does not contain forbidden control characters: NUL (\x00), LF (\x0A), or CR (\x0D) + * + * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type. + * + * @param type The content type string to normalize. + * @returns The normalized (lowercased) content type, or an empty string if invalid. + */ +export function normalizedContentType(type) { + if (!type || type.length === 0) + return ""; + const asciiPrintable = /^[\x20-\x7E]+$/; + if (!asciiPrintable.test(type)) + return ""; + return type.toLowerCase(); +} +//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js.map b/packages/expo-blob/build/utils.js.map new file mode 100644 index 00000000000000..1acb66f43b19ab --- /dev/null +++ b/packages/expo-blob/build/utils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IAClD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n\tif (!type || type.length === 0) return \"\";\n\tconst asciiPrintable = /^[\\x20-\\x7E]+$/;\n\tif (!asciiPrintable.test(type)) return \"\";\n\treturn type.toLowerCase();\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 0e9a740e87c4c7..cc71776fb5fac9 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -18,8 +18,14 @@ declare class ExpoBlobModule extends NativeModule { const NativeBlobModule = requireNativeModule("ExpoBlob"); export class ExpoBlob extends NativeBlobModule.Blob implements Blob { - constructor(blobParts?: any[], options?: BlobPropertyBag) { - super(blobParts?.flat(Infinity), options); + constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { + if (!blobParts) { + super([], options); + } else if (blobParts instanceof Array) { + super(blobParts.flat(Infinity), options); + } else { + super(Array.from(blobParts).flat(Infinity), options); + } } slice(start?: number, end?: number, contentType?: string): ExpoBlob { From e5e7869004973c924db4b4ae888aa918d497db8d Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 13:03:49 +0200 Subject: [PATCH 02/41] in worker & slice tests --- apps/test-suite/tests/Blob.ts | 280 +++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 2 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 69665a7c0b0520..95258e502ac378 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -285,7 +285,6 @@ export async function test({ describe, it, expect, jasmine }) { }); }) test_blob(function() { - return new Blob({ [Symbol.iterator]: Array.prototype[Symbol.iterator], }); @@ -693,7 +692,7 @@ export async function test({ describe, it, expect, jasmine }) { // TODO Why this construction? It does'nt work yet, but it's not what we test for... // var arr = new Uint8Array([t[0]]).buffer; // var b = new Blob([arr], {type:t[1]}); - + var b = new Blob(t[0], {type:t[1]}); expect(b.type).toEqual(t[2]); }); @@ -756,5 +755,282 @@ export async function test({ describe, it, expect, jasmine }) { }); }); }) + + describe('Worker', async () => { + it('Create Blob in Worker', async () => { + const data = "TEST"; + const blob = new Blob([data], {type: "text/plain"}); + expect(await blob.text()).toEqual(data); + }); + }) + + describe('Blob slice overflow', async () => { + var text = ''; + + for (var i = 0; i < 2000; ++i) { + text += 'A'; + } + + it("slice start is negative, relativeStart will be max((size + start), 0)", () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(-1, blob.size); + expect(sliceBlob.size).toBe(1); + }); + + it("slice start is greater than blob size, relativeStart will be min(start, size)", () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size + 1, blob.size); + expect(sliceBlob.size).toBe(0); + }); + + it("slice end is negative, relativeEnd will be max((size + end), 0)", () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, -1); + expect(sliceBlob.size).toBe(1); + }); + + it("slice end is greater than blob size, relativeEnd will be min(end, size)", () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, blob.size + 999); + expect(sliceBlob.size).toBe(2) + }); + }) + + describe('Blob Slice', async () => { + test_blob(() => { + var blobTemp = new Blob(["PASS"]); + return blobTemp.slice(); + }, { + expected: "PASS", + type: "", + desc: "no-argument Blob slice" + } + ); + + describe("Slices", async () => { + var blob1 = new Blob(["squiggle"]); + var blob2 = new Blob(["steak"], {type: "content/type"}); + + test_blob(() => blob1, + { + expected: "squiggle", + type: "", + desc: "blob1." + }); + + test_blob(() => blob2, + { + expected: "steak", + type: "content/type", + desc: "blob2." + }); + + test_blob(() => { + return new Blob().slice(0,0,null); + }, { + expected: "", + type: "null", + desc: "null type Blob slice" + }); + + test_blob(() => { + return new Blob().slice(0,0,undefined); + }, { + expected: "", + type: "", + desc: "undefined type Blob slice" + }); + + test_blob(() => { + return new Blob().slice(0,0); + }, { + expected: "", + type: "", + desc: "no type Blob slice" + }); + + var arrayBuffer = new ArrayBuffer(16); + var int8View = new Int8Array(arrayBuffer); + for (var i = 0; i < 16; i++) { + int8View[i] = i + 65; + } + + var testData = [ + [ + ["PASSSTRING"], + [{start: -6, contents: "STRING"}, + {start: -12, contents: "PASSSTRING"}, + {start: 4, contents: "STRING"}, + {start: 12, contents: ""}, + {start: 0, end: -6, contents: "PASS"}, + {start: 0, end: -12, contents: ""}, + {start: 0, end: 4, contents: "PASS"}, + {start: 0, end: 12, contents: "PASSSTRING"}, + {start: 7, end: 4, contents: ""}] + ], + + // Test 3 strings + [ + ["foo", "bar", "baz"], + [{start: 0, end: 9, contents: "foobarbaz"}, + {start: 0, end: 3, contents: "foo"}, + {start: 3, end: 9, contents: "barbaz"}, + {start: 6, end: 9, contents: "baz"}, + {start: 6, end: 12, contents: "baz"}, + {start: 0, end: 9, contents: "foobarbaz"}, + {start: 0, end: 11, contents: "foobarbaz"}, + {start: 10, end: 15, contents: ""}] + ], + + // Test string, Blob, string + [ + ["foo", blob1, "baz"], + [{start: 0, end: 3, contents: "foo"}, + {start: 3, end: 11, contents: "squiggle"}, + {start: 2, end: 4, contents: "os"}, + {start: 10, end: 12, contents: "eb"}] + ], + + // Test blob, string, blob + [ + [blob1, "foo", blob1], + [{start: 0, end: 8, contents: "squiggle"}, + {start: 7, end: 9, contents: "ef"}, + {start: 10, end: 12, contents: "os"}, + {start: 1, end: 4, contents: "qui"}, + {start: 12, end: 15, contents: "qui"}, + {start: 40, end: 60, contents: ""}] + ], + + // Test blobs all the way down + [ + [blob2, blob1, blob2], + [{start: 0, end: 5, contents: "steak"}, + {start: 5, end: 13, contents: "squiggle"}, + {start: 13, end: 18, contents: "steak"}, + {start: 1, end: 3, contents: "te"}, + {start: 6, end: 10, contents: "quig"}] + ], + + // Test an ArrayBufferView + [ + [int8View, blob1, "foo"], + [{start: 0, end: 8, contents: "ABCDEFGH"}, + {start: 8, end: 18, contents: "IJKLMNOPsq"}, + {start: 17, end: 20, contents: "qui"}, + {start: 4, end: 12, contents: "EFGHIJKL"}] + ], + + // Test a partial ArrayBufferView + [ + [new Uint8Array(arrayBuffer, 3, 5), blob1, "foo"], + [{start: 0, end: 8, contents: "DEFGHsqu"}, + {start: 8, end: 18, contents: "igglefoo"}, + {start: 4, end: 12, contents: "Hsquiggl"}] + ], + + // Test type coercion of a number + [ + [3, int8View, "foo"], + [{start: 0, end: 8, contents: "3ABCDEFG"}, + {start: 8, end: 18, contents: "HIJKLMNOPf"}, + {start: 17, end: 21, contents: "foo"}, + {start: 4, end: 12, contents: "DEFGHIJK"}] + ], + + [ + [(new Uint8Array([0, 255, 0])).buffer, + new Blob(['abcd']), + 'efgh', + 'ijklmnopqrstuvwxyz'], + [{start: 1, end: 4, contents: "\uFFFD\u0000a"}, + {start: 4, end: 8, contents: "bcde"}, + {start: 8, end: 12, contents: "fghi"}, + {start: 1, end: 12, contents: "\uFFFD\u0000abcdefghi"}] + ] + ]; + + testData.forEach(function(data, i) { + var blobs = data[0]; + var tests = data[1]; + tests.forEach(function(expectations, j) { + describe("Slicing test (" + i + "," + j + ").", () => { + var blob = new Blob(blobs); + + it('blob is an instance of Blob', () => { + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + }) + + test_blob(() => { + return expectations.end === undefined + ? blob.slice(expectations.start) + : blob.slice(expectations.start, expectations.end); + }, { + expected: expectations.contents, + type: "", + desc: "Slicing test: slice (" + i + "," + j + ")." + }); + },); + }); + }); + }); + + describe('Invalid content types', () => { + var invalidTypes = [ + "\xFF", + "te\x09xt/plain", + "te\x00xt/plain", + "te\x1Fxt/plain", + "te\x7Fxt/plain" + ]; + invalidTypes.forEach(function(type) { + test_blob(() => { + var blob = new Blob(["PASS"]); + return blob.slice(0, 4, type); + }, { + expected: "PASS", + type: "", + desc: "Invalid contentType (" + JSON.stringify(type) + ")" + }); + }); + }) + + var validTypes = [ + "te(xt/plain", + "te)xt/plain", + "text/plain", + "te@xt/plain", + "te,xt/plain", + "te;xt/plain", + "te:xt/plain", + "te\\xt/plain", + "te\"xt/plain", + "te/xt/plain", + "te[xt/plain", + "te]xt/plain", + "te?xt/plain", + "te=xt/plain", + "te{xt/plain", + "te}xt/plain", + "te\x20xt/plain", + "TEXT/PLAIN", + "text/plain;charset = UTF-8", + "text/plain;charset=UTF-8" + ]; + describe('valid content types', () => { + validTypes.forEach((type) => { + test_blob(() => { + var blob = new Blob(["PASS"]); + return blob.slice(0, 4, type); + }, { + expected: "PASS", + type: type.toLowerCase(), + desc: "Valid contentType (" + JSON.stringify(type) + ")" + }); + }); + }) + }) }); } \ No newline at end of file From 517def170ef9f64205ffc21c332683d6886c57bf Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 13:34:54 +0200 Subject: [PATCH 03/41] constructor dom windows + stream related tests --- apps/test-suite/tests/Blob.ts | 83 +++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 95258e502ac378..e5bf70cf48cb1c 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -3,6 +3,11 @@ import { Platform } from 'expo-modules-core'; export const name = 'Blob'; +var test_error = { + name: "test", + message: "test error", +}; + export async function test({ describe, it, expect, jasmine }) { const test_blob = (fn, expectations) => { var expected = expectations.expected, @@ -336,10 +341,6 @@ export async function test({ describe, it, expect, jasmine }) { desc: "A Uint8Array object should be treated as a sequence for the blobParts argument." }); - var test_error = { - name: "test", - message: "test error", - }; it("The length getter should be invoked and any exceptions should be propagated.", () => { expect(() => { var obj = { @@ -700,6 +701,52 @@ export async function test({ describe, it, expect, jasmine }) { }) }) + describe('constructor dom windows', async () => { + it("Passing platform objects for blobParts should throw a TypeError.", () => { + const document = new Document() + var args = [ + document.createElement("div"), + window, + ]; + args.forEach((arg) => { + expect(() => new Blob(arg)).toThrow(); + }); + }); + + it("A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)", () => { + var element = document.createElement("div"); + element.appendChild(document.createElement("div")); + element.appendChild(document.createElement("p")); + var list = element.children; + Object.defineProperty(list, "length", { + get: function() { throw test_error; } + }); + expect(() => {new Blob(list);}).toThrow(test_error); + }); + + test_blob(function() { + const document = new Document() + var select = document.createElement("select"); + select.appendChild(document.createElement("option")); + return new Blob(select); + }, { + expected: "[object HTMLOptionElement]", + type: "", + desc: "Passing a platform object that supports indexed properties as the blobParts array should work (select)." + }); + + test_blob(function() { + const document = new Document() + var elm = document.createElement("div"); + elm.setAttribute("foo", "bar"); + return new Blob(elm.attributes); + }, { + expected: "[object Attr]", + type: "", + desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." + }); + }) + describe('Text', async () => { it('simple', async () => { const blob = new Blob(["PASS"]); @@ -1032,5 +1079,33 @@ export async function test({ describe, it, expect, jasmine }) { }); }) }) + + describe('stream', async () => { + it('stream byob crash', async () => { + let a = new Blob(['', '', undefined], { }) + let b = a.stream() + let c = new ReadableStreamBYOBReader(b) + let d = new Int16Array(8) + await c.read(d) + c.releaseLock() + await a.text() + await b.cancel() + }) + + it('stream xhr crash', async () => { + // TODO this constructor doesn't work need to fix it + // const blob = new Blob([1, 2]); + const blob = new Blob([Int32Array.from([1, 2])]); + const readable = blob.stream() + const writable = new WritableStream({}, { + size() { + let xhr = new XMLHttpRequest() + xhr.open("POST", "1", false) + xhr.send() + } + }) + readable.pipeThrough({ readable, writable }) + }) + }) }); } \ No newline at end of file From 7e847a95e54eb7457beeb4c1f868e2b71728f031 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 13:48:36 +0200 Subject: [PATCH 04/41] stream tests --- apps/test-suite/tests/Blob.ts | 95 ++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index e5bf70cf48cb1c..0c308b1cf34cb4 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -42,8 +42,42 @@ export async function test({ describe, it, expect, jasmine }) { expect(ab instanceof ArrayBuffer).toBeTruthy(); expect(new Uint8Array(ab)).toEqual(expected); }) - } + + // Helper function that triggers garbage collection while reading a chunk + // if perform_gc is true. + const read_and_gc = async (reader, perform_gc) => { + // Passing Uint8Array for byte streams; non-byte streams will simply ignore it + const read_promise = reader.read(new Uint8Array(64)); + if (perform_gc) { + // TODO Actually perform garbage collection in here + // await garbageCollect(); + } + return read_promise; + } + + // Takes in a ReadableStream and reads from it until it is done, returning + // an array that contains the results of each read operation. If perform_gc + // is true, garbage collection is triggered while reading every chunk. + const read_all_chunks = async (stream, { perform_gc = false, mode } = {}) => { + expect(stream instanceof ReadableStream).toBeTruthy(); + expect('getReader' in stream).toBeTruthy(); + const reader = stream.getReader({ mode }); + + expect('read' in reader).toBeTruthy() + let read_value = await read_and_gc(reader, perform_gc); + + let out = []; + let i = 0; + while (!read_value.done) { + for (let val of read_value.value) { + out[i++] = val; + } + read_value = await read_and_gc(reader, perform_gc); + } + return out; + } + describe('Blob', async () => { describe('Blob creation', () => { it('Empty blob', () => { @@ -1106,6 +1140,65 @@ export async function test({ describe, it, expect, jasmine }) { }) readable.pipeThrough({ readable, writable }) }) + + it("Blob.stream()", async () => { + const blob = new Blob(["PASS"]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + for (let [index, value] of chunks.entries()) { + expect(value).toEqual("PASS".charCodeAt(index)); + } + }) + + it("Blob.stream() empty Blob", async () => { + const blob = new Blob(); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + expect(chunks).toEqual([]); + }) + + it("Blob.stream() non-unicode input", async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + expect(chunks).toEqual(input_arr); + }) + + it("Blob.stream() garbage collection of blob shouldn't break stream " + + "consumption", async() => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + blob = null; + // TODO Actually call garbageCollect() + // await garbageCollect(); + const chunks = await read_all_chunks(stream, { perform_gc: true }); + expect(chunks).toEqual(input_arr); + }) + + it("Blob.stream() garbage collection of stream shouldn't break stream " + + "consumption", async() => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const chunksPromise = read_all_chunks(blob.stream()); + // It somehow matters to do GC here instead of doing `perform_gc: true` + // TODO Actually call garbageCollect() + // await garbageCollect(); + expect(await chunksPromise).toEqual(input_arr); + }) + + it("Reading Blob.stream() with BYOB reader", async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream, { mode: "byob" }); + expect(chunks).toEqual(input_arr); + }) }) }); } \ No newline at end of file From 47da5574e92c7152d221f8b70f38fdfed72f21e6 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 14:49:05 +0200 Subject: [PATCH 05/41] slice fixes --- .../android/src/main/java/expo/modules/blob/Blob.kt | 3 +++ .../src/main/java/expo/modules/blob/BlobModule.kt | 12 +++++++++--- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 5 ++++- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 5 ++++- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt index fe5452c34bcf3c..5e7be6d72c26cc 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt @@ -51,6 +51,9 @@ class Blob() : SharedObject() { } fun slice(start: Int, end: Int, contentType: String): Blob { + if (start >= end) { + return Blob(listOf(), contentType) + } var i: Int = 0 val bps: MutableList = mutableListOf() diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt index 0b9bdddeb7b314..9fd4d38d3fe9d9 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt @@ -2,6 +2,8 @@ package expo.modules.blob import expo.modules.kotlin.modules.Module import expo.modules.kotlin.modules.ModuleDefinition +import kotlin.math.max +import kotlin.math.min class BlobModule : Module() { override fun definition() = ModuleDefinition { @@ -24,12 +26,16 @@ class BlobModule : Module() { Function("slice") { blob: Blob, start: Int?, end: Int?, contentType: String? -> var sliceStart: Int = start ?: 0 - var sliceEnd: Int = end ?: 0 + var sliceEnd: Int = end ?: blob.size if (sliceStart < 0) { - sliceStart = blob.size + sliceStart + sliceStart = max(blob.size + sliceStart, 0) + } else { + sliceStart = min(sliceStart, blob.size) } if (sliceEnd < 0) { - sliceEnd = blob.size + sliceEnd + sliceEnd = max(blob.size + sliceEnd, 0) + } else { + sliceEnd = min(sliceEnd, blob.size) } blob.slice(sliceStart, sliceEnd, contentType ?: "") } diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 1a8d2dff32d89a..592bf6c337aded 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAUxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAaxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 53c193782a6a49..14bc5de3f6e31f 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -3,6 +3,9 @@ import { normalizedContentType } from "./utils"; const NativeBlobModule = requireNativeModule("ExpoBlob"); export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { + if (options) { + options.type = normalizedContentType(options.type); + } if (!blobParts) { super([], options); } @@ -14,7 +17,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { } } slice(start, end, contentType) { - const normalizedType = contentType ?? normalizedContentType(contentType); + const normalizedType = contentType ? normalizedContentType(contentType) : ""; const slicedBlob = super.slice(start, end, normalizedType); Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); return slicedBlob; diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 92a91f40466193..e9c476a9bbd848 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,WAAW,IAAI,qBAAqB,CAAC,WAAW,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = contentType ?? normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = contentType ? normalizedContentType(contentType) : \"\";\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index cc71776fb5fac9..aeb292632b6906 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -19,6 +19,9 @@ const NativeBlobModule = requireNativeModule("ExpoBlob"); export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { + if (options) { + options.type = normalizedContentType(options.type) + } if (!blobParts) { super([], options); } else if (blobParts instanceof Array) { @@ -29,7 +32,7 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } slice(start?: number, end?: number, contentType?: string): ExpoBlob { - const normalizedType = contentType ?? normalizedContentType(contentType); + const normalizedType = contentType ? normalizedContentType(contentType) : ""; const slicedBlob = super.slice(start, end, normalizedType); Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); return slicedBlob; From e80c2e01d46918ed3eb79a66b90c55d454ab6e18 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 15:31:00 +0200 Subject: [PATCH 06/41] Array buffer semi-fix, null type fix, byte offset fix --- .../src/main/java/expo/modules/blob/Blob.kt | 2 +- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 13 ++++++++++--- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/build/utils.d.ts.map | 2 +- packages/expo-blob/build/utils.js | 2 ++ packages/expo-blob/build/utils.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 15 ++++++++++++--- packages/expo-blob/src/utils.ts | 1 + 9 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt index 5e7be6d72c26cc..273fb3500c1f17 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt @@ -89,7 +89,7 @@ private fun TypedArray.bytes(): ByteArray { var ba = ByteArray(this.byteLength) for (i in 0.. { + if (v instanceof ArrayBuffer) { + // TODO maybe do this natively not in typescript? + return new Uint8Array(v); + } + return v; + }; if (!blobParts) { super([], options); } else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity), options); + super(blobParts.flat(Infinity).map(inputMapping), options); } else { - super(Array.from(blobParts).flat(Infinity), options); + super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); } } slice(start, end, contentType) { - const normalizedType = contentType ? normalizedContentType(contentType) : ""; + const normalizedType = normalizedContentType(contentType); const slicedBlob = super.slice(start, end, normalizedType); Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); return slicedBlob; diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index e9c476a9bbd848..cc9f447dfc0fee 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = contentType ? normalizedContentType(contentType) : \"\";\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,CAAO,EAAE,EAAE;YAChC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,OAAO,CAAC,CAAA;QACT,CAAC,CAAA;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts.map b/packages/expo-blob/build/utils.d.ts.map index b19ab9410a6b2a..ac0bc57022e1cf 100644 --- a/packages/expo-blob/build/utils.d.ts.map +++ b/packages/expo-blob/build/utils.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D"} \ No newline at end of file +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3D"} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js b/packages/expo-blob/build/utils.js index 35e1a30b50ac45..aeee6cfcf0facb 100644 --- a/packages/expo-blob/build/utils.js +++ b/packages/expo-blob/build/utils.js @@ -14,6 +14,8 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type) { + if (type === null) + return "null"; if (!type || type.length === 0) return ""; const asciiPrintable = /^[\x20-\x7E]+$/; diff --git a/packages/expo-blob/build/utils.js.map b/packages/expo-blob/build/utils.js.map index 1acb66f43b19ab..3ba32545f28e93 100644 --- a/packages/expo-blob/build/utils.js.map +++ b/packages/expo-blob/build/utils.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IAClD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n\tif (!type || type.length === 0) return \"\";\n\tconst asciiPrintable = /^[\\x20-\\x7E]+$/;\n\tif (!asciiPrintable.test(type)) return \"\";\n\treturn type.toLowerCase();\n}\n"]} \ No newline at end of file +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IAClD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n\tif (type === null) return \"null\"\n\tif (!type || type.length === 0) return \"\";\n\tconst asciiPrintable = /^[\\x20-\\x7E]+$/;\n\tif (!asciiPrintable.test(type)) return \"\";\n\treturn type.toLowerCase();\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index aeb292632b6906..423a8d82c7b6cc 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -22,17 +22,26 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { if (options) { options.type = normalizedContentType(options.type) } + + const inputMapping = (v : any) => { + if (v instanceof ArrayBuffer) { + // TODO maybe do this natively not in typescript? + return new Uint8Array(v) + } + return v + } + if (!blobParts) { super([], options); } else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity), options); + super(blobParts.flat(Infinity).map(inputMapping), options); } else { - super(Array.from(blobParts).flat(Infinity), options); + super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); } } slice(start?: number, end?: number, contentType?: string): ExpoBlob { - const normalizedType = contentType ? normalizedContentType(contentType) : ""; + const normalizedType = normalizedContentType(contentType); const slicedBlob = super.slice(start, end, normalizedType); Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); return slicedBlob; diff --git a/packages/expo-blob/src/utils.ts b/packages/expo-blob/src/utils.ts index f36ed8189b3acc..11c745155ee720 100644 --- a/packages/expo-blob/src/utils.ts +++ b/packages/expo-blob/src/utils.ts @@ -14,6 +14,7 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type?: string): string { + if (type === null) return "null" if (!type || type.length === 0) return ""; const asciiPrintable = /^[\x20-\x7E]+$/; if (!asciiPrintable.test(type)) return ""; From 9a6d120ad5b5723562760c15f52d14bfe46f6b8e Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 16:22:25 +0200 Subject: [PATCH 07/41] array buffer fix --- packages/expo-blob/build/BlobModule.js | 2 +- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 3cabcefc398403..780490540654bf 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -47,7 +47,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { }); } async arrayBuffer() { - return super.bytes().then((bytes) => bytes.buffer); + return super.bytes().then((bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); } } //# sourceMappingURL=BlobModule.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index cc9f447dfc0fee..d3b220a7d35f6b 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,CAAO,EAAE,EAAE;YAChC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,OAAO,CAAC,CAAA;QACT,CAAC,CAAA;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer);\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,CAAO,EAAE,EAAE;YAChC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,OAAO,CAAC,CAAA;QACT,CAAC,CAAA;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7H,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 423a8d82c7b6cc..88dadc971f34e2 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -65,6 +65,6 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } async arrayBuffer(): Promise { - return super.bytes().then((bytes: Uint8Array) => bytes.buffer); + return super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); } } From 440dbf63ec7f2fc0378d95f7d138b92cc453feb6 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 16:54:18 +0200 Subject: [PATCH 08/41] removed android build files --- .../results.bin | 1 - .../transformed/classes/classes_dex/classes.dex | Bin 45756 -> 0 bytes .../results.bin | 1 - .../transformed/classes/classes_dex/classes.dex | Bin 45772 -> 0 bytes 4 files changed, 2 deletions(-) delete mode 100644 packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin delete mode 100644 packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex delete mode 100644 packages/expo-blob/android/build/.transforms/8f8a6c2346678f20ad35dd488b88f140/results.bin delete mode 100644 packages/expo-blob/android/build/.transforms/8f8a6c2346678f20ad35dd488b88f140/transformed/classes/classes_dex/classes.dex diff --git a/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin b/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin deleted file mode 100644 index 0d259ddcb52852..00000000000000 --- a/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/results.bin +++ /dev/null @@ -1 +0,0 @@ -o/classes diff --git a/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex b/packages/expo-blob/android/build/.transforms/4b06e88cbc20d0207e8f18ecaa322547/transformed/classes/classes_dex/classes.dex deleted file mode 100644 index 906cd2e1836d6bae41bfd64f9ea55901218437c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45756 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 d Date: Mon, 21 Jul 2025 17:10:45 +0200 Subject: [PATCH 09/41] Add link to the original tests on which the test suite is based --- apps/test-suite/tests/Blob.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 0c308b1cf34cb4..fcffa2fa69bb5b 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -1,3 +1,5 @@ +// Based on tests in https://github.com/web-platform-tests/wpt/tree/master/FileAPI/blob + import {ExpoBlob as Blob} from "expo-blob" import { Platform } from 'expo-modules-core'; From 245c67abc28e1cb4047b390c3c7fe4f42f87f67c Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 17:54:40 +0200 Subject: [PATCH 10/41] bytes method fixed --- .../src/main/java/expo/modules/blob/Blob.kt | 20 +++++++++++++++++++ .../main/java/expo/modules/blob/BlobModule.kt | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt index 273fb3500c1f17..43c5e1b3892ddb 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt @@ -32,6 +32,18 @@ class Blob() : SharedObject() { return str } + fun bytes(): ByteArray { + var arr = ByteArray(size) + var i = 0 + for (bp in blobParts) { + for (b in bp.bytes()){ + arr[i] = b + i += 1 + } + } + return arr + } + private fun InternalBlobPart.offsetSlice(start: Int, end: Int, offset: Int): InternalBlobPart { var s: Int = start - offset var e: Int = end - offset @@ -159,6 +171,14 @@ sealed class InternalBlobPart() { is BufferPart -> buffer.decodeToString() } } + + fun bytes(): ByteArray { + return when (this) { + is StringPart -> string.toByteArray() + is BlobPart -> blob.bytes() + is BufferPart -> buffer + } + } } enum class EndingType(val str: String = "transparent") : Enumerable { diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt index 9fd4d38d3fe9d9..7609ebc983e180 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt @@ -41,7 +41,7 @@ class BlobModule : Module() { } AsyncFunction("bytes") { blob: Blob -> - blob.text().toByteArray() + blob.bytes() } AsyncFunction("text") { blob: Blob -> From 5d0ce98b0097b1001d9f3cfacfe6ba8d874bb7f7 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Mon, 21 Jul 2025 18:50:30 +0200 Subject: [PATCH 11/41] Blob typescript constructor changes --- apps/test-suite/tests/Blob.ts | 2 +- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 20 ++++++++++++++++++-- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 19 +++++++++++++++++-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index fcffa2fa69bb5b..02bc542719a074 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -42,7 +42,7 @@ export async function test({ describe, it, expect, jasmine }) { const ab = await blob.arrayBuffer(); expect(ab instanceof ArrayBuffer).toBeTruthy(); - expect(new Uint8Array(ab)).toEqual(expected); + expect(new Uint8Array(ab)).toEqual(new Uint8Array(expected)); }) } diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 5d7652dfbf76c0..f2cfffb6837a85 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAsBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA8BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 780490540654bf..7fc0a9ac2d5ee0 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -1,6 +1,12 @@ import { requireNativeModule } from "expo"; import { normalizedContentType } from "./utils"; const NativeBlobModule = requireNativeModule("ExpoBlob"); +const isIterable = (obj) => { + if (obj == null) { + return false; + } + return typeof obj[Symbol.iterator] === 'function'; +}; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { if (options) { @@ -11,17 +17,27 @@ export class ExpoBlob extends NativeBlobModule.Blob { // TODO maybe do this natively not in typescript? return new Uint8Array(v); } + if (typeof v === 'number') { + // Manual type coercion? + return String(v); + } return v; }; - if (!blobParts) { + if (blobParts === undefined) { super([], options); } + else if (!(blobParts instanceof Object)) { + throw TypeError; + } else if (blobParts instanceof Array) { super(blobParts.flat(Infinity).map(inputMapping), options); } - else { + else if (isIterable(blobParts)) { super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); } + else { + throw TypeError; + } } slice(start, end, contentType) { const normalizedType = normalizedContentType(contentType); diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index d3b220a7d35f6b..34bfb3cb3d1d90 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACnD,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,CAAO,EAAE,EAAE;YAChC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,OAAO,CAAC,CAAA;QACT,CAAC,CAAA;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,SAAS,YAAY,KAAK,EAAE,CAAC;YACvC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;QACxE,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7H,CAAC;CACD","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (!blobParts) {\n\t\t\tsuper([], options);\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 88dadc971f34e2..6a887bcf469469 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -17,6 +17,13 @@ declare class ExpoBlobModule extends NativeModule { const NativeBlobModule = requireNativeModule("ExpoBlob"); + +const isIterable = (obj : any) => { + if (obj == null) { + return false; + } + return typeof obj[Symbol.iterator] === 'function'; +} export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { if (options) { @@ -28,15 +35,23 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { // TODO maybe do this natively not in typescript? return new Uint8Array(v) } + if (typeof v === 'number') { + // Manual type coercion? + return String(v) + } return v } - if (!blobParts) { + if (blobParts === undefined) { super([], options); + } else if (!(blobParts instanceof Object)) { + throw TypeError; } else if (blobParts instanceof Array) { super(blobParts.flat(Infinity).map(inputMapping), options); - } else { + } else if( isIterable(blobParts)){ super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); + } else { + throw TypeError } } From 8c14ef2bd944686cb9bfda5e58956225455dea73 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 09:18:44 +0200 Subject: [PATCH 12/41] options bugfixes --- apps/test-suite/tests/Blob.ts | 17 +++++++++++++++-- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 3 +++ packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/build/utils.d.ts.map | 2 +- packages/expo-blob/build/utils.js | 11 ++++++----- packages/expo-blob/build/utils.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 4 ++++ packages/expo-blob/src/utils.ts | 10 ++++++---- 9 files changed, 38 insertions(+), 15 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 02bc542719a074..d98dc777b1f988 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -226,7 +226,7 @@ export async function test({ describe, it, expect, jasmine }) { it('Exception propagation from options', () => { const test_error = {name: 'test string'}; // @ts-expect-error - expect(() => new Blob([], { get endings() { throw test_error; }})).toThrow('test string') + expect(() => new Blob([], { get endings() { throw test_error; }})).toThrow(test_error) }) // TODO weird test, as it could maybe be used lazily and not in the constructor it('The \'endings\' options property is used', () => { @@ -441,6 +441,14 @@ export async function test({ describe, it, expect, jasmine }) { } }; expect(() => new Blob(obj)).toThrow(test_error); + + console.log() + console.log('received: ' + received) + console.log() + console.log('expected: ' + expect) + console.log() + + // Somehow we don't call 0 toString but I don't know why not or why would we expect(received).toEqual([ "Symbol.iterator", @@ -639,11 +647,15 @@ export async function test({ describe, it, expect, jasmine }) { const stringified = []; new Blob([], { + // @ts-ignore get type() { accessed.push('type'); }, + // @ts-ignore get endings() { accessed.push('endings'); } }); new Blob([], { + // @ts-ignore type: { toString: () => { stringified.push('type'); return ''; } }, + // @ts-ignore endings: { toString: () => { stringified.push('endings'); return 'transparent'; } } }); expect(accessed).toEqual(['endings', 'type']); @@ -655,7 +667,8 @@ export async function test({ describe, it, expect, jasmine }) { () => new Blob( [{ toString: function() { throw test_error } }], { - get type() { assert_unreached("type getter should not be called."); } + //@ts-ignore + get type() { expect(0).toBe(1); } }) ).toThrow(test_error); }) diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index f2cfffb6837a85..a044019f768f26 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA8BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAkCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 7fc0a9ac2d5ee0..bda624511adc09 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -10,6 +10,9 @@ const isIterable = (obj) => { export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { if (options) { + // Have to access that in TypeScript as it may throw... + // Also have to access it before type, for some reason... + options.endings; options.type = normalizedContentType(options.type); } const inputMapping = (v) => { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 34bfb3cb3d1d90..7f93b03bc7fdf8 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\t// Have to access that in TypeScript as it may throw...\n\t\t\t// Also have to access it before type, for some reason...\n\t\t\toptions.endings\n\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts.map b/packages/expo-blob/build/utils.d.ts.map index ac0bc57022e1cf..e9f02c601566e9 100644 --- a/packages/expo-blob/build/utils.d.ts.map +++ b/packages/expo-blob/build/utils.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3D"} \ No newline at end of file +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3D"} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js b/packages/expo-blob/build/utils.js index aeee6cfcf0facb..1dee6d3f223bc1 100644 --- a/packages/expo-blob/build/utils.js +++ b/packages/expo-blob/build/utils.js @@ -14,13 +14,14 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type) { - if (type === null) - return "null"; - if (!type || type.length === 0) + if (type === undefined) return ""; + const str = "" + type; + // if (type === null) return "null" + // if (!type || type.length === 0) return ""; const asciiPrintable = /^[\x20-\x7E]+$/; - if (!asciiPrintable.test(type)) + if (!asciiPrintable.test(str)) return ""; - return type.toLowerCase(); + return str.toLowerCase(); } //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js.map b/packages/expo-blob/build/utils.js.map index 3ba32545f28e93..7a02fa8a1aa92f 100644 --- a/packages/expo-blob/build/utils.js.map +++ b/packages/expo-blob/build/utils.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IAClD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n\tif (type === null) return \"null\"\n\tif (!type || type.length === 0) return \"\";\n\tconst asciiPrintable = /^[\\x20-\\x7E]+$/;\n\tif (!asciiPrintable.test(type)) return \"\";\n\treturn type.toLowerCase();\n}\n"]} \ No newline at end of file +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IAClD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IACjC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;IACrB,mCAAmC;IACnC,6CAA6C;IAC7C,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n\tif (type === undefined) return \"\"\n\tconst str = \"\" + type\n\t// if (type === null) return \"null\"\n\t// if (!type || type.length === 0) return \"\";\n\tconst asciiPrintable = /^[\\x20-\\x7E]+$/;\n\tif (!asciiPrintable.test(str)) return \"\";\n\treturn str.toLowerCase();\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 6a887bcf469469..f4364bf4b28048 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -27,6 +27,10 @@ const isIterable = (obj : any) => { export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { if (options) { + // Have to access that in TypeScript as it may throw... + // Also have to access it before type, for some reason... + options.endings + options.type = normalizedContentType(options.type) } diff --git a/packages/expo-blob/src/utils.ts b/packages/expo-blob/src/utils.ts index 11c745155ee720..a648759c5dfda3 100644 --- a/packages/expo-blob/src/utils.ts +++ b/packages/expo-blob/src/utils.ts @@ -14,9 +14,11 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type?: string): string { - if (type === null) return "null" - if (!type || type.length === 0) return ""; + if (type === undefined) return "" + const str = "" + type + // if (type === null) return "null" + // if (!type || type.length === 0) return ""; const asciiPrintable = /^[\x20-\x7E]+$/; - if (!asciiPrintable.test(type)) return ""; - return type.toLowerCase(); + if (!asciiPrintable.test(str)) return ""; + return str.toLowerCase(); } From a90cdcd93b02c7cf6675de97a6d2414f92c4a244 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 10:03:06 +0200 Subject: [PATCH 13/41] toString changed for ExpoBlob --- packages/expo-blob/build/BlobModule.d.ts | 1 + packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 3 +++ packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 4 ++++ 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/expo-blob/build/BlobModule.d.ts b/packages/expo-blob/build/BlobModule.d.ts index 0f9b14aa49bf37..b214ccc5392e62 100644 --- a/packages/expo-blob/build/BlobModule.d.ts +++ b/packages/expo-blob/build/BlobModule.d.ts @@ -18,6 +18,7 @@ export declare class ExpoBlob extends NativeBlobModule.Blob implements Blob { slice(start?: number, end?: number, contentType?: string): ExpoBlob; stream(): ReadableStream; arrayBuffer(): Promise; + toString(): string; } export {}; //# sourceMappingURL=BlobModule.d.ts.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index a044019f768f26..66e6799274c4f0 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAkCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;CAG7C"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAkCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index bda624511adc09..61f49a906c0213 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -68,5 +68,8 @@ export class ExpoBlob extends NativeBlobModule.Blob { async arrayBuffer() { return super.bytes().then((bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); } + toString() { + return "[object Blob]"; + } } //# sourceMappingURL=BlobModule.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 7f93b03bc7fdf8..d8ea4b258ce619 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\t// Have to access that in TypeScript as it may throw...\n\t\t\t// Also have to access it before type, for some reason...\n\t\t\toptions.endings\n\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\t// Have to access that in TypeScript as it may throw...\n\t\t\t// Also have to access it before type, for some reason...\n\t\t\toptions.endings\n\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index f4364bf4b28048..d0783524eb8327 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -86,4 +86,8 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { async arrayBuffer(): Promise { return super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); } + + toString(): string { + return "[object Blob]" + } } From 7db8cd070caec97caac94b51d6dd8e22d2a90714 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 10:21:53 +0200 Subject: [PATCH 14/41] proper options handling --- apps/test-suite/tests/Blob.ts | 2 +- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 21 +++++++++++++------- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 20 ++++++++++++------- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index d98dc777b1f988..a7192bbf60f45a 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -709,7 +709,7 @@ export async function test({ describe, it, expect, jasmine }) { it('Passing ' + JSON.stringify(arg) + ' for options should throw', () => { expect(() => { new Blob([], arg) - }).toThrow() + }).toThrow(TypeError) }) }); }) diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 66e6799274c4f0..ec41d383eca17f 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAkCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAwCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 61f49a906c0213..22a33aedc89054 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -9,11 +9,18 @@ const isIterable = (obj) => { }; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { + let opt; if (options) { - // Have to access that in TypeScript as it may throw... - // Also have to access it before type, for some reason... - options.endings; - options.type = normalizedContentType(options.type); + if (!(options instanceof Object)) { + throw TypeError; + } + opt = { + endings: options.endings, + type: options.type === undefined ? "" : normalizedContentType(options.type) + }; + } + else { + opt = options; } const inputMapping = (v) => { if (v instanceof ArrayBuffer) { @@ -27,16 +34,16 @@ export class ExpoBlob extends NativeBlobModule.Blob { return v; }; if (blobParts === undefined) { - super([], options); + super([], opt); } else if (!(blobParts instanceof Object)) { throw TypeError; } else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity).map(inputMapping), options); + super(blobParts.flat(Infinity).map(inputMapping), opt); } else if (isIterable(blobParts)) { - super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); + super(Array.from(blobParts).flat(Infinity).map(inputMapping), opt); } else { throw TypeError; diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index d8ea4b258ce619..3366bc49ce393c 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tif (options) {\n\t\t\t// Have to access that in TypeScript as it may throw...\n\t\t\t// Also have to access it before type, for some reason...\n\t\t\toptions.endings\n\n\t\t\toptions.type = normalizedContentType(options.type)\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], options);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), options);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), options);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), opt);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), opt);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index d0783524eb8327..7c58f6782fb2e7 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -26,12 +26,18 @@ const isIterable = (obj : any) => { } export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { + let opt if (options) { - // Have to access that in TypeScript as it may throw... - // Also have to access it before type, for some reason... - options.endings + if (!(options instanceof Object)) { + throw TypeError + } - options.type = normalizedContentType(options.type) + opt = { + endings: options.endings, + type: options.type === undefined ? "" : normalizedContentType(options.type) + } + } else { + opt = options } const inputMapping = (v : any) => { @@ -47,13 +53,13 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } if (blobParts === undefined) { - super([], options); + super([], opt); } else if (!(blobParts instanceof Object)) { throw TypeError; } else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity).map(inputMapping), options); + super(blobParts.flat(Infinity).map(inputMapping), opt); } else if( isIterable(blobParts)){ - super(Array.from(blobParts).flat(Infinity).map(inputMapping), options); + super(Array.from(blobParts).flat(Infinity).map(inputMapping), opt); } else { throw TypeError } From cc7397229fc6cf5a5e2cfaf42afe266e587fc7e0 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 11:15:31 +0200 Subject: [PATCH 15/41] simpler constructor --- apps/test-suite/tests/Blob.ts | 8 ++++++-- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 20 ++++---------------- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 19 ++++--------------- 5 files changed, 16 insertions(+), 35 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index a7192bbf60f45a..027789e404d1ee 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -322,7 +322,7 @@ export async function test({ describe, it, expect, jasmine }) { ]; args.forEach((arg) => { // @ts-expect-error - expect(() => new Blob(arg)).toThrow() + expect(() => new Blob(arg)).toThrow(TypeError()) }); }) test_blob(function() { @@ -442,11 +442,15 @@ export async function test({ describe, it, expect, jasmine }) { }; expect(() => new Blob(obj)).toThrow(test_error); + console.log() + console.log() console.log() console.log('received: ' + received) console.log() console.log('expected: ' + expect) console.log() + console.log() + console.log() // Somehow we don't call 0 toString but I don't know why not or why would we @@ -709,7 +713,7 @@ export async function test({ describe, it, expect, jasmine }) { it('Passing ' + JSON.stringify(arg) + ' for options should throw', () => { expect(() => { new Blob([], arg) - }).toThrow(TypeError) + }).toThrow(TypeError()) }) }); }) diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index ec41d383eca17f..e1c3e1e373ee7d 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AASzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAwCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 22a33aedc89054..74b9a1a0fbf87e 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -1,18 +1,12 @@ import { requireNativeModule } from "expo"; import { normalizedContentType } from "./utils"; const NativeBlobModule = requireNativeModule("ExpoBlob"); -const isIterable = (obj) => { - if (obj == null) { - return false; - } - return typeof obj[Symbol.iterator] === 'function'; -}; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { let opt; if (options) { if (!(options instanceof Object)) { - throw TypeError; + throw TypeError(); } opt = { endings: options.endings, @@ -36,17 +30,11 @@ export class ExpoBlob extends NativeBlobModule.Blob { if (blobParts === undefined) { super([], opt); } - else if (!(blobParts instanceof Object)) { - throw TypeError; - } - else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity).map(inputMapping), opt); - } - else if (isIterable(blobParts)) { - super(Array.from(blobParts).flat(Infinity).map(inputMapping), opt); + else if (blobParts === null) { + throw TypeError(); } else { - throw TypeError; + super([...blobParts].flat(Infinity).map(inputMapping), opt); } } slice(start, end, contentType) { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 3366bc49ce393c..614bf662377dc2 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\n\nconst isIterable = (obj : any) => {\n if (obj == null) {\n return false;\n }\n return typeof obj[Symbol.iterator] === 'function';\n}\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (!(blobParts instanceof Object)) {\n\t\t\tthrow TypeError;\n\t\t} else if (blobParts instanceof Array) {\n\t\t\tsuper(blobParts.flat(Infinity).map(inputMapping), opt);\n\t\t} else if( isIterable(blobParts)){\n\t\t\tsuper(Array.from(blobParts).flat(Infinity).map(inputMapping), opt);\n\t\t} else {\n\t\t\tthrow TypeError\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 7c58f6782fb2e7..76d0abe316e131 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -17,19 +17,12 @@ declare class ExpoBlobModule extends NativeModule { const NativeBlobModule = requireNativeModule("ExpoBlob"); - -const isIterable = (obj : any) => { - if (obj == null) { - return false; - } - return typeof obj[Symbol.iterator] === 'function'; -} export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { let opt if (options) { if (!(options instanceof Object)) { - throw TypeError + throw TypeError() } opt = { @@ -54,14 +47,10 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { if (blobParts === undefined) { super([], opt); - } else if (!(blobParts instanceof Object)) { - throw TypeError; - } else if (blobParts instanceof Array) { - super(blobParts.flat(Infinity).map(inputMapping), opt); - } else if( isIterable(blobParts)){ - super(Array.from(blobParts).flat(Infinity).map(inputMapping), opt); + } else if (blobParts === null) { + throw TypeError(); } else { - throw TypeError + super([...blobParts].flat(Infinity).map(inputMapping), opt); } } From 04b80e8616597c1e8f01ec386507ac18175f61f8 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 11:40:17 +0200 Subject: [PATCH 16/41] TypeErrors proper handling & inapplicable DOM related tests removed --- apps/test-suite/tests/Blob.ts | 101 +++++++++++---------- packages/expo-blob/build/BlobModule.js | 2 +- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 2 +- 4 files changed, 54 insertions(+), 53 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 027789e404d1ee..40004f65d9af5e 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -306,7 +306,7 @@ export async function test({ describe, it, expect, jasmine }) { }) // TODO Something wrong with null ? Why should Blob() work and Blob(null) not ??? it('Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.', () => { - var args = [ + [ null, true, false, @@ -319,10 +319,14 @@ export async function test({ describe, it, expect, jasmine }) { new RegExp(), {}, { 0: "FAIL", length: 1 }, - ]; - args.forEach((arg) => { - // @ts-expect-error - expect(() => new Blob(arg)).toThrow(TypeError()) + ].forEach((arg) => { + try { + // @ts-expect-error + new Blob(arg) + expect(false).toBeTruthy() + } catch (err) { + expect(err instanceof TypeError).toBeTruthy() + } }); }) test_blob(function() { @@ -754,51 +758,48 @@ export async function test({ describe, it, expect, jasmine }) { }) }) - describe('constructor dom windows', async () => { - it("Passing platform objects for blobParts should throw a TypeError.", () => { - const document = new Document() - var args = [ - document.createElement("div"), - window, - ]; - args.forEach((arg) => { - expect(() => new Blob(arg)).toThrow(); - }); - }); - - it("A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)", () => { - var element = document.createElement("div"); - element.appendChild(document.createElement("div")); - element.appendChild(document.createElement("p")); - var list = element.children; - Object.defineProperty(list, "length", { - get: function() { throw test_error; } - }); - expect(() => {new Blob(list);}).toThrow(test_error); - }); - - test_blob(function() { - const document = new Document() - var select = document.createElement("select"); - select.appendChild(document.createElement("option")); - return new Blob(select); - }, { - expected: "[object HTMLOptionElement]", - type: "", - desc: "Passing a platform object that supports indexed properties as the blobParts array should work (select)." - }); - - test_blob(function() { - const document = new Document() - var elm = document.createElement("div"); - elm.setAttribute("foo", "bar"); - return new Blob(elm.attributes); - }, { - expected: "[object Attr]", - type: "", - desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." - }); - }) + // HTML ONLY, not applicable + // describe('constructor dom windows', async () => { + // it("Passing platform objects for blobParts should throw a TypeError.", () => { + // var args = [ + // document.createElement("div"), + // window, + // ]; + // args.forEach((arg) => { + // expect(() => new Blob(arg)).toThrow(); + // }); + // }); + // it("A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)", () => { + // var element = document.createElement("div"); + // element.appendChild(document.createElement("div")); + // element.appendChild(document.createElement("p")); + // var list = element.children; + // Object.defineProperty(list, "length", { + // get: function() { throw test_error; } + // }); + // expect(() => {new Blob(list);}).toThrow(test_error); + // }); + + // test_blob(function() { + // var select = document.createElement("select"); + // select.appendChild(document.createElement("option")); + // return new Blob(select); + // }, { + // expected: "[object HTMLOptionElement]", + // type: "", + // desc: "Passing a platform object that supports indexed properties as the blobParts array should work (select)." + // }); + + // test_blob(function() { + // var elm = document.createElement("div"); + // elm.setAttribute("foo", "bar"); + // return new Blob(elm.attributes); + // }, { + // expected: "[object Attr]", + // type: "", + // desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." + // }); + // }) describe('Text', async () => { it('simple', async () => { diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 74b9a1a0fbf87e..2dbf03d3f434e5 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -30,7 +30,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { if (blobParts === undefined) { super([], opt); } - else if (blobParts === null) { + else if (blobParts === null || !(blobParts instanceof Object)) { throw TypeError(); } else { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 614bf662377dc2..6a4e8a1715b40c 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IAClD,YAAY,SAAiC,EAAE,OAAyB;QACvE,IAAI,GAAG,CAAA;QACP,IAAI,OAAO,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;gBAClC,MAAM,SAAS,EAAE,CAAA;YAClB,CAAC;YAED,GAAG,GAAG;gBACL,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC;aAC3E,CAAA;QACF,CAAC;aAAM,CAAC;YACP,GAAG,GAAG,OAAO,CAAA;QACd,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,CAAO,EAAE,EAAE;YAChC,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC3B,wBAAwB;gBACxB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAA;YACjB,CAAC;YACD,OAAO,CAAC,CAAA;QACT,CAAC,CAAA;QAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAChB,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,YAAY,MAAM,CAAC,EAAE,CAAC;YACjE,MAAM,SAAS,EAAE,CAAC;QACnB,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7D,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACvD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,MAAM;QACL,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACrC,IAAI,CAAC,UAAU;gBACd,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACvB,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,KAAK,EAAE,CAAC;gBACpB,CAAC;YACF,CAAC;SACD,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7H,CAAC;IAED,QAAQ;QACH,OAAO,eAAe,CAAA;IACzB,CAAC;CACH","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null || !(blobParts instanceof Object)) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 76d0abe316e131..6c62786df254c4 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -47,7 +47,7 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { if (blobParts === undefined) { super([], opt); - } else if (blobParts === null) { + } else if (blobParts === null || !(blobParts instanceof Object)) { throw TypeError(); } else { super([...blobParts].flat(Infinity).map(inputMapping), opt); From 2eca68de20706922296ec40148aa8c11a3c6a2d1 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 11:54:24 +0200 Subject: [PATCH 17/41] Frozen array test changed --- apps/test-suite/tests/Blob.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 40004f65d9af5e..c9ac3134115abb 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -598,16 +598,21 @@ export async function test({ describe, it, expect, jasmine }) { desc: "Passing BigInt typed arrays as elements of the blobParts array should work." }); - // TODO revisit this one as its implementation is weird in the wpt - it("Passing a FrozenArray as the blobParts array should work (FrozenArray).", async () => { - var channel = new MessageChannel(); - channel.port2.onmessage = this.step_func(function(e) { - var b_ports = new Blob(e.ports); - expect(b_ports.size).toEqual("[object MessagePort]".length) - this.done(); - }); - var channel2 = new MessageChannel(); - channel.port1.postMessage('', [channel2.port1]); + // Message channel doesn't exist in React Native + // it("Passing a FrozenArray as the blobParts array should work (FrozenArray).", async () => { + // var channel = new MessageChannel(); + // channel.port2.onmessage = this.step_func(function(e) { + // var b_ports = new Blob(e.ports); + // expect(b_ports.size).toEqual("[object MessagePort]".length) + // this.done(); + // }); + // var channel2 = new MessageChannel(); + // channel.port1.postMessage('', [channel2.port1]); + // }) + it("Passing a FrozenArray as the blobParts array should work", async () => { + let arr = ["PA", "SS"] + Object.freeze(arr) + expect(await new Blob(arr).text()).toBe("PASS") }) test_blob(function() { From 9ab8021888e7942ec21876e16149d00b52f85856 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 12:06:10 +0200 Subject: [PATCH 18/41] stream fix --- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 7 +++---- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 7 +++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index e1c3e1e373ee7d..c14d6eff1cf9cb 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAiBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 2dbf03d3f434e5..1d65a19c6bb548 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -44,12 +44,11 @@ export class ExpoBlob extends NativeBlobModule.Blob { return slicedBlob; } stream() { - const text = super.syncText(); - const encoder = new TextEncoder(); - const uint8 = encoder.encode(text); + const uint8promise = super.bytes(); let offset = 0; return new ReadableStream({ - pull(controller) { + async pull(controller) { + let uint8 = await uint8promise; if (offset < uint8.length) { controller.enqueue(uint8.subarray(offset)); offset = uint8.length; diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 6a4e8a1715b40c..70423a0cf3e4ac 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null || !(blobParts instanceof Object)) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst text = super.syncText();\n\t\tconst encoder = new TextEncoder();\n\t\tconst uint8 = encoder.encode(text);\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tpull(controller) {\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null || !(blobParts instanceof Object)) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst uint8promise = super.bytes();\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tasync pull(controller) {\n\t\t\t\tlet uint8 = await uint8promise\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 6c62786df254c4..0f37e93281508d 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -62,12 +62,11 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } stream(): ReadableStream { - const text = super.syncText(); - const encoder = new TextEncoder(); - const uint8 = encoder.encode(text); + const uint8promise = super.bytes(); let offset = 0; return new ReadableStream({ - pull(controller) { + async pull(controller) { + let uint8 = await uint8promise if (offset < uint8.length) { controller.enqueue(uint8.subarray(offset)); offset = uint8.length; From f859acbd43f83dd404c80375fc645a04e215332f Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 12:44:58 +0200 Subject: [PATCH 19/41] newline --- apps/test-suite/tests/Blob.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index c9ac3134115abb..fc601cc487ecc6 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -1226,4 +1226,4 @@ export async function test({ describe, it, expect, jasmine }) { }) }) }); -} \ No newline at end of file +} From 0e5097ce2ca7f07c056719606a671a356be3b493 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 15:59:10 +0200 Subject: [PATCH 20/41] constructor changes: different type checks, different mapping, different options handling; prettified --- apps/test-suite/tests/Blob.ts | 2571 ++++++++++-------- packages/expo-blob/build/BlobModule.d.ts | 4 +- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 65 +- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 165 +- 6 files changed, 1515 insertions(+), 1294 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index fc601cc487ecc6..a40b32debb753a 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -1,1229 +1,1414 @@ // Based on tests in https://github.com/web-platform-tests/wpt/tree/master/FileAPI/blob -import {ExpoBlob as Blob} from "expo-blob" +import { ExpoBlob as Blob } from 'expo-blob'; import { Platform } from 'expo-modules-core'; export const name = 'Blob'; var test_error = { - name: "test", - message: "test error", + name: 'test', + message: 'test error', }; -export async function test({ describe, it, expect, jasmine }) { - const test_blob = (fn, expectations) => { - var expected = expectations.expected, - type = expectations.type, - desc = expectations.desc; - - it(desc, async (t) => { - var blob = fn(); - expect(blob instanceof Blob).toBeTruthy(); - expect(blob instanceof File).toBeFalsy(); - expect(blob.type).toEqual(type); - expect(blob.size).toBe(expected.length); - - const text = await blob.text(); - const text1 = blob.syncText(); - expect(text).toEqual(expected); - expect(text).toEqual(text1); - }); +export async function test({ describe, it, expect }) { + const test_blob = (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + + it(desc, async (t) => { + var blob = fn(); + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + expect(blob.type).toEqual(type); + expect(blob.size).toBe(expected.length); + + const text = await blob.text(); + const text1 = blob.syncText(); + expect(text).toEqual(expected); + expect(text).toEqual(text1); + }); + }; + const test_blob_binary = async (fn, expectations) => { + var expected = expectations.expected, + type = expectations.type, + desc = expectations.desc; + it(desc, async () => { + var blob = fn(); + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + expect(blob.type).toBe(type); + expect(blob.size).toBe(expected.length); + + const ab = await blob.arrayBuffer(); + expect(ab instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(ab)).toEqual(new Uint8Array(expected)); + }); + }; + + // Helper function that triggers garbage collection while reading a chunk + // if perform_gc is true. + const read_and_gc = async (reader, perform_gc) => { + // Passing Uint8Array for byte streams; non-byte streams will simply ignore it + const read_promise = reader.read(new Uint8Array(64)); + if (perform_gc) { + // TODO Actually perform garbage collection in here + // await garbageCollect(); } - const test_blob_binary = async (fn, expectations) => { - var expected = expectations.expected, - type = expectations.type, - desc = expectations.desc; - it(desc, async () => { - var blob = fn(); - expect(blob instanceof Blob).toBeTruthy(); - expect(blob instanceof File).toBeFalsy(); - expect(blob.type).toBe(type); - expect(blob.size).toBe(expected.length); - - const ab = await blob.arrayBuffer(); - expect(ab instanceof ArrayBuffer).toBeTruthy(); - expect(new Uint8Array(ab)).toEqual(new Uint8Array(expected)); - }) + return read_promise; + }; + + // Takes in a ReadableStream and reads from it until it is done, returning + // an array that contains the results of each read operation. If perform_gc + // is true, garbage collection is triggered while reading every chunk. + const read_all_chunks = async (stream, { perform_gc = false, mode } = {}) => { + expect(stream instanceof ReadableStream).toBeTruthy(); + expect('getReader' in stream).toBeTruthy(); + const reader = stream.getReader({ mode }); + + expect('read' in reader).toBeTruthy(); + let read_value = await read_and_gc(reader, perform_gc); + + let out = []; + let i = 0; + while (!read_value.done) { + for (let val of read_value.value) { + out[i++] = val; + } + read_value = await read_and_gc(reader, perform_gc); } + return out; + }; + + describe('Blob', async () => { + describe('Blob creation', () => { + it('Empty blob', () => { + const blob = new Blob([]); + expect(blob).toBeTruthy(); + }); + it('String blob', () => { + const blob = new Blob(['ab', 'cd']); + expect(blob).toBeTruthy(); + }); + it('TypedArray blob', () => { + const blob = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); + expect(blob).toBeTruthy(); + }); + it('Blob blob', () => { + const blob = new Blob([new Blob([]), new Blob(['a', 'b'])]); + expect(blob).toBeTruthy(); + }); + it('Mixed blob', () => { + const blob0 = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); + const blob1 = new Blob(['ab', 'cd']); + const blob2 = new Blob(['a', new Blob([new Blob(['b'])])]); + const blob = new Blob([blob0, blob1, blob2]); + expect(blob).toBeTruthy(); + }); + it("Blob don't flatten", async () => { + const blob = new Blob(['aa', ['ab', 'cd'], 'ef']); + expect(await blob.text()).toBe('aaab,cdef'); + }); + }); - // Helper function that triggers garbage collection while reading a chunk - // if perform_gc is true. - const read_and_gc = async (reader, perform_gc) => { - // Passing Uint8Array for byte streams; non-byte streams will simply ignore it - const read_promise = reader.read(new Uint8Array(64)); - if (perform_gc) { - // TODO Actually perform garbage collection in here - // await garbageCollect(); + describe('Array buffer', () => { + it('simple', async () => { + const input_arr = new TextEncoder().encode('PASS'); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }); + it('empty Blob data', async () => { + const input_arr = new TextEncoder().encode(''); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }); + it('non-ascii input', async () => { + const input_arr = new TextEncoder().encode('\u08B8\u000a'); + const blob = new Blob([input_arr]); + const array_buffer = await blob.arrayBuffer(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); + }); + it('non-unicode input', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + // const array_buffer = await blob.arrayBuffer(); + const array_buffer = await blob.bytes(); + // expect(blob.syncText()).toEqual("\u0008\u00F1\u0030\u007B\u0097") + // expect(new Uint8Array(array_buffer) == typed_arr).toBeTruthy() + // expect(new Uint8Array(array_buffer)).toEqual(typed_arr); + expect(blob.size).toBe(5); + expect(array_buffer.length).toBe(5); + console.log(array_buffer.byteOffset); + console.log(array_buffer, typed_arr); + expect(array_buffer).toEqual(typed_arr); + }); + it('concurrent reads', async () => { + const input_arr = new TextEncoder().encode('PASS'); + const blob = new Blob([input_arr]); + const array_buffer_results = await Promise.all([ + blob.arrayBuffer(), + blob.arrayBuffer(), + blob.arrayBuffer(), + ]); + for (let array_buffer of array_buffer_results) { + expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); + expect(new Uint8Array(array_buffer)).toEqual(input_arr); } - return read_promise; - } + }); + }); - // Takes in a ReadableStream and reads from it until it is done, returning - // an array that contains the results of each read operation. If perform_gc - // is true, garbage collection is triggered while reading every chunk. - const read_all_chunks = async (stream, { perform_gc = false, mode } = {}) => { - expect(stream instanceof ReadableStream).toBeTruthy(); - expect('getReader' in stream).toBeTruthy(); - const reader = stream.getReader({ mode }); - - expect('read' in reader).toBeTruthy() - let read_value = await read_and_gc(reader, perform_gc); - - let out = []; - let i = 0; - while (!read_value.done) { - for (let val of read_value.value) { - out[i++] = val; - } - read_value = await read_and_gc(reader, perform_gc); + describe('Bytes', async () => { + it('Simple', async () => { + const input_arr = new TextEncoder().encode('PASS'); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); + }); + it('empty Blob data', async () => { + const input_arr = new TextEncoder().encode(''); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); + }); + it('non-ascii input', async () => { + const input_arr = new TextEncoder().encode('\u08B8\u000a'); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + expect(uint8array).toEqual(input_arr); + }); + it('non-unicode input', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const uint8array = await blob.bytes(); + expect(uint8array).toEqual(typed_arr); + }); + it('concurrent reads', async () => { + const input_arr = new TextEncoder().encode('PASS'); + const blob = new Blob([input_arr]); + const uint8array_results = await Promise.all([blob.bytes(), blob.bytes(), blob.bytes()]); + for (let uint8array of uint8array_results) { + expect(uint8array instanceof Uint8Array).toBeTruthy(); + expect(uint8array).toEqual(input_arr); } - return out; - } + }); + }); - describe('Blob', async () => { - describe('Blob creation', () => { - it('Empty blob', () => { - const blob = new Blob([]); - expect(blob).toBeTruthy(); - }); - it('String blob', () => { - const blob = new Blob(["ab", "cd"]); - expect(blob).toBeTruthy(); - }); - it('TypedArray blob', () => { - const blob = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); - expect(blob).toBeTruthy(); - }); - it('Blob blob', () => { - const blob = new Blob([new Blob([]), new Blob(["a", "b"])]); - expect(blob).toBeTruthy(); - }) - it('Mixed blob', () => { - const blob0 = new Blob([Int32Array.from([-1, 2]), Uint8Array.from([1, 2, 0])]); - const blob1 = new Blob(["ab", "cd"]); - const blob2 = new Blob(["a", new Blob([new Blob(["b"])])]) - const blob = new Blob([blob0, blob1, blob2]); - expect(blob).toBeTruthy(); - }) - it('Blob flatten', () => { - const blob = new Blob(["aa", ["ab", "cd"], "ef"]) - expect(blob).toBeTruthy(); + // Windows platforms use CRLF as the native line ending. All others use LF. + const crlf = Platform.OS == 'windows'; + const native_ending = crlf ? '\r\n' : '\n'; + describe('constructor-endings', async () => { + it('valid endings value', () => { + const blob0 = new Blob([], { endings: 'native' }); + const blob1 = new Blob([], { endings: 'transparent' }); + expect(blob0).toBeTruthy(); + expect(blob1).toBeTruthy(); + }); + it('invalud endings value', () => { + [null, '', 'invalidEnumValue', 'Transparent', 'NATIVE', 0, {}].forEach((ending) => { + // @ts-expect-error + expect(() => new Blob([], { endings: ending })).toThrow(); + }); + }); + it('Exception propagation from options', () => { + const test_error = { name: 'test string' }; + // @ts-expect-error + expect( + () => + new Blob([], { + get endings() { + throw test_error; + }, }) + ).toThrow(test_error); + }); + // TODO weird test, as it could maybe be used lazily and not in the constructor + it("The 'endings' options property is used", () => { + let got = false; + // @ts-expect-error + new Blob([], { + get endings() { + got = true; + }, + }); + expect(got).toBeTruthy(); + }); + const sampleEndings = [ + { name: 'LF', input: '\n', native: native_ending }, + { name: 'CR', input: '\r', native: native_ending }, + + { name: 'CRLF', input: '\r\n', native: native_ending }, + { name: 'CRCR', input: '\r\r', native: native_ending.repeat(2) }, + { name: 'LFCR', input: '\n\r', native: native_ending.repeat(2) }, + { name: 'LFLF', input: '\n\n', native: native_ending.repeat(2) }, + + { name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2) }, + { name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2) }, + { name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2) }, + + { name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2) }, + { name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3) }, + ]; + it('Newlines should not change with endings unspecified', () => { + sampleEndings.forEach((testCase) => { + const blob = new Blob([testCase.input]); + expect(blob.syncText()).toBe(testCase.input); + }); + }); + it('Newlines should not change with endings "transparent"', () => { + sampleEndings.forEach((testCase) => { + const blob = new Blob([testCase.input], { endings: 'transparent' }); + expect(blob.syncText()).toBe(testCase.input); + }); + }); + it('Newlines should match the platform with endings "native"', () => { + sampleEndings.forEach((testCase) => { + const blob = new Blob([testCase.input], { endings: 'native' }); + expect(blob.syncText()).toBe(testCase.native); }); + }); + it('CR/LF in adjacent input strings', () => { + const blob = new Blob(['\r', '\n'], { endings: 'native' }); + const expected = native_ending.repeat(2); + expect(blob.syncText()).toBe(expected); + }); + }); - describe('Array buffer', () => { - it('simple', async () => { - const input_arr = new TextEncoder().encode("PASS"); - const blob = new Blob([input_arr]); - const array_buffer = await blob.arrayBuffer(); - expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); - expect(new Uint8Array(array_buffer)).toEqual(input_arr); - }); - it('empty Blob data', async () => { - const input_arr = new TextEncoder().encode(""); - const blob = new Blob([input_arr]); - const array_buffer = await blob.arrayBuffer(); - expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); - expect(new Uint8Array(array_buffer)).toEqual(input_arr); - }) - it('non-ascii input', async () => { - const input_arr = new TextEncoder().encode("\u08B8\u000a"); - const blob = new Blob([input_arr]); - const array_buffer = await blob.arrayBuffer(); - expect(new Uint8Array(array_buffer)).toEqual(input_arr); - }) - it('non-unicode input', async () => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - const blob = new Blob([typed_arr]); - // const array_buffer = await blob.arrayBuffer(); - const array_buffer = await blob.bytes(); - // expect(blob.syncText()).toEqual("\u0008\u00F1\u0030\u007B\u0097") - // expect(new Uint8Array(array_buffer) == typed_arr).toBeTruthy() - // expect(new Uint8Array(array_buffer)).toEqual(typed_arr); - expect(blob.size).toBe(5) - expect(array_buffer.length).toBe(5) - console.log(array_buffer.byteOffset) - console.log(array_buffer, typed_arr) - expect(array_buffer).toEqual(typed_arr); - }) - it('concurrent reads', async () => { - const input_arr = new TextEncoder().encode("PASS"); - const blob = new Blob([input_arr]); - const array_buffer_results = await Promise.all([blob.arrayBuffer(), - blob.arrayBuffer(), blob.arrayBuffer()]); - for (let array_buffer of array_buffer_results) { - expect(array_buffer instanceof ArrayBuffer).toBeTruthy(); - expect(new Uint8Array(array_buffer)).toEqual(input_arr); - } - }) - }) - - describe('Bytes', async () => { - it('Simple', async () => { - const input_arr = new TextEncoder().encode("PASS"); - const blob = new Blob([input_arr]); - const uint8array = await blob.bytes(); - expect(uint8array instanceof Uint8Array).toBeTruthy(); - expect(uint8array).toEqual(input_arr); - }); - it('empty Blob data', async () => { - const input_arr = new TextEncoder().encode(""); - const blob = new Blob([input_arr]); - const uint8array = await blob.bytes(); - expect(uint8array instanceof Uint8Array).toBeTruthy(); - expect(uint8array).toEqual(input_arr); - }); - it('non-ascii input', async () => { - const input_arr = new TextEncoder().encode("\u08B8\u000a"); - const blob = new Blob([input_arr]); - const uint8array = await blob.bytes(); - expect(uint8array).toEqual(input_arr); - }); - it('non-unicode input', async () => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - const blob = new Blob([typed_arr]); - const uint8array = await blob.bytes(); - expect(uint8array).toEqual(typed_arr); - }); - it('concurrent reads', async () => { - const input_arr = new TextEncoder().encode("PASS"); - const blob = new Blob([input_arr]); - const uint8array_results = await Promise.all([blob.bytes(), - blob.bytes(), blob.bytes()]); - for (let uint8array of uint8array_results) { - expect(uint8array instanceof Uint8Array).toBeTruthy(); - expect(uint8array).toEqual(input_arr); + describe('constructor', () => { + it('globalThis should have a Blob property.', () => { + expect('Blob' in globalThis).toBeTruthy(); + }); + it('Blob.length should be 0', () => { + expect(Blob.length).toBe(0); + }); + it('Blob should be a function', () => { + expect(Blob instanceof Function).toBeTruthy(); + }); + it('Blob constructor with no arguments', () => { + var blob = new Blob(); + expect(blob instanceof Blob).toBeTruthy(); + expect(String(blob)).toBe('[object Blob]'); + expect(blob.size).toBe(0); + expect(blob.type).toBe(''); + }); + it("Blob constructor with no arguments, without 'new'", () => { + expect(() => { + var blob = Blob(); + }).toThrow(); + }); + it('Blob constructor without brackets', () => { + var blob = new Blob(); + expect(blob instanceof Blob).toBeTruthy(); + expect(blob.size).toBe(0); + expect(blob.type).toBe(''); + }); + it('Blob constructor with undefined as first argument', () => { + var blob = new Blob(undefined); + expect(blob instanceof Blob).toBeTruthy(); + expect(String(blob)).toBe('[object Blob]'); + expect(blob.size).toBe(0); + expect(blob.type).toBe(''); + }); + // TODO Something wrong with null ? Why should Blob() work and Blob(null) not ??? + it('Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.', () => { + [ + null, + true, + false, + 0, + 1, + 1.5, + 'FAIL', + new Date(), + // @ts-expect-error + new RegExp(), + {}, + { 0: 'FAIL', length: 1 }, + ].forEach((arg) => { + try { + // @ts-expect-error + new Blob(arg); + expect(false).toBeTruthy(); + } catch (err) { + expect(err instanceof TypeError).toBeTruthy(); + } + }); + }); + test_blob( + function () { + return new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + }); + }, + { + expected: '', + type: '', + desc: 'A plain object with @@iterator should be treated as a sequence for the blobParts argument.', + } + ); + it('A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.', () => { + const blob = new Blob({ + [Symbol.iterator]() { + var i = 0; + return { + next: () => + [{ done: false, value: 'ab' }, { done: false, value: 'cde' }, { done: true }][i++], + }; + }, + }); + expect(blob.size).toBe(5); + }); + it('A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument.', () => { + let blob = new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + 0: 'PASS', + length: 1, + }); + expect(blob.syncText()).toEqual('PASS'); + expect(blob.type).toBe(''); + }); + it('A String object should be treated as a sequence for the blobParts argument.', () => { + let blob = new Blob(new String('xyz')); + expect(blob.syncText()).toEqual('xyz'); + expect(blob.type).toBe(''); + }); + test_blob( + function () { + return new Blob(new String('xyz')); + }, + { + expected: 'xyz', + type: '', + desc: 'A String object should be treated as a sequence for the blobParts argument.', + } + ); + test_blob( + function () { + return new Blob(new Uint8Array([1, 2, 3])); + }, + { + expected: '123', + type: '', + desc: 'A Uint8Array object should be treated as a sequence for the blobParts argument.', + } + ); + + it('The length getter should be invoked and any exceptions should be propagated.', () => { + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + get length() { + throw test_error; + }, + }; + new Blob(obj); + }).toThrow(test_error); + }); + it('ToUint32 should be applied to the length and any exceptions should be propagated.', () => { + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { + valueOf: null, + toString: function () { + throw test_error; + }, + }, + }; + new Blob(obj); + }).toThrow(test_error); + expect(() => { + var obj = { + [Symbol.iterator]: Array.prototype[Symbol.iterator], + length: { + valueOf: function () { + throw test_error; + }, + }, + }; + new Blob(obj); + }).toThrow(test_error); + }); + it('Getters and value conversions should happen in order until an exception is thrown.', () => { + var received = []; + var obj = { + get [Symbol.iterator]() { + received.push('Symbol.iterator'); + return Array.prototype[Symbol.iterator]; + }, + get length() { + received.push('length getter'); + return { + valueOf: function () { + received.push('length valueOf'); + return 3; + }, + }; + }, + get 0() { + received.push('0 getter'); + return { + toString: function () { + received.push('0 toString'); + return 'a'; + }, + }; + }, + get 1() { + received.push('1 getter'); + throw test_error; + }, + get 2() { + received.push('2 getter'); + expect(true).toBe(false); + return 'unreachable'; + }, + }; + expect(() => new Blob(obj)).toThrow(test_error); + + console.log(); + console.log(); + console.log(); + console.log('received: ' + received); + console.log(); + console.log('expected: ' + expect); + console.log(); + console.log(); + console.log(); + + // Somehow we don't call 0 toString but I don't know why not or why would we + expect(received).toEqual([ + 'Symbol.iterator', + 'length getter', + 'length valueOf', + '0 getter', + '0 toString', + 'length getter', + 'length valueOf', + '1 getter', + ]); + }); + it('ToString should be called on elements of the blobParts array and any exceptions should be propagated.', () => { + expect( + () => + new Blob([ + { + toString: function () { + throw test_error; + }, + }, + ]) + ).toThrow(test_error); + expect( + () => + new Blob([ + { + toString: undefined, + valueOf: function () { + throw test_error; + }, + }, + ]) + ).toThrow(test_error); + expect( + () => + new Blob([ + { + toString: function () { + throw test_error; + }, + valueOf: function () { + expect(false).toBe(true); + }, + }, + ]) + ).toThrow(test_error); + // TODO add the proper TypeError type to toThrow + expect(() => new Blob([{ toString: null, valueOf: null }])).toThrow(); + }); + test_blob( + function () { + var arr = [ + { + toString: function () { + arr.pop(); + return 'PASS'; + }, + }, + { toString: function () {} }, + ]; + return new Blob(arr); + }, + { + expected: 'PASS', + type: '', + desc: 'Changes to the blobParts array should be reflected in the returned Blob (pop).', + } + ); + test_blob( + function () { + var arr = [ + { + toString: function () { + if (arr.length === 3) { + return 'A'; } - }); - }) - - // Windows platforms use CRLF as the native line ending. All others use LF. - const crlf = Platform.OS == 'windows'; - const native_ending = crlf ? '\r\n' : '\n'; - describe('constructor-endings', async () => { - it('valid endings value', () => { - const blob0 = new Blob([], {endings: 'native'}); - const blob1 = new Blob([], {endings: 'transparent'}); - expect(blob0).toBeTruthy() - expect(blob1).toBeTruthy() - }) - it('invalud endings value', () => { - [ - null, - '', - 'invalidEnumValue', - 'Transparent', - 'NATIVE', - 0, - {}, - ].forEach((ending) => { - // @ts-expect-error - expect(() => new Blob([], {endings:ending})).toThrow() - }) - }) - it('Exception propagation from options', () => { - const test_error = {name: 'test string'}; - // @ts-expect-error - expect(() => new Blob([], { get endings() { throw test_error; }})).toThrow(test_error) - }) - // TODO weird test, as it could maybe be used lazily and not in the constructor - it('The \'endings\' options property is used', () => { - let got = false; - // @ts-expect-error - new Blob([], { get endings() { got = true; } }); - expect(got).toBeTruthy() - }) - const sampleEndings = [ - {name: 'LF', input: '\n', native: native_ending}, - {name: 'CR', input: '\r', native: native_ending}, - - {name: 'CRLF', input: '\r\n', native: native_ending}, - {name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)}, - {name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)}, - {name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)}, - - {name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)}, - {name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)}, - {name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)}, - - {name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)}, - {name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)}, - - ]; - it('Newlines should not change with endings unspecified', () => { - sampleEndings.forEach(testCase => { - const blob = new Blob([testCase.input]); - expect(blob.syncText()).toBe(testCase.input) - }) - }) - it('Newlines should not change with endings "transparent"', () => { - sampleEndings.forEach(testCase => { - const blob = new Blob([testCase.input], {endings: 'transparent'}); - expect(blob.syncText()).toBe(testCase.input) - }); - }) - it('Newlines should match the platform with endings "native"', () => { - sampleEndings.forEach(testCase => { - const blob = new Blob([testCase.input], {endings: 'native'}); - expect(blob.syncText()).toBe(testCase.native) - }); - }) - it('CR/LF in adjacent input strings', () => { - const blob = new Blob(['\r', '\n'], {endings: 'native'}); - const expected = native_ending.repeat(2); - expect(blob.syncText()).toBe(expected) - }) - }) - - describe('constructor', () => { - it("globalThis should have a Blob property.", () => {expect("Blob" in globalThis).toBeTruthy()}) - it("Blob.length should be 0", () => {expect(Blob.length).toBe(0)}) - it("Blob should be a function", () => {expect(Blob instanceof Function).toBeTruthy()}) - it("Blob constructor with no arguments", () => { - var blob = new Blob() - expect(blob instanceof Blob).toBeTruthy() - expect(String(blob)).toBe('[object Blob]') - expect(blob.size).toBe(0) - expect(blob.type).toBe("") - }) - it("Blob constructor with no arguments, without 'new'", () => { - expect(() => {var blob = Blob();}).toThrow() - }) - it("Blob constructor without brackets", () => { - var blob = new Blob; - expect(blob instanceof Blob).toBeTruthy() - expect(blob.size).toBe(0) - expect(blob.type).toBe("") - }) - it("Blob constructor with undefined as first argument", () => { - var blob = new Blob(undefined) - expect(blob instanceof Blob).toBeTruthy() - expect(String(blob)).toBe('[object Blob]') - expect(blob.size).toBe(0) - expect(blob.type).toBe("") - }) - // TODO Something wrong with null ? Why should Blob() work and Blob(null) not ??? - it('Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.', () => { - [ - null, - true, - false, - 0, - 1, - 1.5, - "FAIL", - new Date(), - // @ts-expect-error - new RegExp(), - {}, - { 0: "FAIL", length: 1 }, - ].forEach((arg) => { - try { - // @ts-expect-error - new Blob(arg) - expect(false).toBeTruthy() - } catch (err) { - expect(err instanceof TypeError).toBeTruthy() - } - }); - }) - test_blob(function() { - return new Blob({ - [Symbol.iterator]: Array.prototype[Symbol.iterator], - }); - }, { - expected: "", - type: "", - desc: "A plain object with @@iterator should be treated as a sequence for the blobParts argument." - }); - it('A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.', () => { - const blob = new Blob({ - [Symbol.iterator]() { - var i = 0; - return {next: () => [ - {done:false, value:'ab'}, - {done:false, value:'cde'}, - {done:true} - ][i++] - }; - } - }); - expect(blob.size).toBe(5) - }) - it('A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument.', () => { - let blob = new Blob({ - [Symbol.iterator]: Array.prototype[Symbol.iterator], - 0: "PASS", - length: 1 - }); - expect(blob.syncText()).toEqual("PASS") - expect(blob.type).toBe("") - }) - it('A String object should be treated as a sequence for the blobParts argument.', () => { - let blob = new Blob(new String("xyz")); - expect(blob.syncText()).toEqual("xyz") - expect(blob.type).toBe("") - }) - test_blob(function() { - return new Blob(new String("xyz")); - }, { - expected: "xyz", - type: "", - desc: "A String object should be treated as a sequence for the blobParts argument." - }); - test_blob(function() { - return new Blob(new Uint8Array([1, 2, 3])); - }, { - expected: "123", - type: "", - desc: "A Uint8Array object should be treated as a sequence for the blobParts argument." - }); - - it("The length getter should be invoked and any exceptions should be propagated.", () => { - expect(() => { - var obj = { - [Symbol.iterator]: Array.prototype[Symbol.iterator], - get length() { throw test_error; } - }; - new Blob(obj) - }).toThrow(test_error) - }) - it("ToUint32 should be applied to the length and any exceptions should be propagated.", () => { - expect(() => { - var obj = { - [Symbol.iterator]: Array.prototype[Symbol.iterator], - length: { - valueOf: null, - toString: function() { throw test_error; } - } - }; - new Blob(obj); - }).toThrow(test_error) - expect(() => { - var obj = { - [Symbol.iterator]: Array.prototype[Symbol.iterator], - length: { valueOf: function() { throw test_error; } } - }; - new Blob(obj); - }).toThrow(test_error) - }) - it("Getters and value conversions should happen in order until an exception is thrown.", () => { - var received = []; - var obj = { - get [Symbol.iterator]() { - received.push("Symbol.iterator"); - return Array.prototype[Symbol.iterator]; - }, - get length() { - received.push("length getter"); - return { - valueOf: function() { - received.push("length valueOf"); - return 3; - } - }; - }, - get 0() { - received.push("0 getter"); - return { - toString: function() { - received.push("0 toString"); - return "a"; - } - }; - }, - get 1() { - received.push("1 getter"); - throw test_error; - }, - get 2() { - received.push("2 getter"); - expect(true).toBe(false); - return "unreachable"; - } - }; - expect(() => new Blob(obj)).toThrow(test_error); - - console.log() - console.log() - console.log() - console.log('received: ' + received) - console.log() - console.log('expected: ' + expect) - console.log() - console.log() - console.log() - - - // Somehow we don't call 0 toString but I don't know why not or why would we - expect(received).toEqual([ - "Symbol.iterator", - "length getter", - "length valueOf", - "0 getter", - "0 toString", - "length getter", - "length valueOf", - "1 getter", - ]); - }) - it("ToString should be called on elements of the blobParts array and any exceptions should be propagated.", () => { - expect(() => new Blob([{ toString: function() { throw test_error; } }])).toThrow(test_error) - expect(() => new Blob([{ toString: undefined, valueOf: function() { throw test_error; } }])).toThrow(test_error) - expect(() => new Blob([{ - toString: function() { throw test_error; }, - valueOf: function() { expect(false).toBe(true); } - }])).toThrow(test_error) - // TODO add the proper TypeError type to toThrow - expect(() => new Blob([{toString: null, valueOf: null}])).toThrow() - }) - test_blob(function() { - var arr = [ - { toString: function() { arr.pop(); return "PASS"; } }, - { toString: function() { } } - ]; - return new Blob(arr); - }, { - expected: "PASS", - type: "", - desc: "Changes to the blobParts array should be reflected in the returned Blob (pop)." - }); - test_blob(function() { - var arr = [ - { - toString: function() { - if (arr.length === 3) { - return "A"; - } - arr.unshift({ - toString: function() { - expect(true).toBe(false) - } - }); - return "P"; - } - }, - { - toString: function() { - return "SS"; - } - } - ]; - return new Blob(arr); - }, { - expected: "PASS", - type: "", - desc: "Changes to the blobParts array should be reflected in the returned Blob (unshift)." - }); - test_blob(function() { - // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17652 - return new Blob([ - null, - undefined, - true, - false, - 0, - 1, - new String("stringobject"), - [], - ['x', 'y'], - {}, - { 0: "FAIL", length: 1 }, - { toString: function() { return "stringA"; } }, - { toString: undefined, valueOf: function() { return "stringB"; } }, - { valueOf: function() { expect(false).toBe(true) } } - ]); - }, { - expected: "nullundefinedtruefalse01stringobjectx,y[object Object][object Object]stringAstringB[object Object]", - type: "", - desc: "ToString should be called on elements of the blobParts array." - }); - test_blob(function() { - return new Blob([ - new ArrayBuffer(8) - ]); - }, { - expected: "\0\0\0\0\0\0\0\0", - type: "", - desc: "ArrayBuffer elements of the blobParts array should be supported." - }); - - test_blob(function() { - return new Blob([ - new Uint8Array([0x50, 0x41, 0x53, 0x53]), - new Int8Array([0x50, 0x41, 0x53, 0x53]), - new Uint16Array([0x4150, 0x5353]), - new Int16Array([0x4150, 0x5353]), - new Uint32Array([0x53534150]), - new Int32Array([0x53534150]), - new Float32Array([0xD341500000]) - ]); - }, { - expected: "PASSPASSPASSPASSPASSPASSPASS", - type: "", - desc: "Passing typed arrays as elements of the blobParts array should work." - }); - test_blob(function() { - return new Blob([ - new Float16Array([2.65625, 58.59375]) - ]); - }, { - expected: "PASS", - type: "", - desc: "Passing a Float16Array as element of the blobParts array should work." - }); - test_blob(function() { - return new Blob([ - // 0x535 3415053534150 - // 0x535 = 0b010100110101 -> Sign = +, Exponent = 1333 - 1023 = 310 - // 0x13415053534150 * 2**(-52) - // ==> 0x13415053534150 * 2**258 = 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680 - new Float64Array([2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680]) - ]); - }, { - expected: "PASSPASS", - type: "", - desc: "Passing a Float64Array as element of the blobParts array should work." - }); - test_blob(function() { - return new Blob([ - new BigInt64Array([BigInt("0x5353415053534150")]), - new BigUint64Array([BigInt("0x5353415053534150")]) - ]); - }, { - expected: "PASSPASSPASSPASS", - type: "", - desc: "Passing BigInt typed arrays as elements of the blobParts array should work." - }); - - // Message channel doesn't exist in React Native - // it("Passing a FrozenArray as the blobParts array should work (FrozenArray).", async () => { - // var channel = new MessageChannel(); - // channel.port2.onmessage = this.step_func(function(e) { - // var b_ports = new Blob(e.ports); - // expect(b_ports.size).toEqual("[object MessagePort]".length) - // this.done(); - // }); - // var channel2 = new MessageChannel(); - // channel.port1.postMessage('', [channel2.port1]); - // }) - it("Passing a FrozenArray as the blobParts array should work", async () => { - let arr = ["PA", "SS"] - Object.freeze(arr) - expect(await new Blob(arr).text()).toBe("PASS") - }) - - test_blob(function() { - var blob = new Blob(['foo']); - return new Blob([blob, blob]); - }, { - expected: "foofoo", - type: "", - desc: "Array with two blobs" - }); - test_blob_binary(function() { - var view = new Uint8Array([0, 255, 0]); - return new Blob([view.buffer, view.buffer]); - }, { - expected: [0, 255, 0, 0, 255, 0], - type: "", - desc: "Array with two buffers" - }); - - test_blob_binary(function() { - var view = new Uint8Array([0, 255, 0, 4]); - var blob = new Blob([view, view]); - expect(blob.size).toBe(8); - var view1 = new Uint16Array(view.buffer, 2); - return new Blob([view1, view.buffer, view1]); - }, { - expected: [0, 4, 0, 255, 0, 4, 0, 4], - type: "", - desc: "Array with two bufferviews" - }); - - // TODO revisit this, why can we pass view but not the buffer? - test_blob(function() { - var view = new Uint8Array([0]); - var blob = new Blob(["fo"]); - return new Blob([view.buffer, blob, "foo"]); - }, { - expected: "\0fofoo", - type: "", - desc: "Array with mixed types" - }); - - it('options properties should be accessed in lexicographic order.', async () => { - const accessed = []; - const stringified = []; - - new Blob([], { - // @ts-ignore - get type() { accessed.push('type'); }, - // @ts-ignore - get endings() { accessed.push('endings'); } - }); - new Blob([], { - // @ts-ignore - type: { toString: () => { stringified.push('type'); return ''; } }, - // @ts-ignore - endings: { toString: () => { stringified.push('endings'); return 'transparent'; } } - }); - expect(accessed).toEqual(['endings', 'type']); - expect(stringified).toEqual(['endings', 'type']); - }) - - it('Arguments should be evaluated from left to right.', async () => { - expect( - () => new Blob( - [{ toString: function() { throw test_error } }], - { - //@ts-ignore - get type() { expect(0).toBe(1); } - }) - ).toThrow(test_error); - }) - - describe('Passing arguments for options', () => { - [ - null, - undefined, - {}, - { unrecognized: true }, - /regex/, - function() {} - ].forEach((arg : any, idx) => { - test_blob(function() { - return new Blob([], arg); - }, { - expected: "", - type: "", - desc: "Passing " + JSON.stringify(arg) + " (index " + idx + ") for options should use the defaults." - }); - test_blob(function() { - return new Blob(["\na\r\nb\n\rc\r"], arg); - }, { - expected: "\na\r\nb\n\rc\r", - type: "", - desc: "Passing " + JSON.stringify(arg) + " (index " + idx + ") for options should use the defaults (with newlines)." - }); - }); - }) - - describe('Blob constructor should throw with invalid property bag', () => { - [ - 123, - 123.4, - true, - 'abc' - ].forEach((arg : any) => { - it('Passing ' + JSON.stringify(arg) + ' for options should throw', () => { - expect(() => { - new Blob([], arg) - }).toThrow(TypeError()) - }) + arr.unshift({ + toString: function () { + expect(true).toBe(false); + }, }); - }) - - describe('Type test', () => { - var type_tests = [ - // blobParts, type, expected type - [[], '', ''], - [[], 'a', 'a'], - [[], 'A', 'a'], - [[], 'text/html', 'text/html'], - [[], 'TEXT/HTML', 'text/html'], - [[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'], - [[], '\u00E5', ''], - [[], '\uD801\uDC7E', ''], // U+1047E - [[], ' image/gif ', ' image/gif '], - [[], '\timage/gif\t', ''], - [[], 'image/gif;\u007f', ''], - [[], '\u0130mage/gif', ''], // uppercase i with dot - [[], '\u0131mage/gif', ''], // lowercase dotless i - [[], 'image/gif\u0000', ''], - // check that type isn't changed based on sniffing - [[0x3C, 0x48, 0x54, 0x4D, 0x4C, 0x3E], 'unknown/unknown', 'unknown/unknown'], // "" - [[0x00, 0xFF], 'text/plain', 'text/plain'], - [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], 'image/png', 'image/png'], // "GIF89a" - ]; - - type_tests.forEach(function(t: Array) { - it("Blob with type " + JSON.stringify(t[1]), () => { - // TODO Why this construction? It does'nt work yet, but it's not what we test for... - // var arr = new Uint8Array([t[0]]).buffer; - // var b = new Blob([arr], {type:t[1]}); - - var b = new Blob(t[0], {type:t[1]}); - expect(b.type).toEqual(t[2]); - }); - }); - }) - }) - - // HTML ONLY, not applicable - // describe('constructor dom windows', async () => { - // it("Passing platform objects for blobParts should throw a TypeError.", () => { - // var args = [ - // document.createElement("div"), - // window, - // ]; - // args.forEach((arg) => { - // expect(() => new Blob(arg)).toThrow(); - // }); - // }); - // it("A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)", () => { - // var element = document.createElement("div"); - // element.appendChild(document.createElement("div")); - // element.appendChild(document.createElement("p")); - // var list = element.children; - // Object.defineProperty(list, "length", { - // get: function() { throw test_error; } - // }); - // expect(() => {new Blob(list);}).toThrow(test_error); - // }); - - // test_blob(function() { - // var select = document.createElement("select"); - // select.appendChild(document.createElement("option")); - // return new Blob(select); - // }, { - // expected: "[object HTMLOptionElement]", - // type: "", - // desc: "Passing a platform object that supports indexed properties as the blobParts array should work (select)." - // }); - - // test_blob(function() { - // var elm = document.createElement("div"); - // elm.setAttribute("foo", "bar"); - // return new Blob(elm.attributes); - // }, { - // expected: "[object Attr]", - // type: "", - // desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." - // }); - // }) - - describe('Text', async () => { - it('simple', async () => { - const blob = new Blob(["PASS"]); - const text = await blob.text(); - expect(text).toBe("PASS"); - }); - it('empty blob data', async () => { - const blob = new Blob(); - const text = await blob.text(); - expect(text).toBe(""); - }); - it('multi-element array in constructor', async () => { - const blob = new Blob(["P", "A", "SS"]); - const text = await blob.text(); - expect(text).toBe("PASS"); - }); - it('non-unicode', async () => { - const non_unicode = "\u0061\u030A"; - const input_arr = new TextEncoder().encode(non_unicode); - const blob = new Blob([input_arr]); - const text = await blob.text(); - expect(text).toBe(non_unicode); - }); - it('different charset param in type option', async () => { - const blob = new Blob(["PASS"], { type: "text/plain;charset=utf-16le" }); - const text = await blob.text(); - expect(text).toBe("PASS"); - }) - it('Sync Text', () => { - const blob = new Blob(["PA", "SS"]); - const text = blob.syncText(); - expect(text).toBe("PASS"); - }) - it('different charset param with non-ascii input', async () => { - const non_unicode = "\u0061\u030A"; - const input_arr = new TextEncoder().encode(non_unicode); - const blob = new Blob([input_arr], { type: "text/plain;charset=utf-16le" }); - const text = await blob.text(); - expect(text).toBe(non_unicode); - }) - it('invalid utf-8 input', async () => { - const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); - const blob = new Blob([input_arr]); - const text = await blob.text(); - expect(text).toBe("\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd"); - }); - it('Promise.all multiple reads', async () => { - const input_arr = new Uint8Array([192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]); - const blob = new Blob([input_arr]); - const text_results = await Promise.all([blob.text(), blob.text(), blob.text()]); - text_results.forEach(text => { - expect(text).toBe("\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd"); - }); - }); - }) - - describe('Worker', async () => { - it('Create Blob in Worker', async () => { - const data = "TEST"; - const blob = new Blob([data], {type: "text/plain"}); - expect(await blob.text()).toEqual(data); - }); - }) + return 'P'; + }, + }, + { + toString: function () { + return 'SS'; + }, + }, + ]; + return new Blob(arr); + }, + { + expected: 'PASS', + type: '', + desc: 'Changes to the blobParts array should be reflected in the returned Blob (unshift).', + } + ); + test_blob( + function () { + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=17652 + return new Blob([ + null, + undefined, + true, + false, + 0, + 1, + new String('stringobject'), + [], + ['x', 'y'], + {}, + { 0: 'FAIL', length: 1 }, + { + toString: function () { + return 'stringA'; + }, + }, + { + toString: undefined, + valueOf: function () { + return 'stringB'; + }, + }, + { + valueOf: function () { + expect(false).toBe(true); + }, + }, + ]); + }, + { + expected: + 'nullundefinedtruefalse01stringobjectx,y[object Object][object Object]stringAstringB[object Object]', + type: '', + desc: 'ToString should be called on elements of the blobParts array.', + } + ); + test_blob( + function () { + return new Blob([new ArrayBuffer(8)]); + }, + { + expected: '\0\0\0\0\0\0\0\0', + type: '', + desc: 'ArrayBuffer elements of the blobParts array should be supported.', + } + ); + + test_blob( + function () { + return new Blob([ + new Uint8Array([0x50, 0x41, 0x53, 0x53]), + new Int8Array([0x50, 0x41, 0x53, 0x53]), + new Uint16Array([0x4150, 0x5353]), + new Int16Array([0x4150, 0x5353]), + new Uint32Array([0x53534150]), + new Int32Array([0x53534150]), + new Float32Array([0xd341500000]), + ]); + }, + { + expected: 'PASSPASSPASSPASSPASSPASSPASS', + type: '', + desc: 'Passing typed arrays as elements of the blobParts array should work.', + } + ); + test_blob( + function () { + return new Blob([new Float16Array([2.65625, 58.59375])]); + }, + { + expected: 'PASS', + type: '', + desc: 'Passing a Float16Array as element of the blobParts array should work.', + } + ); + test_blob( + function () { + return new Blob([ + // 0x535 3415053534150 + // 0x535 = 0b010100110101 -> Sign = +, Exponent = 1333 - 1023 = 310 + // 0x13415053534150 * 2**(-52) + // ==> 0x13415053534150 * 2**258 = 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680 + new Float64Array([ + 2510297372767036725005267563121821874921913208671273727396467555337665343087229079989707079680, + ]), + ]); + }, + { + expected: 'PASSPASS', + type: '', + desc: 'Passing a Float64Array as element of the blobParts array should work.', + } + ); + test_blob( + function () { + return new Blob([ + new BigInt64Array([BigInt('0x5353415053534150')]), + new BigUint64Array([BigInt('0x5353415053534150')]), + ]); + }, + { + expected: 'PASSPASSPASSPASS', + type: '', + desc: 'Passing BigInt typed arrays as elements of the blobParts array should work.', + } + ); + + // Message channel doesn't exist in React Native + // it("Passing a FrozenArray as the blobParts array should work (FrozenArray).", async () => { + // var channel = new MessageChannel(); + // channel.port2.onmessage = this.step_func(function(e) { + // var b_ports = new Blob(e.ports); + // expect(b_ports.size).toEqual("[object MessagePort]".length) + // this.done(); + // }); + // var channel2 = new MessageChannel(); + // channel.port1.postMessage('', [channel2.port1]); + // }) + it('Passing a FrozenArray as the blobParts array should work', async () => { + let arr = ['PA', 'SS']; + Object.freeze(arr); + expect(await new Blob(arr).text()).toBe('PASS'); + }); + + test_blob( + function () { + var blob = new Blob(['foo']); + return new Blob([blob, blob]); + }, + { + expected: 'foofoo', + type: '', + desc: 'Array with two blobs', + } + ); + test_blob_binary( + function () { + var view = new Uint8Array([0, 255, 0]); + return new Blob([view.buffer, view.buffer]); + }, + { + expected: [0, 255, 0, 0, 255, 0], + type: '', + desc: 'Array with two buffers', + } + ); + + test_blob_binary( + function () { + var view = new Uint8Array([0, 255, 0, 4]); + var blob = new Blob([view, view]); + expect(blob.size).toBe(8); + var view1 = new Uint16Array(view.buffer, 2); + return new Blob([view1, view.buffer, view1]); + }, + { + expected: [0, 4, 0, 255, 0, 4, 0, 4], + type: '', + desc: 'Array with two bufferviews', + } + ); + + // TODO revisit this, why can we pass view but not the buffer? + test_blob( + function () { + var view = new Uint8Array([0]); + var blob = new Blob(['fo']); + return new Blob([view.buffer, blob, 'foo']); + }, + { + expected: '\0fofoo', + type: '', + desc: 'Array with mixed types', + } + ); + + it('options properties should be accessed in lexicographic order.', async () => { + const accessed = []; + const stringified = []; + + new Blob([], { + // @ts-ignore + get type() { + accessed.push('type'); + }, + // @ts-ignore + get endings() { + accessed.push('endings'); + }, + }); + new Blob([], { + // @ts-ignore + type: { + toString: () => { + stringified.push('type'); + return ''; + }, + }, + // @ts-ignore + endings: { + toString: () => { + stringified.push('endings'); + return 'transparent'; + }, + }, + }); + expect(accessed).toEqual(['endings', 'type']); + expect(stringified).toEqual(['endings', 'type']); + }); + + it('Arguments should be evaluated from left to right.', async () => { + expect( + () => + new Blob( + [ + { + toString: function () { + throw test_error; + }, + }, + ], + { + //@ts-ignore + get type() { + expect(0).toBe(1); + }, + } + ) + ).toThrow(test_error); + }); + + describe('Passing arguments for options', () => { + [null, undefined, {}, { unrecognized: true }, /regex/, function () {}].forEach( + (arg: any, idx) => { + test_blob( + function () { + return new Blob([], arg); + }, + { + expected: '', + type: '', + desc: + 'Passing ' + + JSON.stringify(arg) + + ' (index ' + + idx + + ') for options should use the defaults.', + } + ); + test_blob( + function () { + return new Blob(['\na\r\nb\n\rc\r'], arg); + }, + { + expected: '\na\r\nb\n\rc\r', + type: '', + desc: + 'Passing ' + + JSON.stringify(arg) + + ' (index ' + + idx + + ') for options should use the defaults (with newlines).', + } + ); + } + ); + }); + + describe('Blob constructor should throw with invalid property bag', () => { + [123, 123.4, true, 'abc'].forEach((arg: any) => { + it('Passing ' + JSON.stringify(arg) + ' for options should throw', () => { + expect(() => { + new Blob([], arg); + }).toThrow(TypeError()); + }); + }); + }); + + describe('Type test', () => { + var type_tests = [ + // blobParts, type, expected type + [[], '', ''], + [[], 'a', 'a'], + [[], 'A', 'a'], + [[], 'text/html', 'text/html'], + [[], 'TEXT/HTML', 'text/html'], + [[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'], + [[], '\u00E5', ''], + [[], '\uD801\uDC7E', ''], // U+1047E + [[], ' image/gif ', ' image/gif '], + [[], '\timage/gif\t', ''], + [[], 'image/gif;\u007f', ''], + [[], '\u0130mage/gif', ''], // uppercase i with dot + [[], '\u0131mage/gif', ''], // lowercase dotless i + [[], 'image/gif\u0000', ''], + // check that type isn't changed based on sniffing + [[0x3c, 0x48, 0x54, 0x4d, 0x4c, 0x3e], 'unknown/unknown', 'unknown/unknown'], // "" + [[0x00, 0xff], 'text/plain', 'text/plain'], + [[0x47, 0x49, 0x46, 0x38, 0x39, 0x61], 'image/png', 'image/png'], // "GIF89a" + ]; + + type_tests.forEach(function (t: Array) { + it('Blob with type ' + JSON.stringify(t[1]), () => { + // TODO Why this construction? It does'nt work yet, but it's not what we test for... + // var arr = new Uint8Array([t[0]]).buffer; + // var b = new Blob([arr], {type:t[1]}); + + var b = new Blob(t[0], { type: t[1] }); + expect(b.type).toEqual(t[2]); + }); + }); + }); + }); - describe('Blob slice overflow', async () => { - var text = ''; + // HTML ONLY, not applicable + // describe('constructor dom windows', async () => { + // it("Passing platform objects for blobParts should throw a TypeError.", () => { + // var args = [ + // document.createElement("div"), + // window, + // ]; + // args.forEach((arg) => { + // expect(() => new Blob(arg)).toThrow(); + // }); + // }); + // it("A platform object that supports indexed properties should be treated as a sequence for the blobParts argument (overwritten 'length'.)", () => { + // var element = document.createElement("div"); + // element.appendChild(document.createElement("div")); + // element.appendChild(document.createElement("p")); + // var list = element.children; + // Object.defineProperty(list, "length", { + // get: function() { throw test_error; } + // }); + // expect(() => {new Blob(list);}).toThrow(test_error); + // }); + + // test_blob(function() { + // var select = document.createElement("select"); + // select.appendChild(document.createElement("option")); + // return new Blob(select); + // }, { + // expected: "[object HTMLOptionElement]", + // type: "", + // desc: "Passing a platform object that supports indexed properties as the blobParts array should work (select)." + // }); + + // test_blob(function() { + // var elm = document.createElement("div"); + // elm.setAttribute("foo", "bar"); + // return new Blob(elm.attributes); + // }, { + // expected: "[object Attr]", + // type: "", + // desc: "Passing an platform object that supports indexed properties as the blobParts array should work (attributes)." + // }); + // }) + + describe('Text', async () => { + it('simple', async () => { + const blob = new Blob(['PASS']); + const text = await blob.text(); + expect(text).toBe('PASS'); + }); + it('empty blob data', async () => { + const blob = new Blob(); + const text = await blob.text(); + expect(text).toBe(''); + }); + it('multi-element array in constructor', async () => { + const blob = new Blob(['P', 'A', 'SS']); + const text = await blob.text(); + expect(text).toBe('PASS'); + }); + it('non-unicode', async () => { + const non_unicode = '\u0061\u030A'; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr]); + const text = await blob.text(); + expect(text).toBe(non_unicode); + }); + it('different charset param in type option', async () => { + const blob = new Blob(['PASS'], { type: 'text/plain;charset=utf-16le' }); + const text = await blob.text(); + expect(text).toBe('PASS'); + }); + it('Sync Text', () => { + const blob = new Blob(['PA', 'SS']); + const text = blob.syncText(); + expect(text).toBe('PASS'); + }); + it('different charset param with non-ascii input', async () => { + const non_unicode = '\u0061\u030A'; + const input_arr = new TextEncoder().encode(non_unicode); + const blob = new Blob([input_arr], { type: 'text/plain;charset=utf-16le' }); + const text = await blob.text(); + expect(text).toBe(non_unicode); + }); + it('invalid utf-8 input', async () => { + const input_arr = new Uint8Array([ + 192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + ]); + const blob = new Blob([input_arr]); + const text = await blob.text(); + expect(text).toBe( + '\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd' + ); + }); + it('Promise.all multiple reads', async () => { + const input_arr = new Uint8Array([ + 192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + ]); + const blob = new Blob([input_arr]); + const text_results = await Promise.all([blob.text(), blob.text(), blob.text()]); + text_results.forEach((text) => { + expect(text).toBe( + '\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd' + ); + }); + }); + }); - for (var i = 0; i < 2000; ++i) { - text += 'A'; - } + describe('Worker', async () => { + it('Create Blob in Worker', async () => { + const data = 'TEST'; + const blob = new Blob([data], { type: 'text/plain' }); + expect(await blob.text()).toEqual(data); + }); + }); - it("slice start is negative, relativeStart will be max((size + start), 0)", () => { - var blob = new Blob([text]); - var sliceBlob = blob.slice(-1, blob.size); - expect(sliceBlob.size).toBe(1); - }); + describe('Blob slice overflow', async () => { + var text = ''; + + for (var i = 0; i < 2000; ++i) { + text += 'A'; + } + + it('slice start is negative, relativeStart will be max((size + start), 0)', () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(-1, blob.size); + expect(sliceBlob.size).toBe(1); + }); + + it('slice start is greater than blob size, relativeStart will be min(start, size)', () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size + 1, blob.size); + expect(sliceBlob.size).toBe(0); + }); + + it('slice end is negative, relativeEnd will be max((size + end), 0)', () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, -1); + expect(sliceBlob.size).toBe(1); + }); + + it('slice end is greater than blob size, relativeEnd will be min(end, size)', () => { + var blob = new Blob([text]); + var sliceBlob = blob.slice(blob.size - 2, blob.size + 999); + expect(sliceBlob.size).toBe(2); + }); + }); - it("slice start is greater than blob size, relativeStart will be min(start, size)", () => { - var blob = new Blob([text]); - var sliceBlob = blob.slice(blob.size + 1, blob.size); - expect(sliceBlob.size).toBe(0); - }); + describe('Blob Slice', async () => { + test_blob( + () => { + var blobTemp = new Blob(['PASS']); + return blobTemp.slice(); + }, + { + expected: 'PASS', + type: '', + desc: 'no-argument Blob slice', + } + ); - it("slice end is negative, relativeEnd will be max((size + end), 0)", () => { - var blob = new Blob([text]); - var sliceBlob = blob.slice(blob.size - 2, -1); - expect(sliceBlob.size).toBe(1); - }); + describe('Slices', async () => { + var blob1 = new Blob(['squiggle']); + var blob2 = new Blob(['steak'], { type: 'content/type' }); - it("slice end is greater than blob size, relativeEnd will be min(end, size)", () => { - var blob = new Blob([text]); - var sliceBlob = blob.slice(blob.size - 2, blob.size + 999); - expect(sliceBlob.size).toBe(2) - }); - }) - - describe('Blob Slice', async () => { - test_blob(() => { - var blobTemp = new Blob(["PASS"]); - return blobTemp.slice(); - }, { - expected: "PASS", - type: "", - desc: "no-argument Blob slice" - } - ); + test_blob(() => blob1, { + expected: 'squiggle', + type: '', + desc: 'blob1.', + }); - describe("Slices", async () => { - var blob1 = new Blob(["squiggle"]); - var blob2 = new Blob(["steak"], {type: "content/type"}); + test_blob(() => blob2, { + expected: 'steak', + type: 'content/type', + desc: 'blob2.', + }); - test_blob(() => blob1, - { - expected: "squiggle", - type: "", - desc: "blob1." - }); + test_blob( + () => { + return new Blob().slice(0, 0, null); + }, + { + expected: '', + type: 'null', + desc: 'null type Blob slice', + } + ); + + test_blob( + () => { + return new Blob().slice(0, 0, undefined); + }, + { + expected: '', + type: '', + desc: 'undefined type Blob slice', + } + ); + + test_blob( + () => { + return new Blob().slice(0, 0); + }, + { + expected: '', + type: '', + desc: 'no type Blob slice', + } + ); + + var arrayBuffer = new ArrayBuffer(16); + var int8View = new Int8Array(arrayBuffer); + for (var i = 0; i < 16; i++) { + int8View[i] = i + 65; + } - test_blob(() => blob2, + var testData = [ + [ + ['PASSSTRING'], + [ + { start: -6, contents: 'STRING' }, + { start: -12, contents: 'PASSSTRING' }, + { start: 4, contents: 'STRING' }, + { start: 12, contents: '' }, + { start: 0, end: -6, contents: 'PASS' }, + { start: 0, end: -12, contents: '' }, + { start: 0, end: 4, contents: 'PASS' }, + { start: 0, end: 12, contents: 'PASSSTRING' }, + { start: 7, end: 4, contents: '' }, + ], + ], + + // Test 3 strings + [ + ['foo', 'bar', 'baz'], + [ + { start: 0, end: 9, contents: 'foobarbaz' }, + { start: 0, end: 3, contents: 'foo' }, + { start: 3, end: 9, contents: 'barbaz' }, + { start: 6, end: 9, contents: 'baz' }, + { start: 6, end: 12, contents: 'baz' }, + { start: 0, end: 9, contents: 'foobarbaz' }, + { start: 0, end: 11, contents: 'foobarbaz' }, + { start: 10, end: 15, contents: '' }, + ], + ], + + // Test string, Blob, string + [ + ['foo', blob1, 'baz'], + [ + { start: 0, end: 3, contents: 'foo' }, + { start: 3, end: 11, contents: 'squiggle' }, + { start: 2, end: 4, contents: 'os' }, + { start: 10, end: 12, contents: 'eb' }, + ], + ], + + // Test blob, string, blob + [ + [blob1, 'foo', blob1], + [ + { start: 0, end: 8, contents: 'squiggle' }, + { start: 7, end: 9, contents: 'ef' }, + { start: 10, end: 12, contents: 'os' }, + { start: 1, end: 4, contents: 'qui' }, + { start: 12, end: 15, contents: 'qui' }, + { start: 40, end: 60, contents: '' }, + ], + ], + + // Test blobs all the way down + [ + [blob2, blob1, blob2], + [ + { start: 0, end: 5, contents: 'steak' }, + { start: 5, end: 13, contents: 'squiggle' }, + { start: 13, end: 18, contents: 'steak' }, + { start: 1, end: 3, contents: 'te' }, + { start: 6, end: 10, contents: 'quig' }, + ], + ], + + // Test an ArrayBufferView + [ + [int8View, blob1, 'foo'], + [ + { start: 0, end: 8, contents: 'ABCDEFGH' }, + { start: 8, end: 18, contents: 'IJKLMNOPsq' }, + { start: 17, end: 20, contents: 'qui' }, + { start: 4, end: 12, contents: 'EFGHIJKL' }, + ], + ], + + // Test a partial ArrayBufferView + [ + [new Uint8Array(arrayBuffer, 3, 5), blob1, 'foo'], + [ + { start: 0, end: 8, contents: 'DEFGHsqu' }, + { start: 8, end: 18, contents: 'igglefoo' }, + { start: 4, end: 12, contents: 'Hsquiggl' }, + ], + ], + + // Test type coercion of a number + [ + [3, int8View, 'foo'], + [ + { start: 0, end: 8, contents: '3ABCDEFG' }, + { start: 8, end: 18, contents: 'HIJKLMNOPf' }, + { start: 17, end: 21, contents: 'foo' }, + { start: 4, end: 12, contents: 'DEFGHIJK' }, + ], + ], + + [ + [new Uint8Array([0, 255, 0]).buffer, new Blob(['abcd']), 'efgh', 'ijklmnopqrstuvwxyz'], + [ + { start: 1, end: 4, contents: '\uFFFD\u0000a' }, + { start: 4, end: 8, contents: 'bcde' }, + { start: 8, end: 12, contents: 'fghi' }, + { start: 1, end: 12, contents: '\uFFFD\u0000abcdefghi' }, + ], + ], + ]; + + testData.forEach(function (data, i) { + var blobs = data[0]; + var tests = data[1]; + tests.forEach(function (expectations, j) { + describe('Slicing test (' + i + ',' + j + ').', () => { + var blob = new Blob(blobs); + + it('blob is an instance of Blob', () => { + expect(blob instanceof Blob).toBeTruthy(); + expect(blob instanceof File).toBeFalsy(); + }); + + test_blob( + () => { + return expectations.end === undefined + ? blob.slice(expectations.start) + : blob.slice(expectations.start, expectations.end); + }, { - expected: "steak", - type: "content/type", - desc: "blob2." - }); - - test_blob(() => { - return new Blob().slice(0,0,null); - }, { - expected: "", - type: "null", - desc: "null type Blob slice" - }); - - test_blob(() => { - return new Blob().slice(0,0,undefined); - }, { - expected: "", - type: "", - desc: "undefined type Blob slice" - }); - - test_blob(() => { - return new Blob().slice(0,0); - }, { - expected: "", - type: "", - desc: "no type Blob slice" - }); - - var arrayBuffer = new ArrayBuffer(16); - var int8View = new Int8Array(arrayBuffer); - for (var i = 0; i < 16; i++) { - int8View[i] = i + 65; + expected: expectations.contents, + type: '', + desc: 'Slicing test: slice (' + i + ',' + j + ').', } - - var testData = [ - [ - ["PASSSTRING"], - [{start: -6, contents: "STRING"}, - {start: -12, contents: "PASSSTRING"}, - {start: 4, contents: "STRING"}, - {start: 12, contents: ""}, - {start: 0, end: -6, contents: "PASS"}, - {start: 0, end: -12, contents: ""}, - {start: 0, end: 4, contents: "PASS"}, - {start: 0, end: 12, contents: "PASSSTRING"}, - {start: 7, end: 4, contents: ""}] - ], - - // Test 3 strings - [ - ["foo", "bar", "baz"], - [{start: 0, end: 9, contents: "foobarbaz"}, - {start: 0, end: 3, contents: "foo"}, - {start: 3, end: 9, contents: "barbaz"}, - {start: 6, end: 9, contents: "baz"}, - {start: 6, end: 12, contents: "baz"}, - {start: 0, end: 9, contents: "foobarbaz"}, - {start: 0, end: 11, contents: "foobarbaz"}, - {start: 10, end: 15, contents: ""}] - ], - - // Test string, Blob, string - [ - ["foo", blob1, "baz"], - [{start: 0, end: 3, contents: "foo"}, - {start: 3, end: 11, contents: "squiggle"}, - {start: 2, end: 4, contents: "os"}, - {start: 10, end: 12, contents: "eb"}] - ], - - // Test blob, string, blob - [ - [blob1, "foo", blob1], - [{start: 0, end: 8, contents: "squiggle"}, - {start: 7, end: 9, contents: "ef"}, - {start: 10, end: 12, contents: "os"}, - {start: 1, end: 4, contents: "qui"}, - {start: 12, end: 15, contents: "qui"}, - {start: 40, end: 60, contents: ""}] - ], - - // Test blobs all the way down - [ - [blob2, blob1, blob2], - [{start: 0, end: 5, contents: "steak"}, - {start: 5, end: 13, contents: "squiggle"}, - {start: 13, end: 18, contents: "steak"}, - {start: 1, end: 3, contents: "te"}, - {start: 6, end: 10, contents: "quig"}] - ], - - // Test an ArrayBufferView - [ - [int8View, blob1, "foo"], - [{start: 0, end: 8, contents: "ABCDEFGH"}, - {start: 8, end: 18, contents: "IJKLMNOPsq"}, - {start: 17, end: 20, contents: "qui"}, - {start: 4, end: 12, contents: "EFGHIJKL"}] - ], - - // Test a partial ArrayBufferView - [ - [new Uint8Array(arrayBuffer, 3, 5), blob1, "foo"], - [{start: 0, end: 8, contents: "DEFGHsqu"}, - {start: 8, end: 18, contents: "igglefoo"}, - {start: 4, end: 12, contents: "Hsquiggl"}] - ], - - // Test type coercion of a number - [ - [3, int8View, "foo"], - [{start: 0, end: 8, contents: "3ABCDEFG"}, - {start: 8, end: 18, contents: "HIJKLMNOPf"}, - {start: 17, end: 21, contents: "foo"}, - {start: 4, end: 12, contents: "DEFGHIJK"}] - ], - - [ - [(new Uint8Array([0, 255, 0])).buffer, - new Blob(['abcd']), - 'efgh', - 'ijklmnopqrstuvwxyz'], - [{start: 1, end: 4, contents: "\uFFFD\u0000a"}, - {start: 4, end: 8, contents: "bcde"}, - {start: 8, end: 12, contents: "fghi"}, - {start: 1, end: 12, contents: "\uFFFD\u0000abcdefghi"}] - ] - ]; - - testData.forEach(function(data, i) { - var blobs = data[0]; - var tests = data[1]; - tests.forEach(function(expectations, j) { - describe("Slicing test (" + i + "," + j + ").", () => { - var blob = new Blob(blobs); - - it('blob is an instance of Blob', () => { - expect(blob instanceof Blob).toBeTruthy(); - expect(blob instanceof File).toBeFalsy(); - }) - - test_blob(() => { - return expectations.end === undefined - ? blob.slice(expectations.start) - : blob.slice(expectations.start, expectations.end); - }, { - expected: expectations.contents, - type: "", - desc: "Slicing test: slice (" + i + "," + j + ")." - }); - },); - }); - }); + ); }); - - describe('Invalid content types', () => { - var invalidTypes = [ - "\xFF", - "te\x09xt/plain", - "te\x00xt/plain", - "te\x1Fxt/plain", - "te\x7Fxt/plain" - ]; - invalidTypes.forEach(function(type) { - test_blob(() => { - var blob = new Blob(["PASS"]); - return blob.slice(0, 4, type); - }, { - expected: "PASS", - type: "", - desc: "Invalid contentType (" + JSON.stringify(type) + ")" - }); - }); - }) - - var validTypes = [ - "te(xt/plain", - "te)xt/plain", - "text/plain", - "te@xt/plain", - "te,xt/plain", - "te;xt/plain", - "te:xt/plain", - "te\\xt/plain", - "te\"xt/plain", - "te/xt/plain", - "te[xt/plain", - "te]xt/plain", - "te?xt/plain", - "te=xt/plain", - "te{xt/plain", - "te}xt/plain", - "te\x20xt/plain", - "TEXT/PLAIN", - "text/plain;charset = UTF-8", - "text/plain;charset=UTF-8" - ]; - describe('valid content types', () => { - validTypes.forEach((type) => { - test_blob(() => { - var blob = new Blob(["PASS"]); - return blob.slice(0, 4, type); - }, { - expected: "PASS", - type: type.toLowerCase(), - desc: "Valid contentType (" + JSON.stringify(type) + ")" - }); - }); - }) - }) - - describe('stream', async () => { - it('stream byob crash', async () => { - let a = new Blob(['', '', undefined], { }) - let b = a.stream() - let c = new ReadableStreamBYOBReader(b) - let d = new Int16Array(8) - await c.read(d) - c.releaseLock() - await a.text() - await b.cancel() - }) - - it('stream xhr crash', async () => { - // TODO this constructor doesn't work need to fix it - // const blob = new Blob([1, 2]); - const blob = new Blob([Int32Array.from([1, 2])]); - const readable = blob.stream() - const writable = new WritableStream({}, { - size() { - let xhr = new XMLHttpRequest() - xhr.open("POST", "1", false) - xhr.send() - } - }) - readable.pipeThrough({ readable, writable }) - }) - - it("Blob.stream()", async () => { - const blob = new Blob(["PASS"]); - const stream = blob.stream(); - const chunks = await read_all_chunks(stream); - for (let [index, value] of chunks.entries()) { - expect(value).toEqual("PASS".charCodeAt(index)); - } - }) - - it("Blob.stream() empty Blob", async () => { - const blob = new Blob(); - const stream = blob.stream(); - const chunks = await read_all_chunks(stream); - expect(chunks).toEqual([]); - }) - - it("Blob.stream() non-unicode input", async () => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - const blob = new Blob([typed_arr]); - const stream = blob.stream(); - const chunks = await read_all_chunks(stream); - expect(chunks).toEqual(input_arr); - }) - - it("Blob.stream() garbage collection of blob shouldn't break stream " + - "consumption", async() => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - let blob = new Blob([typed_arr]); - const stream = blob.stream(); - blob = null; - // TODO Actually call garbageCollect() - // await garbageCollect(); - const chunks = await read_all_chunks(stream, { perform_gc: true }); - expect(chunks).toEqual(input_arr); - }) - - it("Blob.stream() garbage collection of stream shouldn't break stream " + - "consumption", async() => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - let blob = new Blob([typed_arr]); - const chunksPromise = read_all_chunks(blob.stream()); - // It somehow matters to do GC here instead of doing `perform_gc: true` - // TODO Actually call garbageCollect() - // await garbageCollect(); - expect(await chunksPromise).toEqual(input_arr); - }) + }); + }); + }); + + describe('Invalid content types', () => { + var invalidTypes = [ + '\xFF', + 'te\x09xt/plain', + 'te\x00xt/plain', + 'te\x1Fxt/plain', + 'te\x7Fxt/plain', + ]; + invalidTypes.forEach(function (type) { + test_blob( + () => { + var blob = new Blob(['PASS']); + return blob.slice(0, 4, type); + }, + { + expected: 'PASS', + type: '', + desc: 'Invalid contentType (' + JSON.stringify(type) + ')', + } + ); + }); + }); + + var validTypes = [ + 'te(xt/plain', + 'te)xt/plain', + 'text/plain', + 'te@xt/plain', + 'te,xt/plain', + 'te;xt/plain', + 'te:xt/plain', + 'te\\xt/plain', + 'te"xt/plain', + 'te/xt/plain', + 'te[xt/plain', + 'te]xt/plain', + 'te?xt/plain', + 'te=xt/plain', + 'te{xt/plain', + 'te}xt/plain', + 'te\x20xt/plain', + 'TEXT/PLAIN', + 'text/plain;charset = UTF-8', + 'text/plain;charset=UTF-8', + ]; + describe('valid content types', () => { + validTypes.forEach((type) => { + test_blob( + () => { + var blob = new Blob(['PASS']); + return blob.slice(0, 4, type); + }, + { + expected: 'PASS', + type: type.toLowerCase(), + desc: 'Valid contentType (' + JSON.stringify(type) + ')', + } + ); + }); + }); + }); - it("Reading Blob.stream() with BYOB reader", async () => { - const input_arr = [8, 241, 48, 123, 151]; - const typed_arr = new Uint8Array(input_arr); - let blob = new Blob([typed_arr]); - const stream = blob.stream(); - const chunks = await read_all_chunks(stream, { mode: "byob" }); - expect(chunks).toEqual(input_arr); - }) - }) + describe('stream', async () => { + it('stream byob crash', async () => { + let a = new Blob(['', '', undefined], {}); + let b = a.stream(); + let c = new ReadableStreamBYOBReader(b); + let d = new Int16Array(8); + await c.read(d); + c.releaseLock(); + await a.text(); + await b.cancel(); + }); + + it('stream xhr crash', async () => { + // TODO this constructor doesn't work need to fix it + // const blob = new Blob([1, 2]); + const blob = new Blob([Int32Array.from([1, 2])]); + const readable = blob.stream(); + const writable = new WritableStream( + {}, + { + size() { + let xhr = new XMLHttpRequest(); + xhr.open('POST', '1', false); + xhr.send(); + }, + } + ); + readable.pipeThrough({ readable, writable }); + }); + + it('Blob.stream()', async () => { + const blob = new Blob(['PASS']); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + for (let [index, value] of chunks.entries()) { + expect(value).toEqual('PASS'.charCodeAt(index)); + } + }); + + it('Blob.stream() empty Blob', async () => { + const blob = new Blob(); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + expect(chunks).toEqual([]); + }); + + it('Blob.stream() non-unicode input', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream); + expect(chunks).toEqual(input_arr); + }); + + it( + "Blob.stream() garbage collection of blob shouldn't break stream " + 'consumption', + async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + blob = null; + // TODO Actually call garbageCollect() + // await garbageCollect(); + const chunks = await read_all_chunks(stream, { perform_gc: true }); + expect(chunks).toEqual(input_arr); + } + ); + + it( + "Blob.stream() garbage collection of stream shouldn't break stream " + 'consumption', + async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const chunksPromise = read_all_chunks(blob.stream()); + // It somehow matters to do GC here instead of doing `perform_gc: true` + // TODO Actually call garbageCollect() + // await garbageCollect(); + expect(await chunksPromise).toEqual(input_arr); + } + ); + + it('Reading Blob.stream() with BYOB reader', async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + let blob = new Blob([typed_arr]); + const stream = blob.stream(); + const chunks = await read_all_chunks(stream, { mode: 'byob' }); + expect(chunks).toEqual(input_arr); + }); }); + }); } diff --git a/packages/expo-blob/build/BlobModule.d.ts b/packages/expo-blob/build/BlobModule.d.ts index b214ccc5392e62..be6c4133cfd42c 100644 --- a/packages/expo-blob/build/BlobModule.d.ts +++ b/packages/expo-blob/build/BlobModule.d.ts @@ -1,5 +1,5 @@ -import { NativeModule, SharedObject } from "expo"; -import { Blob, BlobPart } from "./BlobModule.types"; +import { NativeModule, SharedObject } from 'expo'; +import { Blob, BlobPart } from './BlobModule.types'; declare class NativeBlob extends SharedObject { readonly size: number; readonly type: string; diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index c14d6eff1cf9cb..f06aee586e63b2 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CAClB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAChD,IAAI,EAAE,OAAO,UAAU,CAAC;CACxB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACtD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoCxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAI7C,QAAQ,IAAI,MAAM;CAGlB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAmCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 1d65a19c6bb548..7a1b74e436242f 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -1,40 +1,53 @@ -import { requireNativeModule } from "expo"; -import { normalizedContentType } from "./utils"; -const NativeBlobModule = requireNativeModule("ExpoBlob"); +import { requireNativeModule } from 'expo'; +import { normalizedContentType } from './utils'; +const NativeBlobModule = requireNativeModule('ExpoBlob'); +const isTypedArray = (v) => { + return (v instanceof Int16Array || + v instanceof Int32Array || + v instanceof Int8Array || + v instanceof BigInt64Array || + v instanceof BigUint64Array || + v instanceof Uint16Array || + v instanceof Uint32Array || + v instanceof Uint8Array || + v instanceof Float32Array || + v instanceof Float64Array); +}; +const getOptions = (options) => { + let opt; + if (options) { + if (!(options instanceof Object)) { + throw TypeError(); + } + opt = { + endings: options.endings, + type: options.type === undefined ? '' : normalizedContentType(options.type), + }; + } + else { + opt = options; + } + return opt; +}; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { - let opt; - if (options) { - if (!(options instanceof Object)) { - throw TypeError(); - } - opt = { - endings: options.endings, - type: options.type === undefined ? "" : normalizedContentType(options.type) - }; - } - else { - opt = options; - } const inputMapping = (v) => { if (v instanceof ArrayBuffer) { - // TODO maybe do this natively not in typescript? return new Uint8Array(v); } - if (typeof v === 'number') { - // Manual type coercion? - return String(v); + if (v instanceof ExpoBlob || isTypedArray(v)) { + return v; } - return v; + return String(v); }; if (blobParts === undefined) { - super([], opt); + super([], getOptions(options)); } else if (blobParts === null || !(blobParts instanceof Object)) { throw TypeError(); } else { - super([...blobParts].flat(Infinity).map(inputMapping), opt); + super([...blobParts].map(inputMapping), getOptions(options)); } } slice(start, end, contentType) { @@ -60,10 +73,12 @@ export class ExpoBlob extends NativeBlobModule.Blob { }); } async arrayBuffer() { - return super.bytes().then((bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); + return super + .bytes() + .then((bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); } toString() { - return "[object Blob]"; + return '[object Blob]'; } } //# sourceMappingURL=BlobModule.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 70423a0cf3e4ac..003a50114a4d1f 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from \"expo\";\nimport { Blob, BlobPart } from \"./BlobModule.types\";\nimport { normalizedContentType } from \"./utils\";\ndeclare class NativeBlob extends SharedObject {\n\treadonly size: number;\n\treadonly type: string;\n\tconstructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob;\n\tbytes(): Promise;\n\ttext(): Promise;\n\tsyncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n\tBlob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule(\"ExpoBlob\");\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n\tconstructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n\t\tlet opt\n\t\tif (options) {\n\t\t\tif (!(options instanceof Object)) {\n\t\t\t\tthrow TypeError()\n\t\t\t}\n\n\t\t\topt = {\n\t\t\t\tendings: options.endings,\n\t\t\t\ttype: options.type === undefined ? \"\" : normalizedContentType(options.type)\n\t\t\t}\n\t\t} else {\n\t\t\topt = options\n\t\t}\n\n\t\tconst inputMapping = (v : any) => {\n\t\t\tif (v instanceof ArrayBuffer) {\n\t\t\t\t// TODO maybe do this natively not in typescript?\n\t\t\t\treturn new Uint8Array(v)\n\t\t\t}\n\t\t\tif (typeof v === 'number') {\n\t\t\t\t// Manual type coercion?\n\t\t\t\treturn String(v)\n\t\t\t}\n\t\t\treturn v\n\t\t}\n\n\t\tif (blobParts === undefined) {\n\t\t\tsuper([], opt);\n\t\t} else if (blobParts === null || !(blobParts instanceof Object)) {\n\t\t\tthrow TypeError();\n\t\t} else {\n\t\t\tsuper([...blobParts].flat(Infinity).map(inputMapping), opt);\n\t\t}\n\t}\n\n\tslice(start?: number, end?: number, contentType?: string): ExpoBlob {\n\t\tconst normalizedType = normalizedContentType(contentType);\n\t\tconst slicedBlob = super.slice(start, end, normalizedType);\n\t\tObject.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n\t\treturn slicedBlob;\n\t}\n\n\tstream(): ReadableStream {\n\t\tconst uint8promise = super.bytes();\n\t\tlet offset = 0;\n\t\treturn new ReadableStream({\n\t\t\tasync pull(controller) {\n\t\t\t\tlet uint8 = await uint8promise\n\t\t\t\tif (offset < uint8.length) {\n\t\t\t\t\tcontroller.enqueue(uint8.subarray(offset));\n\t\t\t\t\toffset = uint8.length;\n\t\t\t\t} else {\n\t\t\t\t\tcontroller.close();\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\n\tasync arrayBuffer(): Promise {\n\t\treturn super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));\n\t}\n\n\ttoString(): string {\n \t \treturn \"[object Blob]\"\n \t}\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,GAAG,CAAC;IACR,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,GAAG,GAAG;YACJ,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC;SAC5E,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,OAAO,CAAC;IAChB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,YAAY,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACpC,KAAK,CAAC,IAAI,CAAC,UAAU;gBACnB,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC;gBAC/B,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC1B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n let opt;\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n opt = {\n endings: options.endings,\n type: options.type === undefined ? '' : normalizedContentType(options.type),\n };\n } else {\n opt = options;\n }\n\n return opt;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || !(blobParts instanceof Object)) {\n throw TypeError();\n } else {\n super([...blobParts].map(inputMapping), getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 0f37e93281508d..2ad4db6f2d2e6f 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -1,87 +1,108 @@ -import { NativeModule, requireNativeModule, SharedObject } from "expo"; -import { Blob, BlobPart } from "./BlobModule.types"; -import { normalizedContentType } from "./utils"; +import { NativeModule, requireNativeModule, SharedObject } from 'expo'; +import { Blob, BlobPart } from './BlobModule.types'; +import { normalizedContentType } from './utils'; declare class NativeBlob extends SharedObject { - readonly size: number; - readonly type: string; - constructor(blobParts?: BlobPart[], options?: BlobPropertyBag); - slice(start?: number, end?: number, contentType?: string): ExpoBlob; - bytes(): Promise; - text(): Promise; - syncText(): string; + readonly size: number; + readonly type: string; + constructor(blobParts?: BlobPart[], options?: BlobPropertyBag); + slice(start?: number, end?: number, contentType?: string): ExpoBlob; + bytes(): Promise; + text(): Promise; + syncText(): string; } declare class ExpoBlobModule extends NativeModule { - Blob: typeof NativeBlob; + Blob: typeof NativeBlob; } -const NativeBlobModule = requireNativeModule("ExpoBlob"); +const NativeBlobModule = requireNativeModule('ExpoBlob'); -export class ExpoBlob extends NativeBlobModule.Blob implements Blob { - constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { - let opt - if (options) { - if (!(options instanceof Object)) { - throw TypeError() - } +const isTypedArray = (v: any): boolean => { + return ( + v instanceof Int16Array || + v instanceof Int32Array || + v instanceof Int8Array || + v instanceof BigInt64Array || + v instanceof BigUint64Array || + v instanceof Uint16Array || + v instanceof Uint32Array || + v instanceof Uint8Array || + v instanceof Float32Array || + v instanceof Float64Array + ); +}; + +const getOptions = (options?: BlobPropertyBag) => { + let opt; + if (options) { + if (!(options instanceof Object)) { + throw TypeError(); + } - opt = { - endings: options.endings, - type: options.type === undefined ? "" : normalizedContentType(options.type) - } - } else { - opt = options - } + opt = { + endings: options.endings, + type: options.type === undefined ? '' : normalizedContentType(options.type), + }; + } else { + opt = options; + } - const inputMapping = (v : any) => { - if (v instanceof ArrayBuffer) { - // TODO maybe do this natively not in typescript? - return new Uint8Array(v) - } - if (typeof v === 'number') { - // Manual type coercion? - return String(v) - } - return v - } + return opt; +}; + +export class ExpoBlob extends NativeBlobModule.Blob implements Blob { + constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { + const inputMapping = (v: any) => { + if (v instanceof ArrayBuffer) { + return new Uint8Array(v); + } + if (v instanceof ExpoBlob || isTypedArray(v)) { + return v; + } + return String(v); + }; - if (blobParts === undefined) { - super([], opt); - } else if (blobParts === null || !(blobParts instanceof Object)) { - throw TypeError(); - } else { - super([...blobParts].flat(Infinity).map(inputMapping), opt); - } - } + if (blobParts === undefined) { + super([], getOptions(options)); + } else if (blobParts === null || !(blobParts instanceof Object)) { + throw TypeError(); + } else { + super([...blobParts].map(inputMapping), getOptions(options)); + } + } - slice(start?: number, end?: number, contentType?: string): ExpoBlob { - const normalizedType = normalizedContentType(contentType); - const slicedBlob = super.slice(start, end, normalizedType); - Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); - return slicedBlob; - } + slice(start?: number, end?: number, contentType?: string): ExpoBlob { + const normalizedType = normalizedContentType(contentType); + const slicedBlob = super.slice(start, end, normalizedType); + Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype); + return slicedBlob; + } - stream(): ReadableStream { - const uint8promise = super.bytes(); - let offset = 0; - return new ReadableStream({ - async pull(controller) { - let uint8 = await uint8promise - if (offset < uint8.length) { - controller.enqueue(uint8.subarray(offset)); - offset = uint8.length; - } else { - controller.close(); - } - }, - }); - } + stream(): ReadableStream { + const uint8promise = super.bytes(); + let offset = 0; + return new ReadableStream({ + async pull(controller) { + let uint8 = await uint8promise; + if (offset < uint8.length) { + controller.enqueue(uint8.subarray(offset)); + offset = uint8.length; + } else { + controller.close(); + } + }, + }); + } - async arrayBuffer(): Promise { - return super.bytes().then((bytes: Uint8Array) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)); - } + async arrayBuffer(): Promise { + return super + .bytes() + .then((bytes: Uint8Array) => + bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) + ); + } - toString(): string { - return "[object Blob]" - } + toString(): string { + return '[object Blob]'; + } } From 218ca6165bad71d00a880abf852b9055c9044acb Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 16:22:19 +0200 Subject: [PATCH 21/41] blob options fix --- apps/test-suite/tests/Blob.ts | 7 +++++++ packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 20 ++++++++++++-------- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 20 +++++++++++++------- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index a40b32debb753a..a22391814b5143 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -776,6 +776,9 @@ export async function test({ describe, it, expect }) { accessed.push('endings'); }, }); + + console.log('GREPMEME'); + console.log(accessed, stringified, 'GREPMEME'); new Blob([], { // @ts-ignore type: { @@ -792,6 +795,10 @@ export async function test({ describe, it, expect }) { }, }, }); + + console.log('GREPMEME'); + console.log(accessed, stringified, 'GREPMEME'); + expect(accessed).toEqual(['endings', 'type']); expect(stringified).toEqual(['endings', 'type']); }); diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index f06aee586e63b2..063cd616c16b95 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAmCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 7a1b74e436242f..2c10847694b3d7 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -14,20 +14,24 @@ const isTypedArray = (v) => { v instanceof Float64Array); }; const getOptions = (options) => { - let opt; if (options) { if (!(options instanceof Object)) { throw TypeError(); } - opt = { - endings: options.endings, - type: options.type === undefined ? '' : normalizedContentType(options.type), + let e = options.endings; + let t = options.type; + if (e && typeof e === 'object') { + e = String(e); + } + if (t && typeof t === 'object') { + t = String(t); + } + return { + endings: e, + type: normalizedContentType(t), }; } - else { - opt = options; - } - return opt; + return options; }; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 003a50114a4d1f..409228dc333116 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n let opt;\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n opt = {\n endings: options.endings,\n type: options.type === undefined ? '' : normalizedContentType(options.type),\n };\n } else {\n opt = options;\n }\n\n return opt;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || !(blobParts instanceof Object)) {\n throw TypeError();\n } else {\n super([...blobParts].map(inputMapping), getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || !(blobParts instanceof Object)) {\n throw TypeError();\n } else {\n super([...blobParts].map(inputMapping), getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 2ad4db6f2d2e6f..076d5c9b5e2518 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -33,21 +33,27 @@ const isTypedArray = (v: any): boolean => { }; const getOptions = (options?: BlobPropertyBag) => { - let opt; if (options) { if (!(options instanceof Object)) { throw TypeError(); } - opt = { - endings: options.endings, - type: options.type === undefined ? '' : normalizedContentType(options.type), + let e = options.endings; + let t = options.type; + if (e && typeof e === 'object') { + e = String(e); + } + if (t && typeof t === 'object') { + t = String(t); + } + + return { + endings: e, + type: normalizedContentType(t), }; - } else { - opt = options; } - return opt; + return options; }; export class ExpoBlob extends NativeBlobModule.Blob implements Blob { From 82e6c236df4e579cec29cf49db99b4323fd00fd6 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Tue, 22 Jul 2025 16:24:48 +0200 Subject: [PATCH 22/41] removed debug logs --- apps/test-suite/tests/Blob.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index a22391814b5143..6e223fdabdde5e 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -143,8 +143,6 @@ export async function test({ describe, it, expect }) { // expect(new Uint8Array(array_buffer)).toEqual(typed_arr); expect(blob.size).toBe(5); expect(array_buffer.length).toBe(5); - console.log(array_buffer.byteOffset); - console.log(array_buffer, typed_arr); expect(array_buffer).toEqual(typed_arr); }); it('concurrent reads', async () => { @@ -473,16 +471,6 @@ export async function test({ describe, it, expect }) { }; expect(() => new Blob(obj)).toThrow(test_error); - console.log(); - console.log(); - console.log(); - console.log('received: ' + received); - console.log(); - console.log('expected: ' + expect); - console.log(); - console.log(); - console.log(); - // Somehow we don't call 0 toString but I don't know why not or why would we expect(received).toEqual([ 'Symbol.iterator', @@ -777,8 +765,6 @@ export async function test({ describe, it, expect }) { }, }); - console.log('GREPMEME'); - console.log(accessed, stringified, 'GREPMEME'); new Blob([], { // @ts-ignore type: { @@ -796,9 +782,6 @@ export async function test({ describe, it, expect }) { }, }); - console.log('GREPMEME'); - console.log(accessed, stringified, 'GREPMEME'); - expect(accessed).toEqual(['endings', 'type']); expect(stringified).toEqual(['endings', 'type']); }); From 416d3ca4f8fc63fa160644f334f2e6aff8e1200d Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 08:09:40 +0200 Subject: [PATCH 23/41] found the proper way to iterate over blobParts in the constructor --- apps/test-suite/tests/Blob.ts | 4 ++++ packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 11 +++++++++-- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 12 ++++++++++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 6e223fdabdde5e..df470c6430589c 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -435,6 +435,8 @@ export async function test({ describe, it, expect }) { }).toThrow(test_error); }); it('Getters and value conversions should happen in order until an exception is thrown.', () => { + console.log('GREPME START'); + var received = []; var obj = { get [Symbol.iterator]() { @@ -471,6 +473,8 @@ export async function test({ describe, it, expect }) { }; expect(() => new Blob(obj)).toThrow(test_error); + console.log(received); + console.log('GREPME END'); // Somehow we don't call 0 toString but I don't know why not or why would we expect(received).toEqual([ 'Symbol.iterator', diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 063cd616c16b95..f5e30a0434a1a9 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAoBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 2c10847694b3d7..11cb06ca5b9604 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -37,21 +37,28 @@ export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { const inputMapping = (v) => { if (v instanceof ArrayBuffer) { + console.log('AB'); return new Uint8Array(v); } if (v instanceof ExpoBlob || isTypedArray(v)) { + console.log('Blob | TypedArray'); return v; } + console.log('to String'); return String(v); }; + let bps = []; if (blobParts === undefined) { super([], getOptions(options)); } - else if (blobParts === null || !(blobParts instanceof Object)) { + else if (blobParts === null || typeof blobParts !== 'object') { throw TypeError(); } else { - super([...blobParts].map(inputMapping), getOptions(options)); + for (let bp of blobParts) { + bps.push(inputMapping(bp)); + } + super(bps, getOptions(options)); } } slice(start, end, contentType) { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 409228dc333116..8445e6a4c2be82 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,YAAY,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,OAAO,IAAI,cAAc,CAAa;YACpC,KAAK,CAAC,IAAI,CAAC,UAAU;gBACnB,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC;gBAC/B,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC1B,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3C,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || !(blobParts instanceof Object)) {\n throw TypeError();\n } else {\n super([...blobParts].map(inputMapping), getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 076d5c9b5e2518..83cace48f8db46 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -60,20 +60,28 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { const inputMapping = (v: any) => { if (v instanceof ArrayBuffer) { + console.log('AB'); return new Uint8Array(v); } if (v instanceof ExpoBlob || isTypedArray(v)) { + console.log('Blob | TypedArray'); return v; } + console.log('to String'); return String(v); }; + let bps: any[] = []; + if (blobParts === undefined) { super([], getOptions(options)); - } else if (blobParts === null || !(blobParts instanceof Object)) { + } else if (blobParts === null || typeof blobParts !== 'object') { throw TypeError(); } else { - super([...blobParts].map(inputMapping), getOptions(options)); + for (let bp of blobParts) { + bps.push(inputMapping(bp)); + } + super(bps, getOptions(options)); } } From e9f98baa4b61446858af76702d05d10fe7b885d4 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 08:42:25 +0200 Subject: [PATCH 24/41] small stream fix, removed todos and console logs --- apps/test-suite/tests/Blob.ts | 34 ++++++++++---------- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 16 +++++---- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 18 +++++++---- 5 files changed, 39 insertions(+), 33 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index df470c6430589c..2e1655e9a9ca55 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -50,7 +50,9 @@ export async function test({ describe, it, expect }) { // if perform_gc is true. const read_and_gc = async (reader, perform_gc) => { // Passing Uint8Array for byte streams; non-byte streams will simply ignore it + console.log('before reader read'); const read_promise = reader.read(new Uint8Array(64)); + console.log('after reader read'); if (perform_gc) { // TODO Actually perform garbage collection in here // await garbageCollect(); @@ -72,10 +74,14 @@ export async function test({ describe, it, expect }) { let out = []; let i = 0; while (!read_value.done) { + console.log('while'); for (let val of read_value.value) { out[i++] = val; + console.log('for'); } + console.log('finished for'); read_value = await read_and_gc(reader, perform_gc); + console.log('read_value after for'); } return out; }; @@ -314,7 +320,7 @@ export async function test({ describe, it, expect }) { expect(blob.size).toBe(0); expect(blob.type).toBe(''); }); - // TODO Something wrong with null ? Why should Blob() work and Blob(null) not ??? + it('Passing non-objects, Dates and RegExps for blobParts should throw a TypeError.', () => { [ null, @@ -435,8 +441,6 @@ export async function test({ describe, it, expect }) { }).toThrow(test_error); }); it('Getters and value conversions should happen in order until an exception is thrown.', () => { - console.log('GREPME START'); - var received = []; var obj = { get [Symbol.iterator]() { @@ -473,9 +477,6 @@ export async function test({ describe, it, expect }) { }; expect(() => new Blob(obj)).toThrow(test_error); - console.log(received); - console.log('GREPME END'); - // Somehow we don't call 0 toString but I don't know why not or why would we expect(received).toEqual([ 'Symbol.iterator', 'length getter', @@ -522,8 +523,13 @@ export async function test({ describe, it, expect }) { }, ]) ).toThrow(test_error); - // TODO add the proper TypeError type to toThrow - expect(() => new Blob([{ toString: null, valueOf: null }])).toThrow(); + + try { + new Blob([{ toString: null, valueOf: null }]); + expect('NOT REACHABLE').toBe(''); + } catch (e) { + expect(e instanceof TypeError).toBeTruthy(); + } }); test_blob( function () { @@ -740,7 +746,6 @@ export async function test({ describe, it, expect }) { } ); - // TODO revisit this, why can we pass view but not the buffer? test_blob( function () { var view = new Uint8Array([0]); @@ -883,11 +888,8 @@ export async function test({ describe, it, expect }) { type_tests.forEach(function (t: Array) { it('Blob with type ' + JSON.stringify(t[1]), () => { - // TODO Why this construction? It does'nt work yet, but it's not what we test for... - // var arr = new Uint8Array([t[0]]).buffer; - // var b = new Blob([arr], {type:t[1]}); - - var b = new Blob(t[0], { type: t[1] }); + var arr = new Uint8Array([t[0]]).buffer; + var b = new Blob([arr], { type: t[1] }); expect(b.type).toEqual(t[2]); }); }); @@ -1324,9 +1326,7 @@ export async function test({ describe, it, expect }) { }); it('stream xhr crash', async () => { - // TODO this constructor doesn't work need to fix it - // const blob = new Blob([1, 2]); - const blob = new Blob([Int32Array.from([1, 2])]); + const blob = new Blob([1, 2]); const readable = blob.stream(); const writable = new WritableStream( {}, diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index f5e30a0434a1a9..92de0f829c3b0c 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAgBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAoBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 11cb06ca5b9604..f88a5ae8be3001 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -69,18 +69,20 @@ export class ExpoBlob extends NativeBlobModule.Blob { } stream() { const uint8promise = super.bytes(); - let offset = 0; return new ReadableStream({ - async pull(controller) { - let uint8 = await uint8promise; - if (offset < uint8.length) { - controller.enqueue(uint8.subarray(offset)); - offset = uint8.length; + type: 'bytes', + async start(controller) { + let bytes = await uint8promise; + if (bytes.length == 0) { + controller.close(); } else { - controller.close(); + controller.enqueue(bytes); } }, + async pull(controller) { + controller.close(); + }, }); } async arrayBuffer() { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 8445e6a4c2be82..c1aaa3e62b8dc6 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n let offset = 0;\n return new ReadableStream({\n async pull(controller) {\n let uint8 = await uint8promise;\n if (offset < uint8.length) {\n controller.enqueue(uint8.subarray(offset));\n offset = uint8.length;\n } else {\n controller.close();\n }\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n return new ReadableStream({\n type: 'bytes',\n\n async start(controller) {\n let bytes = await uint8promise;\n if (bytes.length == 0) {\n controller.close();\n } else {\n controller.enqueue(bytes);\n }\n },\n\n async pull(controller) {\n controller.close();\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 83cace48f8db46..9273e99f85163f 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -94,17 +94,21 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { stream(): ReadableStream { const uint8promise = super.bytes(); - let offset = 0; return new ReadableStream({ - async pull(controller) { - let uint8 = await uint8promise; - if (offset < uint8.length) { - controller.enqueue(uint8.subarray(offset)); - offset = uint8.length; - } else { + type: 'bytes', + + async start(controller) { + let bytes = await uint8promise; + if (bytes.length == 0) { controller.close(); + } else { + controller.enqueue(bytes); } }, + + async pull(controller) { + controller.close(); + }, }); } From 04991810dfe7c4b5cbb5093420d1e83791cfa8bf Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 09:46:11 +0200 Subject: [PATCH 25/41] Changed Expo Blob length to match that of the default Blob --- packages/expo-blob/build/BlobModule.js | 4 ++++ packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index f88a5ae8be3001..4b62305dbe31ab 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -94,4 +94,8 @@ export class ExpoBlob extends NativeBlobModule.Blob { return '[object Blob]'; } } +Object.defineProperty(ExpoBlob, 'length', { + value: 0, + writable: false, +}); //# sourceMappingURL=BlobModule.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index c1aaa3e62b8dc6..237c71dc75e55c 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAClB,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBACjC,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACzB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,GAAG,GAAU,EAAE,CAAC;QAEpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;YACD,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,OAAO,IAAI,cAAc,CAAa;YACpC,IAAI,EAAE,OAAO;YAEb,KAAK,CAAC,KAAK,CAAC,UAAU;gBACpB,IAAI,KAAK,GAAG,MAAM,YAAY,CAAC;gBAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBACtB,UAAU,CAAC,KAAK,EAAE,CAAC;gBACrB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,UAAU;gBACnB,UAAU,CAAC,KAAK,EAAE,CAAC;YACrB,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n return new ReadableStream({\n type: 'bytes',\n\n async start(controller) {\n let bytes = await uint8promise;\n if (bytes.length == 0) {\n controller.close();\n } else {\n controller.enqueue(bytes);\n }\n },\n\n async pull(controller) {\n controller.close();\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n return new ReadableStream({\n type: 'bytes',\n\n async start(controller) {\n let bytes = await uint8promise;\n if (bytes.length == 0) {\n controller.close();\n } else {\n controller.enqueue(bytes);\n }\n },\n\n async pull(controller) {\n controller.close();\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 9273e99f85163f..9ec31a5d6055d8 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -124,3 +124,8 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { return '[object Blob]'; } } + +Object.defineProperty(ExpoBlob, 'length', { + value: 0, + writable: false, +}); From bb7df981abcd6247edc138ca14f5e3d80798cc7c Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 10:15:19 +0200 Subject: [PATCH 26/41] stream fixed, removed todos (performing garbage collection in stream tests now) --- apps/test-suite/tests/Blob.ts | 12 ++---- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 34 ++++++++++++----- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 40 ++++++++++++++------ 5 files changed, 60 insertions(+), 30 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 63c11e5bcbed09..543b6d190871bc 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -52,8 +52,7 @@ export async function test({ describe, it, expect }) { // Passing Uint8Array for byte streams; non-byte streams will simply ignore it const read_promise = reader.read(new Uint8Array(64)); if (perform_gc) { - // TODO Actually perform garbage collection in here - // await garbageCollect(); + gc(); } return read_promise; }; @@ -227,7 +226,7 @@ export async function test({ describe, it, expect }) { }) ).toThrow(test_error); }); - // TODO weird test, as it could maybe be used lazily and not in the constructor + it("The 'endings' options property is used", () => { let got = false; // @ts-expect-error @@ -1366,8 +1365,7 @@ export async function test({ describe, it, expect }) { let blob = new Blob([typed_arr]); const stream = blob.stream(); blob = null; - // TODO Actually call garbageCollect() - // await garbageCollect(); + gc(); const chunks = await read_all_chunks(stream, { perform_gc: true }); expect(chunks).toEqual(input_arr); } @@ -1380,9 +1378,7 @@ export async function test({ describe, it, expect }) { const typed_arr = new Uint8Array(input_arr); let blob = new Blob([typed_arr]); const chunksPromise = read_all_chunks(blob.stream()); - // It somehow matters to do GC here instead of doing `perform_gc: true` - // TODO Actually call garbageCollect() - // await garbageCollect(); + gc(); expect(await chunksPromise).toEqual(input_arr); } ); diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 92de0f829c3b0c..c23d995a8ae43d 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAoBlB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 4b62305dbe31ab..85a0810612c86a 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -68,20 +68,36 @@ export class ExpoBlob extends NativeBlobModule.Blob { return slicedBlob; } stream() { - const uint8promise = super.bytes(); + const self = this; + let offset = 0; + let bytesPromise = null; return new ReadableStream({ type: 'bytes', - async start(controller) { - let bytes = await uint8promise; - if (bytes.length == 0) { + async pull(controller) { + if (!bytesPromise) { + bytesPromise = self.bytes(); + } + const bytes = await bytesPromise; + if (offset >= bytes.length) { controller.close(); + return; } - else { - controller.enqueue(bytes); + if (controller.byobRequest?.view) { + const view = controller.byobRequest.view; + const end = Math.min(offset + view.byteLength, bytes.length); + const chunk = bytes.subarray(offset, end); + view.set(chunk, 0); + controller.byobRequest.respond(chunk.length); + offset = end; + if (offset >= bytes.length) { + controller.close(); + } + return; } - }, - async pull(controller) { - controller.close(); + const chunkSize = 65_536; + const end = Math.min(offset + chunkSize, bytes.length); + controller.enqueue(bytes.subarray(offset, end)); + offset = end; }, }); } diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 237c71dc75e55c..a3afdf56bfe3df 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const uint8promise = super.bytes();\n return new ReadableStream({\n type: 'bytes',\n\n async start(controller) {\n let bytes = await uint8promise;\n if (bytes.length == 0) {\n controller.close();\n } else {\n controller.enqueue(bytes);\n }\n },\n\n async pull(controller) {\n controller.close();\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 9ec31a5d6055d8..75d73495d44121 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -93,21 +93,39 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } stream(): ReadableStream { - const uint8promise = super.bytes(); - return new ReadableStream({ - type: 'bytes', + const self = this; + let offset = 0; + let bytesPromise: Promise | null = null; - async start(controller) { - let bytes = await uint8promise; - if (bytes.length == 0) { + return new ReadableStream({ + type: 'bytes', + async pull(controller: any) { + if (!bytesPromise) { + bytesPromise = self.bytes(); + } + const bytes = await bytesPromise; + if (offset >= bytes.length) { controller.close(); - } else { - controller.enqueue(bytes); + return; + } + + if (controller.byobRequest?.view) { + const view = controller.byobRequest.view; + const end = Math.min(offset + view.byteLength, bytes.length); + const chunk = bytes.subarray(offset, end); + view.set(chunk, 0); + controller.byobRequest.respond(chunk.length); + offset = end; + if (offset >= bytes.length) { + controller.close(); + } + return; } - }, - async pull(controller) { - controller.close(); + const chunkSize = 65_536; + const end = Math.min(offset + chunkSize, bytes.length); + controller.enqueue(bytes.subarray(offset, end)); + offset = end; }, }); } From 4e81b3d92d0da05c48417d037a7ed1c2a56f58da Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 10:29:43 +0200 Subject: [PATCH 27/41] removed debugging logs, added ts-expect-error where necessary, added more type infromation --- apps/test-suite/tests/Blob.ts | 13 ++++++++++--- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 3 --- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 3 --- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 543b6d190871bc..dd16581a7ecc44 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -60,7 +60,10 @@ export async function test({ describe, it, expect }) { // Takes in a ReadableStream and reads from it until it is done, returning // an array that contains the results of each read operation. If perform_gc // is true, garbage collection is triggered while reading every chunk. - const read_all_chunks = async (stream, { perform_gc = false, mode } = {}) => { + const read_all_chunks = async ( + stream, + { perform_gc = false, mode }: { perform_gc?: boolean; mode?: string } = {} + ) => { expect(stream instanceof ReadableStream).toBeTruthy(); expect('getReader' in stream).toBeTruthy(); const reader = stream.getReader({ mode }); @@ -216,10 +219,10 @@ export async function test({ describe, it, expect }) { }); it('Exception propagation from options', () => { const test_error = { name: 'test string' }; - // @ts-expect-error expect( () => new Blob([], { + // @ts-expect-error get endings() { throw test_error; }, @@ -229,8 +232,8 @@ export async function test({ describe, it, expect }) { it("The 'endings' options property is used", () => { let got = false; - // @ts-expect-error new Blob([], { + // @ts-expect-error get endings() { got = true; }, @@ -297,6 +300,7 @@ export async function test({ describe, it, expect }) { }); it("Blob constructor with no arguments, without 'new'", () => { expect(() => { + // @ts-ignore var blob = Blob(); }).toThrow(); }); @@ -352,6 +356,7 @@ export async function test({ describe, it, expect }) { ); it('A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.', () => { const blob = new Blob({ + // @ts-ignore [Symbol.iterator]() { var i = 0; return { @@ -551,6 +556,7 @@ export async function test({ describe, it, expect }) { return 'A'; } arr.unshift({ + // @ts-ignore toString: function () { expect(true).toBe(false); }, @@ -1322,6 +1328,7 @@ export async function test({ describe, it, expect }) { const writable = new WritableStream( {}, { + // @ts-ignore size() { let xhr = new XMLHttpRequest(); xhr.open('POST', '1', false); diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index c23d995a8ae43d..3dadc4695b70be 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IA4BxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAyBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 85a0810612c86a..a60b7efa675b1f 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -37,14 +37,11 @@ export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { const inputMapping = (v) => { if (v instanceof ArrayBuffer) { - console.log('AB'); return new Uint8Array(v); } if (v instanceof ExpoBlob || isTypedArray(v)) { - console.log('Blob | TypedArray'); return v; } - console.log('to String'); return String(v); }; let bps = []; diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index a3afdf56bfe3df..47fe0a9edd37ca 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n console.log('AB');\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n console.log('Blob | TypedArray');\n return v;\n }\n console.log('to String');\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAehD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,GAAG,GAAU,EAAE,CAAC;QAEpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;YACD,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,YAAY,GAA+B,IAAI,CAAC;QAEpD,OAAO,IAAI,cAAc,CAAC;YACxB,IAAI,EAAE,OAAO;YACb,KAAK,CAAC,IAAI,CAAC,UAAe;gBACxB,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC;gBACjC,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,IAAI,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;oBACjC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC;oBACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBAC1C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBACnB,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7C,MAAM,GAAG,GAAG,CAAC;oBACb,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACrB,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,MAAM,SAAS,GAAG,MAAM,CAAC;gBACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACvD,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAChD,MAAM,GAAG,GAAG,CAAC;YACf,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE;IACxC,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,KAAK;CAChB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 75d73495d44121..89a016808ce0ad 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -60,14 +60,11 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { const inputMapping = (v: any) => { if (v instanceof ArrayBuffer) { - console.log('AB'); return new Uint8Array(v); } if (v instanceof ExpoBlob || isTypedArray(v)) { - console.log('Blob | TypedArray'); return v; } - console.log('to String'); return String(v); }; From e9504c2a93a6060bc4916c30639517a16e3bddb2 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 11:51:05 +0200 Subject: [PATCH 28/41] fixed some tests to be more inline with the wpt tests, removed syncText method --- apps/test-suite/tests/Blob.ts | 86 +++++++++---------- .../main/java/expo/modules/blob/BlobModule.kt | 4 - packages/expo-blob/build/BlobModule.d.ts | 1 - packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 1 - 6 files changed, 42 insertions(+), 54 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index dd16581a7ecc44..c779603a7194f4 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -24,9 +24,7 @@ export async function test({ describe, it, expect }) { expect(blob.size).toBe(expected.length); const text = await blob.text(); - const text1 = blob.syncText(); expect(text).toEqual(expected); - expect(text).toEqual(text1); }); }; const test_blob_binary = async (fn, expectations) => { @@ -138,14 +136,9 @@ export async function test({ describe, it, expect }) { const input_arr = [8, 241, 48, 123, 151]; const typed_arr = new Uint8Array(input_arr); const blob = new Blob([typed_arr]); - // const array_buffer = await blob.arrayBuffer(); - const array_buffer = await blob.bytes(); - // expect(blob.syncText()).toEqual("\u0008\u00F1\u0030\u007B\u0097") - // expect(new Uint8Array(array_buffer) == typed_arr).toBeTruthy() - // expect(new Uint8Array(array_buffer)).toEqual(typed_arr); + const array_buffer = await blob.arrayBuffer(); expect(blob.size).toBe(5); - expect(array_buffer.length).toBe(5); - expect(array_buffer).toEqual(typed_arr); + expect(new Uint8Array(array_buffer)).toEqual(new Uint8Array(input_arr)); }); it('concurrent reads', async () => { const input_arr = new TextEncoder().encode('PASS'); @@ -202,7 +195,7 @@ export async function test({ describe, it, expect }) { }); // Windows platforms use CRLF as the native line ending. All others use LF. - const crlf = Platform.OS == 'windows'; + const crlf = Platform.OS === 'windows'; const native_ending = crlf ? '\r\n' : '\n'; describe('constructor-endings', async () => { it('valid endings value', () => { @@ -211,7 +204,7 @@ export async function test({ describe, it, expect }) { expect(blob0).toBeTruthy(); expect(blob1).toBeTruthy(); }); - it('invalud endings value', () => { + it('Invalid endings value', () => { [null, '', 'invalidEnumValue', 'Transparent', 'NATIVE', 0, {}].forEach((ending) => { // @ts-expect-error expect(() => new Blob([], { endings: ending })).toThrow(); @@ -256,28 +249,28 @@ export async function test({ describe, it, expect }) { { name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2) }, { name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3) }, ]; - it('Newlines should not change with endings unspecified', () => { - sampleEndings.forEach((testCase) => { + it('Newlines should not change with endings unspecified', async () => { + sampleEndings.forEach(async (testCase) => { const blob = new Blob([testCase.input]); - expect(blob.syncText()).toBe(testCase.input); + expect(await blob.text()).toBe(testCase.input); }); }); - it('Newlines should not change with endings "transparent"', () => { - sampleEndings.forEach((testCase) => { + it('Newlines should not change with endings "transparent"', async () => { + sampleEndings.forEach(async (testCase) => { const blob = new Blob([testCase.input], { endings: 'transparent' }); - expect(blob.syncText()).toBe(testCase.input); + expect(await blob.text()).toBe(testCase.input); }); }); - it('Newlines should match the platform with endings "native"', () => { - sampleEndings.forEach((testCase) => { + it('Newlines should match the platform with endings "native"', async () => { + sampleEndings.forEach(async (testCase) => { const blob = new Blob([testCase.input], { endings: 'native' }); - expect(blob.syncText()).toBe(testCase.native); + expect(await blob.text()).toBe(testCase.native); }); }); - it('CR/LF in adjacent input strings', () => { + it('CR/LF in adjacent input strings', async () => { const blob = new Blob(['\r', '\n'], { endings: 'native' }); const expected = native_ending.repeat(2); - expect(blob.syncText()).toBe(expected); + expect(await blob.text()).toBe(expected); }); }); @@ -367,20 +360,20 @@ export async function test({ describe, it, expect }) { }); expect(blob.size).toBe(5); }); - it('A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument.', () => { - let blob = new Blob({ - [Symbol.iterator]: Array.prototype[Symbol.iterator], - 0: 'PASS', - length: 1, - }); - expect(blob.syncText()).toEqual('PASS'); - expect(blob.type).toBe(''); - }); - it('A String object should be treated as a sequence for the blobParts argument.', () => { - let blob = new Blob(new String('xyz')); - expect(blob.syncText()).toEqual('xyz'); - expect(blob.type).toBe(''); - }); + test_blob( + function () { + return new Blob({ + [Symbol.iterator]: Array.prototype[Symbol.iterator], + 0: 'PASS', + length: 1, + }); + }, + { + expected: 'PASS', + type: '', + desc: 'A plain object with @@iterator and a length property should be treated as a sequence for the blobParts argument.', + } + ); test_blob( function () { return new Blob(new String('xyz')); @@ -515,7 +508,7 @@ export async function test({ describe, it, expect }) { throw test_error; }, valueOf: function () { - expect(false).toBe(true); + expect('Should not call valueOf if toString is present.').toBe(''); }, }, ]) @@ -537,7 +530,13 @@ export async function test({ describe, it, expect }) { return 'PASS'; }, }, - { toString: function () {} }, + { + toString: function () { + expect( + 'Should have removed the second element of the array rather than called toString() on it.' + ).toBe(''); + }, + }, ]; return new Blob(arr); }, @@ -558,7 +557,7 @@ export async function test({ describe, it, expect }) { arr.unshift({ // @ts-ignore toString: function () { - expect(true).toBe(false); + expect('Should only access index 0 once.').toBe(''); }, }); return 'P'; @@ -606,7 +605,7 @@ export async function test({ describe, it, expect }) { }, { valueOf: function () { - expect(false).toBe(true); + expect('Should not call valueOf if toString is present on the prototype.').toBe(''); }, }, ]); @@ -964,11 +963,6 @@ export async function test({ describe, it, expect }) { const text = await blob.text(); expect(text).toBe('PASS'); }); - it('Sync Text', () => { - const blob = new Blob(['PA', 'SS']); - const text = blob.syncText(); - expect(text).toBe('PASS'); - }); it('different charset param with non-ascii input', async () => { const non_unicode = '\u0061\u030A'; const input_arr = new TextEncoder().encode(non_unicode); @@ -986,7 +980,7 @@ export async function test({ describe, it, expect }) { '\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd' ); }); - it('Promise.all multiple reads', async () => { + it('Blob.text() concurrent reads', async () => { const input_arr = new Uint8Array([ 192, 193, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, ]); diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt index 7609ebc983e180..1b283908f35ce4 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/BlobModule.kt @@ -47,10 +47,6 @@ class BlobModule : Module() { AsyncFunction("text") { blob: Blob -> blob.text() } - - Function("syncText") { blob: Blob -> - blob.text() - } } } } diff --git a/packages/expo-blob/build/BlobModule.d.ts b/packages/expo-blob/build/BlobModule.d.ts index be6c4133cfd42c..610ddfa73e6a59 100644 --- a/packages/expo-blob/build/BlobModule.d.ts +++ b/packages/expo-blob/build/BlobModule.d.ts @@ -7,7 +7,6 @@ declare class NativeBlob extends SharedObject { slice(start?: number, end?: number, contentType?: string): ExpoBlob; bytes(): Promise; text(): Promise; - syncText(): string; } declare class ExpoBlobModule extends NativeModule { Blob: typeof NativeBlob; diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 3dadc4695b70be..1cfa817e52bf19 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB,QAAQ,IAAI,MAAM;CACnB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAyBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CACxB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAyBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 47fe0a9edd37ca..34fc83e4ccce81 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n syncText(): string;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAchD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,GAAG,GAAU,EAAE,CAAC;QAEpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;YACD,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,YAAY,GAA+B,IAAI,CAAC;QAEpD,OAAO,IAAI,cAAc,CAAC;YACxB,IAAI,EAAE,OAAO;YACb,KAAK,CAAC,IAAI,CAAC,UAAe;gBACxB,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC;gBACjC,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,IAAI,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;oBACjC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC;oBACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBAC1C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBACnB,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7C,MAAM,GAAG,GAAG,CAAC;oBACb,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACrB,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,MAAM,SAAS,GAAG,MAAM,CAAC;gBACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACvD,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAChD,MAAM,GAAG,GAAG,CAAC;YACf,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE;IACxC,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,KAAK;CAChB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index 89a016808ce0ad..a552008ce9dd48 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -8,7 +8,6 @@ declare class NativeBlob extends SharedObject { slice(start?: number, end?: number, contentType?: string): ExpoBlob; bytes(): Promise; text(): Promise; - syncText(): string; } declare class ExpoBlobModule extends NativeModule { From c726574fecf056167e43f68e66b9f50b3004d29a Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 13:33:00 +0200 Subject: [PATCH 29/41] now throwing TypeError on invalid options.endings, refactor --- apps/test-suite/tests/Blob.ts | 8 ++- packages/expo-blob/build/BlobModule.d.ts.map | 2 +- packages/expo-blob/build/BlobModule.js | 38 ++----------- packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/build/utils.d.ts | 14 ++++- packages/expo-blob/build/utils.d.ts.map | 2 +- packages/expo-blob/build/utils.js | 51 ++++++++++++++++-- packages/expo-blob/build/utils.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 46 ++-------------- packages/expo-blob/src/utils.ts | 57 ++++++++++++++++++-- 10 files changed, 128 insertions(+), 94 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index c779603a7194f4..ffe1c0c48e65a7 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -206,8 +206,12 @@ export async function test({ describe, it, expect }) { }); it('Invalid endings value', () => { [null, '', 'invalidEnumValue', 'Transparent', 'NATIVE', 0, {}].forEach((ending) => { - // @ts-expect-error - expect(() => new Blob([], { endings: ending })).toThrow(); + try { + // @ts-expect-error + new Blob([], { endings: ending }); + } catch (e) { + expect(e instanceof TypeError).toBeTruthy(); + } }); }); it('Exception propagation from options', () => { diff --git a/packages/expo-blob/build/BlobModule.d.ts.map b/packages/expo-blob/build/BlobModule.d.ts.map index 1cfa817e52bf19..78e473c4f1a334 100644 --- a/packages/expo-blob/build/BlobModule.d.ts.map +++ b/packages/expo-blob/build/BlobModule.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CACxB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAyCzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAyBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file +{"version":3,"file":"BlobModule.d.ts","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,YAAY,EAAE,MAAM,MAAM,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGpD,OAAO,OAAO,UAAW,SAAQ,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAC7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IACnE,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CACxB;AAED,OAAO,OAAO,cAAe,SAAQ,YAAY;IAC/C,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,QAAS,SAAQ,gBAAgB,CAAC,IAAK,YAAW,IAAI;gBACrD,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAyBxE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOnE,MAAM,IAAI,cAAc;IAsClB,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAQ7C,QAAQ,IAAI,MAAM;CAGnB"} \ No newline at end of file diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index a60b7efa675b1f..5de558ce3f9997 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -1,38 +1,6 @@ import { requireNativeModule } from 'expo'; -import { normalizedContentType } from './utils'; +import { isTypedArray, normalizedContentType, preprocessOptions } from './utils'; const NativeBlobModule = requireNativeModule('ExpoBlob'); -const isTypedArray = (v) => { - return (v instanceof Int16Array || - v instanceof Int32Array || - v instanceof Int8Array || - v instanceof BigInt64Array || - v instanceof BigUint64Array || - v instanceof Uint16Array || - v instanceof Uint32Array || - v instanceof Uint8Array || - v instanceof Float32Array || - v instanceof Float64Array); -}; -const getOptions = (options) => { - if (options) { - if (!(options instanceof Object)) { - throw TypeError(); - } - let e = options.endings; - let t = options.type; - if (e && typeof e === 'object') { - e = String(e); - } - if (t && typeof t === 'object') { - t = String(t); - } - return { - endings: e, - type: normalizedContentType(t), - }; - } - return options; -}; export class ExpoBlob extends NativeBlobModule.Blob { constructor(blobParts, options) { const inputMapping = (v) => { @@ -46,7 +14,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { }; let bps = []; if (blobParts === undefined) { - super([], getOptions(options)); + super([], preprocessOptions(options)); } else if (blobParts === null || typeof blobParts !== 'object') { throw TypeError(); @@ -55,7 +23,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { for (let bp of blobParts) { bps.push(inputMapping(bp)); } - super(bps, getOptions(options)); + super(bps, preprocessOptions(options)); } } slice(start, end, contentType) { diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index 34fc83e4ccce81..ef986b40ab7005 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAgB,MAAM,MAAM,CAAC;AAEvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAchD,MAAM,gBAAgB,GAAG,mBAAmB,CAAiB,UAAU,CAAC,CAAC;AAEzE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAW,EAAE;IACvC,OAAO,CACL,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,SAAS;QACtB,CAAC,YAAY,aAAa;QAC1B,CAAC,YAAY,cAAc;QAC3B,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,WAAW;QACxB,CAAC,YAAY,UAAU;QACvB,CAAC,YAAY,YAAY;QACzB,CAAC,YAAY,YAAY,CAC1B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,OAAyB,EAAE,EAAE;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,gBAAgB,CAAC,IAAI;IACjD,YAAY,SAAiC,EAAE,OAAyB;QACtE,MAAM,YAAY,GAAG,CAAC,CAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;gBAC7B,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,YAAY,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,GAAG,GAAU,EAAE,CAAC;QAEpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;gBACzB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;YACD,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAc,EAAE,GAAY,EAAE,WAAoB;QACtD,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC3D,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,IAAI,YAAY,GAA+B,IAAI,CAAC;QAEpD,OAAO,IAAI,cAAc,CAAC;YACxB,IAAI,EAAE,OAAO;YACb,KAAK,CAAC,IAAI,CAAC,UAAe;gBACxB,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,YAAY,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC;gBACjC,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,OAAO;gBACT,CAAC;gBAED,IAAI,UAAU,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;oBACjC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC;oBACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7D,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;oBAC1C,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;oBACnB,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;oBAC7C,MAAM,GAAG,GAAG,CAAC;oBACb,IAAI,MAAM,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;wBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;oBACrB,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,MAAM,SAAS,GAAG,MAAM,CAAC;gBACzB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;gBACvD,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;gBAChD,MAAM,GAAG,GAAG,CAAC;YACf,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,KAAK;aACT,KAAK,EAAE;aACP,IAAI,CAAC,CAAC,KAAiB,EAAE,EAAE,CAC1B,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,CAC1E,CAAC;IACN,CAAC;IAED,QAAQ;QACN,OAAO,eAAe,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE;IACxC,KAAK,EAAE,CAAC;IACR,QAAQ,EAAE,KAAK;CAChB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { normalizedContentType } from './utils';\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nconst isTypedArray = (v: any): boolean => {\n return (\n v instanceof Int16Array ||\n v instanceof Int32Array ||\n v instanceof Int8Array ||\n v instanceof BigInt64Array ||\n v instanceof BigUint64Array ||\n v instanceof Uint16Array ||\n v instanceof Uint32Array ||\n v instanceof Uint8Array ||\n v instanceof Float32Array ||\n v instanceof Float64Array\n );\n};\n\nconst getOptions = (options?: BlobPropertyBag) => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], getOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, getOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { isTypedArray, normalizedContentType, preprocessOptions } from './utils';\n\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], preprocessOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, preprocessOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts b/packages/expo-blob/build/utils.d.ts index a7f581287101ee..8d02494e031ed2 100644 --- a/packages/expo-blob/build/utils.d.ts +++ b/packages/expo-blob/build/utils.d.ts @@ -4,9 +4,8 @@ * Returns the lowercased content type if it is valid, or an empty string otherwise. * * A valid content type: - * - Is not null, undefined, or empty + * - Is not undefined * - Contains only printable ASCII characters (0x20–0x7E) - * - Does not contain forbidden control characters: NUL (\x00), LF (\x0A), or CR (\x0D) * * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type. * @@ -14,4 +13,15 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export declare function normalizedContentType(type?: string): string; +/** + * @param obj The object to check whether it's a Typed Array or not. + * @returns boolean indicating whether the obj is a Typed Array or not. + */ +export declare function isTypedArray(obj: any): boolean; +/** + * Processes the options object and + * @param options + * @returns BlobPropertyBag object + */ +export declare const preprocessOptions: (options?: BlobPropertyBag) => BlobPropertyBag | undefined; //# sourceMappingURL=utils.d.ts.map \ No newline at end of file diff --git a/packages/expo-blob/build/utils.d.ts.map b/packages/expo-blob/build/utils.d.ts.map index ac0bc57022e1cf..6e495637e3f98f 100644 --- a/packages/expo-blob/build/utils.d.ts.map +++ b/packages/expo-blob/build/utils.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM3D"} \ No newline at end of file +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAa9C;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,KAAG,eAAe,GAAG,SAyB/E,CAAC"} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js b/packages/expo-blob/build/utils.js index 6729a56f52e80e..442b45ce90740d 100644 --- a/packages/expo-blob/build/utils.js +++ b/packages/expo-blob/build/utils.js @@ -4,9 +4,8 @@ * Returns the lowercased content type if it is valid, or an empty string otherwise. * * A valid content type: - * - Is not null, undefined, or empty + * - Is not undefined * - Contains only printable ASCII characters (0x20–0x7E) - * - Does not contain forbidden control characters: NUL (\x00), LF (\x0A), or CR (\x0D) * * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type. * @@ -14,12 +13,54 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type) { - if (type === undefined) - return ''; const str = '' + type; const asciiPrintable = /^[\x20-\x7E]+$/; - if (!asciiPrintable.test(str)) + if (type === undefined || !asciiPrintable.test(str)) return ''; return str.toLowerCase(); } +/** + * @param obj The object to check whether it's a Typed Array or not. + * @returns boolean indicating whether the obj is a Typed Array or not. + */ +export function isTypedArray(obj) { + return (obj instanceof Int8Array || + obj instanceof Int16Array || + obj instanceof Int32Array || + obj instanceof BigInt64Array || + obj instanceof Uint8Array || + obj instanceof Uint16Array || + obj instanceof Uint32Array || + obj instanceof BigUint64Array || + obj instanceof Float32Array || + obj instanceof Float64Array); +} +/** + * Processes the options object and + * @param options + * @returns BlobPropertyBag object + */ +export const preprocessOptions = (options) => { + if (options) { + if (!(options instanceof Object)) { + throw TypeError(); + } + let e = options.endings; + let t = options.type; + if (e && typeof e === 'object') { + e = String(e); + } + if (t && typeof t === 'object') { + t = String(t); + } + if (e !== undefined && e !== 'native' && e !== 'transparent') { + throw TypeError(); + } + return { + endings: e, + type: normalizedContentType(t), + }; + } + return options; +}; //# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js.map b/packages/expo-blob/build/utils.js.map index f44a75f70775e1..a4ec45c0d6f324 100644 --- a/packages/expo-blob/build/utils.js.map +++ b/packages/expo-blob/build/utils.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IACjD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not null, undefined, or empty\n * - Contains only printable ASCII characters (0x20–0x7E)\n * - Does not contain forbidden control characters: NUL (\\x00), LF (\\x0A), or CR (\\x0D)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n if (type === undefined) return '';\n const str = '' + type;\n const asciiPrintable = /^[\\x20-\\x7E]+$/;\n if (!asciiPrintable.test(str)) return '';\n return str.toLowerCase();\n}\n"]} \ No newline at end of file +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IACjD,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ;IACnC,OAAO,CACL,GAAG,YAAY,SAAS;QACxB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,aAAa;QAC5B,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,cAAc;QAC7B,GAAG,YAAY,YAAY;QAC3B,GAAG,YAAY,YAAY,CAC5B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAyB,EAA+B,EAAE;IAC1F,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC;YAC7D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not undefined\n * - Contains only printable ASCII characters (0x20–0x7E)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n const str = '' + type;\n const asciiPrintable = /^[\\x20-\\x7E]+$/;\n if (type === undefined || !asciiPrintable.test(str)) return '';\n return str.toLowerCase();\n}\n\n/**\n * @param obj The object to check whether it's a Typed Array or not.\n * @returns boolean indicating whether the obj is a Typed Array or not.\n */\nexport function isTypedArray(obj: any): boolean {\n return (\n obj instanceof Int8Array ||\n obj instanceof Int16Array ||\n obj instanceof Int32Array ||\n obj instanceof BigInt64Array ||\n obj instanceof Uint8Array ||\n obj instanceof Uint16Array ||\n obj instanceof Uint32Array ||\n obj instanceof BigUint64Array ||\n obj instanceof Float32Array ||\n obj instanceof Float64Array\n );\n}\n\n/**\n * Processes the options object and\n * @param options\n * @returns BlobPropertyBag object\n */\nexport const preprocessOptions = (options?: BlobPropertyBag): BlobPropertyBag | undefined => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n if (e !== undefined && e !== 'native' && e !== 'transparent') {\n throw TypeError();\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index a552008ce9dd48..a3f0c7d49e17d0 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -1,6 +1,7 @@ import { NativeModule, requireNativeModule, SharedObject } from 'expo'; import { Blob, BlobPart } from './BlobModule.types'; -import { normalizedContentType } from './utils'; +import { isTypedArray, normalizedContentType, preprocessOptions } from './utils'; + declare class NativeBlob extends SharedObject { readonly size: number; readonly type: string; @@ -16,45 +17,6 @@ declare class ExpoBlobModule extends NativeModule { const NativeBlobModule = requireNativeModule('ExpoBlob'); -const isTypedArray = (v: any): boolean => { - return ( - v instanceof Int16Array || - v instanceof Int32Array || - v instanceof Int8Array || - v instanceof BigInt64Array || - v instanceof BigUint64Array || - v instanceof Uint16Array || - v instanceof Uint32Array || - v instanceof Uint8Array || - v instanceof Float32Array || - v instanceof Float64Array - ); -}; - -const getOptions = (options?: BlobPropertyBag) => { - if (options) { - if (!(options instanceof Object)) { - throw TypeError(); - } - - let e = options.endings; - let t = options.type; - if (e && typeof e === 'object') { - e = String(e); - } - if (t && typeof t === 'object') { - t = String(t); - } - - return { - endings: e, - type: normalizedContentType(t), - }; - } - - return options; -}; - export class ExpoBlob extends NativeBlobModule.Blob implements Blob { constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) { const inputMapping = (v: any) => { @@ -70,14 +32,14 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { let bps: any[] = []; if (blobParts === undefined) { - super([], getOptions(options)); + super([], preprocessOptions(options)); } else if (blobParts === null || typeof blobParts !== 'object') { throw TypeError(); } else { for (let bp of blobParts) { bps.push(inputMapping(bp)); } - super(bps, getOptions(options)); + super(bps, preprocessOptions(options)); } } diff --git a/packages/expo-blob/src/utils.ts b/packages/expo-blob/src/utils.ts index d287c86196bc46..ab86104f0ded0f 100644 --- a/packages/expo-blob/src/utils.ts +++ b/packages/expo-blob/src/utils.ts @@ -4,9 +4,8 @@ * Returns the lowercased content type if it is valid, or an empty string otherwise. * * A valid content type: - * - Is not null, undefined, or empty + * - Is not undefined * - Contains only printable ASCII characters (0x20–0x7E) - * - Does not contain forbidden control characters: NUL (\x00), LF (\x0A), or CR (\x0D) * * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type. * @@ -14,9 +13,59 @@ * @returns The normalized (lowercased) content type, or an empty string if invalid. */ export function normalizedContentType(type?: string): string { - if (type === undefined) return ''; const str = '' + type; const asciiPrintable = /^[\x20-\x7E]+$/; - if (!asciiPrintable.test(str)) return ''; + if (type === undefined || !asciiPrintable.test(str)) return ''; return str.toLowerCase(); } + +/** + * @param obj The object to check whether it's a Typed Array or not. + * @returns boolean indicating whether the obj is a Typed Array or not. + */ +export function isTypedArray(obj: any): boolean { + return ( + obj instanceof Int8Array || + obj instanceof Int16Array || + obj instanceof Int32Array || + obj instanceof BigInt64Array || + obj instanceof Uint8Array || + obj instanceof Uint16Array || + obj instanceof Uint32Array || + obj instanceof BigUint64Array || + obj instanceof Float32Array || + obj instanceof Float64Array + ); +} + +/** + * Processes the options object and + * @param options + * @returns BlobPropertyBag object + */ +export const preprocessOptions = (options?: BlobPropertyBag): BlobPropertyBag | undefined => { + if (options) { + if (!(options instanceof Object)) { + throw TypeError(); + } + + let e = options.endings; + let t = options.type; + if (e && typeof e === 'object') { + e = String(e); + } + if (t && typeof t === 'object') { + t = String(t); + } + if (e !== undefined && e !== 'native' && e !== 'transparent') { + throw TypeError(); + } + + return { + endings: e, + type: normalizedContentType(t), + }; + } + + return options; +}; From baf99026ecbe7fcd744d25f82a668a1f2a847acf Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 13:48:58 +0200 Subject: [PATCH 30/41] documentation --- packages/expo-blob/build/utils.d.ts | 5 ++++- packages/expo-blob/build/utils.d.ts.map | 2 +- packages/expo-blob/build/utils.js | 5 ++++- packages/expo-blob/build/utils.js.map | 2 +- packages/expo-blob/src/utils.ts | 5 ++++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/expo-blob/build/utils.d.ts b/packages/expo-blob/build/utils.d.ts index 8d02494e031ed2..1ae98031608880 100644 --- a/packages/expo-blob/build/utils.d.ts +++ b/packages/expo-blob/build/utils.d.ts @@ -19,7 +19,10 @@ export declare function normalizedContentType(type?: string): string; */ export declare function isTypedArray(obj: any): boolean; /** - * Processes the options object and + * Processes the options object if defined and not null. + * The function coerces .type and .options to 'string' (if they are defined objects) + * TypeError is thrown when the options is not an object or .endings are invalid. + * * @param options * @returns BlobPropertyBag object */ diff --git a/packages/expo-blob/build/utils.d.ts.map b/packages/expo-blob/build/utils.d.ts.map index 6e495637e3f98f..93dc19ab829721 100644 --- a/packages/expo-blob/build/utils.d.ts.map +++ b/packages/expo-blob/build/utils.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAa9C;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,KAAG,eAAe,GAAG,SAyB/E,CAAC"} \ No newline at end of file +{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAa9C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,eAAe,KAAG,eAAe,GAAG,SAyB/E,CAAC"} \ No newline at end of file diff --git a/packages/expo-blob/build/utils.js b/packages/expo-blob/build/utils.js index 442b45ce90740d..059b6aab1feeef 100644 --- a/packages/expo-blob/build/utils.js +++ b/packages/expo-blob/build/utils.js @@ -36,7 +36,10 @@ export function isTypedArray(obj) { obj instanceof Float64Array); } /** - * Processes the options object and + * Processes the options object if defined and not null. + * The function coerces .type and .options to 'string' (if they are defined objects) + * TypeError is thrown when the options is not an object or .endings are invalid. + * * @param options * @returns BlobPropertyBag object */ diff --git a/packages/expo-blob/build/utils.js.map b/packages/expo-blob/build/utils.js.map index a4ec45c0d6f324..3d9ee235b45c7d 100644 --- a/packages/expo-blob/build/utils.js.map +++ b/packages/expo-blob/build/utils.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IACjD,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ;IACnC,OAAO,CACL,GAAG,YAAY,SAAS;QACxB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,aAAa;QAC5B,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,cAAc;QAC7B,GAAG,YAAY,YAAY;QAC3B,GAAG,YAAY,YAAY,CAC5B,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAyB,EAA+B,EAAE;IAC1F,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC;YAC7D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not undefined\n * - Contains only printable ASCII characters (0x20–0x7E)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n const str = '' + type;\n const asciiPrintable = /^[\\x20-\\x7E]+$/;\n if (type === undefined || !asciiPrintable.test(str)) return '';\n return str.toLowerCase();\n}\n\n/**\n * @param obj The object to check whether it's a Typed Array or not.\n * @returns boolean indicating whether the obj is a Typed Array or not.\n */\nexport function isTypedArray(obj: any): boolean {\n return (\n obj instanceof Int8Array ||\n obj instanceof Int16Array ||\n obj instanceof Int32Array ||\n obj instanceof BigInt64Array ||\n obj instanceof Uint8Array ||\n obj instanceof Uint16Array ||\n obj instanceof Uint32Array ||\n obj instanceof BigUint64Array ||\n obj instanceof Float32Array ||\n obj instanceof Float64Array\n );\n}\n\n/**\n * Processes the options object and\n * @param options\n * @returns BlobPropertyBag object\n */\nexport const preprocessOptions = (options?: BlobPropertyBag): BlobPropertyBag | undefined => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n if (e !== undefined && e !== 'native' && e !== 'transparent') {\n throw TypeError();\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n"]} \ No newline at end of file +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAa;IACjD,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,cAAc,GAAG,gBAAgB,CAAC;IACxC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/D,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAQ;IACnC,OAAO,CACL,GAAG,YAAY,SAAS;QACxB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,aAAa;QAC5B,GAAG,YAAY,UAAU;QACzB,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,WAAW;QAC1B,GAAG,YAAY,cAAc;QAC7B,GAAG,YAAY,YAAY;QAC3B,GAAG,YAAY,YAAY,CAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAyB,EAA+B,EAAE;IAC1F,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC,CAAC,OAAO,YAAY,MAAM,CAAC,EAAE,CAAC;YACjC,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;QACxB,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QACrB,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC;YAC7D,MAAM,SAAS,EAAE,CAAC;QACpB,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC;YACV,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC;SAC/B,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC","sourcesContent":["/**\n * Normalizes the content type string for a Blob.\n *\n * Returns the lowercased content type if it is valid, or an empty string otherwise.\n *\n * A valid content type:\n * - Is not undefined\n * - Contains only printable ASCII characters (0x20–0x7E)\n *\n * If any of these conditions are not met, returns an empty string to indicate an invalid or unsafe content type.\n *\n * @param type The content type string to normalize.\n * @returns The normalized (lowercased) content type, or an empty string if invalid.\n */\nexport function normalizedContentType(type?: string): string {\n const str = '' + type;\n const asciiPrintable = /^[\\x20-\\x7E]+$/;\n if (type === undefined || !asciiPrintable.test(str)) return '';\n return str.toLowerCase();\n}\n\n/**\n * @param obj The object to check whether it's a Typed Array or not.\n * @returns boolean indicating whether the obj is a Typed Array or not.\n */\nexport function isTypedArray(obj: any): boolean {\n return (\n obj instanceof Int8Array ||\n obj instanceof Int16Array ||\n obj instanceof Int32Array ||\n obj instanceof BigInt64Array ||\n obj instanceof Uint8Array ||\n obj instanceof Uint16Array ||\n obj instanceof Uint32Array ||\n obj instanceof BigUint64Array ||\n obj instanceof Float32Array ||\n obj instanceof Float64Array\n );\n}\n\n/**\n * Processes the options object if defined and not null.\n * The function coerces .type and .options to 'string' (if they are defined objects)\n * TypeError is thrown when the options is not an object or .endings are invalid.\n *\n * @param options\n * @returns BlobPropertyBag object\n */\nexport const preprocessOptions = (options?: BlobPropertyBag): BlobPropertyBag | undefined => {\n if (options) {\n if (!(options instanceof Object)) {\n throw TypeError();\n }\n\n let e = options.endings;\n let t = options.type;\n if (e && typeof e === 'object') {\n e = String(e);\n }\n if (t && typeof t === 'object') {\n t = String(t);\n }\n if (e !== undefined && e !== 'native' && e !== 'transparent') {\n throw TypeError();\n }\n\n return {\n endings: e,\n type: normalizedContentType(t),\n };\n }\n\n return options;\n};\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/utils.ts b/packages/expo-blob/src/utils.ts index ab86104f0ded0f..b8958556960602 100644 --- a/packages/expo-blob/src/utils.ts +++ b/packages/expo-blob/src/utils.ts @@ -39,7 +39,10 @@ export function isTypedArray(obj: any): boolean { } /** - * Processes the options object and + * Processes the options object if defined and not null. + * The function coerces .type and .options to 'string' (if they are defined objects) + * TypeError is thrown when the options is not an object or .endings are invalid. + * * @param options * @returns BlobPropertyBag object */ From b8fc88e5b6f487b8a8c931fecb2f5f3a84caf835 Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 14:58:19 +0200 Subject: [PATCH 31/41] unicode bugfix --- apps/test-suite/tests/Blob.ts | 12 ++++++++++++ .../android/src/main/java/expo/modules/blob/Blob.kt | 2 +- packages/expo-blob/build/BlobModule.js | 1 + packages/expo-blob/build/BlobModule.js.map | 2 +- packages/expo-blob/src/BlobModule.ts | 1 + 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index ffe1c0c48e65a7..55b75d6b65052c 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -109,6 +109,18 @@ export async function test({ describe, it, expect }) { const blob = new Blob(['aa', ['ab', 'cd'], 'ef']); expect(await blob.text()).toBe('aaab,cdef'); }); + it('Unicode emotes', async () => { + const blob = new Blob(['Hello 🌍 世界']); + console.log('Hello 🌍 世界'.length); + console.log(blob.size); + expect(await blob.text()).toBe('Hello 🌍 世界'); + expect(await blob.bytes()).toEqual( + Uint8Array.from([ + 72, 101, 108, 108, 111, 32, 240, 159, 140, 141, 32, 228, 184, 150, 231, 149, 140, + ]) + ); + expect(await blob.size).toBe(17); + }); }); describe('Array buffer', () => { diff --git a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt index 43c5e1b3892ddb..a7421a4c40f086 100644 --- a/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt +++ b/packages/expo-blob/android/src/main/java/expo/modules/blob/Blob.kt @@ -158,7 +158,7 @@ sealed class InternalBlobPart() { fun size(): Int { return when (this) { - is StringPart -> string.length + is StringPart -> string.toByteArray().size is BlobPart -> blob.size is BufferPart -> buffer.size } diff --git a/packages/expo-blob/build/BlobModule.js b/packages/expo-blob/build/BlobModule.js index 5de558ce3f9997..1125af36eaeb0e 100644 --- a/packages/expo-blob/build/BlobModule.js +++ b/packages/expo-blob/build/BlobModule.js @@ -75,6 +75,7 @@ export class ExpoBlob extends NativeBlobModule.Blob { return '[object Blob]'; } } +// Changed the length property to match that of the default js implementation Object.defineProperty(ExpoBlob, 'length', { value: 0, writable: false, diff --git a/packages/expo-blob/build/BlobModule.js.map b/packages/expo-blob/build/BlobModule.js.map index ef986b40ab7005..c8b354987bb0ce 100644 --- a/packages/expo-blob/build/BlobModule.js.map +++ b/packages/expo-blob/build/BlobModule.js.map @@ -1 +1 @@ -{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { isTypedArray, normalizedContentType, preprocessOptions } from './utils';\n\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], preprocessOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, preprocessOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file +{"version":3,"file":"BlobModule.js","sourceRoot":"","sources":["../src/BlobModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule, SharedObject } from 'expo';\nimport { Blob, BlobPart } from './BlobModule.types';\nimport { isTypedArray, normalizedContentType, preprocessOptions } from './utils';\n\ndeclare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n slice(start?: number, end?: number, contentType?: string): ExpoBlob;\n bytes(): Promise;\n text(): Promise;\n}\n\ndeclare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\nconst NativeBlobModule = requireNativeModule('ExpoBlob');\n\nexport class ExpoBlob extends NativeBlobModule.Blob implements Blob {\n constructor(blobParts?: any[] | Iterable, options?: BlobPropertyBag) {\n const inputMapping = (v: any) => {\n if (v instanceof ArrayBuffer) {\n return new Uint8Array(v);\n }\n if (v instanceof ExpoBlob || isTypedArray(v)) {\n return v;\n }\n return String(v);\n };\n\n let bps: any[] = [];\n\n if (blobParts === undefined) {\n super([], preprocessOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError();\n } else {\n for (let bp of blobParts) {\n bps.push(inputMapping(bp));\n }\n super(bps, preprocessOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): ExpoBlob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, ExpoBlob.prototype);\n return slicedBlob;\n }\n\n stream(): ReadableStream {\n const self = this;\n let offset = 0;\n let bytesPromise: Promise | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: any) {\n if (!bytesPromise) {\n bytesPromise = self.bytes();\n }\n const bytes = await bytesPromise;\n if (offset >= bytes.length) {\n controller.close();\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view = controller.byobRequest.view;\n const end = Math.min(offset + view.byteLength, bytes.length);\n const chunk = bytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= bytes.length) {\n controller.close();\n }\n return;\n }\n\n const chunkSize = 65_536;\n const end = Math.min(offset + chunkSize, bytes.length);\n controller.enqueue(bytes.subarray(offset, end));\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise {\n return super\n .bytes()\n .then((bytes: Uint8Array) =>\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n}\n\n// Changed the length property to match that of the default js implementation\nObject.defineProperty(ExpoBlob, 'length', {\n value: 0,\n writable: false,\n});\n"]} \ No newline at end of file diff --git a/packages/expo-blob/src/BlobModule.ts b/packages/expo-blob/src/BlobModule.ts index a3f0c7d49e17d0..edeb56972c1dda 100644 --- a/packages/expo-blob/src/BlobModule.ts +++ b/packages/expo-blob/src/BlobModule.ts @@ -101,6 +101,7 @@ export class ExpoBlob extends NativeBlobModule.Blob implements Blob { } } +// Changed the length property to match that of the default js implementation Object.defineProperty(ExpoBlob, 'length', { value: 0, writable: false, From cc8277430e462694701d35eb0759e9e7de57914b Mon Sep 17 00:00:00 2001 From: HubertBer Date: Wed, 23 Jul 2025 15:12:55 +0200 Subject: [PATCH 32/41] test large slice start and end values --- apps/test-suite/tests/Blob.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/test-suite/tests/Blob.ts b/apps/test-suite/tests/Blob.ts index 55b75d6b65052c..83f1691d974069 100644 --- a/apps/test-suite/tests/Blob.ts +++ b/apps/test-suite/tests/Blob.ts @@ -121,6 +121,28 @@ export async function test({ describe, it, expect }) { ); expect(await blob.size).toBe(17); }); + describe('large slice start and end', async () => { + it('large positive start', async () => { + let blob = new Blob(['PASS']); + let str = await blob.slice(10000).text(); + expect(str).toBe(''); + }); + it('large negative start', async () => { + let blob = new Blob(['PASS']); + let str = await blob.slice(-10000).text(); + expect(str).toBe('PASS'); + }); + it('large positive end', async () => { + let blob = new Blob(['PASS']); + let str = await blob.slice(0, 10000).text(); + expect(str).toBe('PASS'); + }); + it('large negative end', async () => { + let blob = new Blob(['PASS']); + let str = await blob.slice(0, -10000).text(); + expect(str).toBe(''); + }); + }); }); describe('Array buffer', () => { From 57323d9acffb939991dca2cb2520bc655da9398f Mon Sep 17 00:00:00 2001 From: HubertBer Date: Thu, 24 Jul 2025 12:58:14 +0200 Subject: [PATCH 33/41] performance test setup, first test --- .../src/screens/Blob/BlobScreen.tsx | 8 + .../Blob/expo-blob/BlobPerformanceTest.tsx | 154 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 apps/native-component-list/src/screens/Blob/expo-blob/BlobPerformanceTest.tsx diff --git a/apps/native-component-list/src/screens/Blob/BlobScreen.tsx b/apps/native-component-list/src/screens/Blob/BlobScreen.tsx index 8201da89a6b7a5..724da208abd8f8 100644 --- a/apps/native-component-list/src/screens/Blob/BlobScreen.tsx +++ b/apps/native-component-list/src/screens/Blob/BlobScreen.tsx @@ -50,6 +50,14 @@ export const BlobScreens = [ return optionalRequire(() => require('./expo-blob/BlobBytesScreen')); }, }, + { + name: 'Blob Performance Test', + route: 'expo-blob/performance', + options: {}, + getComponent() { + return optionalRequire(() => require('./expo-blob/BlobPerformanceTest')); + }, + }, ]; export default function BlobScreen() { diff --git a/apps/native-component-list/src/screens/Blob/expo-blob/BlobPerformanceTest.tsx b/apps/native-component-list/src/screens/Blob/expo-blob/BlobPerformanceTest.tsx new file mode 100644 index 00000000000000..f01e6eb3a5841e --- /dev/null +++ b/apps/native-component-list/src/screens/Blob/expo-blob/BlobPerformanceTest.tsx @@ -0,0 +1,154 @@ +import { ExpoBlob } from 'expo-blob'; +import { useState } from 'react'; +import { View, Text, StyleSheet, Button, ScrollView } from 'react-native'; + +import HeadingText from '../../../components/HeadingText'; +import MonoText from '../../../components/MonoText'; +import { Page } from '../../../components/Page'; +import { Q, S } from '@expo/html-elements'; + +type PerformanceTestData = { + key: string; + expoBlobCreation: () => ExpoBlob; + blobCreation: () => Blob; + blobOperation: (blob: Blob | ExpoBlob) => void; + title: string; +}; + +const performanceTest: PerformanceTestData[] = [ + { + key: '10_000 blob [100, 9_900) slice', + blobCreation: () => { + return new Blob(['a'.repeat(10_000)]); + }, + expoBlobCreation: () => { + return new ExpoBlob(['a'.repeat(10_000)]); + }, + blobOperation: (blob: Blob | ExpoBlob) => { + blob.slice(100, 9_900); + }, + title: '10_000 blob [100, 9_900) slice', + }, +]; + +type ArrayBufferExampleItemProps = { + example: PerformanceTestData; + result: { + blobTime: number; + expoBlobTime: number; + } | null; + onEvaluate: (example: PerformanceTestData) => void; +}; + +function ArrayBufferExampleItem({ example, result, onEvaluate }: ArrayBufferExampleItemProps) { + return ( + + {example.title} + + {!result &&