@amazon-devices/expo-file-system 2.0.9000000001 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CMakeLists.txt CHANGED
@@ -12,35 +12,51 @@
12
12
  # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
13
13
  #
14
14
  set(PACKAGE_NAME "expo-file-system")
15
- set(AUTO_LINK_PACKAGE_NAME "${PACKAGE_NAME}-${KEPLER_REACT_NATIVE_MAJOR_VERSION}")
15
+ # The npm major versions the app-deployed artifact so it cannot collide with the frozen system lib
16
+ set(AUTO_LINK_PACKAGE_NAME "${PACKAGE_NAME}-3")
16
17
  set(SOURCES_KEPLER
17
18
  kepler/ExponentFileSystem.cpp
18
19
  kepler/CurlWrapper.cpp
19
20
  )
20
21
 
22
+ # Embed the static VodkaCurl stack (find_package resolves shared libs here).
23
+ # Link order matters: curl -> nghttp2 -> ssl -> crypto -> z.
21
24
  find_package(OpenSSL REQUIRED)
22
25
  find_package(CURL REQUIRED)
23
26
 
27
+ set(_expo_fs_orig_suffixes "${CMAKE_FIND_LIBRARY_SUFFIXES}")
28
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
29
+ find_library(CURL_STATIC NAMES curl REQUIRED)
30
+ find_library(NGHTTP2_STATIC NAMES nghttp2 REQUIRED)
31
+ find_library(SSL_STATIC NAMES ssl REQUIRED)
32
+ find_library(CRYPTO_STATIC NAMES crypto REQUIRED)
33
+ find_library(Z_STATIC NAMES z REQUIRED)
34
+ find_library(PSL_STATIC NAMES psl REQUIRED)
35
+ set(CMAKE_FIND_LIBRARY_SUFFIXES "${_expo_fs_orig_suffixes}")
36
+
24
37
  add_library(${PACKAGE_NAME} STATIC ${SOURCES_KEPLER})
25
38
 
26
39
  target_include_directories(
27
40
  ${PACKAGE_NAME}
28
41
  PRIVATE
29
42
  ${CURL_INCLUDE_DIRS}
43
+ ${OPENSSL_INCLUDE_DIR}
30
44
  )
31
45
 
32
46
  target_link_libraries(
33
47
  ${PACKAGE_NAME}
34
48
  PUBLIC
35
49
  turbomoduleAPI::turbomoduleAPI
36
- OpenSSL::SSL
37
- ${CURL_LIBRARIES}
50
+ ${CURL_STATIC}
51
+ ${NGHTTP2_STATIC}
52
+ ${SSL_STATIC}
53
+ ${CRYPTO_STATIC}
54
+ ${Z_STATIC}
55
+ ${PSL_STATIC}
38
56
  )
39
57
 
40
58
  add_library(${AUTO_LINK_PACKAGE_NAME} SHARED kepler/KeplerInit.cpp)
41
59
 
42
- set_target_properties(${AUTO_LINK_PACKAGE_NAME} PROPERTIES SOVERSION ${KEPLER_REACT_NATIVE_MAJOR_VERSION}.${KEPLER_REACT_NATIVE_MINOR_VERSION})
43
-
44
60
  target_link_libraries(
45
61
  ${AUTO_LINK_PACKAGE_NAME}
46
62
  PRIVATE
@@ -48,6 +64,15 @@ target_link_libraries(
48
64
  ${PACKAGE_NAME}
49
65
  )
50
66
 
67
+ # Hide embedded archive symbols per archive since ALL would also hide our own
68
+ target_link_options(${AUTO_LINK_PACKAGE_NAME} PRIVATE
69
+ "-Wl,--exclude-libs,libcurl.a"
70
+ "-Wl,--exclude-libs,libnghttp2.a"
71
+ "-Wl,--exclude-libs,libssl.a"
72
+ "-Wl,--exclude-libs,libcrypto.a"
73
+ "-Wl,--exclude-libs,libz.a"
74
+ "-Wl,--exclude-libs,libpsl.a")
75
+
51
76
  # Helps in checking undefined references
52
77
  add_executable(${AUTO_LINK_PACKAGE_NAME}_exe kepler/KeplerInit.cpp)
53
78
  target_link_libraries(${AUTO_LINK_PACKAGE_NAME}_exe
@@ -1,5 +1,5 @@
1
1
  import { TurboModuleRegistry } from '@amazon-devices/keplerscript-turbomodule-api';
2
- const nativeModule = TurboModuleRegistry.getEnforcing('ExponentFileSystem');
2
+ const nativeModule = TurboModuleRegistry.getEnforcing('ExponentFileSystemAppTM');
3
3
  let _requestId = 1;
4
4
  const generateRequestId = () => { return _requestId++; };
5
5
  function toHeadersList(headers) {
@@ -1 +1 @@
1
- {"version":3,"file":"ExponentFileSystem.kepler.js","sourceRoot":"","sources":["../src/ExponentFileSystem.kepler.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeplerTurboModule, TurboModuleRegistry } from '@amazon-devices/keplerscript-turbomodule-api';\n\nimport {\n DeletingOptions,\n DownloadOptions,\n FileInfo,\n FileSystemDownloadResult,\n FileSystemUploadOptions,\n FileSystemUploadResult,\n InfoOptions,\n MakeDirectoryOptions,\n ReadingOptions,\n RelocatingOptions,\n WritingOptions,\n} from './FileSystem.types';\nimport { ExponentFileSystemModule } from './types';\n\ninterface Spec extends KeplerTurboModule {\n readonly documentDirectory: () => string;\n readonly cacheDirectory: () => string;\n /**\n * @deprecated this property was removed from expo\n */\n readonly bundledAssets: () => string;\n readonly bundleDirectory: () => string;\n readonly getInfoAsync: (fileUri: string, options: InfoOptions) => Promise<FileInfo>;\n readonly readAsStringAsync: (fileUri: string, options: ReadingOptions) => Promise<string>;\n readonly getContentUriAsync: (fileUri: string) => Promise<string>;\n readonly writeAsStringAsync: (fileUri: string, contents: string, options: WritingOptions) => Promise<void>;\n readonly writeArrayBufferAsync: (fileUri: string, contents: ArrayBuffer) => Promise<void>;\n readonly deleteAsync: (fileUri: string, options: DeletingOptions) => Promise<void>;\n readonly moveAsync: (options: RelocatingOptions) => Promise<void>;\n readonly copyAsync: (options: RelocatingOptions) => Promise<void>;\n readonly makeDirectoryAsync: (fileUri: string, options: MakeDirectoryOptions) => Promise<void>;\n readonly readDirectoryAsync: (fileUri: string) => Promise<string[]>;\n readonly getFreeDiskStorageAsync: () => Promise<string>;\n readonly getTotalDiskCapacityAsync: () => Promise<string | undefined>;\n readonly downloadAsync: (url: string, fileUri: string, options: Record<string, any>, requestId: number, headers: Array<{key: string, value: string}>) => Promise<FileSystemDownloadResult>;\n readonly uploadAsync: (url: string, fileUri: string, options: Record<string, any>, requestId: number, headers: Array<{key: string, value: string}>) => Promise<FileSystemUploadResult>;\n}\n\nconst nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExponentFileSystem') as Spec;\n\nlet _requestId = 1;\nconst generateRequestId = (): number => { return _requestId++; };\n\nfunction toHeadersList(headers?: Record<string, string>): Array<{key: string, value: string}> {\n return Object.entries(headers ?? {}).map(([key, value]) => ({ key, value }));\n}\n\nconst module: ExponentFileSystemModule = {\n get name(): 'ExponentFileSystem' {\n return 'ExponentFileSystem';\n },\n get documentDirectory() {\n return nativeModule.documentDirectory();\n },\n get cacheDirectory() {\n return nativeModule.cacheDirectory();\n },\n /**\n * @deprecated this property was removed from expo\n */\n get bundledAssets() {\n return null;\n },\n get bundleDirectory() {\n return nativeModule.bundleDirectory();\n },\n async getInfoAsync(fileUri: string, options: InfoOptions) {\n return nativeModule.getInfoAsync(fileUri, options);\n },\n async readAsStringAsync(fileUri: string, options: ReadingOptions) {\n return nativeModule.readAsStringAsync(fileUri, options);\n },\n async getContentUriAsync(fileUri: string) {\n return nativeModule.getContentUriAsync(fileUri);\n },\n async writeAsStringAsync(fileUri: string, contents: string, options: WritingOptions) {\n return nativeModule.writeAsStringAsync(fileUri, contents, options);\n },\n async deleteAsync(fileUri: string, options: DeletingOptions) {\n return nativeModule.deleteAsync(fileUri, options);\n },\n async moveAsync(options: RelocatingOptions) {\n return nativeModule.moveAsync(options);\n },\n async copyAsync(options: RelocatingOptions) {\n return nativeModule.copyAsync(options);\n },\n async makeDirectoryAsync(fileUri: string, options: MakeDirectoryOptions) {\n return nativeModule.makeDirectoryAsync(fileUri, options);\n },\n async readDirectoryAsync(fileUri: string) {\n return nativeModule.readDirectoryAsync(fileUri);\n },\n async getFreeDiskStorageAsync() {\n return nativeModule.getFreeDiskStorageAsync().then(parseInt);\n },\n async getTotalDiskCapacityAsync() {\n const result = await nativeModule.getTotalDiskCapacityAsync();\n return result ? parseInt(result) : 0;\n },\n async downloadAsync(url: string, fileUri: string, options: DownloadOptions): Promise<FileSystemDownloadResult> {\n console.log(\"[ExponentFileSystem] downloadAsync: native streaming download via libcurl\");\n console.log(`DOWNLOAD URL: ${url}`);\n console.log(`FILE URL: ${fileUri}`);\n\n try {\n const result = await nativeModule.downloadAsync(url, fileUri, {\n md5: options.md5 ?? false,\n }, generateRequestId(), toHeadersList(options.headers));\n\n return result as FileSystemDownloadResult;\n } catch (error: any) {\n console.error(\"[ExponentFileSystem] Download failed:\", error?.message);\n const message = error?.message ?? JSON.stringify(error);\n throw new Error(`Download failed: ${message}`);\n }\n },\n async uploadAsync(url: string, fileUri: string, options: FileSystemUploadOptions): Promise<FileSystemUploadResult> {\n console.log(\"[ExponentFileSystem] uploadAsync: native streaming upload via libcurl\");\n\n try {\n const result = await nativeModule.uploadAsync(url, fileUri, {\n httpMethod: options.httpMethod ?? \"POST\",\n }, generateRequestId(), toHeadersList(options.headers));\n\n return result as FileSystemUploadResult;\n } catch (error: any) {\n console.error(\"[ExponentFileSystem] Upload failed:\", error?.message);\n const message = error?.message ?? JSON.stringify(error);\n throw new Error(`Upload failed: ${message}`);\n }\n },\n addListener(eventName: string): void{},\n removeListeners(count: number): void{},\n};\n\nexport default module;\n"]}
1
+ {"version":3,"file":"ExponentFileSystem.kepler.js","sourceRoot":"","sources":["../src/ExponentFileSystem.kepler.ts"],"names":[],"mappings":"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","sourcesContent":["import { KeplerTurboModule, TurboModuleRegistry } from '@amazon-devices/keplerscript-turbomodule-api';\n\nimport {\n DeletingOptions,\n DownloadOptions,\n FileInfo,\n FileSystemDownloadResult,\n FileSystemUploadOptions,\n FileSystemUploadResult,\n InfoOptions,\n MakeDirectoryOptions,\n ReadingOptions,\n RelocatingOptions,\n WritingOptions,\n} from './FileSystem.types';\nimport { ExponentFileSystemModule } from './types';\n\ninterface Spec extends KeplerTurboModule {\n readonly documentDirectory: () => string;\n readonly cacheDirectory: () => string;\n /**\n * @deprecated this property was removed from expo\n */\n readonly bundledAssets: () => string;\n readonly bundleDirectory: () => string;\n readonly getInfoAsync: (fileUri: string, options: InfoOptions) => Promise<FileInfo>;\n readonly readAsStringAsync: (fileUri: string, options: ReadingOptions) => Promise<string>;\n readonly getContentUriAsync: (fileUri: string) => Promise<string>;\n readonly writeAsStringAsync: (fileUri: string, contents: string, options: WritingOptions) => Promise<void>;\n readonly writeArrayBufferAsync: (fileUri: string, contents: ArrayBuffer) => Promise<void>;\n readonly deleteAsync: (fileUri: string, options: DeletingOptions) => Promise<void>;\n readonly moveAsync: (options: RelocatingOptions) => Promise<void>;\n readonly copyAsync: (options: RelocatingOptions) => Promise<void>;\n readonly makeDirectoryAsync: (fileUri: string, options: MakeDirectoryOptions) => Promise<void>;\n readonly readDirectoryAsync: (fileUri: string) => Promise<string[]>;\n readonly getFreeDiskStorageAsync: () => Promise<string>;\n readonly getTotalDiskCapacityAsync: () => Promise<string | undefined>;\n readonly downloadAsync: (url: string, fileUri: string, options: Record<string, any>, requestId: number, headers: Array<{key: string, value: string}>) => Promise<FileSystemDownloadResult>;\n readonly uploadAsync: (url: string, fileUri: string, options: Record<string, any>, requestId: number, headers: Array<{key: string, value: string}>) => Promise<FileSystemUploadResult>;\n}\n\nconst nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExponentFileSystemAppTM') as Spec;\n\nlet _requestId = 1;\nconst generateRequestId = (): number => { return _requestId++; };\n\nfunction toHeadersList(headers?: Record<string, string>): Array<{key: string, value: string}> {\n return Object.entries(headers ?? {}).map(([key, value]) => ({ key, value }));\n}\n\nconst module: ExponentFileSystemModule = {\n get name(): 'ExponentFileSystem' {\n return 'ExponentFileSystem';\n },\n get documentDirectory() {\n return nativeModule.documentDirectory();\n },\n get cacheDirectory() {\n return nativeModule.cacheDirectory();\n },\n /**\n * @deprecated this property was removed from expo\n */\n get bundledAssets() {\n return null;\n },\n get bundleDirectory() {\n return nativeModule.bundleDirectory();\n },\n async getInfoAsync(fileUri: string, options: InfoOptions) {\n return nativeModule.getInfoAsync(fileUri, options);\n },\n async readAsStringAsync(fileUri: string, options: ReadingOptions) {\n return nativeModule.readAsStringAsync(fileUri, options);\n },\n async getContentUriAsync(fileUri: string) {\n return nativeModule.getContentUriAsync(fileUri);\n },\n async writeAsStringAsync(fileUri: string, contents: string, options: WritingOptions) {\n return nativeModule.writeAsStringAsync(fileUri, contents, options);\n },\n async deleteAsync(fileUri: string, options: DeletingOptions) {\n return nativeModule.deleteAsync(fileUri, options);\n },\n async moveAsync(options: RelocatingOptions) {\n return nativeModule.moveAsync(options);\n },\n async copyAsync(options: RelocatingOptions) {\n return nativeModule.copyAsync(options);\n },\n async makeDirectoryAsync(fileUri: string, options: MakeDirectoryOptions) {\n return nativeModule.makeDirectoryAsync(fileUri, options);\n },\n async readDirectoryAsync(fileUri: string) {\n return nativeModule.readDirectoryAsync(fileUri);\n },\n async getFreeDiskStorageAsync() {\n return nativeModule.getFreeDiskStorageAsync().then(parseInt);\n },\n async getTotalDiskCapacityAsync() {\n const result = await nativeModule.getTotalDiskCapacityAsync();\n return result ? parseInt(result) : 0;\n },\n async downloadAsync(url: string, fileUri: string, options: DownloadOptions): Promise<FileSystemDownloadResult> {\n console.log(\"[ExponentFileSystem] downloadAsync: native streaming download via libcurl\");\n console.log(`DOWNLOAD URL: ${url}`);\n console.log(`FILE URL: ${fileUri}`);\n\n try {\n const result = await nativeModule.downloadAsync(url, fileUri, {\n md5: options.md5 ?? false,\n }, generateRequestId(), toHeadersList(options.headers));\n\n return result as FileSystemDownloadResult;\n } catch (error: any) {\n console.error(\"[ExponentFileSystem] Download failed:\", error?.message);\n const message = error?.message ?? JSON.stringify(error);\n throw new Error(`Download failed: ${message}`);\n }\n },\n async uploadAsync(url: string, fileUri: string, options: FileSystemUploadOptions): Promise<FileSystemUploadResult> {\n console.log(\"[ExponentFileSystem] uploadAsync: native streaming upload via libcurl\");\n\n try {\n const result = await nativeModule.uploadAsync(url, fileUri, {\n httpMethod: options.httpMethod ?? \"POST\",\n }, generateRequestId(), toHeadersList(options.headers));\n\n return result as FileSystemUploadResult;\n } catch (error: any) {\n console.error(\"[ExponentFileSystem] Upload failed:\", error?.message);\n const message = error?.message ?? JSON.stringify(error);\n throw new Error(`Upload failed: ${message}`);\n }\n },\n addListener(eventName: string): void{},\n removeListeners(count: number): void{},\n};\n\nexport default module;\n"]}
@@ -9,17 +9,17 @@
9
9
  #include "CurlWrapper.h"
10
10
 
11
11
  #include <Kepler/turbomodule/TMLog.h>
12
- #include <fmt/format.h>
13
12
 
14
13
  #include <cstring>
15
14
  #include <fstream>
16
15
  #include <mutex>
16
+ #include <string>
17
17
 
18
18
  static std::once_flag curlInitFlag;
19
19
 
20
20
  CurlWrapper::CurlWrapper() {
21
21
  std::call_once(curlInitFlag, []() {
22
- TMDEBUG(fmt::format("[CurlWrapper] Initializing libcurl (once)"));
22
+ TMDEBUG("[CurlWrapper] Initializing libcurl (once)");
23
23
  curl_global_init(CURL_GLOBAL_ALL);
24
24
  });
25
25
  }
@@ -35,7 +35,7 @@ size_t CurlWrapper::writeToFileCallback(void* contents, size_t size, size_t nmem
35
35
  size_t totalBytes = size * nmemb;
36
36
  file->write(static_cast<const char*>(contents), totalBytes);
37
37
  if (file->fail()) {
38
- TMERROR(fmt::format("[CurlWrapper] Failed to write {} bytes to file", totalBytes));
38
+ TMERROR(std::string("[CurlWrapper] Failed to write ") + std::to_string(totalBytes) + " bytes to file");
39
39
  return 0;
40
40
  }
41
41
  return totalBytes;
@@ -78,6 +78,11 @@ void CurlWrapper::setCommonOptions(
78
78
  curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
79
79
  curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
80
80
  curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
81
+ // libcurl/OpenSSL are statically linked, so their compiled-in default CA
82
+ // location does not match the device. Point TLS verification at the device
83
+ // CA trust store explicitly.
84
+ curl_easy_setopt(curl, CURLOPT_CAINFO, "/etc/ssl/certs/ca-certificates.crt");
85
+ curl_easy_setopt(curl, CURLOPT_CAPATH, "/etc/ssl/certs");
81
86
  // Abort if transfer speed drops below 1 byte/sec for 60 seconds
82
87
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
83
88
  curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60L);
@@ -100,14 +105,14 @@ CurlResponse CurlWrapper::downloadToFile(
100
105
  std::shared_ptr<CURL> curl(curl_easy_init(), curl_easy_cleanup);
101
106
  if (!curl) {
102
107
  response.errorMessage = "Failed to initialize curl";
103
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
108
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
104
109
  return response;
105
110
  }
106
111
 
107
112
  std::ofstream outputFile(filePath, std::ios::binary);
108
113
  if (!outputFile.is_open()) {
109
114
  response.errorMessage = "Failed to open output file: " + filePath;
110
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
115
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
111
116
  return response;
112
117
  }
113
118
 
@@ -122,18 +127,18 @@ CurlResponse CurlWrapper::downloadToFile(
122
127
  curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, errorBuffer);
123
128
  setCommonOptions(curl.get(), requestHeaders, &headerList);
124
129
 
125
- TMDEBUG(fmt::format("[CurlWrapper] Starting download: {} -> {}", url, filePath));
130
+ TMDEBUG(std::string("[CurlWrapper] Starting download: ") + url + " -> " + filePath);
126
131
  CURLcode res = curl_easy_perform(curl.get());
127
132
  outputFile.close();
128
133
 
129
134
  if (res != CURLE_OK) {
130
135
  std::string errorDetail = (errorBuffer[0] != '\0') ? errorBuffer : curl_easy_strerror(res);
131
136
  response.errorMessage = "Download failed: " + errorDetail;
132
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
137
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
133
138
  std::remove(filePath.c_str());
134
139
  } else {
135
140
  curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &response.statusCode);
136
- TMDEBUG(fmt::format("[CurlWrapper] Download complete, HTTP status: {}", response.statusCode));
141
+ TMDEBUG(std::string("[CurlWrapper] Download complete, HTTP status: ") + std::to_string(response.statusCode));
137
142
  if (response.statusCode >= 200 && response.statusCode < 300) {
138
143
  response.success = true;
139
144
  } else {
@@ -158,14 +163,14 @@ CurlResponse CurlWrapper::uploadFromFile(
158
163
  std::shared_ptr<CURL> curl(curl_easy_init(), curl_easy_cleanup);
159
164
  if (!curl) {
160
165
  response.errorMessage = "Failed to initialize curl";
161
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
166
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
162
167
  return response;
163
168
  }
164
169
 
165
170
  std::ifstream inputFile(filePath, std::ios::binary | std::ios::ate);
166
171
  if (!inputFile.is_open()) {
167
172
  response.errorMessage = "Failed to open input file: " + filePath;
168
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
173
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
169
174
  return response;
170
175
  }
171
176
  auto fileSize = inputFile.tellg();
@@ -218,17 +223,17 @@ CurlResponse CurlWrapper::uploadFromFile(
218
223
 
219
224
  setCommonOptions(curl.get(), requestHeaders, &headerList);
220
225
 
221
- TMDEBUG(fmt::format("[CurlWrapper] Starting upload: {} -> {} ({})", filePath, url, httpMethod));
226
+ TMDEBUG(std::string("[CurlWrapper] Starting upload: ") + filePath + " -> " + url + " (" + httpMethod + ")");
222
227
  CURLcode res = curl_easy_perform(curl.get());
223
228
  inputFile.close();
224
229
 
225
230
  if (res != CURLE_OK) {
226
231
  std::string errorDetail = (errorBuffer[0] != '\0') ? errorBuffer : curl_easy_strerror(res);
227
232
  response.errorMessage = "Upload failed: " + errorDetail;
228
- TMERROR(fmt::format("[CurlWrapper] {}", response.errorMessage));
233
+ TMERROR(std::string("[CurlWrapper] ") + response.errorMessage);
229
234
  } else {
230
235
  curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &response.statusCode);
231
- TMDEBUG(fmt::format("[CurlWrapper] Upload complete, HTTP status: {}", response.statusCode));
236
+ TMDEBUG(std::string("[CurlWrapper] Upload complete, HTTP status: ") + std::to_string(response.statusCode));
232
237
  if (response.statusCode >= 200 && response.statusCode < 300) {
233
238
  response.success = true;
234
239
  } else {
@@ -237,7 +242,7 @@ CurlResponse CurlWrapper::uploadFromFile(
237
242
  }
238
243
 
239
244
  if (responseBodyTruncated) {
240
- TMDEBUG(fmt::format("[CurlWrapper] Response body truncated at {}B", MAX_RESPONSE_BODY_SIZE));
245
+ TMDEBUG(std::string("[CurlWrapper] Response body truncated at ") + std::to_string(MAX_RESPONSE_BODY_SIZE) + "B");
241
246
  responseBody += "\n... [truncated]";
242
247
  }
243
248
  response.headers["__response_body__"] = responseBody;
@@ -10,7 +10,6 @@
10
10
 
11
11
  #include <JsonContainer/JsonContainer.h>
12
12
  #include <Kepler/turbomodule/TMLog.h>
13
- #include <fmt/format.h>
14
13
  #include <openssl/evp.h>
15
14
  #include <openssl/md5.h>
16
15
  #include <sys/stat.h>
@@ -72,7 +71,7 @@ T getOrDefault(NativeJSObject map, std::string key, const T& defaultValue) {
72
71
  } // namespace
73
72
 
74
73
  ExponentFileSystem::ExponentFileSystem() : com::amazon::kepler::turbomodule::KeplerTurboModule("ExponentFileSystem") {
75
- TMDEBUG(fmt::format("ExponentFileSystem constructor {}", name()));
74
+ TMDEBUG(std::string("ExponentFileSystem constructor ") + name());
76
75
  curlWrapper_ = std::make_unique<CurlWrapper>();
77
76
  };
78
77
 
@@ -111,10 +110,7 @@ Promise ExponentFileSystem::getInfoAsync(const std::string fileUri, NativeJSObje
111
110
  int status = stat(fileUri.c_str(), &fileStat);
112
111
  if (status != 0) {
113
112
  std::string errorMessage = std::strerror(errno);
114
- TMERROR(fmt::format(
115
- "[ExponentFileSystem] error in `stat()` call: {} while obtaining information about the file: {}!",
116
- errorMessage,
117
- fileUri));
113
+ TMERROR(std::string("[ExponentFileSystem] error in `stat()` call: ") + errorMessage + " while obtaining information about the file: " + fileUri + "!");
118
114
  promise->reject(
119
115
  "Error while obtaining information about the file! `stat()` call exited with error: " +
120
116
  errorMessage);
@@ -135,20 +131,20 @@ Promise ExponentFileSystem::getInfoAsync(const std::string fileUri, NativeJSObje
135
131
 
136
132
  if (shouldIncludeMD5Hash) {
137
133
  std::ifstream stream(fileUri);
138
- TMDEBUG(fmt::format("[MD5] trying to open the file: {} for read", fileUri));
134
+ TMDEBUG(std::string("[MD5] trying to open the file: ") + fileUri + " for read");
139
135
  if (stream.fail()) {
140
136
  std::string errorMessage = std::strerror(errno);
141
- TMERROR(fmt::format("[MD5] unable to open file at {} for read: {}", fileUri, errorMessage));
137
+ TMERROR(std::string("[MD5] unable to open file at ") + fileUri + " for read: " + errorMessage);
142
138
  promise->reject("Error while trying to open the file: " + errorMessage);
143
139
  return;
144
140
  } else {
145
141
  std::stringstream buffer;
146
142
  buffer << stream.rdbuf();
147
143
  std::string content = buffer.str();
148
- TMDEBUG(fmt::format("[MD5] content: {}", content));
144
+ TMDEBUG(std::string("[MD5] content: ") + content);
149
145
  TMDEBUG("[MD5] calculating the file content hash...");
150
146
  std::string hash = md5Hash(content);
151
- TMDEBUG(fmt::format("[MD5] file hash: {}", hash));
147
+ TMDEBUG(std::string("[MD5] file hash: ") + hash);
152
148
  fileInfo.insert({"md5", hash});
153
149
  }
154
150
  }
@@ -163,17 +159,14 @@ Promise ExponentFileSystem::readAsStringAsync(const std::string fileUri, NativeJ
163
159
 
164
160
  return Promise([this, fileUri, desiredEncoding, bytesToSkip, length](std::shared_ptr<Promise> promise) {
165
161
  try {
166
- TMDEBUG(fmt::format("[ExponentFileSystem] path: {}", fileUri));
162
+ TMDEBUG(std::string("[ExponentFileSystem] path: ") + fileUri);
167
163
  std::int32_t fileSize = std::filesystem::file_size(fileUri);
168
164
 
169
165
  std::ifstream stream(fileUri);
170
- TMDEBUG(fmt::format("[ExponentFileSystem] trying to open the file: {} for read", fileUri));
166
+ TMDEBUG(std::string("[ExponentFileSystem] trying to open the file: ") + fileUri + " for read");
171
167
  if (stream.fail()) {
172
168
  std::string errorMessage = std::strerror(errno);
173
- TMERROR(fmt::format(
174
- "[ExponentFileSystem] unable to open file at {} for read: {}",
175
- fileUri,
176
- errorMessage));
169
+ TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for read: " + errorMessage);
177
170
  promise->reject("Error while trying to open the file: " + errorMessage);
178
171
  return;
179
172
  }
@@ -191,7 +184,7 @@ Promise ExponentFileSystem::readAsStringAsync(const std::string fileUri, NativeJ
191
184
  std::string content;
192
185
  content.resize(bytesToRead);
193
186
  stream.read(&content[0], bytesToRead);
194
- TMDEBUG(fmt::format("[ExponentFileSystem] content: {}", content));
187
+ TMDEBUG(std::string("[ExponentFileSystem] content: ") + content);
195
188
 
196
189
  if (desiredEncoding == "base64") {
197
190
  TMDEBUG("[base64] base64 encoding...");
@@ -200,7 +193,7 @@ Promise ExponentFileSystem::readAsStringAsync(const std::string fileUri, NativeJ
200
193
  promise->resolve(content);
201
194
  }
202
195
  } catch (std::exception& e) {
203
- TMERROR(fmt::format("[ExponentFileSystem] reading file exception reason: {}", e.what()));
196
+ TMERROR(std::string("[ExponentFileSystem] reading file exception reason: ") + e.what());
204
197
  promise->reject(e.what());
205
198
  }
206
199
  });
@@ -223,13 +216,10 @@ Promise ExponentFileSystem::writeAsStringAsync(
223
216
  return Promise([this, fileUri, contents, desiredEncoding](std::shared_ptr<Promise> promise) {
224
217
  try {
225
218
  std::ofstream stream(fileUri);
226
- TMDEBUG(fmt::format("[ExponentFileSystem] trying to open the file: {} for write", fileUri));
219
+ TMDEBUG(std::string("[ExponentFileSystem] trying to open the file: ") + fileUri + " for write");
227
220
  if (stream.fail()) {
228
221
  std::string errorMessage = std::strerror(errno);
229
- TMERROR(fmt::format(
230
- "[ExponentFileSystem] unable to open file at {} for write: {}",
231
- fileUri,
232
- errorMessage));
222
+ TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for write: " + errorMessage);
233
223
  promise->reject("Error while trying to open the file: " + errorMessage);
234
224
  return;
235
225
  } else {
@@ -241,7 +231,7 @@ Promise ExponentFileSystem::writeAsStringAsync(
241
231
 
242
232
  if (stream.fail()) {
243
233
  std::string errorMessage = std::strerror(errno);
244
- TMERROR(fmt::format("[ExponentFileSystem] error while writing to file: {}", errorMessage));
234
+ TMERROR(std::string("[ExponentFileSystem] error while writing to file: ") + errorMessage);
245
235
  promise->reject("Error while trying to write to the file: " + errorMessage);
246
236
  return;
247
237
  }
@@ -249,7 +239,7 @@ Promise ExponentFileSystem::writeAsStringAsync(
249
239
  }
250
240
  promise->resolve(NULL);
251
241
  } catch (std::exception& e) {
252
- TMERROR(fmt::format("[ExponentFileSystem] writing file exception reason: {}", e.what()));
242
+ TMERROR(std::string("[ExponentFileSystem] writing file exception reason: ") + e.what());
253
243
  promise->reject(e.what());
254
244
  }
255
245
  });
@@ -264,10 +254,7 @@ Promise ExponentFileSystem::writeArrayBufferAsync(const std::string fileUri, con
264
254
 
265
255
  if (file.fail()) {
266
256
  std::string errorMessage = std::strerror(errno);
267
- TMERROR(fmt::format(
268
- "[ExponentFileSystem] unable to open file at {} for response write: {}",
269
- fileUri,
270
- errorMessage));
257
+ TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for response write: " + errorMessage);
271
258
  promise->reject("File while tring to open the file: " + errorMessage);
272
259
  return;
273
260
  } else {
@@ -275,9 +262,7 @@ Promise ExponentFileSystem::writeArrayBufferAsync(const std::string fileUri, con
275
262
  file.write(reinterpret_cast<const char*>(dataPtr->data()), dataPtr->size());
276
263
  if (file.fail()) {
277
264
  std::string errorMessage = std::strerror(errno);
278
- TMERROR(fmt::format(
279
- "[ExponentFileSystem] error while writing response to file: {}",
280
- errorMessage));
265
+ TMERROR(std::string("[ExponentFileSystem] error while writing response to file: ") + errorMessage);
281
266
  promise->reject("Error while trying to write response to file: " + errorMessage);
282
267
  return;
283
268
  }
@@ -285,7 +270,7 @@ Promise ExponentFileSystem::writeArrayBufferAsync(const std::string fileUri, con
285
270
  }
286
271
  promise->resolve(NULL);
287
272
  } catch (std::exception& e) {
288
- TMERROR(fmt::format("[ExponentFileSystem] writing file exception reason: {}", e.what()));
273
+ TMERROR(std::string("[ExponentFileSystem] writing file exception reason: ") + e.what());
289
274
  promise->reject(e.what());
290
275
  }
291
276
  });
@@ -311,7 +296,7 @@ Promise ExponentFileSystem::deleteAsync(const std::string fileUri, NativeJSObjec
311
296
  std::filesystem::remove(fileUri);
312
297
  promise->resolve(NULL);
313
298
  } catch (std::filesystem::filesystem_error& e) {
314
- TMERROR(fmt::format("[ExponentFileSystem] file deletion exception, reason: {}", e.what()));
299
+ TMERROR(std::string("[ExponentFileSystem] file deletion exception, reason: ") + e.what());
315
300
  promise->reject(e.what());
316
301
  }
317
302
  } else {
@@ -336,7 +321,7 @@ Promise ExponentFileSystem::moveAsync(NativeJSObject options) {
336
321
  std::filesystem::rename(from, to);
337
322
  promise->resolve(NULL);
338
323
  } catch (std::filesystem::filesystem_error& e) {
339
- TMERROR(fmt::format("[ExponentFileSystem] file transfer exception, reason: {}", e.what()));
324
+ TMERROR(std::string("[ExponentFileSystem] file transfer exception, reason: ") + e.what());
340
325
  promise->reject(e.what());
341
326
  }
342
327
  });
@@ -353,11 +338,49 @@ Promise ExponentFileSystem::copyAsync(NativeJSObject options) {
353
338
  return;
354
339
  }
355
340
 
341
+ // Run the copy on a background thread to avoid blocking the JS thread.
342
+ // std::filesystem::copy is synchronous and blocks for the whole copy
343
+ // duration, which trips the KeplerScript thread watchdog on large files.
356
344
  try {
357
- std::filesystem::copy(from, to, std::filesystem::copy_options::recursive);
358
- promise->resolve(NULL);
359
- } catch (std::filesystem::filesystem_error& e) {
360
- TMERROR(fmt::format("[ExponentFileSystem] file transfer exception, reason: {}", e.what()));
345
+ std::thread([promise, from, to]() {
346
+ // If we cannot tell whether the destination already exists,
347
+ // assume it does. Then a failed copy never deletes data we did
348
+ // not create.
349
+ std::error_code existsError;
350
+ bool destExisted = true;
351
+ bool destExistsResult = std::filesystem::exists(to, existsError);
352
+ if (!existsError) {
353
+ destExisted = destExistsResult;
354
+ }
355
+ // A failed copy can leave a truncated destination, but only if
356
+ // this copy created it. Remove just that.
357
+ auto removePartialDestination = [&to, destExisted]() {
358
+ if (destExisted) {
359
+ return;
360
+ }
361
+ std::error_code cleanupError;
362
+ std::filesystem::remove_all(to, cleanupError);
363
+ if (cleanupError) {
364
+ TMERROR(std::string("[ExponentFileSystem] could not remove partial destination, reason: ") + cleanupError.message());
365
+ }
366
+ };
367
+ try {
368
+ std::filesystem::copy(from, to, std::filesystem::copy_options::recursive);
369
+ promise->resolve(NULL);
370
+ } catch (std::exception& e) {
371
+ TMERROR(std::string("[ExponentFileSystem] file transfer exception, reason: ") + e.what());
372
+ removePartialDestination();
373
+ promise->reject(e.what());
374
+ } catch (...) {
375
+ TMERROR(std::string("[ExponentFileSystem] file transfer failed with an unknown exception"));
376
+ removePartialDestination();
377
+ promise->reject("unknown file transfer error");
378
+ }
379
+ }).detach();
380
+ } catch (std::exception& e) {
381
+ // The thread could not start. Reject here so the promise always
382
+ // settles and the JS caller does not wait forever.
383
+ TMERROR(std::string("[ExponentFileSystem] could not start copy thread, reason: ") + e.what());
361
384
  promise->reject(e.what());
362
385
  }
363
386
  });
@@ -385,15 +408,13 @@ Promise ExponentFileSystem::makeDirectoryAsync(const std::string fileUri, Native
385
408
  if (std::filesystem::create_directories(fileUri, errorCode)) {
386
409
  TMDEBUG("[ExponentFileSystem] directory created successfully!");
387
410
  } else {
388
- TMERROR(fmt::format(
389
- "[ExponentFileSystem] directory creation failed, message: {}",
390
- errorCode.message()));
411
+ TMERROR(std::string("[ExponentFileSystem] directory creation failed, message: ") + errorCode.message());
391
412
  promise->reject(errorCode.message());
392
413
  return;
393
414
  }
394
415
  promise->resolve(NULL);
395
416
  } catch (std::exception& e) {
396
- TMERROR(fmt::format("[ExponentFileSystem] directory creation exception, reason: {}", e.what()));
417
+ TMERROR(std::string("[ExponentFileSystem] directory creation exception, reason: ") + e.what());
397
418
  promise->reject(e.what());
398
419
  }
399
420
  });
@@ -422,7 +443,7 @@ Promise ExponentFileSystem::readDirectoryAsync(const std::string fileUri) {
422
443
 
423
444
  promise->resolve(childrenNames);
424
445
  } catch (std::exception& e) {
425
- TMERROR(fmt::format("[ExponentFileSystem] Reading directory exception: {}", e.what()));
446
+ TMERROR(std::string("[ExponentFileSystem] Reading directory exception: ") + e.what());
426
447
  promise->reject(e.what());
427
448
  }
428
449
  });
@@ -436,16 +457,14 @@ Promise ExponentFileSystem::getFreeDiskStorageAsync() {
436
457
  std::error_code errorCode;
437
458
  std::filesystem::space_info spaceInfo = std::filesystem::space(path, errorCode);
438
459
  if (errorCode) {
439
- TMERROR(fmt::format(
440
- "[ExponentFileSystem] Error when getting free disk space: {}",
441
- errorCode.message()));
460
+ TMERROR(std::string("[ExponentFileSystem] Error when getting free disk space: ") + errorCode.message());
442
461
  promise->reject(errorCode.message());
443
462
  return;
444
463
  }
445
- TMDEBUG(fmt::format("[ExponentFileSystem] Free space: {}", std::to_string(spaceInfo.available)));
464
+ TMDEBUG(std::string("[ExponentFileSystem] Free space: ") + std::to_string(spaceInfo.available));
446
465
  promise->resolve(std::to_string(spaceInfo.available));
447
466
  } catch (std::exception& e) {
448
- TMERROR(fmt::format("[ExponentFileSystem] Getting free disk storage exception: {}", e.what()));
467
+ TMERROR(std::string("[ExponentFileSystem] Getting free disk storage exception: ") + e.what());
449
468
  promise->reject(e.what());
450
469
  }
451
470
  });
@@ -477,8 +496,7 @@ Promise ExponentFileSystem::downloadAsync(
477
496
 
478
497
  return Promise(
479
498
  [this, url, fileUri, shouldComputeMd5, requestHeaders](std::shared_ptr<Promise> promise) {
480
- TMDEBUG(fmt::format(
481
- "[ExponentFileSystem] downloadAsync: {} -> {}", url, fileUri));
499
+ TMDEBUG(std::string("[ExponentFileSystem] downloadAsync: ") + url + " -> " + fileUri);
482
500
 
483
501
  // Run curl on a background thread to avoid blocking the JS thread.
484
502
  // curl_easy_perform is synchronous and will block for the entire download
@@ -488,8 +506,7 @@ Promise ExponentFileSystem::downloadAsync(
488
506
  auto result = curlWrapper->downloadToFile(url, fileUri, requestHeaders);
489
507
 
490
508
  if (!result.success) {
491
- TMERROR(fmt::format(
492
- "[ExponentFileSystem] downloadAsync failed: {}", result.errorMessage));
509
+ TMERROR(std::string("[ExponentFileSystem] downloadAsync failed: ") + result.errorMessage);
493
510
  promise->reject(result.errorMessage);
494
511
  return;
495
512
  }
@@ -522,8 +539,7 @@ Promise ExponentFileSystem::downloadAsync(
522
539
  downloadResult.insert({"md5", md5Hash(buffer.str())});
523
540
  }
524
541
  } catch (std::exception& e) {
525
- TMERROR(fmt::format(
526
- "[ExponentFileSystem] MD5 computation failed: {}", e.what()));
542
+ TMERROR(std::string("[ExponentFileSystem] MD5 computation failed: ") + e.what());
527
543
  }
528
544
  }
529
545
 
@@ -554,8 +570,7 @@ Promise ExponentFileSystem::uploadAsync(
554
570
 
555
571
  return Promise(
556
572
  [this, url, fileUri, httpMethod, requestHeaders](std::shared_ptr<Promise> promise) {
557
- TMDEBUG(fmt::format(
558
- "[ExponentFileSystem] uploadAsync: {} -> {} ({})", fileUri, url, httpMethod));
573
+ TMDEBUG(std::string("[ExponentFileSystem] uploadAsync: ") + fileUri + " -> " + url + " (" + httpMethod + ")");
559
574
 
560
575
  // Run curl on a background thread to avoid blocking the JS thread.
561
576
  auto curlWrapper = curlWrapper_.get();
@@ -563,8 +578,7 @@ Promise ExponentFileSystem::uploadAsync(
563
578
  auto result = curlWrapper->uploadFromFile(url, fileUri, httpMethod, requestHeaders);
564
579
 
565
580
  if (!result.success) {
566
- TMERROR(fmt::format(
567
- "[ExponentFileSystem] uploadAsync failed: {}", result.errorMessage));
581
+ TMERROR(std::string("[ExponentFileSystem] uploadAsync failed: ") + result.errorMessage);
568
582
  promise->reject(result.errorMessage);
569
583
  return;
570
584
  }
@@ -14,7 +14,7 @@
14
14
  extern "C" {
15
15
  __attribute__((visibility("default"))) void autoLinkKeplerTurboModulesV1() noexcept {
16
16
  TMDEBUG("autoLinkKeplerTurboModulesV1 for expo-file-system");
17
- KEPLER_REGISTER_TURBO_MODULE_WITH_NAME("ExponentFileSystem", ExponentFileSystem);
17
+ KEPLER_REGISTER_TURBO_MODULE_WITH_NAME("ExponentFileSystemAppTM", ExponentFileSystem);
18
18
  }
19
19
  }
20
20
 
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name": "@amazon-devices/expo-file-system", "version": "2.0.9000000001+15.8.0", "description": "Provides access to the local file system on the device.", "main": "build/index.js", "types": "build/index.d.ts", "sideEffects": false, "scripts": {"build": "expo-module build", "clean": "expo-module clean", "lint": "expo-module lint", "test": "expo-module test", "prepublishOnly": "expo-module prepublishOnly", "expo-module": "expo-module"}, "kepler": {"temporary-alias-for-package": {"name": "expo-file-system", "version": "15.8.0"}}, "keywords": ["react-native", "expo", "file-system", "file"], "repository": {"type": "git", "url": "https://github.com/expo/expo.git", "directory": "packages/expo-file-system"}, "bugs": {"url": "https://github.com/expo/expo/issues"}, "author": "650 Industries, Inc.", "license": "See license in LICENSE", "homepage": "https://docs.expo.dev/versions/latest/sdk/filesystem/", "dependencies": {"uuid": "^3.4.0", "@amazon-devices/keplerscript-turbomodule-api": "^1.0.0"}, "devDependencies": {"expo-module-scripts": "^3.0.0", "jest-expo": "~50.0.0-alpha.0", "@types/glob": "^7.2.0", "@types/minimatch": "^3.0.5"}, "peerDependencies": {"expo": "^50.0.0-alpha.1"}}
1
+ {"name": "@amazon-devices/expo-file-system", "version": "3.0.2+15.8.0", "description": "Provides access to the local file system on the device.", "main": "build/index.js", "types": "build/index.d.ts", "sideEffects": false, "scripts": {"build": "expo-module build", "clean": "expo-module clean", "lint": "expo-module lint", "test": "expo-module test", "prepublishOnly": "expo-module prepublishOnly", "expo-module": "expo-module"}, "kepler": {"temporary-alias-for-package": {"name": "expo-file-system", "version": "15.8.0"}}, "keywords": ["react-native", "expo", "file-system", "file"], "repository": {"type": "git", "url": "https://github.com/expo/expo.git", "directory": "packages/expo-file-system"}, "bugs": {"url": "https://github.com/expo/expo/issues"}, "author": "650 Industries, Inc.", "license": "See license in LICENSE", "homepage": "https://docs.expo.dev/versions/latest/sdk/filesystem/", "dependencies": {"uuid": "^3.4.0", "@amazon-devices/keplerscript-turbomodule-api": "^1.0.0"}, "devDependencies": {"expo-module-scripts": "^3.0.0", "jest-expo": "~50.0.0-alpha.0", "@types/glob": "^7.2.0", "@types/minimatch": "^3.0.5"}, "peerDependencies": {"expo": "^50.0.0-alpha.1"}}
@@ -0,0 +1 @@
1
+ {"root":["./src/withFileSystem.ts"],"version":"5.9.3"}
@@ -10,10 +10,11 @@ module.exports = {
10
10
  platforms: {
11
11
  kepler: {
12
12
  autolink: {
13
- ExponentFileSystemLib: {
14
- provider: 'system',
15
- libraryName: 'libexpo-file-system-2.so.2',
16
- turbomodules: ['ExponentFileSystem'],
13
+ ExponentFileSystemAppLib: {
14
+ provider: 'application',
15
+ linkDynamic: true,
16
+ libraryName: 'libexpo-file-system-3.so',
17
+ turbomodules: ['ExponentFileSystemAppTM'],
17
18
  },
18
19
  },
19
20
  },
@@ -39,7 +39,7 @@ interface Spec extends KeplerTurboModule {
39
39
  readonly uploadAsync: (url: string, fileUri: string, options: Record<string, any>, requestId: number, headers: Array<{key: string, value: string}>) => Promise<FileSystemUploadResult>;
40
40
  }
41
41
 
42
- const nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExponentFileSystem') as Spec;
42
+ const nativeModule = TurboModuleRegistry.getEnforcing<Spec>('ExponentFileSystemAppTM') as Spec;
43
43
 
44
44
  let _requestId = 1;
45
45
  const generateRequestId = (): number => { return _requestId++; };
@@ -1,7 +0,0 @@
1
- {
2
- "versions": {
3
- "2.0.0": "/com.amazon.kepler.expo_file_system_2@IExpoFileSystem_0",
4
- "2.0.9000000000": "/com.amazon.kepler.expo_file_system_2@IExpoFileSystem_0",
5
- "2.0.9000000001": "/com.amazon.kepler.expo_file_system_2@IExpoFileSystem_0"
6
- }
7
- }