expo-file-system 56.0.5 → 56.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/android/build.gradle +2 -3
  3. package/build/Directory.d.ts.map +1 -1
  4. package/build/File.d.ts +2 -0
  5. package/build/File.d.ts.map +1 -1
  6. package/expo-module.config.json +1 -1
  7. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.module → 56.0.6/expo.modules.filesystem-56.0.6.module} +7 -21
  8. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.module.md5 +1 -0
  9. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.module.sha1 +1 -0
  10. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.module.sha256 +1 -0
  11. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.module.sha512 +1 -0
  12. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.pom → 56.0.6/expo.modules.filesystem-56.0.6.pom} +1 -7
  13. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.pom.md5 +1 -0
  14. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.pom.sha1 +1 -0
  15. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.pom.sha256 +1 -0
  16. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.6/expo.modules.filesystem-56.0.6.pom.sha512 +1 -0
  17. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml +4 -4
  18. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.md5 +1 -1
  19. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha1 +1 -1
  20. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha256 +1 -1
  21. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha512 +1 -1
  22. package/mocks/FileSystem.ts +787 -51
  23. package/package.json +5 -5
  24. package/prebuilds/output/debug/xcframeworks/ExpoFileSystem.tar.gz +0 -0
  25. package/prebuilds/output/release/xcframeworks/ExpoFileSystem.tar.gz +0 -0
  26. package/src/Directory.ts +0 -2
  27. package/src/File.ts +10 -0
  28. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.module.md5 +0 -1
  29. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.module.sha1 +0 -1
  30. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.module.sha256 +0 -1
  31. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.module.sha512 +0 -1
  32. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.pom.md5 +0 -1
  33. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.pom.sha1 +0 -1
  34. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.pom.sha256 +0 -1
  35. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/56.0.5/expo.modules.filesystem-56.0.5.pom.sha512 +0 -1
  36. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5-sources.jar → 56.0.6/expo.modules.filesystem-56.0.6-sources.jar} +0 -0
  37. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5-sources.jar.md5 → 56.0.6/expo.modules.filesystem-56.0.6-sources.jar.md5} +0 -0
  38. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5-sources.jar.sha1 → 56.0.6/expo.modules.filesystem-56.0.6-sources.jar.sha1} +0 -0
  39. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5-sources.jar.sha256 → 56.0.6/expo.modules.filesystem-56.0.6-sources.jar.sha256} +0 -0
  40. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5-sources.jar.sha512 → 56.0.6/expo.modules.filesystem-56.0.6-sources.jar.sha512} +0 -0
  41. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.aar → 56.0.6/expo.modules.filesystem-56.0.6.aar} +0 -0
  42. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.aar.md5 → 56.0.6/expo.modules.filesystem-56.0.6.aar.md5} +0 -0
  43. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.aar.sha1 → 56.0.6/expo.modules.filesystem-56.0.6.aar.sha1} +0 -0
  44. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.aar.sha256 → 56.0.6/expo.modules.filesystem-56.0.6.aar.sha256} +0 -0
  45. /package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{56.0.5/expo.modules.filesystem-56.0.5.aar.sha512 → 56.0.6/expo.modules.filesystem-56.0.6.aar.sha512} +0 -0
package/CHANGELOG.md CHANGED
@@ -10,6 +10,20 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 56.0.6 — 2026-05-19
14
+
15
+ ### 🎉 New features
16
+
17
+ - Added `File.json()` and `File.formData()` to satisfy `Blob` type augmentations in consumer projects. ([#45685](https://github.com/expo/expo/pull/45685) by [@mvincentong](https://github.com/mvincentong))
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - Fixed the jest mock's `FileSystemFileHandle` to honor `FileMode` string values (`'r'`, `'w'`, `'wa'`, `'wt'`, `'rw'`) instead of the obsolete name table, expose handle and file metadata, and match the documented default mode for `content://` URIs. The dispatch is now exhaustive over `FileMode`, so future enum additions fail to compile in the mock. (by [@radko93](https://github.com/radko93))
22
+
23
+ ### 💡 Others
24
+
25
+ - [Android] Remove unused native dependencies. ([#45939](https://github.com/expo/expo/pull/45939) by [@jakex7](https://github.com/jakex7))
26
+
13
27
  ## 56.0.5 — 2026-05-15
14
28
 
15
29
  ### 💡 Others
@@ -64,6 +78,7 @@ _This version does not introduce any user-facing changes._
64
78
 
65
79
  ### 💡 Others
66
80
 
81
+ - Add behavioral Jest mocks for the class-based `File` / `Directory` / `Paths` API, backed by an in-memory filesystem so tests can exercise create/write/read/move/copy/delete roundtrips without hand-rolling a `jest.mock`. ([#45027](https://github.com/expo/expo/pull/45027) by [@radko93](https://github.com/radko93))
67
82
  - Deprecate `modificationTime` in favor of new `lastModified`, compatible with web `File` interface. ([#43411](https://github.com/expo/expo/pull/43411) by [@HubertBer](https://github.com/HubertBer))
68
83
  - Deprecate `File.pickFileAsync(arg1, arg2)` in favour of new `File.pickFileAsync(options)`. ([#43411](https://github.com/expo/expo/pull/43411) by [@HubertBer](https://github.com/HubertBer))
69
84
  - [Android] Optimized performance of copy and move operations. ([#44357](https://github.com/expo/expo/pull/44357), [#44358](https://github.com/expo/expo/pull/44358) by [@barthap](https://github.com/barthap))
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '56.0.5'
7
+ version = '56.0.6'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.filesystem"
11
11
  defaultConfig {
12
12
  versionCode 30
13
- versionName "56.0.5"
13
+ versionName "56.0.6"
14
14
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15
15
  }
16
16
  testOptions {
@@ -24,7 +24,6 @@ dependencies {
24
24
  api 'com.squareup.okhttp3:okhttp:4.9.2'
25
25
  api 'com.squareup.okhttp3:okhttp-urlconnection:4.9.2'
26
26
  api 'com.squareup.okio:okio:2.9.0'
27
- api "androidx.legacy:legacy-support-v4:1.0.0"
28
27
  api "androidx.documentfile:documentfile:1.1.0"
29
28
  implementation 'com.facebook.react:react-android'
30
29
 
@@ -1 +1 @@
1
- {"version":3,"file":"Directory.d.ts","sourceRoot":"","sources":["../src/Directory.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAI7F;;;;;;;;;;GAUG;AACH,qBAAa,SAAU,SAAQ,cAAc,CAAC,mBAAmB;IAC/D,MAAM,CAAC,kBAAkB,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvE;;;;;;;;;OASG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;;OAIG;IACM,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAOrC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKvD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAIxC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CACH,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,GAAG,SAAS,CAAC,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,YAAY,GACrB,iBAAiB;CAQrB"}
1
+ {"version":3,"file":"Directory.d.ts","sourceRoot":"","sources":["../src/Directory.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAI7F;;;;;;;;;;GAUG;AACH,qBAAa,SAAU,SAAQ,cAAc,CAAC,mBAAmB;IAC/D,MAAM,CAAC,kBAAkB,EAAE,CAAC,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvE;;;;;;;;;OASG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;;OAIG;IACM,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAMrC;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIvD,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS;IAIxC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CACH,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,GAAG,SAAS,CAAC,KAAK,IAAI,EACvD,OAAO,CAAC,EAAE,YAAY,GACrB,iBAAiB;CAQrB"}
package/build/File.d.ts CHANGED
@@ -103,6 +103,8 @@ export declare class File extends ExpoFileSystem.FileSystemFile implements Blob
103
103
  readableStream(): ReadableStream<Uint8Array<ArrayBuffer>>;
104
104
  writableStream(): WritableStream<Uint8Array<ArrayBufferLike>>;
105
105
  arrayBuffer(): Promise<ArrayBuffer>;
106
+ json(): Promise<any>;
107
+ formData(): ReturnType<Response['formData']>;
106
108
  stream(): ReadableStream<Uint8Array<ArrayBuffer>>;
107
109
  slice(start?: number, end?: number, contentType?: string): Blob;
108
110
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"File.d.ts","sourceRoot":"","sources":["../src/File.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAK9B;;;;;;;;;;GAUG;AACH,qBAAa,IAAK,SAAQ,cAAc,CAAC,cAAe,YAAW,IAAI;IACrE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnB;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IACpF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAC1F;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IA8BpF;;;;;;;;;OASG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,cAAc;IAId,cAAc;IAMR,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAKzC,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAIjD,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/D;;;;;;;;;;OAUG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAInE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,UAAU;IAIlE;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,kBAAkB,CACvB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,IAAI,GAAG,SAAS,EAC7B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY;IAIf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,iBAAiB;CAG9F"}
1
+ {"version":3,"file":"File.d.ts","sourceRoot":"","sources":["../src/File.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAK9B;;;;;;;;;;GAUG;AACH,qBAAa,IAAK,SAAQ,cAAc,CAAC,cAAe,YAAW,IAAI;IACrE;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,iBAAiB,EAAE,CACxB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,GAAG,IAAI,EAC7B,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnB;;;;;;OAMG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IACpF;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAC1F;;;;;;;;;OASG;IACH,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,EAAE,CAAC;IA8BpF;;;;;;;;;OASG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,cAAc;IAId,cAAc;IAMR,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAKnC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;IAIpB,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAMlD,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAIjD,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/D;;;;;;;;;;OAUG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAInE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,UAAU;IAIlE;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,kBAAkB,CACvB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,IAAI,GAAG,SAAS,EAC7B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY;IAIf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,iBAAiB;CAG9F"}
@@ -12,7 +12,7 @@
12
12
  "publication": {
13
13
  "groupId": "host.exp.exponent",
14
14
  "artifactId": "expo.modules.filesystem",
15
- "version": "56.0.5",
15
+ "version": "56.0.6",
16
16
  "repository": "local-maven-repo"
17
17
  }
18
18
  }
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.filesystem",
6
- "version": "56.0.5",
6
+ "version": "56.0.6",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -58,13 +58,6 @@
58
58
  "requires": "2.9.0"
59
59
  }
60
60
  },
61
- {
62
- "group": "androidx.legacy",
63
- "module": "legacy-support-v4",
64
- "version": {
65
- "requires": "1.0.0"
66
- }
67
- },
68
61
  {
69
62
  "group": "androidx.documentfile",
70
63
  "module": "documentfile",
@@ -75,8 +68,8 @@
75
68
  ],
76
69
  "files": [
77
70
  {
78
- "name": "expo.modules.filesystem-56.0.5.aar",
79
- "url": "expo.modules.filesystem-56.0.5.aar",
71
+ "name": "expo.modules.filesystem-56.0.6.aar",
72
+ "url": "expo.modules.filesystem-56.0.6.aar",
80
73
  "size": 689001,
81
74
  "sha512": "9d5a05c062694b0a80f9830a2e39a462f8e1683c304bc0496b64203468706987369437e74cfdeee056f013de1602097b6e4ff1dda3384973ea78a0fedcf30d03",
82
75
  "sha256": "66c88fad23aaf1b650784ba1ef259cf4f0141278893a1b42472ef36b977ccd93",
@@ -147,13 +140,6 @@
147
140
  "requires": "2.9.0"
148
141
  }
149
142
  },
150
- {
151
- "group": "androidx.legacy",
152
- "module": "legacy-support-v4",
153
- "version": {
154
- "requires": "1.0.0"
155
- }
156
- },
157
143
  {
158
144
  "group": "androidx.documentfile",
159
145
  "module": "documentfile",
@@ -171,8 +157,8 @@
171
157
  ],
172
158
  "files": [
173
159
  {
174
- "name": "expo.modules.filesystem-56.0.5.aar",
175
- "url": "expo.modules.filesystem-56.0.5.aar",
160
+ "name": "expo.modules.filesystem-56.0.6.aar",
161
+ "url": "expo.modules.filesystem-56.0.6.aar",
176
162
  "size": 689001,
177
163
  "sha512": "9d5a05c062694b0a80f9830a2e39a462f8e1683c304bc0496b64203468706987369437e74cfdeee056f013de1602097b6e4ff1dda3384973ea78a0fedcf30d03",
178
164
  "sha256": "66c88fad23aaf1b650784ba1ef259cf4f0141278893a1b42472ef36b977ccd93",
@@ -191,8 +177,8 @@
191
177
  },
192
178
  "files": [
193
179
  {
194
- "name": "expo.modules.filesystem-56.0.5-sources.jar",
195
- "url": "expo.modules.filesystem-56.0.5-sources.jar",
180
+ "name": "expo.modules.filesystem-56.0.6-sources.jar",
181
+ "url": "expo.modules.filesystem-56.0.6-sources.jar",
196
182
  "size": 49979,
197
183
  "sha512": "3bdf553eb365ff56c8d0ca441a0fbe5d58a53d94f0f2dc565579dfa6124318c1d3a929d7bbd81efffced17716316872c0c2dbadb03795b1cb9a707741da486b3",
198
184
  "sha256": "6fb4f4458a682db136e3f8316c2c72449f51df3c9067dfab4adb7320e4cd87a0",
@@ -0,0 +1 @@
1
+ 8bb9edfa93e2e55af08f2fe6f06b864d62bb3702ddca2c1a947174350f7cfd87
@@ -0,0 +1 @@
1
+ 61b3a1297673a1c18140ac284bb6e38d86ec406b40966aa5bec804e79b26a775de8024a0dc499f53b9e926a82595223fb9be379ac87e0c6869735973b62e0531
@@ -9,7 +9,7 @@
9
9
  <modelVersion>4.0.0</modelVersion>
10
10
  <groupId>host.exp.exponent</groupId>
11
11
  <artifactId>expo.modules.filesystem</artifactId>
12
- <version>56.0.5</version>
12
+ <version>56.0.6</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.filesystem</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -67,12 +67,6 @@
67
67
  <version>2.9.0</version>
68
68
  <scope>compile</scope>
69
69
  </dependency>
70
- <dependency>
71
- <groupId>androidx.legacy</groupId>
72
- <artifactId>legacy-support-v4</artifactId>
73
- <version>1.0.0</version>
74
- <scope>compile</scope>
75
- </dependency>
76
70
  <dependency>
77
71
  <groupId>androidx.documentfile</groupId>
78
72
  <artifactId>documentfile</artifactId>
@@ -0,0 +1 @@
1
+ b0e49cf8d66adb123b596568005e4ebf6a5475a43850db6e49097baf39179ec5
@@ -0,0 +1 @@
1
+ 3912c1bd64aa96e0a94f5ab76ff34175f3e3939136d4671dbec661c22d79ec6d4d1d175ff1f55367cac6645d77724b1a9d7eb2ccd63385e70d059ff36b3a1dab
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.filesystem</artifactId>
5
5
  <versioning>
6
- <latest>56.0.5</latest>
7
- <release>56.0.5</release>
6
+ <latest>56.0.6</latest>
7
+ <release>56.0.6</release>
8
8
  <versions>
9
- <version>56.0.5</version>
9
+ <version>56.0.6</version>
10
10
  </versions>
11
- <lastUpdated>20260515152500</lastUpdated>
11
+ <lastUpdated>20260519105313</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- b8070f789142e19815310c77d494db60
1
+ 76f4067aaa57513178fe3643c7f4fcee
@@ -1 +1 @@
1
- 8d958b446d8a7db5eab6d6e2e01f207c178eb992
1
+ 333cc2767c02f5b952922e0568c4c55adcdfbab3
@@ -1 +1 @@
1
- b2a1e6d073b5fd57f4c2214864a487c19f88f7163b4fb1419022acdc1f3b968f
1
+ 16478284a6e3560c72a519a3c7368ca80eb11795fb5a384ec3235bab9d8e2883
@@ -1 +1 @@
1
- b3a158c00306aa15a609b8ca88cc124c03ab1b80b6788f33482550e93b869040467f7fe4d38d6e2fda9253949327fe34c4c964b2fb37c327fc873520fec41f63
1
+ 5aae03f2854561fed3217710ced5e4ef5fef073e5d075c2dcecd8d1f4c80e2fbdcb3f2960cbe5278428ccd9bf240c1c09c5b92c422eebc28350e62a05e5e8c02
@@ -1,87 +1,728 @@
1
- export type URL = string;
1
+ /**
2
+ * Hand-maintained mock for the FileSystem native module.
3
+ *
4
+ * Backs the class-based API (`File`, `Directory`, `Paths` from
5
+ * `expo-file-system`) with a small in-memory filesystem so tests can exercise
6
+ * create/write/read/move/copy/delete end-to-end. This module is what
7
+ * `jest-expo`'s preset feeds to `requireNativeModule('FileSystem')`.
8
+ * Timestamps use a logical clock reset by `__resetMockFileSystem()`; do not use
9
+ * `Date.now()` here.
10
+ *
11
+ * DO NOT regenerate this file with `expo-modules-test-core` — the generator
12
+ * emits a bare stub and will overwrite the behavior here. Same pattern as
13
+ * `packages/expo-crypto/mocks/ExpoCryptoAES.ts`.
14
+ */
2
15
 
3
- export type FileSystemPath = any;
16
+ import { FileMode } from '../src/File.types';
4
17
 
18
+ export type URL = string;
19
+ export type FileSystemPath = any;
5
20
  export type DownloadOptions = any;
6
-
7
21
  export type InfoOptions = any;
8
-
9
22
  export type TypedArray = any;
10
-
11
23
  export type CreateOptions = any;
12
24
 
13
25
  export const documentDirectory = 'file:///mock/document/';
14
26
  export const cacheDirectory = 'file:///mock/cache/';
15
27
  export const bundleDirectory = 'file:///mock/bundle/';
16
- export const totalDiskSpace = 1000000000;
17
- export const availableDiskSpace = 500000000;
28
+ export const appleSharedContainers: Record<string, string> = {};
29
+ export const totalDiskSpace = 1_000_000_000;
30
+ export const availableDiskSpace = 500_000_000;
18
31
 
19
- export function info(url: URL): any {}
32
+ type Entry = {
33
+ kind: 'file' | 'dir';
34
+ bytes?: Uint8Array;
35
+ type?: string | null;
36
+ exists: boolean;
37
+ createdAt?: number;
38
+ modifiedAt?: number;
39
+ };
20
40
 
21
- export async function downloadFileAsync(
22
- url: URL,
23
- to: FileSystemPath,
24
- options: DownloadOptions | undefined,
25
- uuid: string | undefined
26
- ): Promise<any> {}
41
+ const store = new Map<string, Entry>();
42
+
43
+ const SEED_DIRS = [documentDirectory, cacheDirectory, bundleDirectory];
44
+
45
+ function seed() {
46
+ const t = nextMockTimestamp();
47
+ for (const uri of SEED_DIRS) {
48
+ store.set(normalizeKey(uri), { kind: 'dir', exists: true, createdAt: t, modifiedAt: t });
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Logical clock used for `createdAt` / `modifiedAt` on stored entries.
54
+ * Resetting the mock filesystem also resets it so each test sees a stable
55
+ * sequence of timestamps independent of wall-clock time.
56
+ */
57
+ let mockClock = 0;
58
+
59
+ function nextMockTimestamp(): number {
60
+ return ++mockClock;
61
+ }
62
+
63
+ /**
64
+ * Test-only helper: reset the in-memory filesystem to a clean state with only
65
+ * the canonical `document`, `cache`, and `bundle` directories seeded.
66
+ * Call from a `beforeEach` to keep tests isolated.
67
+ */
68
+ export function __resetMockFileSystem() {
69
+ store.clear();
70
+ listeners.clear();
71
+ cancelled.clear();
72
+ mockClock = 0;
73
+ seed();
74
+ }
75
+
76
+ seed();
77
+
78
+ function normalizeKey(uri: string): string {
79
+ return uri.endsWith('/') ? uri.slice(0, -1) : uri;
80
+ }
27
81
 
28
- export async function pickDirectoryAsync(initialUri?: string): Promise<any> {}
82
+ function basename(uri: string): string {
83
+ const key = normalizeKey(uri);
84
+ const i = key.lastIndexOf('/');
85
+ return i === -1 ? key : key.slice(i + 1);
86
+ }
29
87
 
30
- export async function pickFileAsync(initialUri?: string, mimeType?: string): Promise<any> {}
88
+ function parentOf(uri: string): string {
89
+ const key = normalizeKey(uri);
90
+ const i = key.lastIndexOf('/');
91
+ return i === -1 ? '' : key.slice(0, i);
92
+ }
93
+
94
+ function joinUri(dir: string, name: string): string {
95
+ const base = normalizeKey(dir);
96
+ return `${base}/${name}`;
97
+ }
98
+
99
+ function utf8Encode(s: string): Uint8Array {
100
+ return new TextEncoder().encode(s);
101
+ }
102
+
103
+ function utf8Decode(b: Uint8Array): string {
104
+ return new TextDecoder('utf-8').decode(b);
105
+ }
106
+
107
+ function base64Encode(bytes: Uint8Array): string {
108
+ return Buffer.from(bytes).toString('base64');
109
+ }
110
+
111
+ function base64Decode(str: string): Uint8Array {
112
+ return new Uint8Array(Buffer.from(str, 'base64'));
113
+ }
114
+
115
+ function fakeMd5(uri: string, size: number): string {
116
+ let h = 0x811c9dc5;
117
+ const src = `${uri}:${size}`;
118
+ for (let i = 0; i < src.length; i++) {
119
+ h = Math.imul(h ^ src.charCodeAt(i), 0x01000193);
120
+ }
121
+ const hex = (h >>> 0).toString(16).padStart(8, '0');
122
+ return hex.repeat(4).slice(0, 32);
123
+ }
124
+
125
+ const listeners = new Map<string, Set<(event: any) => void>>();
126
+ const cancelled = new Set<string>();
127
+
128
+ function emit(event: string, data: any) {
129
+ const set = listeners.get(event);
130
+ if (!set) return;
131
+ for (const fn of set) {
132
+ try {
133
+ fn(data);
134
+ } catch {
135
+ /* ignore listener errors in the mock */
136
+ }
137
+ }
138
+ }
139
+
140
+ export function addListener(event: string, handler: (data: any) => void) {
141
+ let set = listeners.get(event);
142
+ if (!set) {
143
+ set = new Set();
144
+ listeners.set(event, set);
145
+ }
146
+ set.add(handler);
147
+ return {
148
+ remove: () => {
149
+ set!.delete(handler);
150
+ },
151
+ };
152
+ }
153
+
154
+ export function info(uri: string): { exists: boolean; isDirectory: boolean | null } {
155
+ const entry = store.get(normalizeKey(uri));
156
+ if (!entry || !entry.exists) return { exists: false, isDirectory: null };
157
+ return { exists: true, isDirectory: entry.kind === 'dir' };
158
+ }
159
+
160
+ function assertParent(uri: string, allowMissing: boolean) {
161
+ const parent = normalizeKey(parentOf(uri));
162
+ if (!parent) return;
163
+ const entry = store.get(parent);
164
+ if (entry?.exists) return;
165
+ if (!allowMissing) {
166
+ throw new Error('Parent directory does not exist');
167
+ }
168
+ const missing: string[] = [];
169
+ let cursor = parent;
170
+ // Stop walking once we've peeled back to the scheme (e.g. "file://") so we
171
+ // don't seed nonsense keys above the root.
172
+ while (cursor && cursor.length >= 8 && !store.get(cursor)?.exists) {
173
+ missing.unshift(cursor);
174
+ const next = normalizeKey(parentOf(cursor));
175
+ if (next === cursor) break;
176
+ cursor = next;
177
+ }
178
+ const now = nextMockTimestamp();
179
+ for (const dirKey of missing) {
180
+ store.set(dirKey, { kind: 'dir', exists: true, createdAt: now, modifiedAt: now });
181
+ }
182
+ }
31
183
 
32
- export function cancelDownloadAsync(uuid: string): void {}
33
184
  export class FileSystemFile {
34
185
  uri: string;
35
- exists: boolean = false;
186
+
36
187
  constructor(uri: string) {
37
188
  this.uri = uri;
38
189
  }
39
- validatePath(): any {}
40
- textSync(): any {}
41
- base64Sync(): any {}
42
- bytesSync(): any {}
43
- open(): any {}
44
- info(options: InfoOptions | undefined): any {}
45
- write(content: string | TypedArray): any {}
46
- delete(): any {}
47
- create(options: CreateOptions | undefined): any {}
48
- async copy(to: FileSystemPath): Promise<any> {}
49
- copySync(to: FileSystemPath): any {}
50
- async move(to: FileSystemPath): Promise<any> {}
51
- moveSync(to: FileSystemPath): any {}
52
- rename(newName: string): any {}
53
- async text(): Promise<any> {}
54
- async base64(): Promise<any> {}
55
- async bytes(): Promise<any> {}
190
+
191
+ validatePath(): void {}
192
+
193
+ get exists(): boolean {
194
+ const entry = store.get(normalizeKey(this.uri));
195
+ return !!(entry && entry.kind === 'file' && entry.exists);
196
+ }
197
+
198
+ get size(): number {
199
+ const entry = store.get(normalizeKey(this.uri));
200
+ return entry && entry.kind === 'file' && entry.exists ? (entry.bytes?.length ?? 0) : 0;
201
+ }
202
+
203
+ get type(): string {
204
+ const entry = store.get(normalizeKey(this.uri));
205
+ return entry?.type ?? '';
206
+ }
207
+
208
+ get md5(): string | null {
209
+ return this.exists ? fakeMd5(this.uri, this.size) : null;
210
+ }
211
+
212
+ get modificationTime(): number | null {
213
+ const entry = store.get(normalizeKey(this.uri));
214
+ return entry?.exists ? (entry.modifiedAt ?? null) : null;
215
+ }
216
+
217
+ get lastModified(): number | null {
218
+ return this.modificationTime;
219
+ }
220
+
221
+ get creationTime(): number | null {
222
+ const entry = store.get(normalizeKey(this.uri));
223
+ return entry?.exists ? (entry.createdAt ?? null) : null;
224
+ }
225
+
226
+ get contentUri(): string {
227
+ return '';
228
+ }
229
+
230
+ create(options: { intermediates?: boolean; overwrite?: boolean } = {}): void {
231
+ const key = normalizeKey(this.uri);
232
+ const existing = store.get(key);
233
+ if (existing?.exists) {
234
+ if (!options.overwrite) {
235
+ throw new Error('File already exists');
236
+ }
237
+ }
238
+ assertParent(this.uri, !!options.intermediates);
239
+ const now = nextMockTimestamp();
240
+ store.set(key, {
241
+ kind: 'file',
242
+ bytes: new Uint8Array(0),
243
+ exists: true,
244
+ createdAt: now,
245
+ modifiedAt: now,
246
+ });
247
+ }
248
+
249
+ write(
250
+ content: string | Uint8Array,
251
+ options: { append?: boolean; encoding?: 'utf8' | 'base64' } = {}
252
+ ): void {
253
+ assertParent(this.uri, false);
254
+ let bytes: Uint8Array;
255
+ if (typeof content === 'string') {
256
+ bytes = options.encoding === 'base64' ? base64Decode(content) : utf8Encode(content);
257
+ } else {
258
+ bytes = new Uint8Array(content);
259
+ }
260
+ const key = normalizeKey(this.uri);
261
+ const existing = store.get(key);
262
+ const now = nextMockTimestamp();
263
+ const createdAt = existing?.exists ? (existing.createdAt ?? now) : now;
264
+ const type = existing?.type ?? null;
265
+ if (options.append) {
266
+ const prior = existing?.bytes ?? new Uint8Array(0);
267
+ const merged = new Uint8Array(prior.length + bytes.length);
268
+ merged.set(prior, 0);
269
+ merged.set(bytes, prior.length);
270
+ store.set(key, {
271
+ kind: 'file',
272
+ bytes: merged,
273
+ type,
274
+ exists: true,
275
+ createdAt,
276
+ modifiedAt: now,
277
+ });
278
+ } else {
279
+ store.set(key, { kind: 'file', bytes, type, exists: true, createdAt, modifiedAt: now });
280
+ }
281
+ }
282
+
283
+ private readBytesOrThrow(): Uint8Array {
284
+ const entry = store.get(normalizeKey(this.uri));
285
+ if (!entry || entry.kind !== 'file' || !entry.exists) {
286
+ throw new Error('File does not exist');
287
+ }
288
+ return entry.bytes ?? new Uint8Array(0);
289
+ }
290
+
291
+ textSync(): string {
292
+ return utf8Decode(this.readBytesOrThrow());
293
+ }
294
+
295
+ base64Sync(): string {
296
+ return base64Encode(this.readBytesOrThrow());
297
+ }
298
+
299
+ bytesSync(): Uint8Array {
300
+ return new Uint8Array(this.readBytesOrThrow());
301
+ }
302
+
303
+ async text(): Promise<string> {
304
+ return this.textSync();
305
+ }
306
+
307
+ async base64(): Promise<string> {
308
+ return this.base64Sync();
309
+ }
310
+
311
+ async bytes(): Promise<Uint8Array> {
312
+ return this.bytesSync();
313
+ }
314
+
315
+ info(options: { md5?: boolean } = {}): any {
316
+ const entry = store.get(normalizeKey(this.uri));
317
+ if (!entry || entry.kind !== 'file' || !entry.exists) {
318
+ return { exists: false, uri: this.uri };
319
+ }
320
+ const size = entry.bytes?.length ?? 0;
321
+ return {
322
+ exists: true,
323
+ uri: this.uri,
324
+ size,
325
+ modificationTime: entry.modifiedAt ?? null,
326
+ creationTime: entry.createdAt ?? null,
327
+ ...(options.md5 ? { md5: fakeMd5(this.uri, size) } : {}),
328
+ };
329
+ }
330
+
331
+ open(mode?: FileMode): FileSystemFileHandle {
332
+ const key = normalizeKey(this.uri);
333
+ const entry = store.get(key);
334
+ if (!entry || entry.kind !== 'file' || !entry.exists) {
335
+ throw new Error('File does not exist');
336
+ }
337
+ return new FileSystemFileHandle(key, mode ?? defaultModeForUri(this.uri));
338
+ }
339
+
340
+ delete(): void {
341
+ const key = normalizeKey(this.uri);
342
+ const entry = store.get(key);
343
+ if (!entry || !entry.exists) {
344
+ throw new Error('File does not exist');
345
+ }
346
+ store.delete(key);
347
+ }
348
+
349
+ private resolveDestination(destination: FileSystemFile | FileSystemDirectory): string {
350
+ if (destination instanceof FileSystemDirectory) {
351
+ return joinUri(destination.uri, basename(this.uri));
352
+ }
353
+ return normalizeKey(destination.uri);
354
+ }
355
+
356
+ copySync(
357
+ destination: FileSystemFile | FileSystemDirectory,
358
+ options: { overwrite?: boolean } = {}
359
+ ): void {
360
+ const srcKey = normalizeKey(this.uri);
361
+ const entry = store.get(srcKey);
362
+ if (!entry || entry.kind !== 'file' || !entry.exists) {
363
+ throw new Error('File does not exist');
364
+ }
365
+ const destKey = this.resolveDestination(destination);
366
+ const destExisting = store.get(destKey);
367
+ if (destExisting?.exists && !options.overwrite) {
368
+ throw new Error('Destination already exists');
369
+ }
370
+ assertParent(destKey, false);
371
+ const now = nextMockTimestamp();
372
+ store.set(destKey, {
373
+ kind: 'file',
374
+ bytes: new Uint8Array(entry.bytes ?? new Uint8Array(0)),
375
+ type: entry.type ?? null,
376
+ exists: true,
377
+ createdAt: now,
378
+ modifiedAt: now,
379
+ });
380
+ }
381
+
382
+ async copy(
383
+ destination: FileSystemFile | FileSystemDirectory,
384
+ options: { overwrite?: boolean } = {}
385
+ ): Promise<void> {
386
+ this.copySync(destination, options);
387
+ }
388
+
389
+ moveSync(
390
+ destination: FileSystemFile | FileSystemDirectory,
391
+ options: { overwrite?: boolean } = {}
392
+ ): void {
393
+ const srcKey = normalizeKey(this.uri);
394
+ this.copySync(destination, options);
395
+ const destKey = this.resolveDestination(destination);
396
+ if (destKey !== srcKey) {
397
+ store.delete(srcKey);
398
+ }
399
+ this.uri = destKey;
400
+ }
401
+
402
+ async move(
403
+ destination: FileSystemFile | FileSystemDirectory,
404
+ options: { overwrite?: boolean } = {}
405
+ ): Promise<void> {
406
+ this.moveSync(destination, options);
407
+ }
408
+
409
+ rename(newName: string): void {
410
+ const srcKey = normalizeKey(this.uri);
411
+ const entry = store.get(srcKey);
412
+ if (!entry || !entry.exists) {
413
+ throw new Error('File does not exist');
414
+ }
415
+ const destKey = joinUri(parentOf(this.uri), newName);
416
+ store.set(destKey, entry);
417
+ store.delete(srcKey);
418
+ this.uri = destKey;
419
+ }
420
+
56
421
  watch(_callback: any, _options?: any): { remove: () => void } {
57
422
  return { remove: () => {} };
58
423
  }
59
424
  }
60
425
 
61
426
  export class FileSystemFileHandle {
62
- readBytes(bytes: number): any {}
63
- writeBytes(bytes: TypedArray): any {}
64
- close(): any {}
427
+ private readonly key: string;
428
+ private readonly readOnly: boolean;
429
+ private readonly writeOnly: boolean;
430
+ private cursor: number;
431
+ private closed = false;
432
+
433
+ constructor(key: string, mode: FileMode) {
434
+ this.key = key;
435
+ const entry = store.get(key);
436
+ switch (mode) {
437
+ case FileMode.ReadWrite:
438
+ this.readOnly = false;
439
+ this.writeOnly = false;
440
+ this.cursor = 0;
441
+ break;
442
+ case FileMode.ReadOnly:
443
+ this.readOnly = true;
444
+ this.writeOnly = false;
445
+ this.cursor = 0;
446
+ break;
447
+ case FileMode.WriteOnly:
448
+ this.readOnly = false;
449
+ this.writeOnly = true;
450
+ this.cursor = 0;
451
+ break;
452
+ case FileMode.Append:
453
+ this.readOnly = false;
454
+ this.writeOnly = true;
455
+ this.cursor = entry?.bytes?.length ?? 0;
456
+ break;
457
+ case FileMode.Truncate:
458
+ this.readOnly = false;
459
+ this.writeOnly = true;
460
+ store.set(key, {
461
+ kind: 'file',
462
+ bytes: new Uint8Array(0),
463
+ type: entry?.type ?? null,
464
+ exists: true,
465
+ createdAt: entry?.createdAt ?? nextMockTimestamp(),
466
+ modifiedAt: nextMockTimestamp(),
467
+ });
468
+ this.cursor = 0;
469
+ break;
470
+ default:
471
+ assertNever(mode);
472
+ }
473
+ }
474
+
475
+ private ensureOpen() {
476
+ if (this.closed) throw new Error('File handle is closed');
477
+ }
478
+
479
+ get offset(): number | null {
480
+ return this.closed ? null : this.cursor;
481
+ }
482
+
483
+ set offset(value: number | null) {
484
+ if (this.closed || value == null) {
485
+ return;
486
+ }
487
+ this.cursor = value;
488
+ }
489
+
490
+ get size(): number | null {
491
+ if (this.closed) {
492
+ return null;
493
+ }
494
+ const entry = store.get(this.key);
495
+ return entry?.bytes?.length ?? 0;
496
+ }
497
+
498
+ readBytes(count: number): Uint8Array {
499
+ this.ensureOpen();
500
+ if (this.writeOnly) throw new Error('File handle is write-only');
501
+ const entry = store.get(this.key);
502
+ const bytes = entry?.bytes ?? new Uint8Array(0);
503
+ const slice = bytes.slice(this.cursor, this.cursor + count);
504
+ this.cursor += slice.length;
505
+ return slice;
506
+ }
507
+
508
+ writeBytes(buffer: Uint8Array): void {
509
+ this.ensureOpen();
510
+ if (this.readOnly) throw new Error('File handle is read-only');
511
+ const entry = store.get(this.key) ?? {
512
+ kind: 'file' as const,
513
+ bytes: new Uint8Array(0),
514
+ exists: true,
515
+ };
516
+ const prior = entry.bytes ?? new Uint8Array(0);
517
+ const writeOffset = Math.min(this.cursor, prior.length);
518
+ const newLength = Math.max(prior.length, writeOffset + buffer.length);
519
+ const merged = new Uint8Array(newLength);
520
+ merged.set(prior, 0);
521
+ merged.set(buffer, writeOffset);
522
+ store.set(this.key, {
523
+ ...entry,
524
+ kind: 'file',
525
+ bytes: merged,
526
+ exists: true,
527
+ modifiedAt: nextMockTimestamp(),
528
+ });
529
+ this.cursor = writeOffset + buffer.length;
530
+ }
531
+
532
+ close(): void {
533
+ this.closed = true;
534
+ }
535
+ }
536
+
537
+ function assertNever(value: never): never {
538
+ throw new Error(`Unhandled FileMode in jest-expo mock: ${String(value)}`);
539
+ }
540
+
541
+ function defaultModeForUri(uri: string): FileMode {
542
+ return uri.startsWith('content://') ? FileMode.ReadOnly : FileMode.ReadWrite;
65
543
  }
66
544
 
67
545
  export class FileSystemDirectory {
68
546
  uri: string;
69
- exists: boolean = false;
547
+
70
548
  constructor(uri: string) {
71
549
  this.uri = uri;
72
550
  }
73
- info(): any {}
74
- validatePath(): any {}
75
- delete(): any {}
76
- create(options: CreateOptions | undefined): any {}
77
- async copy(to: FileSystemPath): Promise<any> {}
78
- copySync(to: FileSystemPath): any {}
79
- async move(to: FileSystemPath): Promise<any> {}
80
- moveSync(to: FileSystemPath): any {}
81
- rename(newName: string): any {}
82
- listAsRecords(): any {}
83
- createFile(name: string, mimeType: string | null): any {}
84
- createDirectory(name: string): any {}
551
+
552
+ validatePath(): void {}
553
+
554
+ get exists(): boolean {
555
+ const entry = store.get(normalizeKey(this.uri));
556
+ return !!(entry && entry.kind === 'dir' && entry.exists);
557
+ }
558
+
559
+ get size(): number | null {
560
+ const entry = store.get(normalizeKey(this.uri));
561
+ if (!entry || entry.kind !== 'dir' || !entry.exists) {
562
+ return null;
563
+ }
564
+ const prefix = `${normalizeKey(this.uri)}/`;
565
+ let total = 0;
566
+ for (const [key, e] of store) {
567
+ if (!e.exists || e.kind !== 'file' || !key.startsWith(prefix)) continue;
568
+ total += e.bytes?.length ?? 0;
569
+ }
570
+ return total;
571
+ }
572
+
573
+ info(): any {
574
+ const entry = store.get(normalizeKey(this.uri));
575
+ if (!entry || entry.kind !== 'dir' || !entry.exists) {
576
+ return { exists: false, uri: this.uri };
577
+ }
578
+ return {
579
+ exists: true,
580
+ uri: this.uri,
581
+ files: directChildren(this.uri).map((child) => basename(child.uri)),
582
+ size: this.size,
583
+ modificationTime: entry.modifiedAt ?? null,
584
+ creationTime: entry.createdAt ?? null,
585
+ };
586
+ }
587
+
588
+ create(
589
+ options: { intermediates?: boolean; idempotent?: boolean; overwrite?: boolean } = {}
590
+ ): void {
591
+ const key = normalizeKey(this.uri);
592
+ const existing = store.get(key);
593
+ if (existing?.exists) {
594
+ if (options.idempotent) return;
595
+ if (!options.overwrite) {
596
+ throw new Error('Directory already exists');
597
+ }
598
+ deleteSubtree(key);
599
+ }
600
+ assertParent(this.uri, !!options.intermediates);
601
+ const now = nextMockTimestamp();
602
+ store.set(key, { kind: 'dir', exists: true, createdAt: now, modifiedAt: now });
603
+ }
604
+
605
+ delete(): void {
606
+ const key = normalizeKey(this.uri);
607
+ const entry = store.get(key);
608
+ if (!entry || !entry.exists) {
609
+ throw new Error('Directory does not exist');
610
+ }
611
+ deleteSubtree(key);
612
+ }
613
+
614
+ listAsRecords(): { isDirectory: boolean; uri: string }[] {
615
+ const entry = store.get(normalizeKey(this.uri));
616
+ if (!entry || entry.kind !== 'dir' || !entry.exists) {
617
+ throw new Error('Directory does not exist');
618
+ }
619
+ return directChildren(this.uri).map(({ uri, kind }) => ({
620
+ isDirectory: kind === 'dir',
621
+ uri,
622
+ }));
623
+ }
624
+
625
+ createFile(name: string, mimeType: string | null): FileSystemFile {
626
+ const parentKey = normalizeKey(this.uri);
627
+ const parent = store.get(parentKey);
628
+ if (!parent || parent.kind !== 'dir' || !parent.exists) {
629
+ throw new Error('Parent directory does not exist');
630
+ }
631
+ const childKey = joinUri(this.uri, name);
632
+ if (store.get(childKey)?.exists) {
633
+ throw new Error('File already exists');
634
+ }
635
+ const now = nextMockTimestamp();
636
+ store.set(childKey, {
637
+ kind: 'file',
638
+ bytes: new Uint8Array(0),
639
+ type: mimeType ?? null,
640
+ exists: true,
641
+ createdAt: now,
642
+ modifiedAt: now,
643
+ });
644
+ return new FileSystemFile(childKey);
645
+ }
646
+
647
+ createDirectory(name: string): FileSystemDirectory {
648
+ const parentKey = normalizeKey(this.uri);
649
+ const parent = store.get(parentKey);
650
+ if (!parent || parent.kind !== 'dir' || !parent.exists) {
651
+ throw new Error('Parent directory does not exist');
652
+ }
653
+ const childKey = joinUri(this.uri, name);
654
+ if (store.get(childKey)?.exists) {
655
+ throw new Error('Directory already exists');
656
+ }
657
+ const now = nextMockTimestamp();
658
+ store.set(childKey, { kind: 'dir', exists: true, createdAt: now, modifiedAt: now });
659
+ return new FileSystemDirectory(childKey);
660
+ }
661
+
662
+ private resolveDestination(destination: FileSystemFile | FileSystemDirectory): string {
663
+ if (destination instanceof FileSystemDirectory) {
664
+ return joinUri(destination.uri, basename(this.uri));
665
+ }
666
+ return normalizeKey(destination.uri);
667
+ }
668
+
669
+ copySync(
670
+ destination: FileSystemFile | FileSystemDirectory,
671
+ options: { overwrite?: boolean } = {}
672
+ ): void {
673
+ const srcKey = normalizeKey(this.uri);
674
+ const entry = store.get(srcKey);
675
+ if (!entry || entry.kind !== 'dir' || !entry.exists) {
676
+ throw new Error('Directory does not exist');
677
+ }
678
+ const destKey = this.resolveDestination(destination);
679
+ const destExisting = store.get(destKey);
680
+ if (destExisting?.exists && !options.overwrite) {
681
+ throw new Error('Destination already exists');
682
+ }
683
+ assertParent(destKey, false);
684
+ copySubtree(srcKey, destKey);
685
+ }
686
+
687
+ async copy(
688
+ destination: FileSystemFile | FileSystemDirectory,
689
+ options: { overwrite?: boolean } = {}
690
+ ): Promise<void> {
691
+ this.copySync(destination, options);
692
+ }
693
+
694
+ moveSync(
695
+ destination: FileSystemFile | FileSystemDirectory,
696
+ options: { overwrite?: boolean } = {}
697
+ ): void {
698
+ const srcKey = normalizeKey(this.uri);
699
+ this.copySync(destination, options);
700
+ const destKey = this.resolveDestination(destination);
701
+ if (destKey !== srcKey) {
702
+ deleteSubtree(srcKey);
703
+ }
704
+ this.uri = destKey;
705
+ }
706
+
707
+ async move(
708
+ destination: FileSystemFile | FileSystemDirectory,
709
+ options: { overwrite?: boolean } = {}
710
+ ): Promise<void> {
711
+ this.moveSync(destination, options);
712
+ }
713
+
714
+ rename(newName: string): void {
715
+ const srcKey = normalizeKey(this.uri);
716
+ const entry = store.get(srcKey);
717
+ if (!entry || !entry.exists) {
718
+ throw new Error('Directory does not exist');
719
+ }
720
+ const destKey = joinUri(parentOf(this.uri), newName);
721
+ copySubtree(srcKey, destKey);
722
+ deleteSubtree(srcKey);
723
+ this.uri = destKey;
724
+ }
725
+
85
726
  watch(_callback: any, _options?: any): { remove: () => void } {
86
727
  return { remove: () => {} };
87
728
  }
@@ -135,3 +776,98 @@ export class FileSystemDownloadTask extends SharedObject {
135
776
  }
136
777
  cancel(): void {}
137
778
  }
779
+
780
+ function directChildren(dirUri: string): { uri: string; kind: 'file' | 'dir' }[] {
781
+ const prefix = `${normalizeKey(dirUri)}/`;
782
+ const result: { uri: string; kind: 'file' | 'dir' }[] = [];
783
+ for (const [key, entry] of store) {
784
+ if (!entry.exists || !key.startsWith(prefix)) continue;
785
+ const tail = key.slice(prefix.length);
786
+ if (tail.length === 0 || tail.includes('/')) continue;
787
+ result.push({ uri: key, kind: entry.kind });
788
+ }
789
+ return result;
790
+ }
791
+
792
+ function deleteSubtree(rootKey: string) {
793
+ const prefix = `${rootKey}/`;
794
+ const keys: string[] = [rootKey];
795
+ for (const key of store.keys()) {
796
+ if (key.startsWith(prefix)) keys.push(key);
797
+ }
798
+ for (const key of keys) store.delete(key);
799
+ }
800
+
801
+ function copySubtree(srcKey: string, destKey: string) {
802
+ const srcEntry = store.get(srcKey);
803
+ if (!srcEntry) return;
804
+ const now = nextMockTimestamp();
805
+ store.set(destKey, cloneEntry(srcEntry, now));
806
+ const prefix = `${srcKey}/`;
807
+ for (const [key, entry] of store) {
808
+ if (!key.startsWith(prefix)) continue;
809
+ const rewritten = destKey + key.slice(srcKey.length);
810
+ store.set(rewritten, cloneEntry(entry, now));
811
+ }
812
+ }
813
+
814
+ function cloneEntry(entry: Entry, timestamp: number): Entry {
815
+ return {
816
+ kind: entry.kind,
817
+ exists: entry.exists,
818
+ type: entry.type ?? null,
819
+ bytes: entry.bytes ? new Uint8Array(entry.bytes) : undefined,
820
+ createdAt: timestamp,
821
+ modifiedAt: timestamp,
822
+ };
823
+ }
824
+
825
+ export async function downloadFileAsync(
826
+ url: URL,
827
+ to: { uri: string } | undefined,
828
+ options: any,
829
+ uuid: string | undefined
830
+ ): Promise<string> {
831
+ if (options?.signal?.aborted) {
832
+ const err = new Error(options.signal.reason ?? 'The operation was aborted.');
833
+ err.name = 'AbortError';
834
+ throw err;
835
+ }
836
+ const bytes = utf8Encode(`mock:${url}`);
837
+ const toUri = to?.uri ?? joinUri(cacheDirectory, basename(url));
838
+ const toEntry = store.get(normalizeKey(toUri));
839
+ const destKey = toEntry?.kind === 'dir' ? joinUri(toUri, basename(url)) : normalizeKey(toUri);
840
+
841
+ if (uuid) {
842
+ emit('downloadProgress', {
843
+ uuid,
844
+ data: { bytesWritten: bytes.length, totalBytes: bytes.length },
845
+ });
846
+ }
847
+
848
+ if (uuid && cancelled.has(uuid)) {
849
+ cancelled.delete(uuid);
850
+ const err = new Error('Download was cancelled.');
851
+ err.name = 'AbortError';
852
+ throw err;
853
+ }
854
+
855
+ const now = nextMockTimestamp();
856
+ store.set(destKey, { kind: 'file', bytes, exists: true, createdAt: now, modifiedAt: now });
857
+ return destKey;
858
+ }
859
+
860
+ export function cancelDownloadAsync(uuid: string): void {
861
+ cancelled.add(uuid);
862
+ }
863
+
864
+ export async function pickDirectoryAsync(_initialUri?: string): Promise<{ uri: string }> {
865
+ return { uri: 'file:///mock/picked/directory' };
866
+ }
867
+
868
+ export async function pickFileAsync(options?: any): Promise<any> {
869
+ if (options?.multipleFiles) {
870
+ return [{ uri: 'file:///mock/picked/file1.txt' }, { uri: 'file:///mock/picked/file2.txt' }];
871
+ }
872
+ return { uri: 'file:///mock/picked/file.txt' };
873
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-file-system",
3
- "version": "56.0.5",
3
+ "version": "56.0.6",
4
4
  "description": "Provides access to the local file system on the device.",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -41,15 +41,15 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^22.14.0",
44
- "expo": "56.0.0-preview.12",
45
- "jest-expo": "56.0.3",
46
- "expo-module-scripts": "56.0.2"
44
+ "expo": "56.0.0-preview.13",
45
+ "expo-module-scripts": "56.0.2",
46
+ "jest-expo": "56.0.3"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "expo": "*",
50
50
  "react-native": "*"
51
51
  },
52
- "gitHead": "f26be3dd9396bf7c399a1d607865d0fabdbc0d64",
52
+ "gitHead": "290368bc41026449a05a4ebf991b85c3a2fb0e3a",
53
53
  "scripts": {
54
54
  "build": "expo-module build",
55
55
  "clean": "expo-module clean",
package/src/Directory.ts CHANGED
@@ -46,7 +46,6 @@ export class Directory extends ExpoFileSystem.FileSystemDirectory {
46
46
  * @returns An array of `Directory` and `File` instances.
47
47
  */
48
48
  override list(): (Directory | File)[] {
49
- // We need to wrap it in the JS File/Directory classes, and returning SharedObjects in lists is not supported yet on Android.
50
49
  return super
51
50
  .listAsRecords()
52
51
  .map(({ isDirectory, uri }) => (isDirectory ? new Directory(uri) : new File(uri)));
@@ -60,7 +59,6 @@ export class Directory extends ExpoFileSystem.FileSystemDirectory {
60
59
  }
61
60
 
62
61
  createFile(name: string, mimeType: string | null): File {
63
- // Wrapping with the JS child class for additional, JS-only methods.
64
62
  return new File(super.createFile(name, mimeType).uri);
65
63
  }
66
64
 
package/src/File.ts CHANGED
@@ -188,6 +188,16 @@ export class File extends ExpoFileSystem.FileSystemFile implements Blob {
188
188
  return bytes.buffer as ArrayBuffer;
189
189
  }
190
190
 
191
+ async json(): Promise<any> {
192
+ return JSON.parse(await this.text());
193
+ }
194
+
195
+ async formData(): ReturnType<Response['formData']> {
196
+ return new Response(await this.arrayBuffer(), {
197
+ headers: this.type ? { 'Content-Type': this.type } : undefined,
198
+ }).formData();
199
+ }
200
+
191
201
  stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
192
202
  return this.readableStream();
193
203
  }
@@ -1 +0,0 @@
1
- 3678bcfd9bb14d4dae6430960eafacb8e557d0b17791f7885a0d65a45b729c09
@@ -1 +0,0 @@
1
- 544b1afe9f85ee8b4e8378e9a776f9172cca4608192830f051d53e15f015ffa7dc58189441844830f44753118f93f977651ea699e4c885706b7acb01b0dc3e16
@@ -1 +0,0 @@
1
- ca778d7b49ef6a07be35dc52f1e80020087f4c3a5cf027abc4c25de93c3f8240
@@ -1 +0,0 @@
1
- ce05a47311b5d94dba902ae5693fe5d385b79c29bcc999dc2493286b1e6babfb2b80ca81eac595ac8e590f211438e469a5d7fd410210905a73fe5c98e556de01