@openzim/libzim 2.4.4 → 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/src/creator.h ADDED
@@ -0,0 +1,345 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/writer/creator.h>
5
+
6
+ #include <exception>
7
+ #include <functional>
8
+ #include <memory>
9
+ #include <string>
10
+ #include <utility>
11
+
12
+ #include "common.h"
13
+ #include "writerItem.h"
14
+
15
+ // Handles creator_->finishZimCreation() operations in the background off the
16
+ // main thread.
17
+ class CreatorAsyncWorker : public Napi::AsyncWorker {
18
+ public:
19
+ CreatorAsyncWorker(std::shared_ptr<zim::writer::Creator> creator,
20
+ Napi::Promise::Deferred promise)
21
+ : Napi::AsyncWorker(promise.Env()),
22
+ creator_{creator},
23
+ promise_{promise} {}
24
+
25
+ ~CreatorAsyncWorker() {}
26
+
27
+ void Execute() override { creator_->finishZimCreation(); }
28
+
29
+ void OnOK() override {
30
+ auto env = Env();
31
+ promise_.Resolve(env.Undefined());
32
+ }
33
+
34
+ void OnError(const Napi::Error &error) override {
35
+ promise_.Reject(error.Value());
36
+ }
37
+
38
+ private:
39
+ std::shared_ptr<zim::writer::Creator> creator_;
40
+ Napi::Promise::Deferred promise_;
41
+ };
42
+
43
+ class AddItemAsyncWorker : public Napi::AsyncWorker {
44
+ public:
45
+ AddItemAsyncWorker(Napi::Env &env,
46
+ std::shared_ptr<zim::writer::Creator> creator,
47
+ std::shared_ptr<zim::writer::Item> item)
48
+ : Napi::AsyncWorker(env),
49
+ creator_{creator},
50
+ item_{item},
51
+ promise_(Napi::Promise::Deferred::New(env)) {}
52
+
53
+ ~AddItemAsyncWorker() {}
54
+
55
+ Napi::Promise Promise() const { return promise_.Promise(); };
56
+
57
+ void Execute() override { creator_->addItem(item_); }
58
+
59
+ void OnOK() override {
60
+ auto env = Env();
61
+ promise_.Resolve(env.Undefined());
62
+ }
63
+
64
+ void OnError(const Napi::Error &error) override {
65
+ promise_.Reject(error.Value());
66
+ }
67
+
68
+ private:
69
+ std::shared_ptr<zim::writer::Creator> creator_;
70
+ std::shared_ptr<zim::writer::Item> item_;
71
+ Napi::Promise::Deferred promise_;
72
+ };
73
+
74
+ class Creator : public Napi::ObjectWrap<Creator> {
75
+ public:
76
+ explicit Creator(const Napi::CallbackInfo &info)
77
+ : Napi::ObjectWrap<Creator>(info), creator_{nullptr} {
78
+ creator_ = std::make_shared<zim::writer::Creator>();
79
+ }
80
+
81
+ static Napi::Object New(Napi::Env env) {
82
+ auto &constructor = env.GetInstanceData<ModuleConstructors>()->creator;
83
+ return constructor.New({});
84
+ }
85
+
86
+ Napi::Value configVerbose(const Napi::CallbackInfo &info) {
87
+ try {
88
+ creator_->configVerbose(info[0].ToBoolean());
89
+ return info.This();
90
+ } catch (const std::exception &err) {
91
+ throw Napi::Error::New(info.Env(), err.what());
92
+ }
93
+ }
94
+
95
+ Napi::Value configCompression(const Napi::CallbackInfo &info) {
96
+ try {
97
+ auto &&val = Compression::symbolToEnum(info.Env(), info[0]);
98
+ creator_->configCompression(val);
99
+ return info.This();
100
+ } catch (const std::exception &err) {
101
+ throw Napi::Error::New(info.Env(), err.what());
102
+ }
103
+ }
104
+
105
+ Napi::Value configClusterSize(const Napi::CallbackInfo &info) {
106
+ try {
107
+ auto val = info[0].ToNumber().Int64Value();
108
+ if (val < 0) {
109
+ throw Napi::Error::New(info.Env(),
110
+ "cluster size must be a positive integer.");
111
+ }
112
+ creator_->configClusterSize(val);
113
+ return info.This();
114
+ } catch (const std::exception &err) {
115
+ throw Napi::Error::New(info.Env(), err.what());
116
+ }
117
+ }
118
+
119
+ Napi::Value configIndexing(const Napi::CallbackInfo &info) {
120
+ try {
121
+ if (info.Length() < 2) {
122
+ throw Napi::Error::New(info.Env(),
123
+ "configIndexing must be called with "
124
+ "indexing[bool] and language[string]");
125
+ }
126
+
127
+ auto indexing = info[0].ToBoolean();
128
+ auto language = info[1].ToString();
129
+ creator_->configIndexing(indexing, language);
130
+ return info.This();
131
+ } catch (const std::exception &err) {
132
+ throw Napi::Error::New(info.Env(), err.what());
133
+ }
134
+ }
135
+
136
+ Napi::Value configNbWorkers(const Napi::CallbackInfo &info) {
137
+ try {
138
+ auto val = info[0].ToNumber();
139
+ creator_->configNbWorkers(val);
140
+ return info.This();
141
+ } catch (const std::exception &err) {
142
+ throw Napi::Error::New(info.Env(), err.what());
143
+ }
144
+ }
145
+
146
+ Napi::Value startZimCreation(const Napi::CallbackInfo &info) {
147
+ try {
148
+ auto val = info[0].ToString();
149
+ creator_->startZimCreation(val);
150
+ // normal api does not return creator but I'm returning it instead of
151
+ // void because it allows a user to chain config and then start all in
152
+ // one
153
+ return info.This();
154
+ } catch (const std::exception &err) {
155
+ throw Napi::Error::New(info.Env(), err.what());
156
+ }
157
+ }
158
+
159
+ Napi::Value addItem(const Napi::CallbackInfo &info) {
160
+ try {
161
+ auto env = info.Env();
162
+ if (!info[0].IsObject()) {
163
+ throw Napi::Error::New(env, "addItem requires an item object");
164
+ }
165
+
166
+ const auto &stringItem =
167
+ env.GetInstanceData<ModuleConstructors>()->stringItem.Value();
168
+ const auto &fileItem =
169
+ env.GetInstanceData<ModuleConstructors>()->fileItem.Value();
170
+
171
+ std::shared_ptr<zim::writer::Item> item{};
172
+ auto obj = info[0].ToObject();
173
+ if (obj.InstanceOf(stringItem)) {
174
+ item = Napi::ObjectWrap<StringItem>::Unwrap(obj)->getItem();
175
+ } else if (obj.InstanceOf(fileItem)) {
176
+ item = Napi::ObjectWrap<FileItem>::Unwrap(obj)->getItem();
177
+ } else {
178
+ item = std::make_shared<ItemWrapper>(env, info[0].ToObject());
179
+ }
180
+
181
+ if (item == nullptr) {
182
+ // should not get here
183
+ throw Napi::Error::New(env, "addItem failed to created an item");
184
+ }
185
+
186
+ auto wk = new AddItemAsyncWorker(env, creator_, item);
187
+ wk->Queue();
188
+ return wk->Promise();
189
+
190
+ } catch (const std::exception &err) {
191
+ throw Napi::Error::New(info.Env(), err.what());
192
+ }
193
+ }
194
+
195
+ Napi::Value finishZimCreation(const Napi::CallbackInfo &info) {
196
+ try {
197
+ // Start this in a thread using Napi::AsyncWorker
198
+ // pass a promise to the async worker and return it here
199
+ Napi::Promise::Deferred deferred =
200
+ Napi::Promise::Deferred::New(info.Env());
201
+
202
+ auto wk = new CreatorAsyncWorker(creator_, deferred);
203
+ wk->Queue();
204
+ return deferred.Promise();
205
+ } catch (const std::exception &err) {
206
+ throw Napi::Error::New(info.Env(), err.what());
207
+ }
208
+ }
209
+
210
+ void addMetadata(const Napi::CallbackInfo &info) {
211
+ try {
212
+ auto env = info.Env();
213
+ if (info.Length() < 2) {
214
+ throw Napi::Error::New(env,
215
+ "addMetadata requests arguments (name, "
216
+ "content|provider, [mimetype]).");
217
+ }
218
+
219
+ auto name = info[0].ToString().Utf8Value();
220
+ auto content = info[1];
221
+ if (content.IsObject()) { // content provider
222
+ std::unique_ptr<zim::writer::ContentProvider> provider =
223
+ std::make_unique<ContentProviderWrapper>(env, content.ToObject());
224
+ if (info.Length() > 2) { // preserves default argument
225
+ auto mimetype = info[2].ToString().Utf8Value();
226
+ creator_->addMetadata(name, std::move(provider), mimetype);
227
+ } else {
228
+ const std::string mimetype = "text/plain;charset=utf-8";
229
+ creator_->addMetadata(name, std::move(provider), mimetype);
230
+ }
231
+ } else { // string version
232
+ auto str = content.ToString().Utf8Value();
233
+ if (info.Length() > 2) {
234
+ auto mimetype = info[2].ToString().Utf8Value();
235
+ creator_->addMetadata(name, str, mimetype);
236
+ } else {
237
+ creator_->addMetadata(name, str);
238
+ }
239
+ }
240
+ } catch (const std::exception &err) {
241
+ throw Napi::Error::New(info.Env(), err.what());
242
+ }
243
+ }
244
+
245
+ void addIllustration(const Napi::CallbackInfo &info) {
246
+ try {
247
+ auto env = info.Env();
248
+ auto size = info[0].ToNumber().Uint32Value();
249
+ auto content = info[1];
250
+ if (content.IsObject()) {
251
+ std::unique_ptr<zim::writer::ContentProvider> provider =
252
+ std::make_unique<ContentProviderWrapper>(env, content.ToObject());
253
+ creator_->addIllustration(size, std::move(provider));
254
+ } else {
255
+ auto str = content.ToString().Utf8Value();
256
+ creator_->addIllustration(size, str);
257
+ }
258
+ } catch (const std::exception &err) {
259
+ throw Napi::Error::New(info.Env(), err.what());
260
+ }
261
+ }
262
+
263
+ void addRedirection(const Napi::CallbackInfo &info) {
264
+ try {
265
+ auto env = info.Env();
266
+ if (info.Length() < 3) {
267
+ throw Napi::Error::New(env,
268
+ "addRedirection requires arguments: path, "
269
+ "title, targetPath, [hints]");
270
+ }
271
+
272
+ auto path = info[0].As<Napi::String>().Utf8Value();
273
+ auto title = info[1].As<Napi::String>().Utf8Value();
274
+ auto targetPath = info[2].As<Napi::String>().Utf8Value();
275
+ if (info[3].IsObject()) {
276
+ auto hints = Object2Hints(info[3].ToObject());
277
+ creator_->addRedirection(path, title, targetPath, hints);
278
+ } else {
279
+ creator_->addRedirection(path, title, targetPath);
280
+ }
281
+ } catch (const std::exception &err) {
282
+ throw Napi::Error::New(info.Env(), err.what());
283
+ }
284
+ }
285
+
286
+ void setMainPath(const Napi::CallbackInfo &info) {
287
+ try {
288
+ auto env = info.Env();
289
+ if (!info[0].IsString()) {
290
+ throw Napi::Error::New(
291
+ env, "setMainPath requires a string argument: mainPath");
292
+ }
293
+ auto mainPath = info[0].ToString().Utf8Value();
294
+ creator_->setMainPath(mainPath);
295
+ } catch (const std::exception &err) {
296
+ throw Napi::Error::New(info.Env(), err.what());
297
+ }
298
+ }
299
+
300
+ void setUuid(const Napi::CallbackInfo &info) {
301
+ try {
302
+ auto env = info.Env();
303
+ if (!info[0].IsString()) {
304
+ throw Napi::Error::New(env, "setUuid requires a string argument: uuid");
305
+ }
306
+ auto uuid = info[0].ToString().Utf8Value();
307
+ if (uuid.size() != 16) {
308
+ throw Napi::Error::New(env, "uuid must be 16 characters long");
309
+ }
310
+ creator_->setUuid(uuid.c_str());
311
+ } catch (const std::exception &err) {
312
+ throw Napi::Error::New(info.Env(), err.what());
313
+ }
314
+ }
315
+
316
+ static void Init(Napi::Env env, Napi::Object exports,
317
+ ModuleConstructors &constructors) {
318
+ Napi::HandleScope scope(env);
319
+ Napi::Function func = DefineClass(
320
+ env, "Creator",
321
+ {
322
+ InstanceMethod<&Creator::configVerbose>("configVerbose"),
323
+ InstanceMethod<&Creator::configCompression>("configCompression"),
324
+ InstanceMethod<&Creator::configClusterSize>("configClusterSize"),
325
+ InstanceMethod<&Creator::configIndexing>("configIndexing"),
326
+ InstanceMethod<&Creator::configNbWorkers>("configNbWorkers"),
327
+ InstanceMethod<&Creator::startZimCreation>("startZimCreation"),
328
+ InstanceMethod<&Creator::addItem>("addItem"),
329
+ InstanceMethod<&Creator::addMetadata>("addMetadata"),
330
+ InstanceMethod<&Creator::addIllustration>("addIllustration"),
331
+ InstanceMethod<&Creator::addRedirection>("addRedirection"),
332
+ InstanceMethod<&Creator::setMainPath>("setMainPath"),
333
+ InstanceMethod<&Creator::setUuid>("setUuid"),
334
+
335
+ InstanceMethod<&Creator::finishZimCreation>("finishZimCreation"),
336
+ });
337
+
338
+ exports.Set("Creator", func);
339
+ constructors.creator = Napi::Persistent(func);
340
+ }
341
+
342
+ private:
343
+ std::shared_ptr<zim::writer::Creator> creator_;
344
+ };
345
+
package/src/entry.h ADDED
@@ -0,0 +1,116 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/entry.h>
5
+ #include <exception>
6
+ #include <memory>
7
+
8
+ #include "item.h"
9
+
10
+ class Entry : public Napi::ObjectWrap<Entry> {
11
+ public:
12
+ explicit Entry(const Napi::CallbackInfo &info)
13
+ : Napi::ObjectWrap<Entry>(info), entry_{nullptr} {
14
+ auto env = info.Env();
15
+
16
+ if (!info[0].IsExternal()) {
17
+ throw Napi::Error::New(
18
+ env, "Entry must be constructed internally by another class.");
19
+ }
20
+
21
+ try {
22
+ entry_ = std::make_shared<zim::Entry>(
23
+ *info[0].As<Napi::External<zim::Entry>>().Data());
24
+ } catch (const std::exception &err) {
25
+ throw Napi::Error::New(env, err.what());
26
+ }
27
+ }
28
+
29
+ static Napi::Object New(Napi::Env env, zim::Entry &&entry) {
30
+ auto external = Napi::External<zim::Entry>::New(env, &entry);
31
+ auto &constructor = env.GetInstanceData<ModuleConstructors>()->entry;
32
+ return constructor.New({external});
33
+ }
34
+
35
+ Napi::Value isRedirect(const Napi::CallbackInfo &info) {
36
+ try {
37
+ return Napi::Value::From(info.Env(), entry_->isRedirect());
38
+ } catch (const std::exception &err) {
39
+ throw Napi::Error::New(info.Env(), err.what());
40
+ }
41
+ }
42
+
43
+ Napi::Value getTitle(const Napi::CallbackInfo &info) {
44
+ try {
45
+ return Napi::Value::From(info.Env(), entry_->getTitle());
46
+ } catch (const std::exception &err) {
47
+ throw Napi::Error::New(info.Env(), err.what());
48
+ }
49
+ }
50
+
51
+ Napi::Value getPath(const Napi::CallbackInfo &info) {
52
+ try {
53
+ return Napi::Value::From(info.Env(), entry_->getPath());
54
+ } catch (const std::exception &err) {
55
+ throw Napi::Error::New(info.Env(), err.what());
56
+ }
57
+ }
58
+
59
+ Napi::Value getItem(const Napi::CallbackInfo &info) {
60
+ try {
61
+ if (info[0].IsBoolean()) { // follow redirect
62
+ return Item::New(info.Env(), entry_->getItem(info[0].ToBoolean()));
63
+ }
64
+ return Item::New(info.Env(), entry_->getItem());
65
+ } catch (const std::exception &err) {
66
+ throw Napi::Error::New(info.Env(), err.what());
67
+ }
68
+ }
69
+
70
+ Napi::Value getRedirect(const Napi::CallbackInfo &info) {
71
+ try {
72
+ return Item::New(info.Env(), entry_->getRedirect());
73
+ } catch (const std::exception &err) {
74
+ throw Napi::Error::New(info.Env(), err.what());
75
+ }
76
+ }
77
+
78
+ Napi::Value getRedirectEntry(const Napi::CallbackInfo &info) {
79
+ try {
80
+ return Entry::New(info.Env(), entry_->getRedirectEntry());
81
+ } catch (const std::exception &err) {
82
+ throw Napi::Error::New(info.Env(), err.what());
83
+ }
84
+ }
85
+
86
+ Napi::Value getIndex(const Napi::CallbackInfo &info) {
87
+ try {
88
+ return Napi::Value::From(info.Env(), entry_->getIndex());
89
+ } catch (const std::exception &err) {
90
+ throw Napi::Error::New(info.Env(), err.what());
91
+ }
92
+ }
93
+
94
+ static void Init(Napi::Env env, Napi::Object exports,
95
+ ModuleConstructors &constructors) {
96
+ Napi::HandleScope scope(env);
97
+ Napi::Function func = DefineClass(
98
+ env, "Entry",
99
+ {
100
+ InstanceAccessor<&Entry::isRedirect>("isRedirect"),
101
+ InstanceAccessor<&Entry::getTitle>("title"),
102
+ InstanceAccessor<&Entry::getPath>("path"),
103
+ InstanceAccessor<&Entry::getItem>("item"),
104
+ InstanceMethod<&Entry::getItem>("getItem"),
105
+ InstanceAccessor<&Entry::getRedirect>("redirect"),
106
+ InstanceAccessor<&Entry::getRedirectEntry>("redirectEntry"),
107
+ InstanceAccessor<&Entry::getIndex>("index"),
108
+ });
109
+
110
+ exports.Set("Entry", func);
111
+ constructors.entry = Napi::Persistent(func);
112
+ }
113
+
114
+ private:
115
+ std::shared_ptr<zim::Entry> entry_;
116
+ };
@@ -0,0 +1,106 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/entry.h>
5
+ #include <exception>
6
+ #include <memory>
7
+
8
+ #include "entry.h"
9
+
10
+ class EntryRange : public Napi::ObjectWrap<EntryRange> {
11
+ public:
12
+ static constexpr const char *ENTRY_RANGE_CONSTRUCTOR_NAME = "EntryRange";
13
+
14
+ explicit EntryRange(const Napi::CallbackInfo &info)
15
+ : Napi::ObjectWrap<EntryRange>(info) {
16
+ auto env = info.Env();
17
+
18
+ if (!info[0].IsExternal()) {
19
+ throw Napi::Error::New(
20
+ env, "EntryRange must be constructed internally by another class.");
21
+ }
22
+
23
+ if (info[0].IsExternal()) {
24
+ try {
25
+ entry_ = std::make_shared<zim::Entry>(
26
+ *info[0].As<Napi::External<zim::Entry>>().Data());
27
+ } catch (const std::exception &err) {
28
+ throw Napi::Error::New(env, err.what());
29
+ }
30
+ }
31
+ }
32
+
33
+ template <typename RangeT>
34
+ static Napi::Object New(Napi::Env env, RangeT range) {
35
+ Napi::Function iterator = Napi::Function::New(
36
+ env, [range](const Napi::CallbackInfo &info) mutable -> Napi::Value {
37
+ Napi::Env env = info.Env();
38
+ Napi::Object iter = Napi::Object::New(env);
39
+
40
+ auto it = range.begin();
41
+ iter["next"] = Napi::Function::New(
42
+ env,
43
+ [range,
44
+ it](const Napi::CallbackInfo &info) mutable -> Napi::Value {
45
+ Napi::Env env = info.Env();
46
+ Napi::Object res = Napi::Object::New(env);
47
+ if (it != range.end()) {
48
+ res["done"] = false;
49
+ res["value"] = Entry::New(env, zim::Entry(*it));
50
+ it++;
51
+ } else {
52
+ res["done"] = true;
53
+ }
54
+ return res;
55
+ });
56
+ return iter;
57
+ });
58
+
59
+ auto offset = Napi::Function::New(
60
+ env, [range](const Napi::CallbackInfo &info) -> Napi::Value {
61
+ if (info.Length() < 2) {
62
+ throw Napi::Error::New(
63
+ info.Env(), "start and maxResults are required for offset.");
64
+ }
65
+ if (!(info[0].IsNumber() && info[1].IsNumber())) {
66
+ throw Napi::Error::New(
67
+ info.Env(), "start and maxResults must be of type Number.");
68
+ }
69
+ auto start = info[0].ToNumber();
70
+ auto maxResults = info[1].ToNumber();
71
+ return NewEntryRange(info.Env(), range.offset(start, maxResults));
72
+ });
73
+
74
+ auto size = Napi::Value::From(env, range.size());
75
+
76
+ auto &constructor = env.GetInstanceData<ConstructorsMap>()->at(
77
+ ENTRY_RANGE_CONSTRUCTOR_NAME);
78
+ return constructor.New({iterator, size, offset});
79
+ }
80
+
81
+ Napi::Value getSize(const Napi::CallbackInfo &info) {
82
+ try {
83
+ return Napi::Value::From(info.Env(), entry_->getIndex());
84
+ } catch (const std::exception &err) {
85
+ throw Napi::Error::New(info.Env(), err.what());
86
+ }
87
+ }
88
+
89
+ static void Init(Napi::Env env, Napi::Object exports,
90
+ ConstructorsMap &constructors) {
91
+ Napi::HandleScope scope(env);
92
+ Napi::Function func =
93
+ DefineClass(env, "EntryRange",
94
+ {
95
+ InstanceAccessor<&EntryRange::getSize>("size"),
96
+ InstanceMethod<&EntryRange::getItem>("getItem"),
97
+ });
98
+
99
+ exports.Set("EntryRange", func);
100
+ constructors.insert_or_assign(ENTRY_RANGE_CONSTRUCTOR_NAME,
101
+ Napi::Persistent(func));
102
+ }
103
+
104
+ private:
105
+ std::shared_ptr<zim::Entry> entry_;
106
+ };