@openzim/libzim 2.4.4 → 3.1.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/archive.h ADDED
@@ -0,0 +1,494 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/archive.h>
5
+ #include <exception>
6
+ #include <memory>
7
+ #include <sstream>
8
+ #include <string>
9
+
10
+ #include "entry.h"
11
+ #include "item.h"
12
+
13
+ class Archive : public Napi::ObjectWrap<Archive> {
14
+ public:
15
+ explicit Archive(const Napi::CallbackInfo &info)
16
+ : Napi::ObjectWrap<Archive>(info), archive_{nullptr} {
17
+ Napi::Env env = info.Env();
18
+ Napi::HandleScope scope(env);
19
+
20
+ if (info.Length() < 1) {
21
+ throw Napi::Error::New(info.Env(), "Archive requires arguments filepath");
22
+ }
23
+
24
+ try {
25
+ std::string filepath = info[0].ToString();
26
+ archive_ = std::make_shared<zim::Archive>(filepath);
27
+ } catch (const std::exception &e) {
28
+ throw Napi::Error::New(env, e.what());
29
+ }
30
+ }
31
+
32
+ Napi::Value getFilename(const Napi::CallbackInfo &info) {
33
+ try {
34
+ return Napi::Value::From(info.Env(), archive_->getFilename());
35
+ } catch (const std::exception &err) {
36
+ throw Napi::Error::New(info.Env(), err.what());
37
+ }
38
+ }
39
+
40
+ Napi::Value getFilesize(const Napi::CallbackInfo &info) {
41
+ try {
42
+ return Napi::Value::From(info.Env(), archive_->getFilesize());
43
+ } catch (const std::exception &err) {
44
+ throw Napi::Error::New(info.Env(), err.what());
45
+ }
46
+ }
47
+
48
+ Napi::Value getAllEntryCount(const Napi::CallbackInfo &info) {
49
+ try {
50
+ return Napi::Value::From(info.Env(), archive_->getAllEntryCount());
51
+ } catch (const std::exception &err) {
52
+ throw Napi::Error::New(info.Env(), err.what());
53
+ }
54
+ }
55
+
56
+ Napi::Value getEntryCount(const Napi::CallbackInfo &info) {
57
+ try {
58
+ return Napi::Value::From(info.Env(), archive_->getEntryCount());
59
+ } catch (const std::exception &err) {
60
+ throw Napi::Error::New(info.Env(), err.what());
61
+ }
62
+ }
63
+
64
+ Napi::Value getArticleCount(const Napi::CallbackInfo &info) {
65
+ try {
66
+ return Napi::Value::From(info.Env(), archive_->getArticleCount());
67
+ } catch (const std::exception &err) {
68
+ throw Napi::Error::New(info.Env(), err.what());
69
+ }
70
+ }
71
+
72
+ Napi::Value getUuid(const Napi::CallbackInfo &info) {
73
+ try {
74
+ // TODO(kelvinhammond): convert this to
75
+ // static_cast<std::string>(archive_->getUuid()) This didn't work when
76
+ // building because of the below error undefined symbol:
77
+ // _ZNK3zim4UuidcvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEv
78
+ std::ostringstream out;
79
+ out << archive_->getUuid();
80
+ return Napi::Value::From(info.Env(), out.str());
81
+ } catch (const std::exception &err) {
82
+ throw Napi::Error::New(info.Env(), err.what());
83
+ }
84
+ }
85
+
86
+ Napi::Value getMetadata(const Napi::CallbackInfo &info) {
87
+ try {
88
+ auto name = info[0].ToString();
89
+ return Napi::Value::From(info.Env(), archive_->getMetadata(name));
90
+ } catch (const std::exception &err) {
91
+ throw Napi::Error::New(info.Env(), err.what());
92
+ }
93
+ }
94
+
95
+ Napi::Value getMetadataItem(const Napi::CallbackInfo &info) {
96
+ try {
97
+ auto name = info[0].ToString();
98
+ return Item::New(info.Env(), archive_->getMetadataItem(name));
99
+ } catch (const std::exception &err) {
100
+ throw Napi::Error::New(info.Env(), err.what());
101
+ }
102
+ }
103
+
104
+ Napi::Value getMetadataKeys(const Napi::CallbackInfo &info) {
105
+ try {
106
+ auto env = info.Env();
107
+ Napi::HandleScope scope(env);
108
+ auto res = Napi::Array::New(env);
109
+ size_t idx = 0;
110
+ for (const auto &key : archive_->getMetadataKeys()) {
111
+ res.Set(idx++, Napi::String::New(env, key));
112
+ }
113
+ return res;
114
+ } catch (const std::exception &err) {
115
+ throw Napi::Error::New(info.Env(), err.what());
116
+ }
117
+ }
118
+
119
+ Napi::Value getIllustrationItem(const Napi::CallbackInfo &info) {
120
+ try {
121
+ if (info.Length() > 0) {
122
+ auto size = static_cast<unsigned int>(info[0].ToNumber().Uint32Value());
123
+ return Item::New(info.Env(), archive_->getIllustrationItem(size));
124
+ }
125
+ return Item::New(info.Env(), archive_->getIllustrationItem());
126
+ } catch (const std::exception &err) {
127
+ throw Napi::Error::New(info.Env(), err.what());
128
+ }
129
+ }
130
+
131
+ Napi::Value getIllustrationSizes(const Napi::CallbackInfo &info) {
132
+ try {
133
+ auto env = info.Env();
134
+ Napi::HandleScope scope(env);
135
+
136
+ // returns a native Set object
137
+ auto SetConstructor = env.Global().Get("Set").As<Napi::Function>();
138
+ auto result = SetConstructor.New({});
139
+ auto add = result.Get("add").As<Napi::Function>();
140
+ for (const auto &size : archive_->getIllustrationSizes()) {
141
+ add.Call(result, {Napi::Value::From(env, size)});
142
+ }
143
+ return result;
144
+ } catch (const std::exception &err) {
145
+ throw Napi::Error::New(info.Env(), err.what());
146
+ }
147
+ }
148
+
149
+ Napi::Value getEntryByPath(const Napi::CallbackInfo &info) {
150
+ try {
151
+ if (info[0].IsNumber()) {
152
+ auto &&idx = info[0].ToNumber();
153
+ return Entry::New(info.Env(), archive_->getEntryByPath(idx));
154
+ } else if (info[0].IsString()) {
155
+ auto &&path = info[0].ToString();
156
+ return Entry::New(info.Env(), archive_->getEntryByPath(path));
157
+ }
158
+
159
+ throw Napi::Error::New(
160
+ info.Env(), "Entry index must be a string (path) or number (index).");
161
+ } catch (const std::exception &err) {
162
+ throw Napi::Error::New(info.Env(), err.what());
163
+ }
164
+ }
165
+
166
+ Napi::Value getEntryByTitle(const Napi::CallbackInfo &info) {
167
+ try {
168
+ if (info[0].IsNumber()) {
169
+ auto &&idx = info[0].ToNumber();
170
+ return Entry::New(info.Env(), archive_->getEntryByTitle(idx));
171
+ } else if (info[0].IsString()) {
172
+ auto &&path = info[0].ToString();
173
+ return Entry::New(info.Env(), archive_->getEntryByTitle(path));
174
+ }
175
+
176
+ throw Napi::Error::New(
177
+ info.Env(), "Entry index must be a string (path) or number (index).");
178
+ } catch (const std::exception &err) {
179
+ throw Napi::Error::New(info.Env(), err.what());
180
+ }
181
+ }
182
+
183
+ Napi::Value getEntryByClusterOrder(const Napi::CallbackInfo &info) {
184
+ try {
185
+ if (info[0].IsNumber()) {
186
+ auto &&idx = info[0].ToNumber();
187
+ return Entry::New(info.Env(), archive_->getEntryByClusterOrder(idx));
188
+ }
189
+ throw Napi::Error::New(info.Env(), "Entry index must be a number.");
190
+ } catch (const std::exception &err) {
191
+ throw Napi::Error::New(info.Env(), err.what());
192
+ }
193
+ }
194
+
195
+ Napi::Value getMainEntry(const Napi::CallbackInfo &info) {
196
+ try {
197
+ return Entry::New(info.Env(), archive_->getMainEntry());
198
+ } catch (const std::exception &err) {
199
+ throw Napi::Error::New(info.Env(), err.what());
200
+ }
201
+ }
202
+
203
+ Napi::Value getRandomEntry(const Napi::CallbackInfo &info) {
204
+ try {
205
+ return Entry::New(info.Env(), archive_->getRandomEntry());
206
+ } catch (const std::exception &err) {
207
+ throw Napi::Error::New(info.Env(), err.what());
208
+ }
209
+ }
210
+
211
+ Napi::Value hasEntryByPath(const Napi::CallbackInfo &info) {
212
+ try {
213
+ return Napi::Value::From(info.Env(),
214
+ archive_->hasEntryByPath(info[0].ToString()));
215
+ } catch (const std::exception &err) {
216
+ throw Napi::Error::New(info.Env(), err.what());
217
+ }
218
+ }
219
+
220
+ Napi::Value hasEntryByTitle(const Napi::CallbackInfo &info) {
221
+ try {
222
+ return Napi::Value::From(info.Env(),
223
+ archive_->hasEntryByTitle(info[0].ToString()));
224
+ } catch (const std::exception &err) {
225
+ throw Napi::Error::New(info.Env(), err.what());
226
+ }
227
+ }
228
+
229
+ Napi::Value hasMainEntry(const Napi::CallbackInfo &info) {
230
+ try {
231
+ return Napi::Value::From(info.Env(), archive_->hasMainEntry());
232
+ } catch (const std::exception &err) {
233
+ throw Napi::Error::New(info.Env(), err.what());
234
+ }
235
+ }
236
+
237
+ Napi::Value hasIllustration(const Napi::CallbackInfo &info) {
238
+ try {
239
+ if (info.Length() > 0) {
240
+ auto size = static_cast<unsigned int>(info[0].ToNumber().Uint32Value());
241
+ return Napi::Value::From(info.Env(), archive_->hasIllustration(size));
242
+ }
243
+ return Napi::Value::From(info.Env(), archive_->hasIllustration());
244
+ } catch (const std::exception &err) {
245
+ throw Napi::Error::New(info.Env(), err.what());
246
+ }
247
+ }
248
+
249
+ Napi::Value hasFulltextIndex(const Napi::CallbackInfo &info) {
250
+ try {
251
+ return Napi::Value::From(info.Env(), archive_->hasFulltextIndex());
252
+ } catch (const std::exception &err) {
253
+ throw Napi::Error::New(info.Env(), err.what());
254
+ }
255
+ }
256
+
257
+ Napi::Value hasTitleIndex(const Napi::CallbackInfo &info) {
258
+ try {
259
+ return Napi::Value::From(info.Env(), archive_->hasTitleIndex());
260
+ } catch (const std::exception &err) {
261
+ throw Napi::Error::New(info.Env(), err.what());
262
+ }
263
+ }
264
+
265
+ template <typename RangeT>
266
+ static Napi::Value NewEntryRange(Napi::Env env, RangeT range) {
267
+ // should be called from C++ only, exceptions propogated up.
268
+ Napi::Object iterable = Napi::Object::New(env);
269
+ Napi::Function iterator = Napi::Function::New(
270
+ env, [range](const Napi::CallbackInfo &info) mutable -> Napi::Value {
271
+ Napi::Env env = info.Env();
272
+ Napi::Object iter = Napi::Object::New(env);
273
+
274
+ auto it = range.begin();
275
+ iter["next"] = Napi::Function::New(
276
+ env,
277
+ [range,
278
+ it](const Napi::CallbackInfo &info) mutable -> Napi::Value {
279
+ Napi::Env env = info.Env();
280
+ Napi::Object res = Napi::Object::New(env);
281
+ if (it != range.end()) {
282
+ res["done"] = false;
283
+ res["value"] = Entry::New(env, zim::Entry(*it));
284
+ it++;
285
+ } else {
286
+ res["done"] = true;
287
+ }
288
+ return res;
289
+ });
290
+ return iter;
291
+ });
292
+
293
+ iterable.Set(Napi::Symbol::WellKnown(env, "iterator"), iterator);
294
+ iterable["size"] = Napi::Value::From(env, range.size());
295
+ iterable["offset"] = Napi::Function::New(
296
+ env, [range](const Napi::CallbackInfo &info) -> Napi::Value {
297
+ if (info.Length() < 2) {
298
+ throw Napi::Error::New(
299
+ info.Env(), "start and maxResults are required for offset.");
300
+ }
301
+ if (!(info[0].IsNumber() && info[1].IsNumber())) {
302
+ throw Napi::Error::New(
303
+ info.Env(), "start and maxResults must be of type Number.");
304
+ }
305
+ auto start = info[0].ToNumber();
306
+ auto maxResults = info[1].ToNumber();
307
+ return NewEntryRange(info.Env(), range.offset(start, maxResults));
308
+ });
309
+ iterable.Freeze();
310
+
311
+ return iterable;
312
+ }
313
+
314
+ Napi::Value iterByPath(const Napi::CallbackInfo &info) {
315
+ try {
316
+ return NewEntryRange(info.Env(), archive_->iterByPath());
317
+ } catch (const std::exception &err) {
318
+ throw Napi::Error::New(info.Env(), err.what());
319
+ }
320
+ }
321
+
322
+ Napi::Value iterByTitle(const Napi::CallbackInfo &info) {
323
+ try {
324
+ return NewEntryRange(info.Env(), archive_->iterByTitle());
325
+ } catch (const std::exception &err) {
326
+ throw Napi::Error::New(info.Env(), err.what());
327
+ }
328
+ }
329
+
330
+ Napi::Value iterEfficient(const Napi::CallbackInfo &info) {
331
+ try {
332
+ return NewEntryRange(info.Env(), archive_->iterEfficient());
333
+ } catch (const std::exception &err) {
334
+ throw Napi::Error::New(info.Env(), err.what());
335
+ }
336
+ }
337
+
338
+ Napi::Value findByPath(const Napi::CallbackInfo &info) {
339
+ try {
340
+ auto path = info[0].ToString();
341
+ return NewEntryRange(info.Env(), archive_->findByPath(path));
342
+ } catch (const std::exception &err) {
343
+ throw Napi::Error::New(info.Env(), err.what());
344
+ }
345
+ }
346
+
347
+ Napi::Value findByTitle(const Napi::CallbackInfo &info) {
348
+ try {
349
+ auto title = info[0].ToString();
350
+ return NewEntryRange(info.Env(), archive_->findByTitle(title));
351
+ } catch (const std::exception &err) {
352
+ throw Napi::Error::New(info.Env(), err.what());
353
+ }
354
+ }
355
+
356
+ Napi::Value hasChecksum(const Napi::CallbackInfo &info) {
357
+ try {
358
+ return Napi::Value::From(info.Env(), archive_->hasChecksum());
359
+ } catch (const std::exception &err) {
360
+ throw Napi::Error::New(info.Env(), err.what());
361
+ }
362
+ }
363
+
364
+ Napi::Value getChecksum(const Napi::CallbackInfo &info) {
365
+ try {
366
+ return Napi::Value::From(info.Env(), archive_->getChecksum());
367
+ } catch (const std::exception &err) {
368
+ throw Napi::Error::New(info.Env(), err.what());
369
+ }
370
+ }
371
+
372
+ Napi::Value check(const Napi::CallbackInfo &info) {
373
+ try {
374
+ return Napi::Value::From(info.Env(), archive_->check());
375
+ } catch (const std::exception &err) {
376
+ throw Napi::Error::New(info.Env(), err.what());
377
+ }
378
+ }
379
+
380
+ Napi::Value checkIntegrity(const Napi::CallbackInfo &info) {
381
+ try {
382
+ auto env = info.Env();
383
+ const auto &&checkType = IntegrityCheck::symbolToEnum(env, info[0]);
384
+ return Napi::Value::From(env, archive_->checkIntegrity(checkType));
385
+ } catch (const std::exception &err) {
386
+ throw Napi::Error::New(info.Env(), err.what());
387
+ }
388
+ }
389
+
390
+ Napi::Value isMultiPart(const Napi::CallbackInfo &info) {
391
+ try {
392
+ return Napi::Value::From(info.Env(), archive_->isMultiPart());
393
+ } catch (const std::exception &err) {
394
+ throw Napi::Error::New(info.Env(), err.what());
395
+ }
396
+ }
397
+
398
+ Napi::Value hasNewNamespaceScheme(const Napi::CallbackInfo &info) {
399
+ try {
400
+ return Napi::Value::From(info.Env(), archive_->hasNewNamespaceScheme());
401
+ } catch (const std::exception &err) {
402
+ throw Napi::Error::New(info.Env(), err.what());
403
+ }
404
+ }
405
+
406
+ static Napi::Value validate(const Napi::CallbackInfo &info) {
407
+ Napi::Env env = info.Env();
408
+ Napi::HandleScope scope(env);
409
+ try {
410
+ if (info.Length() < 2) {
411
+ throw Napi::Error::New(
412
+ env, "validate requires zimPath and [IntegrityCheck, ...]");
413
+ } else if (!info[0].IsString()) {
414
+ throw Napi::Error::New(env, "zimPath must be a string");
415
+ } else if (!info[1].IsArray()) {
416
+ throw Napi::Error::New(env, "IntegrityCheckList must be an array");
417
+ }
418
+
419
+ auto &&zimPath = info[0].ToString();
420
+ auto symbolList = info[1].As<Napi::Array>();
421
+ zim::IntegrityCheckList flags{};
422
+ for (size_t i = 0; i < symbolList.Length(); i++) {
423
+ const auto bit = IntegrityCheck::symbolToEnum(env, symbolList.Get(i));
424
+ auto &&isAll = (bit == zim::IntegrityCheck::COUNT ||
425
+ static_cast<size_t>(bit) >= flags.size());
426
+ if (isAll) { // This handle IntegrityCheck::COUNT
427
+ flags.set();
428
+ break;
429
+ }
430
+ flags.set(static_cast<size_t>(bit));
431
+ }
432
+ return Napi::Value::From(env, zim::validate(zimPath, flags));
433
+ } catch (const std::exception &err) {
434
+ throw Napi::Error::New(env, err.what());
435
+ }
436
+ }
437
+
438
+ static void Init(Napi::Env env, Napi::Object exports,
439
+ ModuleConstructors &constructors) {
440
+ Napi::HandleScope scope(env);
441
+ Napi::Function func = DefineClass(
442
+ env, "Archive",
443
+ {
444
+ InstanceAccessor<&Archive::getFilename>("filename"),
445
+ InstanceAccessor<&Archive::getFilesize>("filesize"),
446
+ InstanceAccessor<&Archive::getAllEntryCount>("allEntryCount"),
447
+ InstanceAccessor<&Archive::getEntryCount>("entryCount"),
448
+ InstanceAccessor<&Archive::getArticleCount>("articleCount"),
449
+ InstanceAccessor<&Archive::getUuid>("uuid"),
450
+ InstanceMethod<&Archive::getMetadata>("getMetadata"),
451
+ InstanceMethod<&Archive::getMetadataItem>("getMetadataItem"),
452
+ InstanceAccessor<&Archive::getMetadataKeys>("metadataKeys"),
453
+ InstanceMethod<&Archive::getIllustrationItem>(
454
+ "getIllustrationItem"),
455
+ InstanceAccessor<&Archive::getIllustrationSizes>(
456
+ "illustrationSizes"),
457
+ InstanceMethod<&Archive::getEntryByPath>("getEntryByPath"),
458
+ InstanceMethod<&Archive::getEntryByTitle>("getEntryByTitle"),
459
+ InstanceMethod<&Archive::getEntryByClusterOrder>(
460
+ "getEntryByClusterOrder"),
461
+ InstanceAccessor<&Archive::getMainEntry>("mainEntry"),
462
+ InstanceAccessor<&Archive::getRandomEntry>("randomEntry"),
463
+ InstanceMethod<&Archive::getRandomEntry>("getRandomEntry"),
464
+ InstanceMethod<&Archive::hasEntryByPath>("hasEntryByPath"),
465
+ InstanceMethod<&Archive::hasEntryByTitle>("hasEntryByTitle"),
466
+ InstanceMethod<&Archive::hasMainEntry>("hasMainEntry"),
467
+ InstanceMethod<&Archive::hasIllustration>("hasIllustration"),
468
+ InstanceMethod<&Archive::hasFulltextIndex>("hasFulltextIndex"),
469
+ InstanceMethod<&Archive::hasTitleIndex>("hasTitleIndex"),
470
+ InstanceMethod<&Archive::iterByPath>("iterByPath"),
471
+ InstanceMethod<&Archive::iterByTitle>("iterByTitle"),
472
+ InstanceMethod<&Archive::iterEfficient>("iterEfficient"),
473
+ InstanceMethod<&Archive::findByPath>("findByPath"),
474
+ InstanceMethod<&Archive::findByTitle>("findByTitle"),
475
+ InstanceAccessor<&Archive::hasChecksum>("hasChecksum"),
476
+ InstanceAccessor<&Archive::getChecksum>("checksum"),
477
+ InstanceMethod<&Archive::check>("check"),
478
+ InstanceMethod<&Archive::checkIntegrity>("checkIntegrity"),
479
+ InstanceAccessor<&Archive::isMultiPart>("isMultiPart"),
480
+ InstanceAccessor<&Archive::hasNewNamespaceScheme>(
481
+ "hasNewNamespaceScheme"),
482
+ StaticMethod<&Archive::validate>("validate"),
483
+ });
484
+
485
+ exports.Set("Archive", func);
486
+ constructors.archive = Napi::Persistent(func);
487
+ }
488
+
489
+ // internal module methods
490
+ std::shared_ptr<zim::Archive> archive() { return archive_; }
491
+
492
+ private:
493
+ std::shared_ptr<zim::Archive> archive_;
494
+ };
package/src/blob.h ADDED
@@ -0,0 +1,101 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/blob.h>
5
+ #include <exception>
6
+ #include <map>
7
+ #include <memory>
8
+ #include <string>
9
+
10
+ #include "common.h"
11
+
12
+ class Blob : public Napi::ObjectWrap<Blob> {
13
+ public:
14
+ explicit Blob(const Napi::CallbackInfo &info)
15
+ : Napi::ObjectWrap<Blob>(info), blob_{std::make_shared<zim::Blob>()} {
16
+ Napi::Env env = info.Env();
17
+ Napi::HandleScope scope(env);
18
+
19
+ if (info[0].IsExternal()) { // handle internal zim::Blob
20
+ blob_ = std::make_shared<zim::Blob>(
21
+ *info[0].As<Napi::External<zim::Blob>>().Data());
22
+ } else if (info.Length() > 0) { // use refcontent_ and copy content
23
+ // TODO(kelvinhammond): avoid copying content somehow in certain scenarios
24
+ // if possible Maybe use a reference object??? What is the lifecycle of
25
+ // that and what if it's changed?
26
+
27
+ size_t size = 0;
28
+ std::shared_ptr<char> data = nullptr;
29
+ if (info[0].IsArrayBuffer()) { // handle ArrayBuffer
30
+ auto buf = info[0].As<Napi::ArrayBuffer>();
31
+ size = buf.ByteLength();
32
+ data = std::shared_ptr<char>(new char[size]);
33
+ memcpy(data.get(), buf.Data(), size);
34
+ } else if (info[0].IsBuffer()) { // handle Buffer
35
+ auto buf = info[0].As<Napi::Buffer<char>>();
36
+ size = buf.Length();
37
+ data = std::shared_ptr<char>(new char[size]);
38
+ memcpy(data.get(), buf.Data(), size);
39
+ } else { // all others toString()
40
+ auto str = info[0].ToString().Utf8Value(); // coerce to string
41
+ size = str.size();
42
+ data = std::shared_ptr<char>(new char[size]);
43
+ memcpy(data.get(), str.c_str(), size);
44
+ }
45
+
46
+ blob_ = std::make_shared<zim::Blob>(data, size); // blob takes ownership
47
+ }
48
+ }
49
+
50
+ static Napi::Object New(Napi::Env env, zim::Blob &blob) {
51
+ auto external = Napi::External<zim::Blob>::New(env, &blob);
52
+ auto &constructor = env.GetInstanceData<ModuleConstructors>()->blob;
53
+ return constructor.New({external});
54
+ }
55
+
56
+ Napi::Value getData(const Napi::CallbackInfo &info) {
57
+ try {
58
+ // TODO(kelvinhammond): find a way to have a readonly buffer in NodeJS
59
+ return Napi::Buffer<char>::Copy(info.Env(), blob_->data(), blob_->size());
60
+ } catch (const std::exception &err) {
61
+ throw Napi::Error::New(info.Env(), err.what());
62
+ }
63
+ }
64
+
65
+ Napi::Value toString(const Napi::CallbackInfo &info) {
66
+ try {
67
+ return Napi::Value::From(info.Env(), (std::string)*blob_);
68
+ } catch (const std::exception &err) {
69
+ throw Napi::Error::New(info.Env(), err.what());
70
+ }
71
+ }
72
+
73
+ Napi::Value getSize(const Napi::CallbackInfo &info) {
74
+ try {
75
+ return Napi::Value::From(info.Env(), blob_->size());
76
+ } catch (const std::exception &err) {
77
+ throw Napi::Error::New(info.Env(), err.what());
78
+ }
79
+ }
80
+
81
+ static void Init(Napi::Env env, Napi::Object exports,
82
+ ModuleConstructors &constructors) {
83
+ Napi::HandleScope scope(env);
84
+ Napi::Function func =
85
+ DefineClass(env, "Blob",
86
+ {
87
+ InstanceAccessor<&Blob::getData>("data"),
88
+ InstanceAccessor<&Blob::getSize>("size"),
89
+ InstanceMethod<&Blob::toString>("toString"),
90
+ });
91
+
92
+ exports.Set("Blob", func);
93
+ constructors.blob = Napi::Persistent(func);
94
+ }
95
+
96
+ // internal module methods
97
+ std::shared_ptr<zim::Blob> blob() const { return blob_; }
98
+
99
+ private:
100
+ std::shared_ptr<zim::Blob> blob_;
101
+ };