expo-file-system 18.2.0-canary-20250630-547cd82 → 18.2.0-canary-20250709-136b77f

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 (72) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt +1 -1
  4. package/android/src/main/java/expo/modules/filesystem/FileSystemRecords.kt +1 -3
  5. package/android/src/main/java/expo/modules/filesystem/next/FileSystemDirectory.kt +5 -2
  6. package/android/src/main/java/expo/modules/filesystem/next/FileSystemFile.kt +55 -2
  7. package/android/src/main/java/expo/modules/filesystem/next/FileSystemNextExceptions.kt +5 -0
  8. package/android/src/main/java/expo/modules/filesystem/next/FileSystemNextModule.kt +25 -0
  9. package/android/src/main/java/expo/modules/filesystem/next/FileSystemNextRecords.kt +14 -0
  10. package/android/src/main/java/expo/modules/filesystem/next/FileSystemPath.kt +8 -5
  11. package/build/next/ExpoFileSystem.types.d.ts +58 -2
  12. package/build/next/ExpoFileSystem.types.d.ts.map +1 -1
  13. package/build/next/FileSystem.d.ts +5 -0
  14. package/build/next/FileSystem.d.ts.map +1 -1
  15. package/build/next/index.d.ts +1 -1
  16. package/build/next/index.d.ts.map +1 -1
  17. package/expo-module.config.json +1 -1
  18. package/ios/ExpoFileSystem.podspec +1 -1
  19. package/ios/FileSystemRecords.swift +0 -1
  20. package/ios/Next/FileSystemDirectory.swift +1 -4
  21. package/ios/Next/FileSystemFile.swift +65 -21
  22. package/ios/Next/FileSystemNextExceptions.swift +12 -0
  23. package/ios/Next/FileSystemNextModule.swift +32 -0
  24. package/ios/Next/FileSystemNextRecords.swift +14 -0
  25. package/ios/Next/FileSystemPath.swift +7 -0
  26. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar +0 -0
  27. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar.md5 +1 -0
  28. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar.sha1 +1 -0
  29. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar.sha256 +1 -0
  30. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar.sha512 +1 -0
  31. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar +0 -0
  32. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar.md5 +1 -0
  33. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar.sha1 +1 -0
  34. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar.sha256 +1 -0
  35. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar.sha512 +1 -0
  36. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.module → 18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.module} +22 -22
  37. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.module.md5 +1 -0
  38. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.module.sha1 +1 -0
  39. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.module.sha256 +1 -0
  40. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.module.sha512 +1 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/{18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.pom → 18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.pom} +1 -1
  42. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.pom.md5 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.pom.sha1 +1 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.pom.sha256 +1 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250709-136b77f/expo.modules.filesystem-18.2.0-canary-20250709-136b77f.pom.sha512 +1 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml +4 -4
  47. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.md5 +1 -1
  48. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha1 +1 -1
  49. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha256 +1 -1
  50. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/maven-metadata.xml.sha512 +1 -1
  51. package/package.json +4 -4
  52. package/src/next/ExpoFileSystem.types.ts +64 -2
  53. package/src/next/FileSystem.ts +15 -8
  54. package/src/next/index.ts +7 -1
  55. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar +0 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar.md5 +0 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar.sha1 +0 -1
  58. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar.sha256 +0 -1
  59. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar.sha512 +0 -1
  60. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar +0 -0
  61. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar.md5 +0 -1
  62. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar.sha1 +0 -1
  63. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar.sha256 +0 -1
  64. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar.sha512 +0 -1
  65. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.module.md5 +0 -1
  66. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.module.sha1 +0 -1
  67. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.module.sha256 +0 -1
  68. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.module.sha512 +0 -1
  69. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.pom.md5 +0 -1
  70. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.pom.sha1 +0 -1
  71. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.pom.sha256 +0 -1
  72. package/local-maven-repo/host/exp/exponent/expo.modules.filesystem/18.2.0-canary-20250630-547cd82/expo.modules.filesystem-18.2.0-canary-20250630-547cd82.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -7,12 +7,19 @@
7
7
  ### 🎉 New features
8
8
 
9
9
  - Add total and available sizes, directory sizes. ([#37594](https://github.com/expo/expo/pull/37594) by [@aleqsio](https://github.com/aleqsio))
10
+ - Add info method, modificationTime and creationTime properties to file-system/next. ([#37505](https://github.com/expo/expo/pull/37505) by [@Wenszel](https://github.com/Wenszel))
10
11
  - Add support for custom headers in downloadFileAsync ([#36108](https://github.com/expo/expo/pull/36108) by [@leonhh](https://github.com/leonhh))
11
12
 
12
13
  ### 🐛 Bug fixes
13
14
 
15
+ - Update exists logic to align with documentation ([#37692](https://github.com/expo/expo/pull/37692) by [@Wenszel](https://github.com/Wenszel))
14
16
  - Fix memory usage issue in getInfoAsync ([#37417](https://github.com/expo/expo/pull/37417) by [@Wenszel](https://github.com/Wenszel))
15
17
  - Improved type safety in the FileSystem module to support tsconfig setups with stricter rules than the default. ([#37107](https://github.com/expo/expo/pull/37107) by [@hirbod](https://github.com/hirbod))
18
+ - Added required modifiers in the FileSystem module to support tsconfig setups with stricter rules than the default. ([#37467](https://github.com/expo/expo/pull/37467)
19
+
20
+ ### 💡 Others
21
+
22
+ ## 18.1.11 - 2025-07-01
16
23
 
17
24
  ### 💡 Others
18
25
 
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '18.2.0-canary-20250630-547cd82'
7
+ version = '18.2.0-canary-20250709-136b77f'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.filesystem"
11
11
  defaultConfig {
12
12
  versionCode 30
13
- versionName "18.2.0-canary-20250630-547cd82"
13
+ versionName "18.2.0-canary-20250709-136b77f"
14
14
  }
15
15
  }
16
16
 
@@ -65,7 +65,7 @@ private const val EXUploadProgressEventName = "expo-file-system.uploadProgress"
65
65
  private const val MIN_EVENT_DT_MS: Long = 100
66
66
  private const val DIR_PERMISSIONS_REQUEST_CODE = 5394
67
67
 
68
- private fun slashifyFilePath(path: String?): String? {
68
+ fun slashifyFilePath(path: String?): String? {
69
69
  return if (path == null) {
70
70
  null
71
71
  } else if (path.startsWith("file:///")) {
@@ -6,9 +6,7 @@ import expo.modules.kotlin.types.Enumerable
6
6
 
7
7
  data class InfoOptions(
8
8
  @Field
9
- val md5: Boolean?,
10
- @Field
11
- val size: Boolean?
9
+ val md5: Boolean?
12
10
  ) : Record
13
11
 
14
12
  data class DeletingOptions(
@@ -16,8 +16,11 @@ class FileSystemDirectory(file: File) : FileSystemPath(file) {
16
16
  }
17
17
 
18
18
  val exists: Boolean get() {
19
- validatePermission(Permission.READ)
20
- return file.isDirectory
19
+ return if (checkPermission(Permission.READ)) {
20
+ file.isDirectory
21
+ } else {
22
+ false
23
+ }
21
24
  }
22
25
 
23
26
  val size: Long get() {
@@ -1,14 +1,21 @@
1
1
  package expo.modules.filesystem.next
2
2
 
3
3
  import android.net.Uri
4
+ import android.os.Build
4
5
  import android.util.Base64
5
6
  import android.webkit.MimeTypeMap
7
+ import expo.modules.filesystem.InfoOptions
8
+ import expo.modules.filesystem.slashifyFilePath
6
9
  import expo.modules.interfaces.filesystem.Permission
7
10
  import expo.modules.kotlin.apifeatures.EitherType
8
11
  import expo.modules.kotlin.typedarray.TypedArray
9
12
  import java.io.File
10
13
  import java.io.FileOutputStream
14
+ import java.nio.file.attribute.BasicFileAttributes
11
15
  import java.security.MessageDigest
16
+ import kotlin.io.path.Path
17
+ import kotlin.io.path.readAttributes
18
+ import kotlin.time.Duration.Companion.milliseconds
12
19
 
13
20
  @OptIn(EitherType::class)
14
21
  class FileSystemFile(file: File) : FileSystemPath(file) {
@@ -26,8 +33,11 @@ class FileSystemFile(file: File) : FileSystemPath(file) {
26
33
  }
27
34
 
28
35
  val exists: Boolean get() {
29
- validatePermission(Permission.READ)
30
- return file.isFile
36
+ return if (checkPermission(Permission.READ)) {
37
+ file.isFile
38
+ } else {
39
+ false
40
+ }
31
41
  }
32
42
 
33
43
  fun create(options: CreateOptions = CreateOptions()) {
@@ -111,4 +121,47 @@ class FileSystemFile(file: File) : FileSystemPath(file) {
111
121
  return MimeTypeMap.getFileExtensionFromUrl(file.path)
112
122
  ?.run { MimeTypeMap.getSingleton().getMimeTypeFromExtension(lowercase()) }
113
123
  }
124
+
125
+ val modificationTime: Long get() {
126
+ validateType()
127
+ return file.lastModified()
128
+ }
129
+
130
+ val creationTime: Long? get() {
131
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
132
+ validateType()
133
+ val attributes = Path(file.path).readAttributes<BasicFileAttributes>()
134
+ return attributes.creationTime().toMillis().milliseconds.inWholeMilliseconds
135
+ } else {
136
+ return null
137
+ }
138
+ }
139
+
140
+ fun info(options: InfoOptions?): FileInfo {
141
+ validateType()
142
+ validatePermission(Permission.READ)
143
+ if (!file.exists()) {
144
+ val fileInfo = FileInfo(
145
+ exists = false,
146
+ uri = slashifyFilePath(file.toURI().toString())
147
+ )
148
+ return fileInfo
149
+ }
150
+ when {
151
+ file.toURI().scheme == "file" -> {
152
+ val fileInfo = FileInfo(
153
+ exists = true,
154
+ uri = slashifyFilePath(file.toURI().toString()),
155
+ size = size,
156
+ modificationTime = modificationTime,
157
+ creationTime = creationTime
158
+ )
159
+ if (options != null && options.md5 == true) {
160
+ fileInfo.md5 = md5
161
+ }
162
+ return fileInfo
163
+ }
164
+ else -> throw UnableToGetInfoException("file schema ${file.toURI().scheme} is not supported")
165
+ }
166
+ }
114
167
  }
@@ -44,6 +44,11 @@ internal class UnableToWriteHandleException(reason: String) :
44
44
  "Unable to write to a file handle: '$reason'"
45
45
  )
46
46
 
47
+ internal class UnableToGetInfoException(reason: String) :
48
+ CodedException(
49
+ "Unable to get info from a file: '$reason'"
50
+ )
51
+
47
52
  internal class DestinationAlreadyExistsException :
48
53
  CodedException(
49
54
  "Destination already exists"
@@ -3,6 +3,7 @@ package expo.modules.filesystem.next
3
3
  import android.content.Context
4
4
  import android.net.Uri
5
5
  import android.webkit.URLUtil
6
+ import expo.modules.filesystem.InfoOptions
6
7
  import expo.modules.interfaces.filesystem.Permission
7
8
  import expo.modules.kotlin.apifeatures.EitherType
8
9
  import expo.modules.kotlin.devtools.await
@@ -17,6 +18,7 @@ import okhttp3.Request
17
18
  import java.io.File
18
19
  import java.io.FileOutputStream
19
20
  import java.net.URI
21
+ import java.util.EnumSet
20
22
 
21
23
  class FileSystemNextModule : Module() {
22
24
  private val context: Context
@@ -79,6 +81,17 @@ class FileSystemNextModule : Module() {
79
81
  return@Coroutine destination.path
80
82
  }
81
83
 
84
+ Function("info") { url: URI ->
85
+ val file = File(url)
86
+ val permissions = appContext.filePermission?.getPathPermissions(appContext.reactContext, file.path)
87
+ ?: EnumSet.noneOf(Permission::class.java)
88
+ if (permissions.contains(Permission.READ) && file.exists()) {
89
+ PathInfo(exists = file.exists(), isDirectory = file.isDirectory)
90
+ } else {
91
+ PathInfo(exists = false, isDirectory = null)
92
+ }
93
+ }
94
+
82
95
  Class(FileSystemFile::class) {
83
96
  Constructor { uri: URI ->
84
97
  FileSystemFile(File(uri.path))
@@ -120,10 +133,22 @@ class FileSystemNextModule : Module() {
120
133
  file.bytes()
121
134
  }
122
135
 
136
+ Function("info") { file: FileSystemFile, options: InfoOptions? ->
137
+ file.info(options)
138
+ }
139
+
123
140
  Property("exists") { file: FileSystemFile ->
124
141
  file.exists
125
142
  }
126
143
 
144
+ Property("modificationTime") { file: FileSystemFile ->
145
+ file.modificationTime
146
+ }
147
+
148
+ Property("creationTime") { file: FileSystemFile ->
149
+ file.creationTime
150
+ }
151
+
127
152
  Function("copy") { file: FileSystemFile, destination: FileSystemPath ->
128
153
  file.copy(destination)
129
154
  }
@@ -14,3 +14,17 @@ data class DownloadOptionsNext(
14
14
  @Field
15
15
  val headers: Map<String, String> = emptyMap()
16
16
  ) : Record
17
+
18
+ data class FileInfo(
19
+ @Field var exists: Boolean,
20
+ @Field var uri: String?,
21
+ @Field var md5: String? = null,
22
+ @Field var size: Long? = null,
23
+ @Field var modificationTime: Long? = null,
24
+ @Field var creationTime: Long? = null
25
+ ) : Record
26
+
27
+ data class PathInfo(
28
+ @Field var exists: Boolean,
29
+ @Field var isDirectory: Boolean?
30
+ ) : Record
@@ -63,12 +63,15 @@ abstract class FileSystemPath(public var file: File) : SharedObject() {
63
63
  return destination.file
64
64
  }
65
65
 
66
- fun validatePermission(permission: Permission): Boolean {
67
- val permissions = appContext?.filePermission?.getPathPermissions(appContext?.reactContext, file.path) ?: EnumSet.noneOf(Permission::class.java)
68
- if (permissions.contains(permission)) {
69
- return true
66
+ fun validatePermission(permission: Permission) {
67
+ if (!checkPermission(permission)) {
68
+ throw InvalidPermissionException(permission)
70
69
  }
71
- throw InvalidPermissionException(permission)
70
+ }
71
+
72
+ fun checkPermission(permission: Permission): Boolean {
73
+ val permissions = appContext?.filePermission?.getPathPermissions(appContext?.reactContext, file.path) ?: EnumSet.noneOf(Permission::class.java)
74
+ return permissions.contains(permission)
72
75
  }
73
76
 
74
77
  fun validateCanCreate(options: CreateOptions) {
@@ -36,8 +36,7 @@ export declare class Directory {
36
36
  */
37
37
  delete(): void;
38
38
  /**
39
- * A boolean representing if a directory exists. `true` if the directory exists, `false` otherwise.
40
- * Also, `false` if the application does not have read access to the file.
39
+ * A boolean representing if a directory exists and can be accessed.
41
40
  */
42
41
  exists: boolean;
43
42
  /**
@@ -125,6 +124,12 @@ export declare class File {
125
124
  * @throws Error if the directory does not exist or cannot be deleted.
126
125
  */
127
126
  delete(): void;
127
+ /**
128
+ * Retrieves an object containing properties of a file
129
+ * @throws Error If the application does not have read access to the file, or if the path does not point to a file (e.g., it points to a directory).
130
+ * @returns An object with file metadata (e.g., size, creation date, etc.).
131
+ */
132
+ info(options?: InfoOptions): FileInfo;
128
133
  /**
129
134
  * A boolean representing if a file exists. `true` if the file exists, `false` otherwise.
130
135
  * Also, `false` if the application does not have read access to the file.
@@ -168,6 +173,14 @@ export declare class File {
168
173
  * A md5 hash of the file. Null if the file does not exist, or it cannot be read.
169
174
  */
170
175
  md5: string | null;
176
+ /**
177
+ * A last modification time of the file expressed in milliseconds since epoch. Returns a Null if the file does not exist, or it cannot be read.
178
+ */
179
+ modificationTime: number | null;
180
+ /**
181
+ * A creation time of the file expressed in milliseconds since epoch. Returns null if the file does not exist, cannot be read or the Android version is earlier than API 26.
182
+ */
183
+ creationTime: number | null;
171
184
  /**
172
185
  * A mime type of the file. Null if the file does not exist, or it cannot be read.
173
186
  */
@@ -180,4 +193,47 @@ export declare class FileHandle {
180
193
  offset: number | null;
181
194
  size: number | null;
182
195
  }
196
+ export type FileInfo = {
197
+ /**
198
+ * Indicates whether the file exists.
199
+ */
200
+ exists: boolean;
201
+ /**
202
+ * A `file://` URI pointing to the file. This is the same as the `fileUri` input parameter.
203
+ */
204
+ uri?: string;
205
+ /**
206
+ * The size of the file in bytes.
207
+ */
208
+ size?: number;
209
+ /**
210
+ * The last modification time of the file expressed in milliseconds since epoch.
211
+ */
212
+ modificationTime?: number;
213
+ /**
214
+ * A creation time of the file expressed in milliseconds since epoch. Returns null if the Android version is earlier than API 26.
215
+ */
216
+ creationTime?: number;
217
+ /**
218
+ * Present if the `md5` option was truthy. Contains the MD5 hash of the file.
219
+ */
220
+ md5?: string;
221
+ };
222
+ export type InfoOptions = {
223
+ /**
224
+ * Whether to return the MD5 hash of the file.
225
+ * @default false
226
+ */
227
+ md5?: boolean;
228
+ };
229
+ export type PathInfo = {
230
+ /**
231
+ * Indicates whether the path exists. Returns true if it exists; false if the path does not exist or if there is no read permission.
232
+ */
233
+ exists: boolean;
234
+ /**
235
+ * Indicates whether the path is a directory. Returns true or false if the path exists; otherwise, returns null.
236
+ */
237
+ isDirectory: boolean | null;
238
+ };
183
239
  //# sourceMappingURL=ExpoFileSystem.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/next/ExpoFileSystem.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/next/ExpoFileSystem.types.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import ExpoFileSystem from './ExpoFileSystem';
2
+ import type { PathInfo } from './ExpoFileSystem.types';
2
3
  import { PathUtilities } from './pathUtilities';
3
4
  export declare class Paths extends PathUtilities {
4
5
  /**
@@ -18,6 +19,10 @@ export declare class Paths extends PathUtilities {
18
19
  * A property that represents the available space on device's internal storage, represented in bytes.
19
20
  */
20
21
  static get availableDiskSpace(): number;
22
+ /**
23
+ * Returns an object that indicates if the specified path represents a directory.
24
+ */
25
+ static info(...uris: string[]): PathInfo;
21
26
  }
22
27
  /**
23
28
  * @hidden
@@ -1 +1 @@
1
- {"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/next/FileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,qBAAa,KAAM,SAAQ,aAAa;IACtC;;OAEG;IACH,MAAM,KAAK,KAAK,cAEf;IAED;;OAEG;IACH,MAAM,KAAK,QAAQ,cAElB;IACD,MAAM,KAAK,qBAAqB,8BAS/B;IAED;;OAEG;IACH,MAAM,KAAK,cAAc,WAExB;IAED;;OAEG;IACH,MAAM,KAAK,kBAAkB,WAE5B;CACF;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,IAAI;IAChC,IAAI,EAAE,IAAI,CAAC;IAEX;;OAEG;IACH,GAAG,EAAE,MAAM,CAAc;gBAEb,IAAI,EAAE,IAAI;IAKtB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAInC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAIlC,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC;IAIpC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;CAGhE;AAED,qBAAa,IAAK,SAAQ,cAAc,CAAC,cAAc;IACrD;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,IAAI,IAAI;IAOZ,IAAI,eAAe,cAElB;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,cAAc;IAId,cAAc;CAGf;AAYD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAc,CAAC,mBAAmB;IAC/D;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,eAAe,cAElB;IAED;;;;OAIG;IACH,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE;IAO5B;;OAEG;IACH,IAAI,IAAI,WAEP;CACF"}
1
+ {"version":3,"file":"FileSystem.d.ts","sourceRoot":"","sources":["../../src/next/FileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAmB,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,qBAAa,KAAM,SAAQ,aAAa;IACtC;;OAEG;IACH,MAAM,KAAK,KAAK,cAEf;IAED;;OAEG;IACH,MAAM,KAAK,QAAQ,cAElB;IACD,MAAM,KAAK,qBAAqB,8BAS/B;IAED;;OAEG;IACH,MAAM,KAAK,cAAc,WAExB;IAED;;OAEG;IACH,MAAM,KAAK,kBAAkB,WAE5B;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ;CAGzC;AAED;;GAEG;AACH,qBAAa,QAAS,SAAQ,IAAI;IAChC,IAAI,EAAE,IAAI,CAAC;IAEX;;OAEG;IACH,GAAG,EAAE,MAAM,CAAc;gBAEb,IAAI,EAAE,IAAI;IAKtB,IAAa,IAAI,IAAI,MAAM,CAE1B;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAa,IAAI,IAAI,MAAM,CAE1B;IAEc,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAInC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAIhC,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC;IAIzB,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC;IAIpC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;CAGzE;AAED,qBAAa,IAAK,SAAQ,cAAc,CAAC,cAAc;IACrD;;;;;;;OAOG;gBACS,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE;IAQlD,IAAI,IAAI,IAAI;IAOZ,IAAI,eAAe,cAElB;IAED;;;OAGG;IACH,IAAI,SAAS,WAEZ;IAED;;OAEG;IACH,IAAI,IAAI,WAEP;IAED,cAAc;IAId,cAAc;CAGf;AAYD;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAAc,CAAC,mBAAmB;IAC/D;;;;;;;OAOG;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;CACF"}
@@ -1,3 +1,3 @@
1
1
  export * from './FileSystem';
2
- export { type CreateOptions, type FileHandle } from './ExpoFileSystem.types';
2
+ export { type CreateOptions, type FileHandle, type FileInfo, type InfoOptions, type PathInfo, } from './ExpoFileSystem.types';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/next/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAE7B,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,QAAQ,GACd,MAAM,wBAAwB,CAAC"}
@@ -12,7 +12,7 @@
12
12
  "publication": {
13
13
  "groupId": "host.exp.exponent",
14
14
  "artifactId": "expo.modules.filesystem",
15
- "version": "18.2.0-canary-20250630-547cd82",
15
+ "version": "18.2.0-canary-20250709-136b77f",
16
16
  "repository": "local-maven-repo"
17
17
  }
18
18
  }
@@ -15,7 +15,7 @@ Pod::Spec.new do |s|
15
15
  :osx => '11.0',
16
16
  :tvos => '15.1'
17
17
  }
18
- s.swift_version = '5.4'
18
+ s.swift_version = '5.9'
19
19
  s.source = { :git => 'https://github.com/expo/expo.git' }
20
20
  s.static_framework = true
21
21
 
@@ -4,7 +4,6 @@ import ExpoModulesCore
4
4
 
5
5
  struct InfoOptions: Record {
6
6
  @Field var md5: Bool = false
7
- @Field var size: Bool = false
8
7
  }
9
8
 
10
9
  struct ReadingOptions: Record {
@@ -46,12 +46,9 @@ internal final class FileSystemDirectory: FileSystemPath {
46
46
  }
47
47
 
48
48
  override var exists: Bool {
49
- do {
50
- try validatePermission(.read)
51
- } catch {
49
+ guard checkPermission(.read) else {
52
50
  return false
53
51
  }
54
-
55
52
  var isDirectory: ObjCBool = false
56
53
  if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
57
54
  return isDirectory.boolValue
@@ -33,15 +33,14 @@ internal final class FileSystemFile: FileSystemPath {
33
33
  }
34
34
 
35
35
  override var exists: Bool {
36
- get throws {
37
- try validatePermission(.read)
38
-
39
- var isDirectory: ObjCBool = false
40
- if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
41
- return !isDirectory.boolValue
42
- }
36
+ guard checkPermission(.read) else {
43
37
  return false
44
38
  }
39
+ var isDirectory: ObjCBool = false
40
+ if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
41
+ return !isDirectory.boolValue
42
+ }
43
+ return false
45
44
  }
46
45
 
47
46
  // TODO: Move to the constructor once error is rethrowed
@@ -51,20 +50,6 @@ internal final class FileSystemFile: FileSystemPath {
51
50
  }
52
51
  }
53
52
 
54
- var size: Int64 {
55
- get throws {
56
- try validatePermission(.read)
57
- let attributes: [FileAttributeKey: Any] = try FileManager.default.attributesOfItem(atPath: url.path)
58
- guard let size = attributes[.size] else {
59
- throw UnableToGetSizeException("attributes do not contain size")
60
- }
61
- guard let size = size as? NSNumber else {
62
- throw UnableToGetSizeException("size is not a number")
63
- }
64
- return size.int64Value
65
- }
66
- }
67
-
68
53
  var md5: String {
69
54
  get throws {
70
55
  try validatePermission(.read)
@@ -74,6 +59,39 @@ internal final class FileSystemFile: FileSystemPath {
74
59
  }
75
60
  }
76
61
 
62
+ var size: Int64 {
63
+ get throws {
64
+ return try getAttribute(.size, atPath: url.path)
65
+ }
66
+ }
67
+
68
+ var modificationTime: Int64 {
69
+ get throws {
70
+ let modificationDate: Date = try getAttribute(.modificationDate, atPath: url.path)
71
+ return Int64(modificationDate.timeIntervalSince1970 * 1000)
72
+ }
73
+ }
74
+
75
+ var creationTime: Int64 {
76
+ get throws {
77
+ let creationDate: Date = try getAttribute(.creationDate, atPath: url.path)
78
+ return Int64(creationDate.timeIntervalSince1970 * 1000)
79
+ }
80
+ }
81
+
82
+ private func getAttribute<T>(_ key: FileAttributeKey, atPath path: String) throws -> T {
83
+ try validatePermission(.read)
84
+ let attributes = try FileManager.default.attributesOfItem(atPath: path)
85
+
86
+ guard let attribute = attributes[key] else {
87
+ throw UnableToGetFileAttribute("attributes do not contain \(key)")
88
+ }
89
+ guard let attributeCasted = attribute as? T else {
90
+ throw UnableToGetFileAttribute("\(key) is not of expected type")
91
+ }
92
+ return attributeCasted
93
+ }
94
+
77
95
  var type: String? {
78
96
  let pathExtension = url.pathExtension
79
97
  if let utType = UTType(filenameExtension: pathExtension),
@@ -112,4 +130,30 @@ internal final class FileSystemFile: FileSystemPath {
112
130
  try validatePermission(.read)
113
131
  return try Data(contentsOf: url).base64EncodedString()
114
132
  }
133
+
134
+ func info(options: InfoOptions) throws -> FileInfo {
135
+ try validateType()
136
+ try validatePermission(.read)
137
+ if !exists {
138
+ let result = FileInfo()
139
+ result.exists = false
140
+ result.uri = url.absoluteString
141
+ return result
142
+ }
143
+ switch url.scheme {
144
+ case "file":
145
+ let result = FileInfo()
146
+ result.exists = true
147
+ result.uri = url.absoluteString
148
+ result.size = try size
149
+ result.modificationTime = try modificationTime
150
+ result.creationTime = try creationTime
151
+ if options.md5 {
152
+ result.md5 = try md5
153
+ }
154
+ return result
155
+ default:
156
+ throw UnableToGetInfoException("url scheme \(String(describing: url.scheme)) is not supported")
157
+ }
158
+ }
115
159
  }
@@ -25,6 +25,12 @@ internal final class InvalidTypeDirectoryException: Exception {
25
25
  }
26
26
  }
27
27
 
28
+ internal final class UnableToGetFileAttribute: GenericException<String> {
29
+ override var reason: String {
30
+ "Unable to get file attribute: \(param)"
31
+ }
32
+ }
33
+
28
34
  internal final class UnableToGetSizeException: GenericException<String> {
29
35
  override var reason: String {
30
36
  "Unable to get file or directory size: \(param)"
@@ -49,6 +55,12 @@ internal final class UnableToReadHandleException: GenericException<String> {
49
55
  }
50
56
  }
51
57
 
58
+ internal final class UnableToGetInfoException: GenericException<String> {
59
+ override var reason: String {
60
+ "Unable to get info from a file: \(param)"
61
+ }
62
+ }
63
+
52
64
  internal final class DestinationAlreadyExistsException: Exception {
53
65
  override var reason: String {
54
66
  "Destination already exists"
@@ -94,6 +94,26 @@ public final class FileSystemNextModule: Module {
94
94
  downloadTask.resume()
95
95
  }
96
96
 
97
+ Function("info") { (url: URL) in
98
+ let output = PathInfo()
99
+ output.exists = false
100
+ output.isDirectory = nil
101
+
102
+ guard let permissionsManager: EXFilePermissionModuleInterface = appContext?.legacyModule(implementing: EXFilePermissionModuleInterface.self) else {
103
+ return output
104
+ }
105
+
106
+ if permissionsManager.getPathPermissions(url.path).contains(.read) {
107
+ var isDirectory: ObjCBool = false
108
+ if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
109
+ output.exists = true
110
+ output.isDirectory = isDirectory.boolValue
111
+ return output
112
+ }
113
+ }
114
+ return output
115
+ }
116
+
97
117
  Class(FileSystemFile.self) {
98
118
  Constructor { (url: URL) in
99
119
  return FileSystemFile(url: url.standardizedFileURL)
@@ -121,6 +141,10 @@ public final class FileSystemNextModule: Module {
121
141
  return try FileSystemFileHandle(file: file)
122
142
  }
123
143
 
144
+ Function("info") { (file: FileSystemFile, options: InfoOptions?) in
145
+ return try file.info(options: options ?? InfoOptions())
146
+ }
147
+
124
148
  Function("write") { (file, content: Either<String, TypedArray>) in
125
149
  if let content: String = content.get() {
126
150
  try file.write(content)
@@ -138,6 +162,14 @@ public final class FileSystemNextModule: Module {
138
162
  try? file.md5
139
163
  }
140
164
 
165
+ Property("modificationTime") { file in
166
+ try? file.modificationTime
167
+ }
168
+
169
+ Property("creationTime") { file in
170
+ try? file.creationTime
171
+ }
172
+
141
173
  Property("type") { file in
142
174
  file.type
143
175
  }
@@ -10,3 +10,17 @@ struct CreateOptions: Record {
10
10
  struct DownloadOptionsNext: Record {
11
11
  @Field var headers: [String: String]?
12
12
  }
13
+
14
+ struct FileInfo: Record {
15
+ @Field var exists: Bool
16
+ @Field var uri: String?
17
+ @Field var md5: String?
18
+ @Field var size: Int64?
19
+ @Field var modificationTime: Int64?
20
+ @Field var creationTime: Int64?
21
+ }
22
+
23
+ struct PathInfo: Record {
24
+ @Field var exists: Bool
25
+ @Field var isDirectory: Bool?
26
+ }
@@ -13,6 +13,13 @@ internal class FileSystemPath: SharedObject {
13
13
  try ensurePathPermission(appContext, path: url.path, flag: flag)
14
14
  }
15
15
 
16
+ func checkPermission(_ flag: EXFileSystemPermissionFlags) -> Bool {
17
+ guard let permissionsManager: EXFilePermissionModuleInterface = appContext?.legacyModule(implementing: EXFilePermissionModuleInterface.self) else {
18
+ return false
19
+ }
20
+ return permissionsManager.getPathPermissions(url.path).contains(flag)
21
+ }
22
+
16
23
  func validateCanCreate(_ options: CreateOptions) throws {
17
24
  if try !options.overwrite && exists {
18
25
  throw FileAlreadyExistsException("File already exists")
@@ -0,0 +1 @@
1
+ 98b6f2472be68a98a5d1058fa135fae02bc92f8fb957929c3f6737a41bf378d3bb5412990bab5234e627c54078c9af39b1186d1bd7381056b125a1f3562cebdb
@@ -0,0 +1 @@
1
+ f1f24f5df8aab351bfdb6db9df20b4924d2a8fb63d0c41cf9ed38436f4dd291553f202970754a11a21aab1a57ba9c72b7dcff1ee7933415d5dd65c4ebb71c2f8
@@ -3,7 +3,7 @@
3
3
  "component": {
4
4
  "group": "host.exp.exponent",
5
5
  "module": "expo.modules.filesystem",
6
- "version": "18.2.0-canary-20250630-547cd82",
6
+ "version": "18.2.0-canary-20250709-136b77f",
7
7
  "attributes": {
8
8
  "org.gradle.status": "release"
9
9
  }
@@ -68,13 +68,13 @@
68
68
  ],
69
69
  "files": [
70
70
  {
71
- "name": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar",
72
- "url": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar",
73
- "size": 297034,
74
- "sha512": "13c97c6f77623c748f9060b8e32381a9c30ed87b73340c28e7b76f9bc9b67274dbc8a51b4fb6e00ed55f12300bfb7e03c1740312f203b83d704004452b38f420",
75
- "sha256": "165e26238ba196e77f65446d891b5a4345a441715b6a4c7ab9590f54764167e1",
76
- "sha1": "a99c6723ad826834da9b601369b1479e21e9115d",
77
- "md5": "778a5cb8f6dd28aeba3919f1d162c8f3"
71
+ "name": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar",
72
+ "url": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar",
73
+ "size": 310095,
74
+ "sha512": "f1f24f5df8aab351bfdb6db9df20b4924d2a8fb63d0c41cf9ed38436f4dd291553f202970754a11a21aab1a57ba9c72b7dcff1ee7933415d5dd65c4ebb71c2f8",
75
+ "sha256": "bfbfd9ab75c81c2720f13efc284bb52331714d0c3416fb85defb04c9926db14d",
76
+ "sha1": "c27e52b1df3c9a115a63e3b3618d6fa7dbeb2b9b",
77
+ "md5": "8a997f07f9023ed299dd81b964e9251e"
78
78
  }
79
79
  ]
80
80
  },
@@ -139,13 +139,13 @@
139
139
  ],
140
140
  "files": [
141
141
  {
142
- "name": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar",
143
- "url": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82.aar",
144
- "size": 297034,
145
- "sha512": "13c97c6f77623c748f9060b8e32381a9c30ed87b73340c28e7b76f9bc9b67274dbc8a51b4fb6e00ed55f12300bfb7e03c1740312f203b83d704004452b38f420",
146
- "sha256": "165e26238ba196e77f65446d891b5a4345a441715b6a4c7ab9590f54764167e1",
147
- "sha1": "a99c6723ad826834da9b601369b1479e21e9115d",
148
- "md5": "778a5cb8f6dd28aeba3919f1d162c8f3"
142
+ "name": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar",
143
+ "url": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f.aar",
144
+ "size": 310095,
145
+ "sha512": "f1f24f5df8aab351bfdb6db9df20b4924d2a8fb63d0c41cf9ed38436f4dd291553f202970754a11a21aab1a57ba9c72b7dcff1ee7933415d5dd65c4ebb71c2f8",
146
+ "sha256": "bfbfd9ab75c81c2720f13efc284bb52331714d0c3416fb85defb04c9926db14d",
147
+ "sha1": "c27e52b1df3c9a115a63e3b3618d6fa7dbeb2b9b",
148
+ "md5": "8a997f07f9023ed299dd81b964e9251e"
149
149
  }
150
150
  ]
151
151
  },
@@ -159,13 +159,13 @@
159
159
  },
160
160
  "files": [
161
161
  {
162
- "name": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar",
163
- "url": "expo.modules.filesystem-18.2.0-canary-20250630-547cd82-sources.jar",
164
- "size": 21575,
165
- "sha512": "879b501c85367721cbc11faa3f10b8f398f31b9752502aa3a3961fb08e0a276a24c6d43ae201ac503f43534e897fa9c16ac811cef124c2845373a79d9e299dbf",
166
- "sha256": "863069af8776823249a16a25c383a2a29e83a81280b54019fffff2e315df6313",
167
- "sha1": "0be5554077e6b064ca45298e482138ae4d9103e2",
168
- "md5": "5c8338515c4174c54c84a80f0b08ea80"
162
+ "name": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar",
163
+ "url": "expo.modules.filesystem-18.2.0-canary-20250709-136b77f-sources.jar",
164
+ "size": 22326,
165
+ "sha512": "98b6f2472be68a98a5d1058fa135fae02bc92f8fb957929c3f6737a41bf378d3bb5412990bab5234e627c54078c9af39b1186d1bd7381056b125a1f3562cebdb",
166
+ "sha256": "0fa90938cd6bab9dd42eb8317887efc803b66fc48bc50f7ee396265df2ebad90",
167
+ "sha1": "f302df70d5ce843a2f3bb8f24d315f50e9ddd704",
168
+ "md5": "5f5f798cae572b28dd58b38fbd38e0ae"
169
169
  }
170
170
  ]
171
171
  }
@@ -0,0 +1 @@
1
+ 8c0de70d1b858125864b97af83cdde2d415260ad53fc5f0e15dc13c7f636e3fba7151807aa37295cc5ae54026a5f4127eb203c5d377224ed60edaa18364b7fa9
@@ -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>18.2.0-canary-20250630-547cd82</version>
12
+ <version>18.2.0-canary-20250709-136b77f</version>
13
13
  <packaging>aar</packaging>
14
14
  <name>expo.modules.filesystem</name>
15
15
  <url>https://github.com/expo/expo</url>
@@ -0,0 +1 @@
1
+ 8af3c63cbbe5428300a5b5107b300ffbb5ab86e1d64815d0d914c0aab202b6377deaf94543001ae7fddb68a57c43072b3533d8b44990238d014be3135fb4cbc6
@@ -3,11 +3,11 @@
3
3
  <groupId>host.exp.exponent</groupId>
4
4
  <artifactId>expo.modules.filesystem</artifactId>
5
5
  <versioning>
6
- <latest>18.2.0-canary-20250630-547cd82</latest>
7
- <release>18.2.0-canary-20250630-547cd82</release>
6
+ <latest>18.2.0-canary-20250709-136b77f</latest>
7
+ <release>18.2.0-canary-20250709-136b77f</release>
8
8
  <versions>
9
- <version>18.2.0-canary-20250630-547cd82</version>
9
+ <version>18.2.0-canary-20250709-136b77f</version>
10
10
  </versions>
11
- <lastUpdated>20250630093942</lastUpdated>
11
+ <lastUpdated>20250709123258</lastUpdated>
12
12
  </versioning>
13
13
  </metadata>
@@ -1 +1 @@
1
- 339d845e591ef0783f86538957f108fe
1
+ 6445c4c02bdf253ba0b4429ccfd426a6
@@ -1 +1 @@
1
- 43466fab1747963a2a04a00526443d1d2b88183c
1
+ b1871c8a0dc4fc91e38e8e3796df00237f5d75fc
@@ -1 +1 @@
1
- 09ede7838cc14b5ca492eb4ed884a95d09ac5361915b063d27c9821a711141e3
1
+ e1c837646ad4911a6ef235c938a62e242ea514e13f7b3b8ff577cda970f4b083
@@ -1 +1 @@
1
- d46d832cb4b902315b7f4c0146797ca04a891b907d7cc7cce5c89557eee1be1ab0b9a6bef52a29d22a19a535fccbdf3ff443d308482f6a6fc3ec07b6bfd190ab
1
+ 4cfc126a4f14f58802f6d1f4f64f25a6b70584380ae8d8480fced19985d0e2b031a15701b7d86c4f7a0a298e8909c6fafe77493dbd3e3ec793ea8ecdab7a35f3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-file-system",
3
- "version": "18.2.0-canary-20250630-547cd82",
3
+ "version": "18.2.0-canary-20250709-136b77f",
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",
@@ -35,11 +35,11 @@
35
35
  "preset": "expo-module-scripts"
36
36
  },
37
37
  "devDependencies": {
38
- "expo-module-scripts": "4.1.9-canary-20250630-547cd82",
39
- "jest-expo": "54.0.0-canary-20250630-547cd82"
38
+ "expo-module-scripts": "4.1.10-canary-20250709-136b77f",
39
+ "jest-expo": "54.0.0-canary-20250709-136b77f"
40
40
  },
41
41
  "peerDependencies": {
42
- "expo": "54.0.0-canary-20250630-547cd82",
42
+ "expo": "54.0.0-canary-20250709-136b77f",
43
43
  "react-native": "*"
44
44
  }
45
45
  }
@@ -41,8 +41,7 @@ export declare class Directory {
41
41
  delete(): void;
42
42
 
43
43
  /**
44
- * A boolean representing if a directory exists. `true` if the directory exists, `false` otherwise.
45
- * Also, `false` if the application does not have read access to the file.
44
+ * A boolean representing if a directory exists and can be accessed.
46
45
  */
47
46
  exists: boolean;
48
47
 
@@ -143,6 +142,13 @@ export declare class File {
143
142
  */
144
143
  delete(): void;
145
144
 
145
+ /**
146
+ * Retrieves an object containing properties of a file
147
+ * @throws Error If the application does not have read access to the file, or if the path does not point to a file (e.g., it points to a directory).
148
+ * @returns An object with file metadata (e.g., size, creation date, etc.).
149
+ */
150
+ info(options?: InfoOptions): FileInfo;
151
+
146
152
  /**
147
153
  * A boolean representing if a file exists. `true` if the file exists, `false` otherwise.
148
154
  * Also, `false` if the application does not have read access to the file.
@@ -198,6 +204,16 @@ export declare class File {
198
204
  */
199
205
  md5: string | null;
200
206
 
207
+ /**
208
+ * A last modification time of the file expressed in milliseconds since epoch. Returns a Null if the file does not exist, or it cannot be read.
209
+ */
210
+ modificationTime: number | null;
211
+
212
+ /**
213
+ * A creation time of the file expressed in milliseconds since epoch. Returns null if the file does not exist, cannot be read or the Android version is earlier than API 26.
214
+ */
215
+ creationTime: number | null;
216
+
201
217
  /**
202
218
  * A mime type of the file. Null if the file does not exist, or it cannot be read.
203
219
  */
@@ -230,3 +246,49 @@ export declare class FileHandle {
230
246
  */
231
247
  size: number | null;
232
248
  }
249
+
250
+ export type FileInfo = {
251
+ /**
252
+ * Indicates whether the file exists.
253
+ */
254
+ exists: boolean;
255
+ /**
256
+ * A `file://` URI pointing to the file. This is the same as the `fileUri` input parameter.
257
+ */
258
+ uri?: string;
259
+ /**
260
+ * The size of the file in bytes.
261
+ */
262
+ size?: number;
263
+ /**
264
+ * The last modification time of the file expressed in milliseconds since epoch.
265
+ */
266
+ modificationTime?: number;
267
+ /**
268
+ * A creation time of the file expressed in milliseconds since epoch. Returns null if the Android version is earlier than API 26.
269
+ */
270
+ creationTime?: number;
271
+ /**
272
+ * Present if the `md5` option was truthy. Contains the MD5 hash of the file.
273
+ */
274
+ md5?: string;
275
+ };
276
+
277
+ export type InfoOptions = {
278
+ /**
279
+ * Whether to return the MD5 hash of the file.
280
+ * @default false
281
+ */
282
+ md5?: boolean;
283
+ };
284
+
285
+ export type PathInfo = {
286
+ /**
287
+ * Indicates whether the path exists. Returns true if it exists; false if the path does not exist or if there is no read permission.
288
+ */
289
+ exists: boolean;
290
+ /**
291
+ * Indicates whether the path is a directory. Returns true or false if the path exists; otherwise, returns null.
292
+ */
293
+ isDirectory: boolean | null;
294
+ };
@@ -1,5 +1,5 @@
1
1
  import ExpoFileSystem from './ExpoFileSystem';
2
- import type { DownloadOptions } from './ExpoFileSystem.types';
2
+ import type { DownloadOptions, PathInfo } from './ExpoFileSystem.types';
3
3
  import { PathUtilities } from './pathUtilities';
4
4
  import { FileSystemReadableStreamSource, FileSystemWritableSink } from './streams';
5
5
 
@@ -41,6 +41,13 @@ export class Paths extends PathUtilities {
41
41
  static get availableDiskSpace() {
42
42
  return ExpoFileSystem.availableDiskSpace;
43
43
  }
44
+
45
+ /**
46
+ * Returns an object that indicates if the specified path represents a directory.
47
+ */
48
+ static info(...uris: string[]): PathInfo {
49
+ return ExpoFileSystem.info(uris.join('/'));
50
+ }
44
51
  }
45
52
 
46
53
  /**
@@ -59,7 +66,7 @@ export class FileBlob extends Blob {
59
66
  this.file = file;
60
67
  }
61
68
 
62
- get size(): number {
69
+ override get size(): number {
63
70
  return this.file.size ?? 0;
64
71
  }
65
72
 
@@ -70,15 +77,15 @@ export class FileBlob extends Blob {
70
77
  return this.file.name;
71
78
  }
72
79
 
73
- get type(): string {
80
+ override get type(): string {
74
81
  return this.file.type ?? '';
75
82
  }
76
83
 
77
- async arrayBuffer(): Promise<ArrayBuffer> {
84
+ override async arrayBuffer(): Promise<ArrayBuffer> {
78
85
  return this.file.bytes().buffer as ArrayBuffer;
79
86
  }
80
87
 
81
- async text(): Promise<string> {
88
+ override async text(): Promise<string> {
82
89
  return this.file.text();
83
90
  }
84
91
 
@@ -86,11 +93,11 @@ export class FileBlob extends Blob {
86
93
  return this.file.bytes();
87
94
  }
88
95
 
89
- stream(): ReadableStream<Uint8Array> {
96
+ override stream(): ReadableStream<Uint8Array> {
90
97
  return this.file.readableStream();
91
98
  }
92
99
 
93
- slice(start?: number, end?: number, contentType?: string): Blob {
100
+ override slice(start?: number, end?: number, contentType?: string): Blob {
94
101
  return new Blob([this.file.bytes().slice(start, end)], { type: contentType });
95
102
  }
96
103
  }
@@ -188,7 +195,7 @@ export class Directory extends ExpoFileSystem.FileSystemDirectory {
188
195
  * Calling this method if the parent directory does not exist will throw an error.
189
196
  * @returns An array of `Directory` and `File` instances.
190
197
  */
191
- list(): (Directory | File)[] {
198
+ override list(): (Directory | File)[] {
192
199
  // We need to wrap it in the JS File/Directory classes, and returning SharedObjects in lists is not supported yet on Android.
193
200
  return super
194
201
  .listAsRecords()
package/src/next/index.ts CHANGED
@@ -1,3 +1,9 @@
1
1
  export * from './FileSystem';
2
2
 
3
- export { type CreateOptions, type FileHandle } from './ExpoFileSystem.types';
3
+ export {
4
+ type CreateOptions,
5
+ type FileHandle,
6
+ type FileInfo,
7
+ type InfoOptions,
8
+ type PathInfo,
9
+ } from './ExpoFileSystem.types';
@@ -1 +0,0 @@
1
- 879b501c85367721cbc11faa3f10b8f398f31b9752502aa3a3961fb08e0a276a24c6d43ae201ac503f43534e897fa9c16ac811cef124c2845373a79d9e299dbf
@@ -1 +0,0 @@
1
- 13c97c6f77623c748f9060b8e32381a9c30ed87b73340c28e7b76f9bc9b67274dbc8a51b4fb6e00ed55f12300bfb7e03c1740312f203b83d704004452b38f420
@@ -1 +0,0 @@
1
- 8bd9ba6c48aad6dbdb6e96273b25c5c6cd511ff0128df3aa64b63a07ac387416e3c44abced46be0727d332c8d4ae948e21e9ef46927bcc8b18bfda48623d06c8
@@ -1 +0,0 @@
1
- f9b6ac71ab9e7bf7fc84d0daed7fb6b5b4ac2c6b30814bf7f37827cfbfc7050b305ba4a7f4612498285d78a46009b39ad4881f5d6c99bff7c2f51c518406ba3b