@openzim/libzim 3.5.0 → 4.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/.env +1 -1
- package/Changelog +7 -0
- package/README.md +36 -0
- package/binding.gyp +2 -2
- package/bundle-libzim.js +1 -1
- package/dist/index.d.ts +43 -8
- package/dist/index.js +5 -0
- package/download-libzim.js +1 -2
- package/package.json +21 -21
- package/src/archive.h +110 -91
- package/src/blob.h +31 -18
- package/src/common.h +2 -6
- package/src/contentProvider.h +134 -52
- package/src/creator.h +81 -23
- package/src/entry.h +0 -1
- package/src/illustration.h +167 -0
- package/src/index.d.ts +43 -8
- package/src/index.js +5 -0
- package/src/item.h +5 -5
- package/src/module.cc +26 -0
- package/src/openconfig.h +83 -0
- package/src/search.h +1 -14
- package/src/suggestion.h +0 -10
- package/src/writerItem.h +180 -92
- package/src/entryrange.h +0 -106
package/src/blob.h
CHANGED
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
class Blob : public Napi::ObjectWrap<Blob> {
|
|
13
13
|
public:
|
|
14
14
|
explicit Blob(const Napi::CallbackInfo &info)
|
|
15
|
-
: Napi::ObjectWrap<Blob>(info), blob_{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
blob_ = std::make_shared<zim::Blob>(
|
|
21
|
-
*info[0].As<Napi::External<zim::Blob>>().Data());
|
|
15
|
+
: Napi::ObjectWrap<Blob>(info), blob_{} {
|
|
16
|
+
if (info[0].IsExternal()) {
|
|
17
|
+
// handle internal zim::Blob
|
|
18
|
+
// Copy blob (shared_ptr) from external
|
|
19
|
+
blob_ = zim::Blob(*info[0].As<Napi::External<zim::Blob>>().Data());
|
|
22
20
|
} else if (info.Length() > 0) { // use refcontent_ and copy content
|
|
23
21
|
// TODO(kelvinhammond): avoid copying content somehow in certain scenarios
|
|
24
22
|
// if possible Maybe use a reference object??? What is the lifecycle of
|
|
@@ -38,28 +36,31 @@ class Blob : public Napi::ObjectWrap<Blob> {
|
|
|
38
36
|
data = std::shared_ptr<char>(new char[size],
|
|
39
37
|
std::default_delete<char[]>());
|
|
40
38
|
memcpy(data.get(), buf.Data(), size);
|
|
41
|
-
} else {
|
|
42
|
-
auto str = info[0].
|
|
39
|
+
} else if (info[0].IsString()) { // all others toString()
|
|
40
|
+
auto str = info[0].As<Napi::String>().Utf8Value(); // coerce to string
|
|
43
41
|
size = str.size();
|
|
44
42
|
data = std::shared_ptr<char>(new char[size],
|
|
45
43
|
std::default_delete<char[]>());
|
|
46
44
|
memcpy(data.get(), str.c_str(), size);
|
|
45
|
+
} else {
|
|
46
|
+
throw Napi::Error::New(
|
|
47
|
+
info.Env(),
|
|
48
|
+
"Blob constructor expects an ArrayBuffer, Buffer, or String");
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
blob_ =
|
|
51
|
+
blob_ = zim::Blob(data, size); // blob takes ownership
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
static Napi::Object New(Napi::Env env, zim::Blob &blob) {
|
|
54
56
|
auto external = Napi::External<zim::Blob>::New(env, &blob);
|
|
55
|
-
|
|
56
|
-
return constructor.New({external});
|
|
57
|
+
return GetConstructor(env).New({external});
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
Napi::Value getData(const Napi::CallbackInfo &info) {
|
|
60
61
|
try {
|
|
61
62
|
// TODO(kelvinhammond): find a way to have a readonly buffer in NodeJS
|
|
62
|
-
return Napi::Buffer<char>::Copy(info.Env(), blob_
|
|
63
|
+
return Napi::Buffer<char>::Copy(info.Env(), blob_.data(), blob_.size());
|
|
63
64
|
} catch (const std::exception &err) {
|
|
64
65
|
throw Napi::Error::New(info.Env(), err.what());
|
|
65
66
|
}
|
|
@@ -67,7 +68,7 @@ class Blob : public Napi::ObjectWrap<Blob> {
|
|
|
67
68
|
|
|
68
69
|
Napi::Value toString(const Napi::CallbackInfo &info) {
|
|
69
70
|
try {
|
|
70
|
-
return Napi::Value::From(info.Env(), (std::string)
|
|
71
|
+
return Napi::Value::From(info.Env(), (std::string)blob_);
|
|
71
72
|
} catch (const std::exception &err) {
|
|
72
73
|
throw Napi::Error::New(info.Env(), err.what());
|
|
73
74
|
}
|
|
@@ -75,15 +76,27 @@ class Blob : public Napi::ObjectWrap<Blob> {
|
|
|
75
76
|
|
|
76
77
|
Napi::Value getSize(const Napi::CallbackInfo &info) {
|
|
77
78
|
try {
|
|
78
|
-
return Napi::Value::From(info.Env(), blob_
|
|
79
|
+
return Napi::Value::From(info.Env(), blob_.size());
|
|
79
80
|
} catch (const std::exception &err) {
|
|
80
81
|
throw Napi::Error::New(info.Env(), err.what());
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
static bool InstanceOf(Napi::Env env, Napi::Value value) {
|
|
86
|
+
if (!value.IsObject()) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
Napi::Object obj = value.As<Napi::Object>();
|
|
90
|
+
Napi::FunctionReference &constructor = GetConstructor(env);
|
|
91
|
+
return obj.InstanceOf(constructor.Value());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static Napi::FunctionReference &GetConstructor(Napi::Env env) {
|
|
95
|
+
return env.GetInstanceData<ModuleConstructors>()->blob;
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
85
99
|
ModuleConstructors &constructors) {
|
|
86
|
-
Napi::HandleScope scope(env);
|
|
87
100
|
Napi::Function func =
|
|
88
101
|
DefineClass(env, "Blob",
|
|
89
102
|
{
|
|
@@ -97,8 +110,8 @@ class Blob : public Napi::ObjectWrap<Blob> {
|
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
// internal module methods
|
|
100
|
-
|
|
113
|
+
const zim::Blob &blob() const { return blob_; }
|
|
101
114
|
|
|
102
115
|
private:
|
|
103
|
-
|
|
116
|
+
zim::Blob blob_;
|
|
104
117
|
};
|
package/src/common.h
CHANGED
|
@@ -17,6 +17,8 @@ using CompressionMap =
|
|
|
17
17
|
|
|
18
18
|
struct ModuleConstructors {
|
|
19
19
|
Napi::FunctionReference archive;
|
|
20
|
+
Napi::FunctionReference openConfig;
|
|
21
|
+
Napi::FunctionReference illustrationInfo;
|
|
20
22
|
Napi::FunctionReference entry;
|
|
21
23
|
Napi::FunctionReference item;
|
|
22
24
|
Napi::FunctionReference blob;
|
|
@@ -60,7 +62,6 @@ class Compression : public Napi::ObjectWrap<Compression> {
|
|
|
60
62
|
|
|
61
63
|
auto& compressionMap =
|
|
62
64
|
env.GetInstanceData<ModuleConstructors>()->compressionMap;
|
|
63
|
-
Napi::HandleScope scope(env);
|
|
64
65
|
for (const auto& [bit, symbolRef] : compressionMap) {
|
|
65
66
|
if (!symbolRef.IsEmpty() && symbolRef.Value() == value) {
|
|
66
67
|
return bit;
|
|
@@ -71,8 +72,6 @@ class Compression : public Napi::ObjectWrap<Compression> {
|
|
|
71
72
|
|
|
72
73
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
73
74
|
ModuleConstructors& constructors) {
|
|
74
|
-
Napi::HandleScope scope(env);
|
|
75
|
-
|
|
76
75
|
constexpr auto attrs =
|
|
77
76
|
static_cast<napi_property_attributes>(napi_default | napi_enumerable);
|
|
78
77
|
std::vector<PropertyDescriptor> props;
|
|
@@ -107,7 +106,6 @@ class IntegrityCheck : public Napi::ObjectWrap<IntegrityCheck> {
|
|
|
107
106
|
}
|
|
108
107
|
auto& integrityCheckMap =
|
|
109
108
|
env.GetInstanceData<ModuleConstructors>()->integrityCheckMap;
|
|
110
|
-
Napi::HandleScope scope(env);
|
|
111
109
|
for (const auto& [bit, symbolRef] : integrityCheckMap) {
|
|
112
110
|
if (!symbolRef.IsEmpty() && symbolRef.Value() == value) {
|
|
113
111
|
return bit;
|
|
@@ -118,8 +116,6 @@ class IntegrityCheck : public Napi::ObjectWrap<IntegrityCheck> {
|
|
|
118
116
|
|
|
119
117
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
120
118
|
ModuleConstructors& constructors) {
|
|
121
|
-
Napi::HandleScope scope(env);
|
|
122
|
-
|
|
123
119
|
constexpr auto attrs =
|
|
124
120
|
static_cast<napi_property_attributes>(napi_default | napi_enumerable);
|
|
125
121
|
std::vector<PropertyDescriptor> props;
|
package/src/contentProvider.h
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
#include <exception>
|
|
7
7
|
#include <functional>
|
|
8
8
|
#include <future>
|
|
9
|
+
#include <iostream>
|
|
9
10
|
#include <memory>
|
|
11
|
+
#include <string>
|
|
10
12
|
#include <string_view>
|
|
11
13
|
#include <thread>
|
|
12
14
|
#include <utility>
|
|
@@ -14,31 +16,92 @@
|
|
|
14
16
|
#include "blob.h"
|
|
15
17
|
#include "common.h"
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Thread Safe Function wrapper for calling the ContentProvider.feed function
|
|
21
|
+
* asynchronously from libzim
|
|
22
|
+
*/
|
|
23
|
+
class FeedTSFN {
|
|
24
|
+
public:
|
|
25
|
+
using BlobPtr = zim::Blob;
|
|
26
|
+
|
|
27
|
+
FeedTSFN() = delete;
|
|
28
|
+
|
|
29
|
+
FeedTSFN(Napi::Env &env, Napi::Function &feedFunc) {
|
|
30
|
+
tsfn_ = TSFN::New(env,
|
|
31
|
+
feedFunc, // JavaScript function called asynchronously
|
|
32
|
+
"FeedTSFN", // name
|
|
33
|
+
0, // max queue size (0 = unlimited).
|
|
34
|
+
1, // initial thread count
|
|
35
|
+
nullptr); // context
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
~FeedTSFN() { tsfn_.Release(); }
|
|
39
|
+
|
|
40
|
+
BlobPtr feed() {
|
|
41
|
+
try {
|
|
42
|
+
DataType promise;
|
|
43
|
+
auto future = promise.get_future();
|
|
44
|
+
tsfn_.NonBlockingCall(&promise);
|
|
45
|
+
return future.get();
|
|
46
|
+
} catch (const std::exception &e) {
|
|
47
|
+
std::cerr << "FeedTSFN feed() exception: " << e.what() << std::endl;
|
|
48
|
+
throw std::runtime_error(std::string("Error in FeedTSFN feed(): ") +
|
|
49
|
+
e.what());
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private:
|
|
54
|
+
using DataType = std::promise<BlobPtr>;
|
|
55
|
+
using Context = void;
|
|
56
|
+
|
|
57
|
+
static void CallJs(Napi::Env env, Napi::Function callback, Context *context,
|
|
58
|
+
DataType *data) {
|
|
59
|
+
// Is the JavaScript environment still available to call into, eg. the TSFN
|
|
60
|
+
// is not aborted
|
|
61
|
+
if (env != nullptr) {
|
|
62
|
+
try {
|
|
63
|
+
// call feed(): object
|
|
64
|
+
auto result = callback.Call({});
|
|
65
|
+
if (Blob::InstanceOf(env, result)) {
|
|
66
|
+
auto blob = Blob::Unwrap(result.As<Napi::Object>())->blob();
|
|
67
|
+
// Note: Cannot move, blob could be used in nodejs world still
|
|
68
|
+
data->set_value(blob);
|
|
69
|
+
} else {
|
|
70
|
+
data->set_exception(std::make_exception_ptr(std::runtime_error(
|
|
71
|
+
"Expected an object of type Blob from feed()")));
|
|
72
|
+
}
|
|
73
|
+
} catch (const std::exception &e) {
|
|
74
|
+
data->set_exception(std::make_exception_ptr(e));
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
data->set_exception(std::make_exception_ptr(
|
|
78
|
+
std::runtime_error("Environment is shut down")));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
using TSFN = Napi::TypedThreadSafeFunction<Context, DataType, CallJs>;
|
|
83
|
+
|
|
84
|
+
private:
|
|
85
|
+
TSFN tsfn_;
|
|
86
|
+
};
|
|
87
|
+
|
|
17
88
|
/**
|
|
18
89
|
* Wraps the js world ObjectWrap and Objects to a proper content provider for
|
|
19
90
|
* use with libzim
|
|
20
91
|
*/
|
|
21
92
|
class ContentProviderWrapper : public zim::writer::ContentProvider {
|
|
22
93
|
public:
|
|
23
|
-
explicit ContentProviderWrapper(Napi::Env env, const Napi::Object &provider)
|
|
24
|
-
|
|
25
|
-
MAIN_THREAD_ID = std::this_thread::get_id();
|
|
94
|
+
explicit ContentProviderWrapper(Napi::Env env, const Napi::Object &provider) {
|
|
95
|
+
size_ = parseSize(provider.Get("size"));
|
|
26
96
|
|
|
27
97
|
if (!provider.Get("feed").IsFunction()) {
|
|
28
98
|
throw std::runtime_error("ContentProvider.feed must be a function.");
|
|
29
99
|
}
|
|
30
100
|
|
|
31
101
|
auto feedFunc = provider.Get("feed").As<Napi::Function>();
|
|
32
|
-
|
|
33
|
-
size_ = parseSize(provider.Get("size"));
|
|
34
|
-
provider_ = Napi::Persistent(provider);
|
|
35
|
-
|
|
36
|
-
tsfn_ = Napi::ThreadSafeFunction::New(env, feedFunc,
|
|
37
|
-
"getContentProvider.feed", 0, 1);
|
|
102
|
+
feedTSFN_ = std::make_unique<FeedTSFN>(env, feedFunc);
|
|
38
103
|
}
|
|
39
104
|
|
|
40
|
-
~ContentProviderWrapper() { tsfn_.Release(); }
|
|
41
|
-
|
|
42
105
|
zim::size_type getSize() const override { return size_; }
|
|
43
106
|
|
|
44
107
|
/** Parse the size, supports BigInt */
|
|
@@ -60,46 +123,13 @@ class ContentProviderWrapper : public zim::writer::ContentProvider {
|
|
|
60
123
|
return static_cast<uint64_t>(val);
|
|
61
124
|
}
|
|
62
125
|
|
|
63
|
-
zim::Blob feed() override {
|
|
64
|
-
if (MAIN_THREAD_ID == std::this_thread::get_id()) {
|
|
65
|
-
// on main thread for some reason, do it here
|
|
66
|
-
auto blobObj = feed_.Call(provider_.Value(), {});
|
|
67
|
-
if (!blobObj.IsObject()) {
|
|
68
|
-
throw std::runtime_error("ContentProvider.feed must return a blob");
|
|
69
|
-
}
|
|
70
|
-
auto blob = Napi::ObjectWrap<Blob>::Unwrap(blobObj.ToObject());
|
|
71
|
-
return *(blob->blob());
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// called from a thread
|
|
75
|
-
std::promise<zim::Blob> promise;
|
|
76
|
-
auto future = promise.get_future();
|
|
77
|
-
|
|
78
|
-
auto callback = [&promise, this](Napi::Env env, Napi::Function feedFunc) {
|
|
79
|
-
auto blobObj = feedFunc.Call(provider_.Value(), {});
|
|
80
|
-
if (!blobObj.IsObject()) {
|
|
81
|
-
throw std::runtime_error("ContentProvider.feed must return a blob");
|
|
82
|
-
}
|
|
83
|
-
auto blob = Napi::ObjectWrap<Blob>::Unwrap(blobObj.ToObject());
|
|
84
|
-
promise.set_value(*(blob->blob()));
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
auto status = tsfn_.BlockingCall(callback);
|
|
88
|
-
if (status != napi_ok) {
|
|
89
|
-
throw std::runtime_error("Error calling ThreadSafeFunction");
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return future.get();
|
|
93
|
-
}
|
|
126
|
+
zim::Blob feed() override { return feedTSFN_->feed(); }
|
|
94
127
|
|
|
95
128
|
private:
|
|
96
|
-
// track the main thread
|
|
97
|
-
std::thread::id MAIN_THREAD_ID;
|
|
98
|
-
// js world reference, could be an ObjectWrap provider or custom js object
|
|
99
|
-
Napi::ObjectReference provider_;
|
|
100
|
-
Napi::FunctionReference feed_;
|
|
101
|
-
Napi::ThreadSafeFunction tsfn_;
|
|
102
129
|
zim::size_type size_;
|
|
130
|
+
// Unique pointer so that it isn't copied and the destructor will only be
|
|
131
|
+
// called once
|
|
132
|
+
std::unique_ptr<FeedTSFN> feedTSFN_;
|
|
103
133
|
};
|
|
104
134
|
|
|
105
135
|
class StringProvider : public Napi::ObjectWrap<StringProvider> {
|
|
@@ -144,6 +174,11 @@ class StringProvider : public Napi::ObjectWrap<StringProvider> {
|
|
|
144
174
|
}
|
|
145
175
|
|
|
146
176
|
Napi::Value getSize(const Napi::CallbackInfo &info) {
|
|
177
|
+
if (!provider_) {
|
|
178
|
+
throw Napi::Error::New(
|
|
179
|
+
info.Env(), "StringProvider has been moved and is no longer valid.");
|
|
180
|
+
}
|
|
181
|
+
|
|
147
182
|
try {
|
|
148
183
|
return Napi::Value::From(info.Env(), provider_->getSize());
|
|
149
184
|
} catch (const std::exception &err) {
|
|
@@ -152,8 +187,12 @@ class StringProvider : public Napi::ObjectWrap<StringProvider> {
|
|
|
152
187
|
}
|
|
153
188
|
|
|
154
189
|
Napi::Value feed(const Napi::CallbackInfo &info) {
|
|
190
|
+
if (!provider_) {
|
|
191
|
+
throw Napi::Error::New(
|
|
192
|
+
info.Env(), "StringProvider has been moved and is no longer valid.");
|
|
193
|
+
}
|
|
194
|
+
|
|
155
195
|
try {
|
|
156
|
-
// TODO(kelvinhammond): need a way to move this to avoid copying
|
|
157
196
|
auto blob = provider_->feed();
|
|
158
197
|
return Blob::New(info.Env(), blob);
|
|
159
198
|
} catch (const std::exception &err) {
|
|
@@ -161,9 +200,21 @@ class StringProvider : public Napi::ObjectWrap<StringProvider> {
|
|
|
161
200
|
}
|
|
162
201
|
}
|
|
163
202
|
|
|
203
|
+
static bool InstanceOf(Napi::Env env, Napi::Value value) {
|
|
204
|
+
if (!value.IsObject()) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
Napi::Object obj = value.As<Napi::Object>();
|
|
208
|
+
Napi::FunctionReference &constructor = GetConstructor(env);
|
|
209
|
+
return obj.InstanceOf(constructor.Value());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static Napi::FunctionReference &GetConstructor(Napi::Env env) {
|
|
213
|
+
return env.GetInstanceData<ModuleConstructors>()->stringProvider;
|
|
214
|
+
}
|
|
215
|
+
|
|
164
216
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
165
217
|
ModuleConstructors &constructors) {
|
|
166
|
-
Napi::HandleScope scope(env);
|
|
167
218
|
Napi::Function func =
|
|
168
219
|
DefineClass(env, "StringProvider",
|
|
169
220
|
{
|
|
@@ -176,6 +227,11 @@ class StringProvider : public Napi::ObjectWrap<StringProvider> {
|
|
|
176
227
|
constructors.stringProvider = Napi::Persistent(func);
|
|
177
228
|
}
|
|
178
229
|
|
|
230
|
+
// Internal use only
|
|
231
|
+
std::unique_ptr<zim::writer::StringProvider> &&unwrapProvider() {
|
|
232
|
+
return std::move(provider_);
|
|
233
|
+
}
|
|
234
|
+
|
|
179
235
|
private:
|
|
180
236
|
std::unique_ptr<zim::writer::StringProvider> provider_;
|
|
181
237
|
};
|
|
@@ -220,6 +276,11 @@ class FileProvider : public Napi::ObjectWrap<FileProvider> {
|
|
|
220
276
|
}
|
|
221
277
|
|
|
222
278
|
Napi::Value getSize(const Napi::CallbackInfo &info) {
|
|
279
|
+
if (!provider_) {
|
|
280
|
+
throw Napi::Error::New(
|
|
281
|
+
info.Env(), "FileProvider has been moved and is no longer valid.");
|
|
282
|
+
}
|
|
283
|
+
|
|
223
284
|
try {
|
|
224
285
|
return Napi::Value::From(info.Env(), provider_->getSize());
|
|
225
286
|
} catch (const std::exception &err) {
|
|
@@ -228,8 +289,12 @@ class FileProvider : public Napi::ObjectWrap<FileProvider> {
|
|
|
228
289
|
}
|
|
229
290
|
|
|
230
291
|
Napi::Value feed(const Napi::CallbackInfo &info) {
|
|
292
|
+
if (!provider_) {
|
|
293
|
+
throw Napi::Error::New(
|
|
294
|
+
info.Env(), "FileProvider has been moved and is no longer valid.");
|
|
295
|
+
}
|
|
296
|
+
|
|
231
297
|
try {
|
|
232
|
-
// TODO(kelvinhammond): need a way to move this to avoid copying
|
|
233
298
|
auto blob = provider_->feed();
|
|
234
299
|
return Blob::New(info.Env(), blob);
|
|
235
300
|
} catch (const std::exception &err) {
|
|
@@ -237,9 +302,21 @@ class FileProvider : public Napi::ObjectWrap<FileProvider> {
|
|
|
237
302
|
}
|
|
238
303
|
}
|
|
239
304
|
|
|
305
|
+
static bool InstanceOf(Napi::Env env, Napi::Value value) {
|
|
306
|
+
if (!value.IsObject()) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
Napi::Object obj = value.As<Napi::Object>();
|
|
310
|
+
Napi::FunctionReference &constructor = GetConstructor(env);
|
|
311
|
+
return obj.InstanceOf(constructor.Value());
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static Napi::FunctionReference &GetConstructor(Napi::Env env) {
|
|
315
|
+
return env.GetInstanceData<ModuleConstructors>()->fileProvider;
|
|
316
|
+
}
|
|
317
|
+
|
|
240
318
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
241
319
|
ModuleConstructors &constructors) {
|
|
242
|
-
Napi::HandleScope scope(env);
|
|
243
320
|
Napi::Function func =
|
|
244
321
|
DefineClass(env, "FileProvider",
|
|
245
322
|
{
|
|
@@ -252,6 +329,11 @@ class FileProvider : public Napi::ObjectWrap<FileProvider> {
|
|
|
252
329
|
constructors.fileProvider = Napi::Persistent(func);
|
|
253
330
|
}
|
|
254
331
|
|
|
332
|
+
// Internal use only
|
|
333
|
+
std::unique_ptr<zim::writer::FileProvider> &&unwrapProvider() {
|
|
334
|
+
return std::move(provider_);
|
|
335
|
+
}
|
|
336
|
+
|
|
255
337
|
private:
|
|
256
338
|
std::unique_ptr<zim::writer::FileProvider> provider_;
|
|
257
339
|
};
|
package/src/creator.h
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <napi.h>
|
|
4
|
+
#include <zim/illustration.h>
|
|
4
5
|
#include <zim/writer/creator.h>
|
|
5
6
|
|
|
6
7
|
#include <exception>
|
|
7
8
|
#include <functional>
|
|
9
|
+
#include <iostream>
|
|
10
|
+
#include <map>
|
|
8
11
|
#include <memory>
|
|
9
12
|
#include <string>
|
|
10
13
|
#include <utility>
|
|
11
14
|
|
|
12
15
|
#include "common.h"
|
|
16
|
+
#include "illustration.h"
|
|
13
17
|
#include "writerItem.h"
|
|
14
18
|
|
|
15
19
|
// Handles creator_->finishZimCreation() operations in the background off the
|
|
@@ -24,7 +28,14 @@ class CreatorAsyncWorker : public Napi::AsyncWorker {
|
|
|
24
28
|
|
|
25
29
|
~CreatorAsyncWorker() {}
|
|
26
30
|
|
|
27
|
-
void Execute() override {
|
|
31
|
+
void Execute() override {
|
|
32
|
+
try {
|
|
33
|
+
creator_->finishZimCreation();
|
|
34
|
+
} catch (const std::exception &e) {
|
|
35
|
+
std::cerr << "Error: finishZimCreation failed: " << e.what() << std::endl;
|
|
36
|
+
SetError(e.what());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
28
39
|
|
|
29
40
|
void OnOK() override {
|
|
30
41
|
auto env = Env();
|
|
@@ -54,7 +65,15 @@ class AddItemAsyncWorker : public Napi::AsyncWorker {
|
|
|
54
65
|
|
|
55
66
|
Napi::Promise Promise() const { return promise_.Promise(); };
|
|
56
67
|
|
|
57
|
-
void Execute() override {
|
|
68
|
+
void Execute() override {
|
|
69
|
+
try {
|
|
70
|
+
creator_->addItem(item_);
|
|
71
|
+
} catch (const std::exception &e) {
|
|
72
|
+
std::cerr << "Error: AddItemAsyncWorker failed: " << e.what()
|
|
73
|
+
<< std::endl;
|
|
74
|
+
SetError(e.what());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
58
77
|
|
|
59
78
|
void OnOK() override {
|
|
60
79
|
auto env = Env();
|
|
@@ -163,16 +182,11 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
163
182
|
throw Napi::Error::New(env, "addItem requires an item object");
|
|
164
183
|
}
|
|
165
184
|
|
|
166
|
-
const auto &stringItem =
|
|
167
|
-
env.GetInstanceData<ModuleConstructors>()->stringItem.Value();
|
|
168
|
-
const auto &fileItem =
|
|
169
|
-
env.GetInstanceData<ModuleConstructors>()->fileItem.Value();
|
|
170
|
-
|
|
171
185
|
std::shared_ptr<zim::writer::Item> item{};
|
|
172
|
-
auto obj = info[0].
|
|
173
|
-
if (
|
|
186
|
+
auto obj = info[0].As<Napi::Object>();
|
|
187
|
+
if (StringItem::InstanceOf(env, obj)) {
|
|
174
188
|
item = Napi::ObjectWrap<StringItem>::Unwrap(obj)->getItem();
|
|
175
|
-
} else if (
|
|
189
|
+
} else if (FileItem::InstanceOf(env, obj)) {
|
|
176
190
|
item = Napi::ObjectWrap<FileItem>::Unwrap(obj)->getItem();
|
|
177
191
|
} else {
|
|
178
192
|
item = std::make_shared<ItemWrapper>(env, info[0].ToObject());
|
|
@@ -218,9 +232,28 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
218
232
|
|
|
219
233
|
auto name = info[0].ToString().Utf8Value();
|
|
220
234
|
auto content = info[1];
|
|
221
|
-
if (content.IsObject()) {
|
|
222
|
-
|
|
223
|
-
|
|
235
|
+
if (content.IsObject()) {
|
|
236
|
+
// addMetadata(name: string, content: ContentProvider, [mimetype])
|
|
237
|
+
auto obj = content.As<Napi::Object>();
|
|
238
|
+
std::unique_ptr<zim::writer::ContentProvider> provider{nullptr};
|
|
239
|
+
|
|
240
|
+
// Determine the type of ContentProvider
|
|
241
|
+
if (StringProvider::InstanceOf(env, content)) {
|
|
242
|
+
auto wrapped = Napi::ObjectWrap<StringProvider>::Unwrap(obj);
|
|
243
|
+
provider = wrapped->unwrapProvider();
|
|
244
|
+
} else if (FileProvider::InstanceOf(env, content)) {
|
|
245
|
+
auto wrapped = Napi::ObjectWrap<FileProvider>::Unwrap(obj);
|
|
246
|
+
provider = wrapped->unwrapProvider();
|
|
247
|
+
} else {
|
|
248
|
+
// Fallback to generic ContentProviderWrapper
|
|
249
|
+
provider = std::make_unique<ContentProviderWrapper>(env, obj);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (provider == nullptr) {
|
|
253
|
+
throw Napi::Error::New(
|
|
254
|
+
env, "addMetadata failed to create ContentProvider from object");
|
|
255
|
+
}
|
|
256
|
+
|
|
224
257
|
if (info.Length() > 2) { // preserves default argument
|
|
225
258
|
auto mimetype = info[2].ToString().Utf8Value();
|
|
226
259
|
creator_->addMetadata(name, std::move(provider), mimetype);
|
|
@@ -228,7 +261,7 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
228
261
|
const std::string mimetype = "text/plain;charset=utf-8";
|
|
229
262
|
creator_->addMetadata(name, std::move(provider), mimetype);
|
|
230
263
|
}
|
|
231
|
-
} else { // string
|
|
264
|
+
} else { // addMetadata(name: string, content: string, [mimetype])
|
|
232
265
|
auto str = content.ToString().Utf8Value();
|
|
233
266
|
if (info.Length() > 2) {
|
|
234
267
|
auto mimetype = info[2].ToString().Utf8Value();
|
|
@@ -238,6 +271,7 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
238
271
|
}
|
|
239
272
|
}
|
|
240
273
|
} catch (const std::exception &err) {
|
|
274
|
+
std::cerr << "Error: addMetadata failed: " << err.what() << std::endl;
|
|
241
275
|
throw Napi::Error::New(info.Env(), err.what());
|
|
242
276
|
}
|
|
243
277
|
}
|
|
@@ -245,15 +279,40 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
245
279
|
void addIllustration(const Napi::CallbackInfo &info) {
|
|
246
280
|
try {
|
|
247
281
|
auto env = info.Env();
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
282
|
+
|
|
283
|
+
// Inline template function to handle both size and IllustrationInfo
|
|
284
|
+
const auto addIllusWithContent = [&](auto &opt1) {
|
|
285
|
+
auto content = info[1];
|
|
286
|
+
if (content.IsObject()) {
|
|
287
|
+
std::unique_ptr<zim::writer::ContentProvider> provider =
|
|
288
|
+
std::make_unique<ContentProviderWrapper>(env, content.ToObject());
|
|
289
|
+
creator_->addIllustration(opt1, std::move(provider));
|
|
290
|
+
} else {
|
|
291
|
+
auto str = content.ToString().Utf8Value();
|
|
292
|
+
creator_->addIllustration(opt1, str);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
auto arg0 = info[0];
|
|
297
|
+
if (arg0.IsNumber()) {
|
|
298
|
+
auto size = arg0.ToNumber().Uint32Value();
|
|
299
|
+
addIllusWithContent(size);
|
|
300
|
+
} else if (arg0.IsObject()) {
|
|
301
|
+
// Parse as IllustrationInfo
|
|
302
|
+
auto obj = arg0.ToObject();
|
|
303
|
+
|
|
304
|
+
// getIllustrationItem(illusInfo: IllustrationInfo)
|
|
305
|
+
// getIllustrationItem(illusInfo: object)
|
|
306
|
+
auto illusInfo =
|
|
307
|
+
IllustrationInfo::InstanceOf(env, obj)
|
|
308
|
+
? IllustrationInfo::Unwrap(obj)->getInternalIllustrationInfo()
|
|
309
|
+
: IllustrationInfo::infoFrom(obj);
|
|
310
|
+
addIllusWithContent(illusInfo);
|
|
254
311
|
} else {
|
|
255
|
-
|
|
256
|
-
|
|
312
|
+
throw Napi::Error::New(
|
|
313
|
+
env,
|
|
314
|
+
"addIllustration first argument must be size[number] or "
|
|
315
|
+
"IllustrationInfo[object]");
|
|
257
316
|
}
|
|
258
317
|
} catch (const std::exception &err) {
|
|
259
318
|
throw Napi::Error::New(info.Env(), err.what());
|
|
@@ -315,7 +374,6 @@ class Creator : public Napi::ObjectWrap<Creator> {
|
|
|
315
374
|
|
|
316
375
|
static void Init(Napi::Env env, Napi::Object exports,
|
|
317
376
|
ModuleConstructors &constructors) {
|
|
318
|
-
Napi::HandleScope scope(env);
|
|
319
377
|
Napi::Function func = DefineClass(
|
|
320
378
|
env, "Creator",
|
|
321
379
|
{
|
package/src/entry.h
CHANGED