@helloao/cli 0.0.5 → 0.0.6
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/README.md +125 -126
- package/actions.d.ts +64 -0
- package/actions.js +159 -43
- package/cli.js +88 -31
- package/db.d.ts +22 -9
- package/db.js +155 -154
- package/files.d.ts +7 -2
- package/files.js +29 -8
- package/migrations/20240623183848_add_book_order/migration.sql +26 -26
- package/migrations/20240629194121_add_chapter_links/migration.sql +45 -45
- package/migrations/20240629194513_add_chapter_content/migration.sql +30 -30
- package/migrations/20240705221833_remove_unused_columns/migration.sql +27 -27
- package/package.json +5 -2
- package/prisma-gen/edge.js +5 -5
- package/prisma-gen/index.js +7 -7
- package/s3.d.ts +32 -2
- package/s3.js +105 -12
- package/schema.prisma +154 -154
- package/uploads.d.ts +15 -5
- package/uploads.js +9 -89
package/cli.js
CHANGED
|
@@ -11,39 +11,101 @@ const linkedom_1 = require("linkedom");
|
|
|
11
11
|
const downloads_1 = require("./downloads");
|
|
12
12
|
const uploads_1 = require("./uploads");
|
|
13
13
|
const actions_1 = require("./actions");
|
|
14
|
-
const files_1 = require("./files");
|
|
15
|
-
const dataset_1 = require("@helloao/tools/generation/dataset");
|
|
16
|
-
const iterators_1 = require("@helloao/tools/parser/iterators");
|
|
17
14
|
const db_1 = require("./db");
|
|
15
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
18
16
|
async function start() {
|
|
19
17
|
const parser = new linkedom_1.DOMParser();
|
|
20
18
|
globalThis.DOMParser = linkedom_1.DOMParser;
|
|
21
19
|
globalThis.Element = linkedom_1.Element;
|
|
22
20
|
globalThis.Node = linkedom_1.Node;
|
|
23
21
|
const program = new commander_1.Command();
|
|
24
|
-
program
|
|
22
|
+
program
|
|
23
|
+
.name('helloao')
|
|
25
24
|
.description('A CLI for managing a Free Use Bible API.')
|
|
26
25
|
.version('0.0.1');
|
|
27
|
-
program
|
|
26
|
+
program
|
|
27
|
+
.command('init [path]')
|
|
28
28
|
.description('Initialize a new Bible API DB.')
|
|
29
29
|
.option('--source <path>', 'The source database to copy from.')
|
|
30
30
|
.option('--language <languages...>', 'The language(s) that the database should be initialized with.')
|
|
31
31
|
.action(async (dbPath, options) => {
|
|
32
32
|
await (0, actions_1.initDb)(dbPath, options);
|
|
33
33
|
});
|
|
34
|
-
program
|
|
34
|
+
program
|
|
35
|
+
.command('import-translation <dir> [dirs...]')
|
|
35
36
|
.description('Imports a translation from the given directory into the database.')
|
|
36
37
|
.option('--overwrite', 'Whether to overwrite existing files.')
|
|
37
38
|
.action(async (dir, dirs, options) => {
|
|
38
39
|
await (0, actions_1.importTranslation)(dir, dirs, options);
|
|
39
40
|
});
|
|
40
|
-
program
|
|
41
|
+
program
|
|
42
|
+
.command('import-translations <dir>')
|
|
41
43
|
.description('Imports all translations from the given directory into the database.')
|
|
42
44
|
.option('--overwrite', 'Whether to overwrite existing files.')
|
|
43
45
|
.action(async (dir, options) => {
|
|
44
46
|
await (0, actions_1.importTranslations)(dir, options);
|
|
45
47
|
});
|
|
46
|
-
program
|
|
48
|
+
program
|
|
49
|
+
.command('upload-test-translation <input>')
|
|
50
|
+
.description(`Uploads a translation to the HelloAO Free Bible API test S3 bucket.\nRequires access to the HelloAO Free Bible API test S3 bucket.\nFor inquiries, please contact hello@helloao.org.`)
|
|
51
|
+
.option('--batch-size <size>', 'The number of translations to generate API files for in each batch.', '50')
|
|
52
|
+
.option('--translations <translations...>', 'The translations to generate API files for.')
|
|
53
|
+
.option('--overwrite', 'Whether to overwrite existing files.')
|
|
54
|
+
.option('--overwrite-common-files', 'Whether to overwrite only common files.')
|
|
55
|
+
.option('--file-pattern <pattern>', 'The file pattern regex that should be used to filter the files that are generated.')
|
|
56
|
+
.option('--use-common-name', 'Whether to use the common name for the book chapter API link. If false, then book IDs are used.')
|
|
57
|
+
.option('--generate-audio-files', 'Whether to replace the audio URLs in the dataset with ones that are hosted locally.')
|
|
58
|
+
.option('--profile <profile>', 'The AWS profile to use for uploading to S3.')
|
|
59
|
+
.option('--access-key-id <accessKeyId>', 'The AWS access key ID to use for uploading to S3.')
|
|
60
|
+
.option('--secret-access-key <secretAccessKey>', 'The AWS Secret Access Key to use for uploading to S3.')
|
|
61
|
+
.option('--pretty', 'Whether to generate pretty-printed JSON files.')
|
|
62
|
+
.option('--s3-url <s3Url>', 'The S3 bucket URL to upload the files to.', 's3://ao-bible-api-public-uploads')
|
|
63
|
+
.action(async (input, options) => {
|
|
64
|
+
const good = await (0, prompts_1.confirm)({
|
|
65
|
+
message: 'Uploaded files will be publicly accessible. Continue?',
|
|
66
|
+
default: false,
|
|
67
|
+
});
|
|
68
|
+
if (!good) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const result = await (0, actions_1.uploadTestTranslation)(input, options);
|
|
72
|
+
console.log('\n');
|
|
73
|
+
console.log('Version: ', result.version);
|
|
74
|
+
console.log('Uploaded to: ', result.uploadS3Url);
|
|
75
|
+
console.log('URL: ', result.url);
|
|
76
|
+
console.log('Available Translations:', result.availableTranslationsUrl);
|
|
77
|
+
});
|
|
78
|
+
program
|
|
79
|
+
.command('upload-test-translations <input>')
|
|
80
|
+
.description(`Uploads all the translations in the given input directory to the HelloAO Free Bible API test S3 bucket.\nRequires access to the HelloAO Free Bible API test S3 bucket.\nFor inquiries, please contact hello@helloao.org.`)
|
|
81
|
+
.option('--batch-size <size>', 'The number of translations to generate API files for in each batch.', '50')
|
|
82
|
+
.option('--translations <translations...>', 'The translations to generate API files for.')
|
|
83
|
+
.option('--overwrite', 'Whether to overwrite existing files.')
|
|
84
|
+
.option('--overwrite-common-files', 'Whether to overwrite only common files.')
|
|
85
|
+
.option('--file-pattern <pattern>', 'The file pattern regex that should be used to filter the files that are generated.')
|
|
86
|
+
.option('--use-common-name', 'Whether to use the common name for the book chapter API link. If false, then book IDs are used.')
|
|
87
|
+
.option('--generate-audio-files', 'Whether to replace the audio URLs in the dataset with ones that are hosted locally.')
|
|
88
|
+
.option('--profile <profile>', 'The AWS profile to use for uploading to S3.')
|
|
89
|
+
.option('--access-key-id <accessKeyId>', 'The AWS access key ID to use for uploading to S3.')
|
|
90
|
+
.option('--secret-access-key <secretAccessKey>', 'The AWS Secret Access Key to use for uploading to S3.')
|
|
91
|
+
.option('--pretty', 'Whether to generate pretty-printed JSON files.')
|
|
92
|
+
.option('--s3-url <s3Url>', 'The S3 bucket URL to upload the files to.', 's3://ao-bible-api-public-uploads')
|
|
93
|
+
.action(async (input, options) => {
|
|
94
|
+
const good = await (0, prompts_1.confirm)({
|
|
95
|
+
message: 'Uploaded files will be publicly accessible. Continue?',
|
|
96
|
+
default: false,
|
|
97
|
+
});
|
|
98
|
+
if (!good) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const result = await (0, actions_1.uploadTestTranslations)(input, options);
|
|
102
|
+
console.log('\nVersion: ', result.version);
|
|
103
|
+
console.log('Uploaded to: ', result.uploadS3Url);
|
|
104
|
+
console.log('URL: ', result.url);
|
|
105
|
+
console.log('Available Translations:', result.availableTranslationsUrl);
|
|
106
|
+
});
|
|
107
|
+
program
|
|
108
|
+
.command('generate-translation-files <input> <dir>')
|
|
47
109
|
.description('Generates API files from the given input translation.')
|
|
48
110
|
.option('--batch-size <size>', 'The number of translations to generate API files for in each batch.', '50')
|
|
49
111
|
.option('--translations <translations...>', 'The translations to generate API files for.')
|
|
@@ -53,17 +115,14 @@ async function start() {
|
|
|
53
115
|
.option('--use-common-name', 'Whether to use the common name for the book chapter API link. If false, then book IDs are used.')
|
|
54
116
|
.option('--generate-audio-files', 'Whether to replace the audio URLs in the dataset with ones that are hosted locally.')
|
|
55
117
|
.option('--profile <profile>', 'The AWS profile to use for uploading to S3.')
|
|
118
|
+
.option('--access-key-id <accessKeyId>', 'The AWS access key ID to use for uploading to S3.')
|
|
119
|
+
.option('--secret-access-key <secretAccessKey>', 'The AWS Secret Access Key to use for uploading to S3.')
|
|
56
120
|
.option('--pretty', 'Whether to generate pretty-printed JSON files.')
|
|
57
121
|
.action(async (input, dest, options) => {
|
|
58
|
-
|
|
59
|
-
globalThis.DOMParser = linkedom_1.DOMParser;
|
|
60
|
-
globalThis.Element = linkedom_1.Element;
|
|
61
|
-
globalThis.Node = linkedom_1.Node;
|
|
62
|
-
const files = await (0, files_1.loadTranslationFiles)(path_1.default.resolve(input));
|
|
63
|
-
const dataset = (0, dataset_1.generateDataset)(files, parser);
|
|
64
|
-
await (0, uploads_1.serializeAndUploadDatasets)(path_1.default.resolve(dest), (0, iterators_1.toAsyncIterable)([dataset]), options);
|
|
122
|
+
await (0, actions_1.generateTranslationFiles)(input, dest, options);
|
|
65
123
|
});
|
|
66
|
-
program
|
|
124
|
+
program
|
|
125
|
+
.command('generate-translations-files <input> <dir>')
|
|
67
126
|
.description('Generates API files from the given input translations.')
|
|
68
127
|
.option('--batch-size <size>', 'The number of translations to generate API files for in each batch.', '50')
|
|
69
128
|
.option('--translations <translations...>', 'The translations to generate API files for.')
|
|
@@ -73,21 +132,14 @@ async function start() {
|
|
|
73
132
|
.option('--use-common-name', 'Whether to use the common name for the book chapter API link. If false, then book IDs are used.')
|
|
74
133
|
.option('--generate-audio-files', 'Whether to replace the audio URLs in the dataset with ones that are hosted locally.')
|
|
75
134
|
.option('--profile <profile>', 'The AWS profile to use for uploading to S3.')
|
|
135
|
+
.option('--access-key-id <accessKeyId>', 'The AWS access key ID to use for uploading to S3.')
|
|
136
|
+
.option('--secret-access-key <secretAccessKey>', 'The AWS Secret Access Key to use for uploading to S3.')
|
|
76
137
|
.option('--pretty', 'Whether to generate pretty-printed JSON files.')
|
|
77
138
|
.action(async (input, dest, options) => {
|
|
78
|
-
|
|
79
|
-
globalThis.DOMParser = linkedom_1.DOMParser;
|
|
80
|
-
globalThis.Element = linkedom_1.Element;
|
|
81
|
-
globalThis.Node = linkedom_1.Node;
|
|
82
|
-
const dirs = await (0, promises_1.readdir)(path_1.default.resolve(input));
|
|
83
|
-
const batchSize = parseInt(options.batchSize);
|
|
84
|
-
for (let b of (0, iterators_1.batch)(dirs, batchSize)) {
|
|
85
|
-
const files = await (0, files_1.loadTranslationsFiles)(b);
|
|
86
|
-
const dataset = (0, dataset_1.generateDataset)(files, parser);
|
|
87
|
-
await (0, uploads_1.serializeAndUploadDatasets)(dest, (0, iterators_1.toAsyncIterable)([dataset]), options);
|
|
88
|
-
}
|
|
139
|
+
await (0, actions_1.generateTranslationsFiles)(input, dest, options);
|
|
89
140
|
});
|
|
90
|
-
program
|
|
141
|
+
program
|
|
142
|
+
.command('upload-api-files')
|
|
91
143
|
.argument('<dest>', 'The destination to upload the API files to.')
|
|
92
144
|
.description('Uploads API files to the specified destination. For S3, use the format s3://bucket-name/path/to/folder.')
|
|
93
145
|
.option('--batch-size <size>', 'The number of translations to generate API files for in each batch.', '50')
|
|
@@ -98,6 +150,8 @@ async function start() {
|
|
|
98
150
|
.option('--use-common-name', 'Whether to use the common name for the book chapter API link. If false, then book IDs are used.')
|
|
99
151
|
.option('--generate-audio-files', 'Whether to replace the audio URLs in the dataset with ones that are hosted locally.')
|
|
100
152
|
.option('--profile <profile>', 'The AWS profile to use for uploading to S3.')
|
|
153
|
+
.option('--access-key-id <accessKeyId>', 'The AWS access key ID to use for uploading to S3.')
|
|
154
|
+
.option('--secret-access-key <secretAccessKey>', 'The AWS Secret Access Key to use for uploading to S3.')
|
|
101
155
|
.option('--pretty', 'Whether to generate pretty-printed JSON files.')
|
|
102
156
|
.action(async (dest, options) => {
|
|
103
157
|
const db = (0, db_1.getPrismaDbFromDir)(process.cwd());
|
|
@@ -108,19 +162,22 @@ async function start() {
|
|
|
108
162
|
db.$disconnect();
|
|
109
163
|
}
|
|
110
164
|
});
|
|
111
|
-
program
|
|
165
|
+
program
|
|
166
|
+
.command('fetch-translations <dir> [translations...]')
|
|
112
167
|
.description('Fetches the specified translations from fetch.bible and places them in the given directory.')
|
|
113
168
|
.option('-a, --all', 'Fetch all translations. If omitted, only undownloaded translations will be fetched.')
|
|
114
169
|
.action(async (dir, translations, options) => {
|
|
115
170
|
await (0, actions_1.fetchTranslations)(dir, translations, options);
|
|
116
171
|
});
|
|
117
|
-
program
|
|
172
|
+
program
|
|
173
|
+
.command('fetch-audio <dir> [translations...]')
|
|
118
174
|
.description('Fetches the specified audio translations and places them in the given directory.\nTranslations should be in the format "translationId/audioId". e.g. "BSB/gilbert"')
|
|
119
175
|
.option('-a, --all', 'Fetch all translations. If omitted, only undownloaded translations will be fetched.')
|
|
120
176
|
.action(async (dir, translations, options) => {
|
|
121
177
|
await (0, actions_1.fetchAudio)(dir, translations, options);
|
|
122
178
|
});
|
|
123
|
-
program
|
|
179
|
+
program
|
|
180
|
+
.command('fetch-bible-metadata <dir>')
|
|
124
181
|
.description('Fetches the Theographic bible metadata and places it in the given directory.')
|
|
125
182
|
.action(async (dir) => {
|
|
126
183
|
let files = [
|
package/db.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { PrismaClient } from
|
|
1
|
+
import { PrismaClient } from './prisma-gen';
|
|
2
2
|
import { Database } from 'better-sqlite3';
|
|
3
|
-
import { DatasetOutput, DatasetTranslation, DatasetTranslationBook } from
|
|
4
|
-
import { InputFile, TranslationBookChapter } from
|
|
5
|
-
import {
|
|
6
|
-
import { DOMParser } from
|
|
7
|
-
import {
|
|
3
|
+
import { DatasetOutput, DatasetTranslation, DatasetTranslationBook } from '@helloao/tools/generation/dataset';
|
|
4
|
+
import { InputFile, TranslationBookChapter } from '@helloao/tools/generation';
|
|
5
|
+
import { GenerateApiOptions } from '@helloao/tools/generation/api';
|
|
6
|
+
import { DOMParser } from 'linkedom';
|
|
7
|
+
import { Readable } from 'stream';
|
|
8
8
|
/**
|
|
9
9
|
* Imports the translations from the given directories into the database.
|
|
10
10
|
* @param db The database to import the translations into.
|
|
@@ -50,6 +50,14 @@ export declare function getPrismaDbFromDir(dir: string): PrismaClient<{
|
|
|
50
50
|
}, never, import("prisma-gen/runtime/library").DefaultArgs>;
|
|
51
51
|
export declare function getDbFromDir(dir: string): Promise<Database>;
|
|
52
52
|
export declare function getDb(dbPath: string): Promise<Database>;
|
|
53
|
+
export interface SerializedFile {
|
|
54
|
+
path: string;
|
|
55
|
+
content: string | Readable;
|
|
56
|
+
/**
|
|
57
|
+
* Gets the base64-encoded SHA256 hash of the content of the file.
|
|
58
|
+
*/
|
|
59
|
+
sha256?(): string;
|
|
60
|
+
}
|
|
53
61
|
/**
|
|
54
62
|
* Loads the datasets from the database in a series of batches.
|
|
55
63
|
* @param db The database.
|
|
@@ -57,7 +65,12 @@ export declare function getDb(dbPath: string): Promise<Database>;
|
|
|
57
65
|
* @param translationsToLoad The list of translations to load. If not provided, all translations will be loaded.
|
|
58
66
|
*/
|
|
59
67
|
export declare function loadDatasets(db: PrismaClient, translationsPerBatch?: number, translationsToLoad?: string[]): AsyncGenerator<DatasetOutput>;
|
|
60
|
-
export
|
|
68
|
+
export interface SerializeApiOptions extends GenerateApiOptions {
|
|
69
|
+
/**
|
|
70
|
+
* Whether the output should be pretty-printed.
|
|
71
|
+
*/
|
|
72
|
+
pretty?: boolean;
|
|
73
|
+
}
|
|
61
74
|
/**
|
|
62
75
|
* Generates and serializes the API files for the datasets that are stored in the database.
|
|
63
76
|
* Yields each batch of serialized files.
|
|
@@ -67,7 +80,7 @@ export type SerializeDatasetOptions = SerializeApiOptions & GenerateApiOptions;
|
|
|
67
80
|
* @param translationsPerBatch The number of translations that should be loaded and written per batch.
|
|
68
81
|
* @param translations The list of translations that should be loaded. If not provided, all translations will be loaded.
|
|
69
82
|
*/
|
|
70
|
-
export declare function serializeFilesFromDatabase(db: PrismaClient, options?:
|
|
83
|
+
export declare function serializeFilesFromDatabase(db: PrismaClient, options?: SerializeApiOptions, translationsPerBatch?: number, translations?: string[]): AsyncGenerator<SerializedFile[]>;
|
|
71
84
|
/**
|
|
72
85
|
* Generates and serializes the API files for the given datasets.
|
|
73
86
|
* Yields each batch of serialized files.
|
|
@@ -75,5 +88,5 @@ export declare function serializeFilesFromDatabase(db: PrismaClient, options?: S
|
|
|
75
88
|
* @param datasets The datasets to serialize.
|
|
76
89
|
* @param options The options to use for generating and serializing the files.
|
|
77
90
|
*/
|
|
78
|
-
export declare function serializeDatasets(datasets: AsyncIterable<DatasetOutput>, options?:
|
|
91
|
+
export declare function serializeDatasets(datasets: AsyncIterable<DatasetOutput>, options?: SerializeApiOptions): AsyncGenerator<SerializedFile[]>;
|
|
79
92
|
//# sourceMappingURL=db.d.ts.map
|