@amazon-devices/expo-file-system 2.0.9000000000 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +41 -7
- package/README.kepler.md +1 -0
- package/build/ExponentFileSystem.kepler.d.ts.map +1 -1
- package/build/ExponentFileSystem.kepler.js +28 -45
- package/build/ExponentFileSystem.kepler.js.map +1 -1
- package/build/FileSystem.d.ts.map +1 -1
- package/build/FileSystem.js +0 -2
- package/build/FileSystem.js.map +1 -1
- package/kepler/CurlWrapper.cpp +254 -0
- package/kepler/CurlWrapper.h +56 -0
- package/kepler/ExponentFileSystem.cpp +290 -171
- package/kepler/ExponentFileSystem.h +39 -24
- package/kepler/KeplerInit.cpp +5 -10
- package/lib/aarch64-release/libexpo-file-system-3.so +0 -0
- package/lib/armv7-release/libexpo-file-system-3.so +0 -0
- package/lib/x86_64-release/libexpo-file-system-3.so +0 -0
- package/package.json +1 -1
- package/react-native.config.js +5 -3
- package/src/ExponentFileSystem.kepler.ts +33 -60
- package/src/FileSystem.ts +0 -2
- package/kepler/NetworkingRequestHandler.cpp +0 -47
|
@@ -9,18 +9,26 @@
|
|
|
9
9
|
#include "ExponentFileSystem.h"
|
|
10
10
|
|
|
11
11
|
#include <JsonContainer/JsonContainer.h>
|
|
12
|
-
#include <
|
|
13
|
-
#include <KeplerScript/utils/KeplerPath.h>
|
|
12
|
+
#include <Kepler/turbomodule/TMLog.h>
|
|
14
13
|
#include <openssl/evp.h>
|
|
15
14
|
#include <openssl/md5.h>
|
|
16
15
|
#include <sys/stat.h>
|
|
17
16
|
|
|
17
|
+
#include <algorithm>
|
|
18
18
|
#include <filesystem>
|
|
19
19
|
#include <fstream>
|
|
20
20
|
#include <iostream>
|
|
21
21
|
#include <memory>
|
|
22
22
|
#include <sstream>
|
|
23
23
|
#include <string>
|
|
24
|
+
#include <thread>
|
|
25
|
+
|
|
26
|
+
#include "CurlWrapper.h"
|
|
27
|
+
|
|
28
|
+
using com::amazon::kepler::turbomodule::ArrayBuffer;
|
|
29
|
+
using com::amazon::kepler::turbomodule::NativeJSArray;
|
|
30
|
+
using com::amazon::kepler::turbomodule::NativeJSObject;
|
|
31
|
+
using com::amazon::kepler::turbomodule::Promise;
|
|
24
32
|
|
|
25
33
|
namespace {
|
|
26
34
|
std::string md5Hash(const std::string& content) {
|
|
@@ -54,7 +62,7 @@ std::string base64Encode(const std::string& input) {
|
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
template<typename T>
|
|
57
|
-
T getOrDefault(
|
|
65
|
+
T getOrDefault(NativeJSObject map, std::string key, const T& defaultValue) {
|
|
58
66
|
if (map.count(key)) {
|
|
59
67
|
return std::get<T>(map[key]);
|
|
60
68
|
}
|
|
@@ -62,8 +70,9 @@ T getOrDefault(volta::react::NativeJSObject map, std::string key, const T& defau
|
|
|
62
70
|
}
|
|
63
71
|
} // namespace
|
|
64
72
|
|
|
65
|
-
ExponentFileSystem::ExponentFileSystem() :
|
|
66
|
-
|
|
73
|
+
ExponentFileSystem::ExponentFileSystem() : com::amazon::kepler::turbomodule::KeplerTurboModule("ExponentFileSystem") {
|
|
74
|
+
TMDEBUG(std::string("ExponentFileSystem constructor ") + name());
|
|
75
|
+
curlWrapper_ = std::make_unique<CurlWrapper>();
|
|
67
76
|
};
|
|
68
77
|
|
|
69
78
|
/**
|
|
@@ -79,21 +88,19 @@ std::string ExponentFileSystem::cacheDirectory() {
|
|
|
79
88
|
};
|
|
80
89
|
|
|
81
90
|
std::string ExponentFileSystem::bundleDirectory() {
|
|
82
|
-
|
|
91
|
+
const std::string bundleName = "index.hermes.bundle";
|
|
92
|
+
return std::filesystem::absolute("pkg/bundle/" + bundleName);
|
|
83
93
|
};
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
const std::string fileUri,
|
|
87
|
-
volta::react::NativeJSObject options) {
|
|
95
|
+
Promise ExponentFileSystem::getInfoAsync(const std::string fileUri, NativeJSObject options) {
|
|
88
96
|
bool shouldIncludeFileSize = getOrDefault(options, "size", false);
|
|
89
97
|
bool shouldIncludeMD5Hash = getOrDefault(options, "md5", false);
|
|
90
98
|
|
|
91
|
-
return
|
|
92
|
-
std::shared_ptr<volta::react::Promise> promise) {
|
|
99
|
+
return Promise([this, fileUri, shouldIncludeFileSize, shouldIncludeMD5Hash](std::shared_ptr<Promise> promise) {
|
|
93
100
|
bool exists = std::filesystem::exists(fileUri);
|
|
94
101
|
|
|
95
102
|
if (!exists) {
|
|
96
|
-
promise->resolve(
|
|
103
|
+
promise->resolve(NativeJSObject{{"exists", false}, {"isDirectory", false}});
|
|
97
104
|
return;
|
|
98
105
|
}
|
|
99
106
|
|
|
@@ -102,15 +109,18 @@ volta::react::Promise ExponentFileSystem::getInfoAsync(
|
|
|
102
109
|
struct stat fileStat;
|
|
103
110
|
int status = stat(fileUri.c_str(), &fileStat);
|
|
104
111
|
if (status != 0) {
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
std::string errorMessage = std::strerror(errno);
|
|
113
|
+
TMERROR(std::string("[ExponentFileSystem] error in `stat()` call: ") + errorMessage + " while obtaining information about the file: " + fileUri + "!");
|
|
114
|
+
promise->reject(
|
|
115
|
+
"Error while obtaining information about the file! `stat()` call exited with error: " +
|
|
116
|
+
errorMessage);
|
|
107
117
|
return;
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
std::uintmax_t modificationTime = fileStat.st_mtime;
|
|
111
121
|
std::uintmax_t fileSize = getSize(fileUri);
|
|
112
122
|
|
|
113
|
-
auto fileInfo =
|
|
123
|
+
auto fileInfo = NativeJSObject{
|
|
114
124
|
{"exists", true},
|
|
115
125
|
{"isDirectory", isDirectory},
|
|
116
126
|
{"modification", std::to_string(modificationTime)}};
|
|
@@ -121,19 +131,20 @@ volta::react::Promise ExponentFileSystem::getInfoAsync(
|
|
|
121
131
|
|
|
122
132
|
if (shouldIncludeMD5Hash) {
|
|
123
133
|
std::ifstream stream(fileUri);
|
|
124
|
-
|
|
134
|
+
TMDEBUG(std::string("[MD5] trying to open the file: ") + fileUri + " for read");
|
|
125
135
|
if (stream.fail()) {
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
std::string errorMessage = std::strerror(errno);
|
|
137
|
+
TMERROR(std::string("[MD5] unable to open file at ") + fileUri + " for read: " + errorMessage);
|
|
138
|
+
promise->reject("Error while trying to open the file: " + errorMessage);
|
|
128
139
|
return;
|
|
129
140
|
} else {
|
|
130
141
|
std::stringstream buffer;
|
|
131
142
|
buffer << stream.rdbuf();
|
|
132
143
|
std::string content = buffer.str();
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
TMDEBUG(std::string("[MD5] content: ") + content);
|
|
145
|
+
TMDEBUG("[MD5] calculating the file content hash...");
|
|
135
146
|
std::string hash = md5Hash(content);
|
|
136
|
-
|
|
147
|
+
TMDEBUG(std::string("[MD5] file hash: ") + hash);
|
|
137
148
|
fileInfo.insert({"md5", hash});
|
|
138
149
|
}
|
|
139
150
|
}
|
|
@@ -141,140 +152,140 @@ volta::react::Promise ExponentFileSystem::getInfoAsync(
|
|
|
141
152
|
});
|
|
142
153
|
};
|
|
143
154
|
|
|
144
|
-
|
|
145
|
-
const std::string fileUri,
|
|
146
|
-
volta::react::NativeJSObject options) {
|
|
155
|
+
Promise ExponentFileSystem::readAsStringAsync(const std::string fileUri, NativeJSObject options) {
|
|
147
156
|
std::string desiredEncoding = getOrDefault(options, "encoding", std::string("utf8"));
|
|
148
157
|
int bytesToSkip = int(getOrDefault(options, "position", static_cast<double>(0)));
|
|
149
158
|
int length = getOrDefault(options, "length", static_cast<double>(-1));
|
|
150
159
|
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
std::int32_t fileSize = std::filesystem::file_size(fileUri);
|
|
156
|
-
|
|
157
|
-
std::ifstream stream(fileUri);
|
|
158
|
-
volta::react::KPLog.debug("[ExponentFileSystem] trying to open the file: {} for read", fileUri);
|
|
159
|
-
if (stream.fail()) {
|
|
160
|
-
volta::react::KPLog.error("[ExponentFileSystem] unable to open file at {} for read", fileUri);
|
|
161
|
-
promise->reject("Error while trying to open the file");
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
160
|
+
return Promise([this, fileUri, desiredEncoding, bytesToSkip, length](std::shared_ptr<Promise> promise) {
|
|
161
|
+
try {
|
|
162
|
+
TMDEBUG(std::string("[ExponentFileSystem] path: ") + fileUri);
|
|
163
|
+
std::int32_t fileSize = std::filesystem::file_size(fileUri);
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
std::ifstream stream(fileUri);
|
|
166
|
+
TMDEBUG(std::string("[ExponentFileSystem] trying to open the file: ") + fileUri + " for read");
|
|
167
|
+
if (stream.fail()) {
|
|
168
|
+
std::string errorMessage = std::strerror(errno);
|
|
169
|
+
TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for read: " + errorMessage);
|
|
170
|
+
promise->reject("Error while trying to open the file: " + errorMessage);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
170
173
|
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
if (bytesToSkip >= fileSize) {
|
|
175
|
+
TMERROR("[ExponentFileSystem] cannot skip beyond end of file!");
|
|
176
|
+
promise->reject("Cannot skip beyond end of file");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
173
179
|
|
|
174
|
-
|
|
175
|
-
std::
|
|
176
|
-
content.resize(bytesToRead);
|
|
177
|
-
stream.read(&content[0], bytesToRead);
|
|
178
|
-
volta::react::KPLog.debug("[ExponentFileSystem] content: {}", content);
|
|
180
|
+
std::int32_t bytesToRead =
|
|
181
|
+
length < 0 ? std::min(fileSize, fileSize - bytesToSkip) : std::min(length, fileSize - bytesToSkip);
|
|
179
182
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
183
|
+
stream.seekg(bytesToSkip, std::ios::beg);
|
|
184
|
+
std::string content;
|
|
185
|
+
content.resize(bytesToRead);
|
|
186
|
+
stream.read(&content[0], bytesToRead);
|
|
187
|
+
TMDEBUG(std::string("[ExponentFileSystem] content: ") + content);
|
|
188
|
+
|
|
189
|
+
if (desiredEncoding == "base64") {
|
|
190
|
+
TMDEBUG("[base64] base64 encoding...");
|
|
191
|
+
promise->resolve(base64Encode(content));
|
|
192
|
+
} else {
|
|
193
|
+
promise->resolve(content);
|
|
194
|
+
}
|
|
195
|
+
} catch (std::exception& e) {
|
|
196
|
+
TMERROR(std::string("[ExponentFileSystem] reading file exception reason: ") + e.what());
|
|
197
|
+
promise->reject(e.what());
|
|
198
|
+
}
|
|
199
|
+
});
|
|
191
200
|
};
|
|
192
201
|
|
|
193
|
-
|
|
194
|
-
return
|
|
202
|
+
Promise ExponentFileSystem::getContentUriAsync(const std::string fileUri) {
|
|
203
|
+
return Promise([this, &fileUri](std::shared_ptr<Promise> promise) {
|
|
195
204
|
std::string contentUri = std::filesystem::absolute(fileUri);
|
|
196
205
|
|
|
197
206
|
promise->resolve(contentUri);
|
|
198
207
|
});
|
|
199
208
|
};
|
|
200
209
|
|
|
201
|
-
|
|
210
|
+
Promise ExponentFileSystem::writeAsStringAsync(
|
|
202
211
|
const std::string fileUri,
|
|
203
212
|
const std::string contents,
|
|
204
|
-
|
|
213
|
+
NativeJSObject options) {
|
|
205
214
|
std::string desiredEncoding = getOrDefault(options, "encoding", std::string("utf8"));
|
|
206
215
|
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
return Promise([this, fileUri, contents, desiredEncoding](std::shared_ptr<Promise> promise) {
|
|
217
|
+
try {
|
|
218
|
+
std::ofstream stream(fileUri);
|
|
219
|
+
TMDEBUG(std::string("[ExponentFileSystem] trying to open the file: ") + fileUri + " for write");
|
|
220
|
+
if (stream.fail()) {
|
|
221
|
+
std::string errorMessage = std::strerror(errno);
|
|
222
|
+
TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for write: " + errorMessage);
|
|
223
|
+
promise->reject("Error while trying to open the file: " + errorMessage);
|
|
224
|
+
return;
|
|
225
|
+
} else {
|
|
226
|
+
if (desiredEncoding == "base64") {
|
|
227
|
+
stream << base64Encode(contents);
|
|
228
|
+
} else {
|
|
229
|
+
stream << contents;
|
|
230
|
+
}
|
|
222
231
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
stream.close();
|
|
229
|
-
}
|
|
230
|
-
promise->resolve();
|
|
231
|
-
} catch (std::exception& e) {
|
|
232
|
-
volta::react::KPLog.error("[ExponentFileSystem] writing file exception reason: {}", e.what());
|
|
233
|
-
promise->reject(e.what());
|
|
232
|
+
if (stream.fail()) {
|
|
233
|
+
std::string errorMessage = std::strerror(errno);
|
|
234
|
+
TMERROR(std::string("[ExponentFileSystem] error while writing to file: ") + errorMessage);
|
|
235
|
+
promise->reject("Error while trying to write to the file: " + errorMessage);
|
|
236
|
+
return;
|
|
234
237
|
}
|
|
235
|
-
|
|
238
|
+
stream.close();
|
|
239
|
+
}
|
|
240
|
+
promise->resolve(NULL);
|
|
241
|
+
} catch (std::exception& e) {
|
|
242
|
+
TMERROR(std::string("[ExponentFileSystem] writing file exception reason: ") + e.what());
|
|
243
|
+
promise->reject(e.what());
|
|
244
|
+
}
|
|
245
|
+
});
|
|
236
246
|
};
|
|
237
247
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const volta::react::ArrayBuffer arrayBuffer) {
|
|
241
|
-
return volta::react::Promise([this, fileUri, arrayBuffer](std::shared_ptr<volta::react::Promise> promise) {
|
|
248
|
+
Promise ExponentFileSystem::writeArrayBufferAsync(const std::string fileUri, const ArrayBuffer arrayBuffer) {
|
|
249
|
+
return Promise([this, fileUri, arrayBuffer](std::shared_ptr<Promise> promise) {
|
|
242
250
|
try {
|
|
243
251
|
std::ofstream file(fileUri, std::ios::binary);
|
|
244
|
-
|
|
252
|
+
auto dataPtr =
|
|
253
|
+
std::make_shared<std::vector<uint8_t>>(arrayBuffer.data(), arrayBuffer.data() + arrayBuffer.size());
|
|
245
254
|
|
|
246
255
|
if (file.fail()) {
|
|
247
|
-
|
|
248
|
-
|
|
256
|
+
std::string errorMessage = std::strerror(errno);
|
|
257
|
+
TMERROR(std::string("[ExponentFileSystem] unable to open file at ") + fileUri + " for response write: " + errorMessage);
|
|
258
|
+
promise->reject("File while tring to open the file: " + errorMessage);
|
|
249
259
|
return;
|
|
250
260
|
} else {
|
|
251
261
|
if (dataPtr && !dataPtr->empty()) {
|
|
252
262
|
file.write(reinterpret_cast<const char*>(dataPtr->data()), dataPtr->size());
|
|
253
263
|
if (file.fail()) {
|
|
254
|
-
|
|
255
|
-
|
|
264
|
+
std::string errorMessage = std::strerror(errno);
|
|
265
|
+
TMERROR(std::string("[ExponentFileSystem] error while writing response to file: ") + errorMessage);
|
|
266
|
+
promise->reject("Error while trying to write response to file: " + errorMessage);
|
|
256
267
|
return;
|
|
257
268
|
}
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
|
-
promise->resolve();
|
|
271
|
+
promise->resolve(NULL);
|
|
261
272
|
} catch (std::exception& e) {
|
|
262
|
-
|
|
273
|
+
TMERROR(std::string("[ExponentFileSystem] writing file exception reason: ") + e.what());
|
|
263
274
|
promise->reject(e.what());
|
|
264
275
|
}
|
|
265
276
|
});
|
|
266
277
|
}
|
|
267
278
|
|
|
268
|
-
|
|
279
|
+
Promise ExponentFileSystem::deleteAsync(const std::string fileUri, NativeJSObject options) {
|
|
269
280
|
bool idempotent = getOrDefault(options, "idempotent", false);
|
|
270
281
|
|
|
271
|
-
return
|
|
282
|
+
return Promise([this, fileUri, idempotent](std::shared_ptr<Promise> promise) {
|
|
272
283
|
if (!std::filesystem::exists(fileUri)) {
|
|
273
284
|
if (idempotent) {
|
|
274
|
-
promise->resolve();
|
|
285
|
+
promise->resolve(NULL);
|
|
275
286
|
return;
|
|
276
287
|
} else {
|
|
277
|
-
|
|
288
|
+
TMERROR("[ExponentFileSystem] file does not exist at the specified URI!");
|
|
278
289
|
promise->reject("File does not exist at the specified URI");
|
|
279
290
|
return;
|
|
280
291
|
}
|
|
@@ -283,77 +294,73 @@ volta::react::Promise ExponentFileSystem::deleteAsync(const std::string fileUri,
|
|
|
283
294
|
if (std::filesystem::is_regular_file(fileUri)) {
|
|
284
295
|
try {
|
|
285
296
|
std::filesystem::remove(fileUri);
|
|
286
|
-
promise->resolve();
|
|
297
|
+
promise->resolve(NULL);
|
|
287
298
|
} catch (std::filesystem::filesystem_error& e) {
|
|
288
|
-
|
|
299
|
+
TMERROR(std::string("[ExponentFileSystem] file deletion exception, reason: ") + e.what());
|
|
289
300
|
promise->reject(e.what());
|
|
290
301
|
}
|
|
291
302
|
} else {
|
|
292
|
-
|
|
303
|
+
TMERROR("[ExponentFileSystem] resource is not a regular file!");
|
|
293
304
|
promise->reject("Resource is not a regular file");
|
|
294
305
|
}
|
|
295
306
|
});
|
|
296
307
|
};
|
|
297
308
|
|
|
298
|
-
|
|
309
|
+
Promise ExponentFileSystem::moveAsync(NativeJSObject options) {
|
|
299
310
|
std::string from = getOrDefault(options, "from", std::string(""));
|
|
300
311
|
std::string to = getOrDefault(options, "to", std::string(""));
|
|
301
312
|
|
|
302
|
-
return
|
|
313
|
+
return Promise([this, from, to](std::shared_ptr<Promise> promise) {
|
|
303
314
|
if (from.empty() || to.empty()) {
|
|
304
|
-
|
|
305
|
-
"[ExponentFileSystem]::moveAsync 'from' or 'to' not provided but they are both required");
|
|
315
|
+
TMERROR("[ExponentFileSystem]::moveAsync 'from' or 'to' not provided but they are both required");
|
|
306
316
|
promise->reject("'from' or 'to' not provided but they are both required");
|
|
307
317
|
return;
|
|
308
318
|
}
|
|
309
319
|
|
|
310
320
|
try {
|
|
311
321
|
std::filesystem::rename(from, to);
|
|
312
|
-
promise->resolve();
|
|
322
|
+
promise->resolve(NULL);
|
|
313
323
|
} catch (std::filesystem::filesystem_error& e) {
|
|
314
|
-
|
|
324
|
+
TMERROR(std::string("[ExponentFileSystem] file transfer exception, reason: ") + e.what());
|
|
315
325
|
promise->reject(e.what());
|
|
316
326
|
}
|
|
317
327
|
});
|
|
318
328
|
};
|
|
319
329
|
|
|
320
|
-
|
|
330
|
+
Promise ExponentFileSystem::copyAsync(NativeJSObject options) {
|
|
321
331
|
std::string from = getOrDefault(options, "from", std::string(""));
|
|
322
332
|
std::string to = getOrDefault(options, "to", std::string(""));
|
|
323
333
|
|
|
324
|
-
return
|
|
334
|
+
return Promise([this, from, to](std::shared_ptr<Promise> promise) {
|
|
325
335
|
if (from.empty() || to.empty()) {
|
|
326
|
-
|
|
327
|
-
"[ExponentFileSystem]::copyAsync 'from' or 'to' not provided but they are both required");
|
|
336
|
+
TMERROR("[ExponentFileSystem]::copyAsync 'from' or 'to' not provided but they are both required");
|
|
328
337
|
promise->reject("'from' or 'to' not provided but they are both required");
|
|
329
338
|
return;
|
|
330
339
|
}
|
|
331
340
|
|
|
332
341
|
try {
|
|
333
342
|
std::filesystem::copy(from, to, std::filesystem::copy_options::recursive);
|
|
334
|
-
promise->resolve();
|
|
343
|
+
promise->resolve(NULL);
|
|
335
344
|
} catch (std::filesystem::filesystem_error& e) {
|
|
336
|
-
|
|
345
|
+
TMERROR(std::string("[ExponentFileSystem] file transfer exception, reason: ") + e.what());
|
|
337
346
|
promise->reject(e.what());
|
|
338
347
|
}
|
|
339
348
|
});
|
|
340
349
|
};
|
|
341
350
|
|
|
342
|
-
|
|
343
|
-
const std::string fileUri,
|
|
344
|
-
volta::react::NativeJSObject options) {
|
|
351
|
+
Promise ExponentFileSystem::makeDirectoryAsync(const std::string fileUri, NativeJSObject options) {
|
|
345
352
|
bool idempotent = getOrDefault(options, "intermediates", false);
|
|
346
353
|
|
|
347
|
-
return
|
|
354
|
+
return Promise([this, fileUri, idempotent](std::shared_ptr<Promise> promise) {
|
|
348
355
|
try {
|
|
349
356
|
bool exists = std::filesystem::exists(fileUri);
|
|
350
357
|
|
|
351
358
|
if (exists) {
|
|
352
359
|
if (idempotent) {
|
|
353
|
-
promise->resolve();
|
|
360
|
+
promise->resolve(NULL);
|
|
354
361
|
return;
|
|
355
362
|
}
|
|
356
|
-
|
|
363
|
+
TMERROR("[ExponentFileSystem] directory already exists!");
|
|
357
364
|
promise->reject("Directory already exists");
|
|
358
365
|
return;
|
|
359
366
|
}
|
|
@@ -361,36 +368,34 @@ volta::react::Promise ExponentFileSystem::makeDirectoryAsync(
|
|
|
361
368
|
std::error_code errorCode;
|
|
362
369
|
|
|
363
370
|
if (std::filesystem::create_directories(fileUri, errorCode)) {
|
|
364
|
-
|
|
371
|
+
TMDEBUG("[ExponentFileSystem] directory created successfully!");
|
|
365
372
|
} else {
|
|
366
|
-
|
|
367
|
-
"[ExponentFileSystem] directory creation failed, message: {}",
|
|
368
|
-
errorCode.message());
|
|
373
|
+
TMERROR(std::string("[ExponentFileSystem] directory creation failed, message: ") + errorCode.message());
|
|
369
374
|
promise->reject(errorCode.message());
|
|
370
375
|
return;
|
|
371
376
|
}
|
|
372
|
-
promise->resolve();
|
|
377
|
+
promise->resolve(NULL);
|
|
373
378
|
} catch (std::exception& e) {
|
|
374
|
-
|
|
379
|
+
TMERROR(std::string("[ExponentFileSystem] directory creation exception, reason: ") + e.what());
|
|
375
380
|
promise->reject(e.what());
|
|
376
381
|
}
|
|
377
382
|
});
|
|
378
383
|
};
|
|
379
384
|
|
|
380
|
-
|
|
381
|
-
return
|
|
385
|
+
Promise ExponentFileSystem::readDirectoryAsync(const std::string fileUri) {
|
|
386
|
+
return Promise([this, fileUri](std::shared_ptr<Promise> promise) {
|
|
382
387
|
try {
|
|
383
388
|
if (!std::filesystem::exists(fileUri)) {
|
|
384
|
-
|
|
389
|
+
TMERROR("[ExponentFileSystem] directory does not exist at the specified URI!");
|
|
385
390
|
promise->reject("Directory does not exist at the specified URI");
|
|
386
391
|
return;
|
|
387
392
|
} else if (!std::filesystem::is_directory(fileUri)) {
|
|
388
|
-
|
|
393
|
+
TMERROR("[ExponentFileSystem] resource at the specified URI is not a directory!");
|
|
389
394
|
promise->reject("Resource at the specified URI is not a directory");
|
|
390
395
|
return;
|
|
391
396
|
}
|
|
392
397
|
|
|
393
|
-
auto childrenNames =
|
|
398
|
+
auto childrenNames = NativeJSArray{};
|
|
394
399
|
|
|
395
400
|
// Symbolic links of the directory entries are not followed
|
|
396
401
|
// during this enumeration.
|
|
@@ -400,60 +405,173 @@ volta::react::Promise ExponentFileSystem::readDirectoryAsync(const std::string f
|
|
|
400
405
|
|
|
401
406
|
promise->resolve(childrenNames);
|
|
402
407
|
} catch (std::exception& e) {
|
|
403
|
-
|
|
408
|
+
TMERROR(std::string("[ExponentFileSystem] Reading directory exception: ") + e.what());
|
|
404
409
|
promise->reject(e.what());
|
|
405
410
|
}
|
|
406
411
|
});
|
|
407
412
|
};
|
|
408
413
|
|
|
409
|
-
|
|
410
|
-
return
|
|
414
|
+
Promise ExponentFileSystem::getFreeDiskStorageAsync() {
|
|
415
|
+
return Promise([](std::shared_ptr<Promise> promise) {
|
|
411
416
|
try {
|
|
412
417
|
std::string path = std::filesystem::absolute("data");
|
|
413
418
|
|
|
414
419
|
std::error_code errorCode;
|
|
415
420
|
std::filesystem::space_info spaceInfo = std::filesystem::space(path, errorCode);
|
|
416
421
|
if (errorCode) {
|
|
417
|
-
|
|
418
|
-
"[ExponentFileSystem] Error when getting free disk space: {}",
|
|
419
|
-
errorCode.message());
|
|
422
|
+
TMERROR(std::string("[ExponentFileSystem] Error when getting free disk space: ") + errorCode.message());
|
|
420
423
|
promise->reject(errorCode.message());
|
|
421
424
|
return;
|
|
422
425
|
}
|
|
423
|
-
|
|
426
|
+
TMDEBUG(std::string("[ExponentFileSystem] Free space: ") + std::to_string(spaceInfo.available));
|
|
424
427
|
promise->resolve(std::to_string(spaceInfo.available));
|
|
425
428
|
} catch (std::exception& e) {
|
|
426
|
-
|
|
429
|
+
TMERROR(std::string("[ExponentFileSystem] Getting free disk storage exception: ") + e.what());
|
|
427
430
|
promise->reject(e.what());
|
|
428
431
|
}
|
|
429
432
|
});
|
|
430
433
|
};
|
|
431
434
|
|
|
432
|
-
|
|
433
|
-
return
|
|
435
|
+
Promise ExponentFileSystem::getTotalDiskCapacityAsync() {
|
|
436
|
+
return Promise([](std::shared_ptr<Promise> promise) { promise->resolve(NULL); });
|
|
434
437
|
};
|
|
435
438
|
|
|
436
|
-
|
|
437
|
-
const std::string,
|
|
438
|
-
const std::string,
|
|
439
|
-
|
|
440
|
-
double
|
|
441
|
-
std::vector<utils::json::JsonContainer>) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
439
|
+
Promise ExponentFileSystem::downloadAsync(
|
|
440
|
+
const std::string url,
|
|
441
|
+
const std::string fileUri,
|
|
442
|
+
NativeJSObject options,
|
|
443
|
+
double /* requestId */,
|
|
444
|
+
std::vector<utils::json::JsonContainer> headers) {
|
|
445
|
+
bool shouldComputeMd5 = getOrDefault(options, "md5", false);
|
|
446
|
+
|
|
447
|
+
// Convert headers vector to std::map
|
|
448
|
+
std::map<std::string, std::string> requestHeaders;
|
|
449
|
+
for (auto header : headers) {
|
|
450
|
+
try {
|
|
451
|
+
auto key = header["key"].getValue<std::string>();
|
|
452
|
+
auto value = header["value"].getValue<std::string>();
|
|
453
|
+
requestHeaders[key] = value;
|
|
454
|
+
} catch (...) {
|
|
455
|
+
// Skip malformed header entries
|
|
456
|
+
}
|
|
457
|
+
}
|
|
446
458
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
459
|
+
return Promise(
|
|
460
|
+
[this, url, fileUri, shouldComputeMd5, requestHeaders](std::shared_ptr<Promise> promise) {
|
|
461
|
+
TMDEBUG(std::string("[ExponentFileSystem] downloadAsync: ") + url + " -> " + fileUri);
|
|
462
|
+
|
|
463
|
+
// Run curl on a background thread to avoid blocking the JS thread.
|
|
464
|
+
// curl_easy_perform is synchronous and will block for the entire download
|
|
465
|
+
// duration, which triggers the KeplerScript thread watchdog on large files.
|
|
466
|
+
auto curlWrapper = curlWrapper_.get();
|
|
467
|
+
std::thread([promise, curlWrapper, url, fileUri, shouldComputeMd5, requestHeaders]() {
|
|
468
|
+
auto result = curlWrapper->downloadToFile(url, fileUri, requestHeaders);
|
|
469
|
+
|
|
470
|
+
if (!result.success) {
|
|
471
|
+
TMERROR(std::string("[ExponentFileSystem] downloadAsync failed: ") + result.errorMessage);
|
|
472
|
+
promise->reject(result.errorMessage);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Build response headers as NativeJSObject
|
|
477
|
+
auto responseHeaders = NativeJSObject{};
|
|
478
|
+
std::string mimeType;
|
|
479
|
+
for (const auto& [key, value] : result.headers) {
|
|
480
|
+
if (key == "__response_body__") continue;
|
|
481
|
+
responseHeaders.insert({key, value});
|
|
482
|
+
std::string lowerKey = key;
|
|
483
|
+
std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
|
|
484
|
+
if (lowerKey == "content-type") {
|
|
485
|
+
mimeType = value.substr(0, value.find(';'));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
auto downloadResult = NativeJSObject{
|
|
490
|
+
{"uri", fileUri},
|
|
491
|
+
{"status", static_cast<double>(result.statusCode)},
|
|
492
|
+
{"headers", responseHeaders},
|
|
493
|
+
{"mimeType", mimeType.empty() ? std::string("") : mimeType}};
|
|
494
|
+
|
|
495
|
+
if (shouldComputeMd5) {
|
|
496
|
+
try {
|
|
497
|
+
std::ifstream stream(fileUri, std::ios::binary);
|
|
498
|
+
if (stream.good()) {
|
|
499
|
+
std::stringstream buffer;
|
|
500
|
+
buffer << stream.rdbuf();
|
|
501
|
+
downloadResult.insert({"md5", md5Hash(buffer.str())});
|
|
502
|
+
}
|
|
503
|
+
} catch (std::exception& e) {
|
|
504
|
+
TMERROR(std::string("[ExponentFileSystem] MD5 computation failed: ") + e.what());
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
promise->resolve(downloadResult);
|
|
509
|
+
}).detach();
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
Promise ExponentFileSystem::uploadAsync(
|
|
514
|
+
const std::string url,
|
|
515
|
+
const std::string fileUri,
|
|
516
|
+
NativeJSObject options,
|
|
517
|
+
double /* requestId */,
|
|
518
|
+
std::vector<utils::json::JsonContainer> headers) {
|
|
519
|
+
std::string httpMethod = getOrDefault(options, "httpMethod", std::string("POST"));
|
|
520
|
+
|
|
521
|
+
// Convert headers vector to std::map
|
|
522
|
+
std::map<std::string, std::string> requestHeaders;
|
|
523
|
+
for (auto header : headers) {
|
|
524
|
+
try {
|
|
525
|
+
auto key = header["key"].getValue<std::string>();
|
|
526
|
+
auto value = header["value"].getValue<std::string>();
|
|
527
|
+
requestHeaders[key] = value;
|
|
528
|
+
} catch (...) {
|
|
529
|
+
// Skip malformed header entries
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return Promise(
|
|
534
|
+
[this, url, fileUri, httpMethod, requestHeaders](std::shared_ptr<Promise> promise) {
|
|
535
|
+
TMDEBUG(std::string("[ExponentFileSystem] uploadAsync: ") + fileUri + " -> " + url + " (" + httpMethod + ")");
|
|
536
|
+
|
|
537
|
+
// Run curl on a background thread to avoid blocking the JS thread.
|
|
538
|
+
auto curlWrapper = curlWrapper_.get();
|
|
539
|
+
std::thread([promise, curlWrapper, url, fileUri, httpMethod, requestHeaders]() {
|
|
540
|
+
auto result = curlWrapper->uploadFromFile(url, fileUri, httpMethod, requestHeaders);
|
|
541
|
+
|
|
542
|
+
if (!result.success) {
|
|
543
|
+
TMERROR(std::string("[ExponentFileSystem] uploadAsync failed: ") + result.errorMessage);
|
|
544
|
+
promise->reject(result.errorMessage);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Build response headers as NativeJSObject
|
|
549
|
+
auto responseHeaders = NativeJSObject{};
|
|
550
|
+
std::string mimeType;
|
|
551
|
+
std::string responseBody;
|
|
552
|
+
for (const auto& [key, value] : result.headers) {
|
|
553
|
+
if (key == "__response_body__") {
|
|
554
|
+
responseBody = value;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
responseHeaders.insert({key, value});
|
|
558
|
+
std::string lowerKey = key;
|
|
559
|
+
std::transform(lowerKey.begin(), lowerKey.end(), lowerKey.begin(), ::tolower);
|
|
560
|
+
if (lowerKey == "content-type") {
|
|
561
|
+
mimeType = value.substr(0, value.find(';'));
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
auto uploadResult = NativeJSObject{
|
|
566
|
+
{"body", responseBody},
|
|
567
|
+
{"status", static_cast<double>(result.statusCode)},
|
|
568
|
+
{"headers", responseHeaders},
|
|
569
|
+
{"mimeType", mimeType.empty() ? std::string("") : mimeType}};
|
|
570
|
+
|
|
571
|
+
promise->resolve(uploadResult);
|
|
572
|
+
}).detach();
|
|
573
|
+
});
|
|
574
|
+
}
|
|
457
575
|
|
|
458
576
|
std::uintmax_t ExponentFileSystem::getSize(const std::string& path) {
|
|
459
577
|
if (std::filesystem::is_regular_file(path)) {
|
|
@@ -475,8 +593,9 @@ std::uintmax_t ExponentFileSystem::getSize(const std::string& path) {
|
|
|
475
593
|
};
|
|
476
594
|
|
|
477
595
|
void ExponentFileSystem::aggregateMethods(
|
|
478
|
-
|
|
479
|
-
|
|
596
|
+
com::amazon::kepler::turbomodule::MethodAggregator<com::amazon::kepler::turbomodule::KeplerTurboModule>&
|
|
597
|
+
methodAggregator) const noexcept {
|
|
598
|
+
TMDEBUG("methodAggregator");
|
|
480
599
|
|
|
481
600
|
methodAggregator.addMethod("documentDirectory", 0, &ExponentFileSystem::documentDirectory);
|
|
482
601
|
methodAggregator.addMethod("cacheDirectory", 0, &ExponentFileSystem::cacheDirectory);
|
|
@@ -494,5 +613,5 @@ void ExponentFileSystem::aggregateMethods(
|
|
|
494
613
|
methodAggregator.addMethod("getFreeDiskStorageAsync", 0, &ExponentFileSystem::getFreeDiskStorageAsync);
|
|
495
614
|
methodAggregator.addMethod("getTotalDiskCapacityAsync", 0, &ExponentFileSystem::getTotalDiskCapacityAsync);
|
|
496
615
|
methodAggregator.addMethod("downloadAsync", 5, &ExponentFileSystem::downloadAsync);
|
|
497
|
-
methodAggregator.addMethod("uploadAsync",
|
|
616
|
+
methodAggregator.addMethod("uploadAsync", 5, &ExponentFileSystem::uploadAsync);
|
|
498
617
|
}
|