expo-blob 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.eslintrc.js +2 -0
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +148 -0
  4. package/android/build.gradle +15 -0
  5. package/android/src/main/AndroidManifest.xml +2 -0
  6. package/android/src/main/java/expo/modules/blob/Blob.kt +185 -0
  7. package/android/src/main/java/expo/modules/blob/BlobModule.kt +50 -0
  8. package/build/ExpoBlob.d.ts +12 -0
  9. package/build/ExpoBlob.d.ts.map +1 -0
  10. package/build/ExpoBlob.js +90 -0
  11. package/build/ExpoBlob.js.map +1 -0
  12. package/build/ExpoBlob.types.d.ts +65 -0
  13. package/build/ExpoBlob.types.d.ts.map +1 -0
  14. package/build/ExpoBlob.types.js +2 -0
  15. package/build/ExpoBlob.types.js.map +1 -0
  16. package/build/ExpoBlob.web.d.ts +5 -0
  17. package/build/ExpoBlob.web.d.ts.map +1 -0
  18. package/build/ExpoBlob.web.js +2 -0
  19. package/build/ExpoBlob.web.js.map +1 -0
  20. package/build/index.d.ts +3 -0
  21. package/build/index.d.ts.map +1 -0
  22. package/build/index.js +2 -0
  23. package/build/index.js.map +1 -0
  24. package/build/utils.d.ts +48 -0
  25. package/build/utils.d.ts.map +1 -0
  26. package/build/utils.js +88 -0
  27. package/build/utils.js.map +1 -0
  28. package/expo-module.config.json +15 -0
  29. package/ios/Blob.swift +108 -0
  30. package/ios/BlobPart.swift +29 -0
  31. package/ios/BlobUtils.swift +39 -0
  32. package/ios/ExpoBlob.podspec +34 -0
  33. package/ios/ExpoBlob.swift +44 -0
  34. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0-sources.jar +0 -0
  35. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0-sources.jar.md5 +1 -0
  36. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0-sources.jar.sha1 +1 -0
  37. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0-sources.jar.sha256 +1 -0
  38. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0-sources.jar.sha512 +1 -0
  39. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.aar +0 -0
  40. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.aar.md5 +1 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.aar.sha1 +1 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.aar.sha256 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.aar.sha512 +1 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.module +87 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.module.md5 +1 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.module.sha1 +1 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.module.sha256 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.module.sha512 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.pom +35 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.pom.md5 +1 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.pom.sha1 +1 -0
  52. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.pom.sha256 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.blob/0.1.0/expo.modules.blob-0.1.0.pom.sha512 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.blob/maven-metadata.xml +13 -0
  55. package/local-maven-repo/host/exp/exponent/expo.modules.blob/maven-metadata.xml.md5 +1 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.blob/maven-metadata.xml.sha1 +1 -0
  57. package/local-maven-repo/host/exp/exponent/expo.modules.blob/maven-metadata.xml.sha256 +1 -0
  58. package/local-maven-repo/host/exp/exponent/expo.modules.blob/maven-metadata.xml.sha512 +1 -0
  59. package/package.json +36 -1
  60. package/src/ExpoBlob.ts +114 -0
  61. package/src/ExpoBlob.types.ts +75 -0
  62. package/src/ExpoBlob.web.ts +1 -0
  63. package/src/index.ts +2 -0
  64. package/src/utils.ts +94 -0
  65. package/tsconfig.json +9 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,2 @@
1
+ // @generated by expo-module-scripts
2
+ module.exports = require('expo-module-scripts/eslintrc.base.js');
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## Unpublished
4
+
5
+ ### 🛠 Breaking changes
6
+
7
+ ### 🎉 New features
8
+
9
+ ### 🐛 Bug fixes
10
+
11
+ ### 💡 Others
12
+
13
+ ## 0.1.0 — 2025-08-15
14
+
15
+ _This version does not introduce any user-facing changes._
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # ExpoBlob
2
+
3
+ A Expo package that provides a Blob implementation for handling binary data, similar to the web Blob API.
4
+
5
+ # Installation in managed Expo projects
6
+
7
+ For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/blob/).
8
+
9
+ # Installation in bare React Native projects
10
+
11
+ For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
12
+
13
+ ### Add the package to your npm dependencies
14
+
15
+ ```bash
16
+ npx expo install expo-blob
17
+ ```
18
+
19
+ ### Configure for Android
20
+
21
+ No additional set up necessary.
22
+
23
+ ### Configure for iOS
24
+
25
+ Run `npx pod-install` after installing the npm package.
26
+
27
+ ## Usage
28
+
29
+ ### Basic Blob Creation
30
+
31
+ ```typescript
32
+ import { ExpoBlob as Blob } from 'expo-blob';
33
+
34
+ // Create an empty blob
35
+ const emptyBlob = new Blob();
36
+
37
+ // Create a blob from text
38
+ const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' });
39
+
40
+ // Create a blob from binary data
41
+ const binaryBlob = new Blob([new Uint8Array([1, 2, 3, 4])], {
42
+ type: 'application/octet-stream',
43
+ });
44
+
45
+ // Create a blob from mixed content
46
+ const mixedBlob = new Blob(
47
+ [
48
+ 'Text content',
49
+ new Uint8Array([65, 66, 67]), // ABC in ASCII
50
+ 'More text',
51
+ ],
52
+ { type: 'text/plain' }
53
+ );
54
+ ```
55
+
56
+ ### Blob Properties
57
+
58
+ ```typescript
59
+ const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
60
+
61
+ console.log(blob.size); // 13 (bytes)
62
+ console.log(blob.type); // "text/plain"
63
+ ```
64
+
65
+ ### Reading Blob Content
66
+
67
+ ```typescript
68
+ const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
69
+
70
+ // Read as text
71
+ const text = await blob.text();
72
+ console.log(text); // "Hello, World!"
73
+
74
+ // Read as bytes
75
+ const bytes = await blob.bytes();
76
+ console.log(bytes); // Uint8Array(13) [72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]
77
+
78
+ // Read as ArrayBuffer
79
+ const arrayBuffer = await blob.arrayBuffer();
80
+ console.log(arrayBuffer); // ArrayBuffer(13)
81
+ ```
82
+
83
+ ### Slicing Blobs
84
+
85
+ ```typescript
86
+ const blob = new Blob(['Hello, World!'], { type: 'text/plain' });
87
+
88
+ // Slice from position 0 to 5
89
+ const slice1 = blob.slice(0, 5);
90
+ console.log(await slice1.text()); // "Hello"
91
+
92
+ // Slice from position 7 to end
93
+ const slice2 = blob.slice(7);
94
+ console.log(await slice2.text()); // "World!"
95
+
96
+ // Slice with custom type
97
+ const slice3 = blob.slice(0, 5, 'text/html');
98
+ console.log(slice3.type); // "text/html"
99
+ ```
100
+
101
+ ### Streaming
102
+
103
+ ```typescript
104
+ const blob = new Blob(['Large content...'], { type: 'text/plain' });
105
+
106
+ // Create a readable stream
107
+ const stream = blob.stream();
108
+ const reader = stream.getReader();
109
+
110
+ // Read chunks
111
+ while (true) {
112
+ const { done, value } = await reader.read();
113
+ if (done) break;
114
+ console.log('Chunk:', value);
115
+ }
116
+ ```
117
+
118
+ ## API Reference
119
+
120
+ ### Constructor
121
+
122
+ ```typescript
123
+ new Blob(blobParts?: BlobPart[], options?: BlobPropertyBag)
124
+ ```
125
+
126
+ **Parameters:**
127
+
128
+ - `blobParts` (optional): Array of data to include in the blob
129
+ - `options` (optional): Configuration object with:
130
+ - `type`: MIME type string
131
+ - `endings`: Line ending normalization ('transparent' or 'native')
132
+
133
+ ### Properties
134
+
135
+ - `size`: The size of the blob in bytes (read-only)
136
+ - `type`: The MIME type of the blob (read-only)
137
+
138
+ ### Methods
139
+
140
+ - `slice(start?, end?, contentType?)`: Returns a new Blob containing a subset of the data
141
+ - `text()`: Returns a Promise that resolves with the blob's content as a string
142
+ - `bytes()`: Returns a Promise that resolves with the blob's content as a Uint8Array
143
+ - `arrayBuffer()`: Returns a Promise that resolves with the blob's content as an ArrayBuffer
144
+ - `stream()`: Returns a ReadableStream for streaming the blob's content
145
+
146
+ # Contributing
147
+
148
+ Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).
@@ -0,0 +1,15 @@
1
+ plugins {
2
+ id 'com.android.library'
3
+ id 'expo-module-gradle-plugin'
4
+ }
5
+
6
+ group = 'host.exp.exponent'
7
+ version = '0.1.0'
8
+
9
+ android {
10
+ namespace "expo.modules.blob"
11
+ defaultConfig {
12
+ versionCode 1
13
+ versionName '0.1.0'
14
+ }
15
+ }
@@ -0,0 +1,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -0,0 +1,185 @@
1
+ package expo.modules.blob
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+ import expo.modules.kotlin.sharedobjects.SharedObject
6
+ import expo.modules.kotlin.typedarray.TypedArray
7
+ import expo.modules.kotlin.types.EitherOfThree
8
+ import expo.modules.kotlin.types.Enumerable
9
+ import java.io.ByteArrayOutputStream
10
+ import kotlin.math.max
11
+ import kotlin.math.min
12
+
13
+ internal const val DEFAULT_TYPE = ""
14
+
15
+ internal class Blob(val blobParts: List<InternalBlobPart> = listOf(), rawType: String = DEFAULT_TYPE) : SharedObject() {
16
+ val size: Int by lazy {
17
+ blobParts.sumOf { it.size() }
18
+ }
19
+
20
+ val type = if (validType(rawType)) {
21
+ rawType.lowercase()
22
+ } else {
23
+ DEFAULT_TYPE
24
+ }
25
+
26
+ override fun getAdditionalMemoryPressure(): Int {
27
+ return size
28
+ }
29
+
30
+ fun bytesToStream(byteStream: ByteArrayOutputStream) {
31
+ for (blobPart in blobParts) {
32
+ blobPart.bytesToStream(byteStream)
33
+ }
34
+ }
35
+
36
+ fun bytes(): ByteArray {
37
+ val byteStream = ByteArrayOutputStream(size)
38
+ bytesToStream(byteStream)
39
+ return byteStream.toByteArray()
40
+ }
41
+
42
+ private fun InternalBlobPart.offsetSlice(start: Int, end: Int, offset: Int): InternalBlobPart {
43
+ val startIndex: Int = max(start - offset, 0)
44
+ val endIndex: Int = min(end - offset, size())
45
+ if (startIndex == 0 && endIndex == size()) {
46
+ return this
47
+ }
48
+ return when (this) {
49
+ is InternalBlobPart.StringWrapper -> InternalBlobPart.BufferWrapper(cachedBytes.slice(startIndex..<endIndex).toByteArray())
50
+ is InternalBlobPart.BlobWrapper -> InternalBlobPart.BlobWrapper(blob.slice(startIndex, endIndex, ""))
51
+ is InternalBlobPart.BufferWrapper -> InternalBlobPart.BufferWrapper(buffer.slice(startIndex..<endIndex).toByteArray())
52
+ }
53
+ }
54
+
55
+ fun slice(start: Int, end: Int, contentType: String): Blob {
56
+ if (start <= 0 && end >= size) {
57
+ return Blob(blobParts, contentType)
58
+ }
59
+ if (start >= end) {
60
+ return Blob(listOf(), contentType)
61
+ }
62
+ var i = 0
63
+ val bps = mutableListOf<InternalBlobPart>()
64
+ for (blobPart in blobParts) {
65
+ if (i + blobPart.size() <= start) {
66
+ i += blobPart.size()
67
+ continue
68
+ }
69
+ if (i >= end) {
70
+ break
71
+ }
72
+ bps.add(blobPart.offsetSlice(start, end, i))
73
+ i += blobPart.size()
74
+ }
75
+
76
+ return Blob(bps, contentType)
77
+ }
78
+ }
79
+
80
+ private fun validType(type: String): Boolean {
81
+ for (char in type) {
82
+ if (char.code < 0x20 || char.code > 0x7E) {
83
+ return false
84
+ }
85
+ }
86
+ return true
87
+ }
88
+
89
+ internal typealias BlobPart = EitherOfThree<String, Blob, TypedArray>
90
+
91
+ private fun TypedArray.bytes(): ByteArray {
92
+ val byteArray = ByteArray(this.byteLength)
93
+
94
+ for (i in 0..<this.byteLength) {
95
+ byteArray[i] = this.readByte(i)
96
+ }
97
+
98
+ return byteArray
99
+ }
100
+
101
+ private fun String.toNativeNewlines(): String {
102
+ val result = StringBuilder(length)
103
+ var prevCR = false
104
+ for (char in this) {
105
+ if (char == '\r') {
106
+ result.append('\n')
107
+ prevCR = true
108
+ continue
109
+ }
110
+ if (!prevCR || char != '\n') {
111
+ result.append(char)
112
+ }
113
+ prevCR = false
114
+ }
115
+ return result.toString()
116
+ }
117
+
118
+ internal fun List<BlobPart>.internal(nativeNewlines: Boolean): List<InternalBlobPart> {
119
+ return this.map { blobPart: BlobPart ->
120
+ if (blobPart.`is`(String::class)) {
121
+ blobPart.get(String::class).let {
122
+ val str = if (nativeNewlines) {
123
+ it.toNativeNewlines()
124
+ } else {
125
+ it
126
+ }
127
+ InternalBlobPart.StringWrapper(str)
128
+ }
129
+ } else if (blobPart.`is`(Blob::class)) {
130
+ blobPart.get(Blob::class).let {
131
+ InternalBlobPart.BlobWrapper(it)
132
+ }
133
+ } else {
134
+ blobPart.get(TypedArray::class).let {
135
+ InternalBlobPart.BufferWrapper(it.bytes())
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ internal fun makeBlob(blobParts: List<BlobPart>?, options: BlobOptionsBag?): Blob {
142
+ val safeBlobParts = blobParts ?: listOf()
143
+ val safeOptions = options ?: BlobOptionsBag()
144
+ return Blob(safeBlobParts.internal(safeOptions.endings == EndingType.NATIVE), safeOptions.type)
145
+ }
146
+
147
+ internal sealed class InternalBlobPart {
148
+ class StringWrapper(string: String) : InternalBlobPart() {
149
+ val cachedBytes: ByteArray by lazy {
150
+ string.toByteArray()
151
+ }
152
+ }
153
+
154
+ data class BlobWrapper(val blob: Blob) : InternalBlobPart()
155
+ data class BufferWrapper(val buffer: ByteArray) : InternalBlobPart()
156
+
157
+ fun size(): Int {
158
+ return when (this) {
159
+ is StringWrapper -> cachedBytes.size
160
+ is BlobWrapper -> blob.size
161
+ is BufferWrapper -> buffer.size
162
+ }
163
+ }
164
+
165
+ fun bytesToStream(byteStream: ByteArrayOutputStream) {
166
+ when (this) {
167
+ is StringWrapper -> byteStream.write(cachedBytes)
168
+ is BlobWrapper -> blob.bytesToStream(byteStream)
169
+ is BufferWrapper -> byteStream.write(buffer)
170
+ }
171
+ }
172
+ }
173
+
174
+ internal enum class EndingType(val str: String) : Enumerable {
175
+ TRANSPARENT("transparent"),
176
+ NATIVE("native")
177
+ }
178
+
179
+ internal class BlobOptionsBag : Record {
180
+ @Field
181
+ val type: String = DEFAULT_TYPE
182
+
183
+ @Field
184
+ val endings: EndingType = EndingType.TRANSPARENT
185
+ }
@@ -0,0 +1,50 @@
1
+ package expo.modules.blob
2
+
3
+ import expo.modules.kotlin.modules.Module
4
+ import expo.modules.kotlin.modules.ModuleDefinition
5
+ import kotlin.math.max
6
+ import kotlin.math.min
7
+
8
+ class BlobModule : Module() {
9
+ override fun definition() = ModuleDefinition {
10
+ Name("ExpoBlob")
11
+
12
+ Class(Blob::class) {
13
+ Constructor { blobParts: List<BlobPart>?, options: BlobOptionsBag? ->
14
+ makeBlob(blobParts, options)
15
+ }
16
+
17
+ Property("size") { blob: Blob ->
18
+ blob.size
19
+ }
20
+
21
+ Property("type") { blob: Blob ->
22
+ blob.type
23
+ }
24
+
25
+ Function("slice") { blob: Blob, start: Int?, end: Int?, contentType: String? ->
26
+ var sliceStart: Int = start ?: 0
27
+ var sliceEnd: Int = end ?: blob.size
28
+ if (sliceStart < 0) {
29
+ sliceStart = max(blob.size + sliceStart, 0)
30
+ } else {
31
+ sliceStart = min(sliceStart, blob.size)
32
+ }
33
+ if (sliceEnd < 0) {
34
+ sliceEnd = max(blob.size + sliceEnd, 0)
35
+ } else {
36
+ sliceEnd = min(sliceEnd, blob.size)
37
+ }
38
+ blob.slice(sliceStart, sliceEnd, contentType ?: "")
39
+ }
40
+
41
+ AsyncFunction("bytes") { blob: Blob ->
42
+ blob.bytes()
43
+ }
44
+
45
+ AsyncFunction("text") { blob: Blob ->
46
+ blob.bytes().decodeToString()
47
+ }
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,12 @@
1
+ import { BlobPart, ExpoBlobModule } from './ExpoBlob.types';
2
+ declare const NativeBlobModule: ExpoBlobModule;
3
+ export declare class Blob extends NativeBlobModule.Blob {
4
+ constructor(blobParts?: BlobPart[] | Iterable<BlobPart>, options?: BlobPropertyBag);
5
+ slice(start?: number, end?: number, contentType?: string): Blob;
6
+ stream(): ReadableStream;
7
+ arrayBuffer(): Promise<ArrayBuffer>;
8
+ toString(): string;
9
+ static get length(): number;
10
+ }
11
+ export {};
12
+ //# sourceMappingURL=ExpoBlob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.d.ts","sourceRoot":"","sources":["../src/ExpoBlob.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAkB5D,QAAA,MAAM,gBAAgB,gBAAkD,CAAC;AAEzE,qBAAa,IAAK,SAAQ,gBAAgB,CAAC,IAAI;gBACjC,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe;IAmBlF,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAO/D,MAAM,IAAI,cAAc;IAgDlB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAQzC,QAAQ,IAAI,MAAM;IAKlB,MAAM,KAAK,MAAM,WAEhB;CACF"}
@@ -0,0 +1,90 @@
1
+ import { requireNativeModule } from 'expo';
2
+ import { DEFAULT_CHUNK_SIZE, isTypedArray, normalizedContentType, preprocessOptions, } from './utils';
3
+ const inputMapping = (blobPart) => {
4
+ if (blobPart instanceof ArrayBuffer) {
5
+ return new Uint8Array(blobPart);
6
+ }
7
+ if (blobPart instanceof Blob || isTypedArray(blobPart)) {
8
+ return blobPart;
9
+ }
10
+ return String(blobPart);
11
+ };
12
+ const NativeBlobModule = requireNativeModule('ExpoBlob');
13
+ export class Blob extends NativeBlobModule.Blob {
14
+ constructor(blobParts, options) {
15
+ if (!new.target) {
16
+ throw new TypeError("Blob constructor requires 'new' operator");
17
+ }
18
+ const processedBlobParts = [];
19
+ if (blobParts === undefined) {
20
+ super([], preprocessOptions(options));
21
+ }
22
+ else if (blobParts === null || typeof blobParts !== 'object') {
23
+ throw TypeError('Blob constructor requires blobParts to be a non-null object or undefined');
24
+ }
25
+ else {
26
+ for (const blobPart of blobParts) {
27
+ processedBlobParts.push(inputMapping(blobPart));
28
+ }
29
+ super(processedBlobParts, preprocessOptions(options));
30
+ }
31
+ }
32
+ slice(start, end, contentType) {
33
+ const normalizedType = normalizedContentType(contentType);
34
+ const slicedBlob = super.slice(start, end, normalizedType);
35
+ Object.setPrototypeOf(slicedBlob, Blob.prototype);
36
+ return slicedBlob;
37
+ }
38
+ stream() {
39
+ let getBlobBytes = this.bytes.bind(this);
40
+ let offset = 0;
41
+ let cachedBytes = null;
42
+ return new ReadableStream({
43
+ type: 'bytes',
44
+ async pull(controller) {
45
+ if (!cachedBytes) {
46
+ if (!getBlobBytes) {
47
+ throw new Error('Cannot read from a closed stream');
48
+ }
49
+ cachedBytes = await getBlobBytes();
50
+ getBlobBytes = null;
51
+ }
52
+ if (offset >= cachedBytes.length) {
53
+ controller.close();
54
+ cachedBytes = null;
55
+ return;
56
+ }
57
+ if (controller.byobRequest?.view) {
58
+ const view = new Uint8Array(controller.byobRequest.view.buffer, controller.byobRequest.view.byteOffset, controller.byobRequest.view.byteLength);
59
+ const end = Math.min(offset + view.byteLength, cachedBytes.length);
60
+ const chunk = cachedBytes.subarray(offset, end);
61
+ view.set(chunk, 0);
62
+ controller.byobRequest.respond(chunk.length);
63
+ offset = end;
64
+ if (offset >= cachedBytes.length) {
65
+ controller.close();
66
+ cachedBytes = null;
67
+ }
68
+ return;
69
+ }
70
+ const chunkSize = DEFAULT_CHUNK_SIZE;
71
+ const end = Math.min(offset + chunkSize, cachedBytes.length);
72
+ controller.enqueue(cachedBytes.subarray(offset, end));
73
+ offset = end;
74
+ },
75
+ });
76
+ }
77
+ async arrayBuffer() {
78
+ return super.bytes().then((bytes) =>
79
+ // The Blob spec requires we always return a new ArrayBuffer even when its bounds match the TypedArray's
80
+ bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength));
81
+ }
82
+ toString() {
83
+ return '[object Blob]';
84
+ }
85
+ // Changed the length property to match that of the default js implementation
86
+ static get length() {
87
+ return 0;
88
+ }
89
+ }
90
+ //# sourceMappingURL=ExpoBlob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.js","sourceRoot":"","sources":["../src/ExpoBlob.ts"],"names":[],"mappings":"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","sourcesContent":["import { requireNativeModule } from 'expo';\n\nimport { BlobPart, ExpoBlobModule } from './ExpoBlob.types';\nimport {\n DEFAULT_CHUNK_SIZE,\n isTypedArray,\n normalizedContentType,\n preprocessOptions,\n} from './utils';\n\nconst inputMapping = (blobPart: BlobPart) => {\n if (blobPart instanceof ArrayBuffer) {\n return new Uint8Array(blobPart);\n }\n if (blobPart instanceof Blob || isTypedArray(blobPart)) {\n return blobPart;\n }\n return String(blobPart);\n};\n\nconst NativeBlobModule = requireNativeModule<ExpoBlobModule>('ExpoBlob');\n\nexport class Blob extends NativeBlobModule.Blob {\n constructor(blobParts?: BlobPart[] | Iterable<BlobPart>, options?: BlobPropertyBag) {\n if (!new.target) {\n throw new TypeError(\"Blob constructor requires 'new' operator\");\n }\n\n const processedBlobParts: BlobPart[] = [];\n\n if (blobParts === undefined) {\n super([], preprocessOptions(options));\n } else if (blobParts === null || typeof blobParts !== 'object') {\n throw TypeError('Blob constructor requires blobParts to be a non-null object or undefined');\n } else {\n for (const blobPart of blobParts) {\n processedBlobParts.push(inputMapping(blobPart));\n }\n super(processedBlobParts, preprocessOptions(options));\n }\n }\n\n slice(start?: number, end?: number, contentType?: string): Blob {\n const normalizedType = normalizedContentType(contentType);\n const slicedBlob = super.slice(start, end, normalizedType);\n Object.setPrototypeOf(slicedBlob, Blob.prototype);\n return slicedBlob as Blob;\n }\n\n stream(): ReadableStream {\n let getBlobBytes: (() => Promise<Uint8Array>) | null = this.bytes.bind(this);\n let offset = 0;\n let cachedBytes: Uint8Array | null = null;\n\n return new ReadableStream({\n type: 'bytes',\n async pull(controller: ReadableByteStreamController) {\n if (!cachedBytes) {\n if (!getBlobBytes) {\n throw new Error('Cannot read from a closed stream');\n }\n cachedBytes = await getBlobBytes();\n getBlobBytes = null;\n }\n\n if (offset >= cachedBytes.length) {\n controller.close();\n cachedBytes = null;\n return;\n }\n\n if (controller.byobRequest?.view) {\n const view: Uint8Array = new Uint8Array(\n controller.byobRequest.view.buffer,\n controller.byobRequest.view.byteOffset,\n controller.byobRequest.view.byteLength\n );\n const end = Math.min(offset + view.byteLength, cachedBytes.length);\n const chunk = cachedBytes.subarray(offset, end);\n view.set(chunk, 0);\n controller.byobRequest.respond(chunk.length);\n offset = end;\n if (offset >= cachedBytes.length) {\n controller.close();\n cachedBytes = null;\n }\n return;\n }\n\n const chunkSize = DEFAULT_CHUNK_SIZE;\n const end = Math.min(offset + chunkSize, cachedBytes.length);\n controller.enqueue(cachedBytes.subarray(offset, end) as ArrayBufferView<ArrayBuffer>);\n offset = end;\n },\n });\n }\n\n async arrayBuffer(): Promise<ArrayBuffer> {\n return super.bytes().then(\n (bytes: Uint8Array) =>\n // The Blob spec requires we always return a new ArrayBuffer even when its bounds match the TypedArray's\n bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer\n );\n }\n\n toString(): string {\n return '[object Blob]';\n }\n\n // Changed the length property to match that of the default js implementation\n static get length() {\n return 0;\n }\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { NativeModule, SharedObject } from 'expo';
2
+ export declare class Blob {
3
+ /**
4
+ * The size of the `Blob` in bytes.
5
+ */
6
+ readonly size: number;
7
+ /**
8
+ * The MIME type of the `Blob`, or the empty string if the type cannot be determined.
9
+ */
10
+ readonly type: string;
11
+ /**
12
+ * Creates a new `Blob` object from the given parts and options.
13
+ * @param blobParts An array of `BlobPart` to include in the `Blob`.
14
+ * @param options An optional `BlobPropertyBag` dictionary.
15
+ */
16
+ constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);
17
+ /**
18
+ * @param start The starting byte index (inclusive) represented as a signed 32 bit integer (up to 2^31 - 1).
19
+ * @param end The ending byte index (exclusive) represented as a signed 32 bit integer (up to 2^31 - 1).
20
+ * @param contentType The MIME type of the new `Blob`. If not provided, defaults to an empty string.
21
+ * @returns A new `Blob` object containing the data in the specified range of bytes of the source `Blob`.
22
+ */
23
+ slice(start?: number, end?: number, contentType?: string): Blob;
24
+ /**
25
+ * @returns Promise resolving to the `Blob`'s binary data as a `Uint8Array`.
26
+ */
27
+ bytes(): Promise<Uint8Array>;
28
+ /**
29
+ * @returns Promise that resolves with the entire contents of the `Blob` as a UTF-8 string.
30
+ */
31
+ text(): Promise<string>;
32
+ /**
33
+ * > **Note**: The current implementation loads the entire `Blob` into memory before streaming.
34
+ * @returns A `ReadableStream` of the `Blob`'s data.
35
+ */
36
+ stream(): ReadableStream;
37
+ /**
38
+ * @returns Promise resolving to the `Blob`'s binary data as an `ArrayBuffer`.
39
+ */
40
+ arrayBuffer(): Promise<ArrayBuffer>;
41
+ }
42
+ /**
43
+ * @hidden
44
+ * @private
45
+ */
46
+ export declare class NativeBlob extends SharedObject {
47
+ readonly size: number;
48
+ readonly type: string;
49
+ constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);
50
+ slice(start?: number, end?: number, contentType?: string): Blob;
51
+ bytes(): Promise<Uint8Array>;
52
+ text(): Promise<string>;
53
+ }
54
+ /**
55
+ * @hidden
56
+ * @private
57
+ */
58
+ export declare class ExpoBlobModule extends NativeModule {
59
+ Blob: typeof NativeBlob;
60
+ }
61
+ /**
62
+ * Represents a part of a `Blob`. Can be a `string`, `ArrayBuffer`, `ArrayBufferView`, or another `Blob`.
63
+ */
64
+ export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob;
65
+ //# sourceMappingURL=ExpoBlob.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.types.d.ts","sourceRoot":"","sources":["../src/ExpoBlob.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAElD,MAAM,CAAC,OAAO,OAAO,IAAI;IACvB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;gBACS,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAE7D;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAE/D;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAE5B;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IACvB;;;OAGG;IACH,MAAM,IAAI,cAAc;IAExB;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,YAAY;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe;IAE7D,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAC/D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAC5B,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,YAAY;IACtD,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ExpoBlob.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.types.js","sourceRoot":"","sources":["../src/ExpoBlob.types.ts"],"names":[],"mappings":"","sourcesContent":["import { NativeModule, SharedObject } from 'expo';\n\nexport declare class Blob {\n /**\n * The size of the `Blob` in bytes.\n */\n readonly size: number;\n /**\n * The MIME type of the `Blob`, or the empty string if the type cannot be determined.\n */\n readonly type: string;\n\n /**\n * Creates a new `Blob` object from the given parts and options.\n * @param blobParts An array of `BlobPart` to include in the `Blob`.\n * @param options An optional `BlobPropertyBag` dictionary.\n */\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\n /**\n * @param start The starting byte index (inclusive) represented as a signed 32 bit integer (up to 2^31 - 1).\n * @param end The ending byte index (exclusive) represented as a signed 32 bit integer (up to 2^31 - 1).\n * @param contentType The MIME type of the new `Blob`. If not provided, defaults to an empty string.\n * @returns A new `Blob` object containing the data in the specified range of bytes of the source `Blob`.\n */\n slice(start?: number, end?: number, contentType?: string): Blob;\n\n /**\n * @returns Promise resolving to the `Blob`'s binary data as a `Uint8Array`.\n */\n bytes(): Promise<Uint8Array>;\n\n /**\n * @returns Promise that resolves with the entire contents of the `Blob` as a UTF-8 string.\n */\n text(): Promise<string>;\n /**\n * > **Note**: The current implementation loads the entire `Blob` into memory before streaming.\n * @returns A `ReadableStream` of the `Blob`'s data.\n */\n stream(): ReadableStream;\n\n /**\n * @returns Promise resolving to the `Blob`'s binary data as an `ArrayBuffer`.\n */\n arrayBuffer(): Promise<ArrayBuffer>;\n}\n\n/**\n * @hidden\n * @private\n */\nexport declare class NativeBlob extends SharedObject {\n readonly size: number;\n readonly type: string;\n\n constructor(blobParts?: BlobPart[], options?: BlobPropertyBag);\n\n slice(start?: number, end?: number, contentType?: string): Blob;\n bytes(): Promise<Uint8Array>;\n text(): Promise<string>;\n}\n\n/**\n * @hidden\n * @private\n */\nexport declare class ExpoBlobModule extends NativeModule {\n Blob: typeof NativeBlob;\n}\n\n/**\n * Represents a part of a `Blob`. Can be a `string`, `ArrayBuffer`, `ArrayBufferView`, or another `Blob`.\n */\nexport type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob;\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const ExpoBlob: {
2
+ new (blobParts?: BlobPart[], options?: BlobPropertyBag): Blob;
3
+ prototype: Blob;
4
+ };
5
+ //# sourceMappingURL=ExpoBlob.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.web.d.ts","sourceRoot":"","sources":["../src/ExpoBlob.web.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ;;;CAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const ExpoBlob = Blob;
2
+ //# sourceMappingURL=ExpoBlob.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpoBlob.web.js","sourceRoot":"","sources":["../src/ExpoBlob.web.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC","sourcesContent":["export const ExpoBlob = Blob;\n"]}
@@ -0,0 +1,3 @@
1
+ export * from './ExpoBlob';
2
+ export type { BlobPart } from './ExpoBlob.types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './ExpoBlob';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC","sourcesContent":["export * from './ExpoBlob';\nexport type { BlobPart } from './ExpoBlob.types';\n"]}