@helloao/cli 0.0.8 → 0.0.9

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/db.js DELETED
@@ -1,663 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.importTranslations = importTranslations;
30
- exports.importTranslationBatch = importTranslationBatch;
31
- exports.importTranslationFileBatch = importTranslationFileBatch;
32
- exports.getChangedOrNewInputFiles = getChangedOrNewInputFiles;
33
- exports.insertFileMetadata = insertFileMetadata;
34
- exports.insertTranslations = insertTranslations;
35
- exports.insertTranslationBooks = insertTranslationBooks;
36
- exports.insertTranslationContent = insertTranslationContent;
37
- exports.getDbPathFromDir = getDbPathFromDir;
38
- exports.getDbPath = getDbPath;
39
- exports.getPrismaDbFromDir = getPrismaDbFromDir;
40
- exports.getDbFromDir = getDbFromDir;
41
- exports.getDb = getDb;
42
- exports.loadDatasets = loadDatasets;
43
- exports.serializeFilesFromDatabase = serializeFilesFromDatabase;
44
- exports.serializeDatasets = serializeDatasets;
45
- const prisma_gen_1 = require("./prisma-gen");
46
- const path_1 = __importStar(require("path"));
47
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
48
- const fs_extra_1 = require("fs-extra");
49
- const node_crypto_1 = require("node:crypto");
50
- const dataset_1 = require("@helloao/tools/generation/dataset");
51
- const api_1 = require("@helloao/tools/generation/api");
52
- const files_1 = require("./files");
53
- const hash_js_1 = require("hash.js");
54
- const cliPath = require.resolve('./index');
55
- const migrationsPath = path_1.default.resolve((0, path_1.dirname)(cliPath), 'migrations');
56
- /**
57
- * Imports the translations from the given directories into the database.
58
- * @param db The database to import the translations into.
59
- * @param dirs The directories to import the translations from.
60
- * @param parser The DOM parser that should be used for USX files.
61
- * @param overwrite Whether to force a reload of the translations.
62
- */
63
- async function importTranslations(db, dirs, parser, overwrite) {
64
- let batches = [];
65
- while (dirs.length > 0) {
66
- batches.push(dirs.splice(0, 10));
67
- }
68
- console.log('Processing', batches.length, 'batches of translations');
69
- for (let i = 0; i < batches.length; i++) {
70
- const batch = batches[i];
71
- console.log(`Processing batch ${i + 1} of ${batches.length}`);
72
- await importTranslationBatch(db, batch, parser, overwrite);
73
- }
74
- }
75
- /**
76
- * Imports a batch of translations from the given directories into the database.
77
- * @param db The database to import the translations into.
78
- * @param dirs The directories that contain the translations.
79
- * @param parser The DOM parser that should be used for USX files.
80
- * @param overwrite Whether to force a reload of the translations.
81
- */
82
- async function importTranslationBatch(db, dirs, parser, overwrite) {
83
- const promises = [];
84
- for (let dir of dirs) {
85
- const fullPath = path_1.default.resolve(dir);
86
- promises.push((0, files_1.loadTranslationFiles)(fullPath).then((files) => files ?? []));
87
- }
88
- const allFiles = await Promise.all(promises);
89
- const files = allFiles.flat();
90
- await importTranslationFileBatch(db, files, parser, overwrite);
91
- }
92
- /**
93
- * Parses and imports the given files into the database.
94
- * @param db The database to import the files into.
95
- * @param files The files that should be parsed.
96
- * @param parser The DOM parser that should be used for USX files.
97
- * @param overwrite Whether to force a reload of the translations.
98
- */
99
- async function importTranslationFileBatch(db, files, parser, overwrite) {
100
- console.log('Importing', files.length, 'files');
101
- if (overwrite) {
102
- console.log('Overwriting existing translations.');
103
- }
104
- const changedFiles = overwrite
105
- ? files
106
- : getChangedOrNewInputFiles(db, files);
107
- console.log('Processing', changedFiles.length, 'changed files');
108
- console.log('Skipping', files.length - changedFiles.length, 'unchanged files');
109
- const output = (0, dataset_1.generateDataset)(changedFiles, parser);
110
- console.log('Generated', output.translations.length, 'translations');
111
- insertTranslations(db, output.translations);
112
- updateTranslationHashes(db, output.translations);
113
- insertFileMetadata(db, changedFiles);
114
- console.log(`Inserted ${output.translations} translations into DB`);
115
- }
116
- /**
117
- * Filters the given input files to only include those that have changed.
118
- * @param db The database to check for changes.
119
- * @param files The files to filter.
120
- */
121
- function getChangedOrNewInputFiles(db, files) {
122
- const fileExists = db.prepare('SELECT COUNT(*) as c FROM InputFile WHERE translationId = @translationId AND name = @name AND sha256 = @sha256;');
123
- return files.filter((f) => {
124
- const count = fileExists.get({
125
- translationId: f.metadata.translation.id,
126
- name: path_1.default.basename(f.name),
127
- sha256: f.sha256,
128
- });
129
- return count.c <= 0;
130
- });
131
- }
132
- function insertFileMetadata(db, files) {
133
- const fileUpsert = db.prepare(`INSERT INTO InputFile(
134
- translationId,
135
- name,
136
- format,
137
- sha256,
138
- sizeInBytes
139
- ) VALUES (
140
- @translationId,
141
- @name,
142
- @format,
143
- @sha256,
144
- @sizeInBytes
145
- ) ON CONFLICT(translationId, name) DO
146
- UPDATE SET
147
- format=excluded.format,
148
- sha256=excluded.sha256,
149
- sizeInBytes=excluded.sizeInBytes;`);
150
- const insertManyFiles = db.transaction((files) => {
151
- for (let file of files) {
152
- fileUpsert.run({
153
- translationId: file.metadata.translation.id,
154
- name: path_1.default.basename(file.name),
155
- format: file.fileType,
156
- sha256: file.sha256,
157
- sizeInBytes: file.content.length,
158
- });
159
- }
160
- });
161
- insertManyFiles(files);
162
- }
163
- function insertTranslations(db, translations) {
164
- const translationUpsert = db.prepare(`INSERT INTO Translation(
165
- id,
166
- name,
167
- language,
168
- shortName,
169
- textDirection,
170
- licenseUrl,
171
- website,
172
- englishName
173
- ) VALUES (
174
- @id,
175
- @name,
176
- @language,
177
- @shortName,
178
- @textDirection,
179
- @licenseUrl,
180
- @website,
181
- @englishName
182
- ) ON CONFLICT(id) DO
183
- UPDATE SET
184
- name=excluded.name,
185
- language=excluded.language,
186
- shortName=excluded.shortName,
187
- textDirection=excluded.textDirection,
188
- licenseUrl=excluded.licenseUrl,
189
- website=excluded.website,
190
- englishName=excluded.englishName;`);
191
- const insertManyTranslations = db.transaction((translations) => {
192
- for (let translation of translations) {
193
- translationUpsert.run({
194
- id: translation.id,
195
- name: translation.name,
196
- language: translation.language,
197
- shortName: translation.shortName,
198
- textDirection: translation.textDirection,
199
- licenseUrl: translation.licenseUrl,
200
- website: translation.website,
201
- englishName: translation.englishName,
202
- });
203
- }
204
- });
205
- insertManyTranslations(translations);
206
- for (let translation of translations) {
207
- insertTranslationBooks(db, translation, translation.books);
208
- }
209
- }
210
- function insertTranslationBooks(db, translation, translationBooks) {
211
- const bookUpsert = db.prepare(`INSERT INTO Book(
212
- id,
213
- translationId,
214
- title,
215
- name,
216
- commonName,
217
- numberOfChapters,
218
- \`order\`
219
- ) VALUES (
220
- @id,
221
- @translationId,
222
- @title,
223
- @name,
224
- @commonName,
225
- @numberOfChapters,
226
- @bookOrder
227
- ) ON CONFLICT(id,translationId) DO
228
- UPDATE SET
229
- title=excluded.title,
230
- name=excluded.name,
231
- commonName=excluded.commonName,
232
- numberOfChapters=excluded.numberOfChapters;`);
233
- const insertMany = db.transaction((books) => {
234
- for (let book of books) {
235
- if (!book) {
236
- continue;
237
- }
238
- bookUpsert.run({
239
- id: book.id,
240
- translationId: translation.id,
241
- title: book.title,
242
- name: book.name,
243
- commonName: book.commonName,
244
- numberOfChapters: book.chapters.length,
245
- bookOrder: book.order ?? 9999,
246
- });
247
- }
248
- });
249
- insertMany(translationBooks);
250
- for (let book of translationBooks) {
251
- insertTranslationContent(db, translation, book, book.chapters);
252
- }
253
- }
254
- function insertTranslationContent(db, translation, book, chapters) {
255
- const chapterUpsert = db.prepare(`INSERT INTO Chapter(
256
- translationId,
257
- bookId,
258
- number,
259
- json
260
- ) VALUES (
261
- @translationId,
262
- @bookId,
263
- @number,
264
- @json
265
- ) ON CONFLICT(translationId,bookId,number) DO
266
- UPDATE SET
267
- json=excluded.json;`);
268
- const verseUpsert = db.prepare(`INSERT INTO ChapterVerse(
269
- translationId,
270
- bookId,
271
- chapterNumber,
272
- number,
273
- text,
274
- contentJson
275
- ) VALUES (
276
- @translationId,
277
- @bookId,
278
- @chapterNumber,
279
- @number,
280
- @text,
281
- @contentJson
282
- ) ON CONFLICT(translationId,bookId,chapterNumber,number) DO
283
- UPDATE SET
284
- text=excluded.text,
285
- contentJson=excluded.contentJson;`);
286
- const footnoteUpsert = db.prepare(`INSERT INTO ChapterFootnote(
287
- translationId,
288
- bookId,
289
- chapterNumber,
290
- id,
291
- verseNumber,
292
- text
293
- ) VALUES (
294
- @translationId,
295
- @bookId,
296
- @chapterNumber,
297
- @id,
298
- @verseNumber,
299
- @text
300
- ) ON CONFLICT(translationId,bookId,chapterNumber,id) DO
301
- UPDATE SET
302
- verseNumber=excluded.verseNumber,
303
- text=excluded.text;`);
304
- const chapterAudioUpsert = db.prepare(`INSERT INTO ChapterAudioUrl(
305
- translationId,
306
- bookId,
307
- number,
308
- reader,
309
- url
310
- ) VALUES (
311
- @translationId,
312
- @bookId,
313
- @number,
314
- @reader,
315
- @url
316
- ) ON CONFLICT(translationId,bookId,number,reader) DO
317
- UPDATE SET
318
- url=excluded.url;`);
319
- const insertChaptersAndVerses = db.transaction(() => {
320
- for (let chapter of chapters) {
321
- let verses = [];
322
- let footnotes = new Map();
323
- for (let c of chapter.chapter.footnotes) {
324
- footnotes.set(c.noteId, {
325
- id: c.noteId,
326
- text: c.text,
327
- });
328
- }
329
- for (let c of chapter.chapter.content) {
330
- if (c.type === 'verse') {
331
- const verse = c;
332
- if (!verse.number) {
333
- console.error('Verse missing number', translation.id, book.id, chapter.chapter.number, verse.number);
334
- continue;
335
- }
336
- let text = '';
337
- for (let c of verse.content) {
338
- if (typeof c === 'string') {
339
- text += c + ' ';
340
- }
341
- else if (typeof c === 'object') {
342
- if ('lineBreak' in c) {
343
- text += '\n';
344
- }
345
- else if ('text' in c) {
346
- text += c.text + ' ';
347
- }
348
- else if ('noteId' in c) {
349
- const note = footnotes.get(c.noteId);
350
- if (note) {
351
- note.verseNumber = verse.number;
352
- }
353
- }
354
- }
355
- }
356
- let contentJson = JSON.stringify(verse.content);
357
- verses.push({
358
- number: verse.number,
359
- text: text.trimEnd(),
360
- contentJson,
361
- });
362
- }
363
- }
364
- chapterUpsert.run({
365
- translationId: translation.id,
366
- bookId: book.id,
367
- number: chapter.chapter.number,
368
- json: JSON.stringify(chapter.chapter),
369
- });
370
- for (let verse of verses) {
371
- verseUpsert.run({
372
- translationId: translation.id,
373
- bookId: book.id,
374
- chapterNumber: chapter.chapter.number,
375
- number: verse.number,
376
- text: verse.text,
377
- contentJson: verse.contentJson,
378
- });
379
- }
380
- for (let footnote of footnotes.values()) {
381
- footnoteUpsert.run({
382
- translationId: translation.id,
383
- bookId: book.id,
384
- chapterNumber: chapter.chapter.number,
385
- id: footnote.id,
386
- verseNumber: footnote.verseNumber,
387
- text: footnote.text,
388
- });
389
- }
390
- for (let reader in chapter.thisChapterAudioLinks) {
391
- const url = chapter.thisChapterAudioLinks[reader];
392
- if (url) {
393
- chapterAudioUpsert.run({
394
- translationId: translation.id,
395
- bookId: book.id,
396
- number: chapter.chapter.number,
397
- reader: reader,
398
- url,
399
- });
400
- }
401
- }
402
- }
403
- });
404
- insertChaptersAndVerses();
405
- }
406
- /**
407
- * Updates the hashes for the translations in the database.
408
- * @param db The database to update the hashes in.
409
- * @param translations The translations to update the hashes for.
410
- */
411
- function updateTranslationHashes(db, translations) {
412
- console.log(`Updating hashes for ${translations.length} translations.`);
413
- const updateTranslationHash = db.prepare(`UPDATE Translation SET sha256 = @sha256 WHERE id = @translationId;`);
414
- const updateBookHash = db.prepare(`UPDATE Book SET sha256 = @sha256 WHERE translationId = @translationId AND id = @bookId;`);
415
- const updateChapterHash = db.prepare(`UPDATE Chapter SET sha256 = @sha256 WHERE translationId = @translationId AND bookId = @bookId AND number = @chapterNumber;`);
416
- const getBooks = db.prepare('SELECT * FROM Book WHERE translationId = ?;');
417
- const getChapters = db.prepare('SELECT * FROM Chapter WHERE translationId = @translationId AND bookId = @bookId;');
418
- for (let translation of translations) {
419
- const translationSha = (0, hash_js_1.sha256)()
420
- .update(translation.id)
421
- .update(translation.name)
422
- .update(translation.language)
423
- .update(translation.licenseUrl)
424
- .update(translation.textDirection)
425
- .update(translation.website)
426
- .update(translation.englishName)
427
- .update(translation.shortName);
428
- const books = getBooks.all(translation.id);
429
- for (let book of books) {
430
- const chapters = getChapters.all({
431
- translationId: translation.id,
432
- bookId: book.id,
433
- });
434
- const bookSha = (0, hash_js_1.sha256)()
435
- .update(book.translationId)
436
- .update(book.id)
437
- .update(book.numberOfChapters)
438
- .update(book.order)
439
- .update(book.name)
440
- .update(book.title)
441
- .update(book.commonName);
442
- for (let chapter of chapters) {
443
- const hash = (0, hash_js_1.sha256)()
444
- .update(chapter.translationId)
445
- .update(chapter.bookId)
446
- .update(chapter.number)
447
- .update(chapter.json)
448
- .digest('hex');
449
- chapter.sha256 = hash;
450
- bookSha.update(hash);
451
- }
452
- const updateChapters = db.transaction(() => {
453
- for (let chapter of chapters) {
454
- updateChapterHash.run({
455
- sha256: chapter.sha256,
456
- translationId: chapter.translationId,
457
- bookId: chapter.bookId,
458
- chapterNumber: chapter.number,
459
- });
460
- }
461
- });
462
- updateChapters();
463
- const bookHash = bookSha.digest('hex');
464
- book.sha256 = bookHash;
465
- translationSha.update(bookHash);
466
- }
467
- const updateBooks = db.transaction(() => {
468
- for (let book of books) {
469
- updateBookHash.run({
470
- sha256: book.sha256,
471
- translationId: book.translationId,
472
- bookId: book.id,
473
- });
474
- }
475
- });
476
- updateBooks();
477
- const hash = translationSha.digest('hex');
478
- translation.sha256 = hash;
479
- }
480
- const updateTranslations = db.transaction(() => {
481
- for (let translation of translations) {
482
- updateTranslationHash.run({
483
- sha256: translation.sha256,
484
- translationId: translation.id,
485
- });
486
- }
487
- });
488
- updateTranslations();
489
- console.log(`Updated.`);
490
- }
491
- function getDbPathFromDir(dir) {
492
- dir = dir || process.cwd();
493
- return path_1.default.resolve(dir, 'bible-api.db');
494
- }
495
- function getDbPath(p) {
496
- if (p) {
497
- return path_1.default.resolve(p);
498
- }
499
- return getDbPathFromDir(process.cwd());
500
- }
501
- function getPrismaDbFromDir(dir) {
502
- const dbPath = getDbPathFromDir(dir);
503
- const prisma = new prisma_gen_1.PrismaClient({
504
- datasources: {
505
- db: {
506
- url: `file:${dbPath}`,
507
- },
508
- },
509
- });
510
- return prisma;
511
- }
512
- async function getDbFromDir(dir) {
513
- const dbPath = getDbPathFromDir(dir);
514
- const db = await getDb(dbPath);
515
- return db;
516
- }
517
- async function getDb(dbPath) {
518
- const db = new better_sqlite3_1.default(dbPath, {});
519
- db.exec(`CREATE TABLE IF NOT EXISTS "_prisma_migrations" (
520
- "id" TEXT PRIMARY KEY NOT NULL,
521
- "checksum" TEXT NOT NULL,
522
- "finished_at" DATETIME,
523
- "migration_name" TEXT NOT NULL,
524
- "logs" TEXT,
525
- "rolled_back_at" DATETIME,
526
- "started_at" DATETIME NOT NULL DEFAULT current_timestamp,
527
- "applied_steps_count" INTEGER UNSIGNED NOT NULL DEFAULT 0
528
- );`);
529
- const migrations = await (0, fs_extra_1.readdir)(migrationsPath);
530
- const appliedMigrations = db
531
- .prepare('SELECT * FROM _prisma_migrations;')
532
- .all();
533
- let missingMigrations = [];
534
- for (let migration of migrations) {
535
- if (appliedMigrations.some((m) => m.migration_name === migration)) {
536
- continue;
537
- }
538
- if (path_1.default.extname(migration) !== '') {
539
- continue;
540
- }
541
- missingMigrations.push(migration);
542
- }
543
- const insertMigrationStatement = db.prepare('INSERT INTO _prisma_migrations (id, checksum, started_at, finished_at, migration_name, applied_steps_count, logs, rolled_back_at) VALUES (?, ?, ?, ?, ?, ?, NULL, NULL);');
544
- for (let missingMigration of missingMigrations) {
545
- console.log(`Applying migration ${missingMigration}...`);
546
- const migration = path_1.default.resolve(migrationsPath, missingMigration, 'migration.sql');
547
- const migrationFile = await (0, fs_extra_1.readFile)(migration, 'utf8');
548
- db.exec(migrationFile);
549
- insertMigrationStatement.run((0, node_crypto_1.randomUUID)(), '', new Date().toISOString(), new Date().toISOString(), missingMigration, 1);
550
- }
551
- return db;
552
- }
553
- /**
554
- * Loads the datasets from the database in a series of batches.
555
- * @param db The database.
556
- * @param translationsPerBatch The number of translations to load per batch.
557
- * @param translationsToLoad The list of translations to load. If not provided, all translations will be loaded.
558
- */
559
- async function* loadDatasets(db, translationsPerBatch = 50, translationsToLoad) {
560
- let offset = 0;
561
- let pageSize = translationsPerBatch;
562
- console.log('Generating API files in batches of', pageSize);
563
- const totalTranslations = await db.translation.count();
564
- const totalBatches = Math.ceil(totalTranslations / pageSize);
565
- let batchNumber = 1;
566
- while (true) {
567
- console.log('Generating API batch', batchNumber, 'of', totalBatches);
568
- batchNumber++;
569
- const query = {
570
- skip: offset,
571
- take: pageSize,
572
- };
573
- if (translationsToLoad && translationsToLoad.length > 0) {
574
- query.where = {
575
- id: {
576
- in: translationsToLoad,
577
- },
578
- };
579
- }
580
- const translations = await db.translation.findMany(query);
581
- if (translations.length <= 0) {
582
- break;
583
- }
584
- const dataset = {
585
- translations: [],
586
- };
587
- for (let translation of translations) {
588
- const datasetTranslation = {
589
- ...translation,
590
- shortName: translation.shortName,
591
- textDirection: translation.textDirection,
592
- books: [],
593
- };
594
- dataset.translations.push(datasetTranslation);
595
- const books = await db.book.findMany({
596
- where: {
597
- translationId: translation.id,
598
- },
599
- orderBy: {
600
- order: 'asc',
601
- },
602
- });
603
- for (let book of books) {
604
- const chapters = await db.chapter.findMany({
605
- where: {
606
- translationId: translation.id,
607
- bookId: book.id,
608
- },
609
- orderBy: {
610
- number: 'asc',
611
- },
612
- });
613
- const audioLinks = await db.chapterAudioUrl.findMany({
614
- where: {
615
- translationId: translation.id,
616
- bookId: book.id,
617
- },
618
- orderBy: [{ number: 'asc' }, { reader: 'asc' }],
619
- });
620
- const bookChapters = chapters.map((chapter) => {
621
- return {
622
- chapter: JSON.parse(chapter.json),
623
- thisChapterAudioLinks: audioLinks
624
- .filter((link) => link.number === chapter.number)
625
- .reduce((acc, link) => {
626
- acc[link.reader] = link.url;
627
- return acc;
628
- }, {}),
629
- };
630
- });
631
- const datasetBook = {
632
- ...book,
633
- chapters: bookChapters,
634
- };
635
- datasetTranslation.books.push(datasetBook);
636
- }
637
- }
638
- yield dataset;
639
- offset += pageSize;
640
- }
641
- }
642
- /**
643
- * Generates and serializes the API files for the datasets that are stored in the database.
644
- * Yields each batch of serialized files.
645
- * @param db The database that the dataset should be loaded from.
646
- * @param options The options to use for serializing the files.
647
- * @param apiOptions The options to use for generating the API files.
648
- * @param translationsPerBatch The number of translations that should be loaded and written per batch.
649
- * @param translations The list of translations that should be loaded. If not provided, all translations will be loaded.
650
- */
651
- function serializeFilesFromDatabase(db, options = {}, translationsPerBatch = 50, translations) {
652
- return serializeDatasets(loadDatasets(db, translationsPerBatch, translations), options);
653
- }
654
- /**
655
- * Generates and serializes the API files for the given datasets.
656
- * Yields each batch of serialized files.
657
- *
658
- * @param datasets The datasets to serialize.
659
- * @param options The options to use for generating and serializing the files.
660
- */
661
- function serializeDatasets(datasets, options = {}) {
662
- return (0, files_1.serializeOutputFiles)((0, api_1.generateOutputFilesFromDatasets)(datasets, options), options);
663
- }
package/downloads.js DELETED
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.downloadFile = downloadFile;
4
- const fs_extra_1 = require("fs-extra");
5
- const node_stream_1 = require("node:stream");
6
- const promises_1 = require("node:stream/promises");
7
- async function downloadFile(url, path) {
8
- console.log('Downloading', url, 'to', path);
9
- const reader = await fetch(url).then(r => r.body);
10
- const writeStream = (0, fs_extra_1.createWriteStream)(path);
11
- await (0, promises_1.finished)(node_stream_1.Readable.fromWeb(reader).pipe(writeStream));
12
- }