tablefacts 0.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/.env.example +14 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +313 -0
- package/bin/tablefacts.mjs +28 -0
- package/package.json +77 -0
- package/src/index.mjs +52 -0
- package/src/instagram/README.md +135 -0
- package/src/instagram/download.mjs +62 -0
- package/src/instagram/index.mjs +355 -0
- package/src/instagram/links.mjs +55 -0
- package/src/instagram/record.mjs +18 -0
- package/src/lib/edge.mjs +49 -0
- package/src/lib/env.mjs +35 -0
- package/src/lib/errors.mjs +39 -0
- package/src/lib/files.mjs +10 -0
- package/src/lib/images.mjs +20 -0
- package/src/lib/log.mjs +17 -0
- package/src/lib/photos.mjs +42 -0
- package/src/lib/playwright.mjs +13 -0
- package/src/lib/project.mjs +42 -0
- package/src/lib/text.mjs +7 -0
- package/src/lib/types.mjs +247 -0
- package/src/menu/README.md +97 -0
- package/src/menu/cluvi/config.mjs +33 -0
- package/src/menu/cluvi/extract.mjs +24 -0
- package/src/menu/cluvi/import.mjs +30 -0
- package/src/menu/cluvi/source.mjs +156 -0
- package/src/menu/index.mjs +9 -0
- package/src/menu/lib/db.mjs +119 -0
- package/src/menu/lib/import.mjs +126 -0
- package/src/menu/lib/menu.mjs +95 -0
- package/src/menu/lib/run.mjs +83 -0
- package/src/menu/raw/config.mjs +38 -0
- package/src/menu/raw/extract.mjs +72 -0
- package/src/menu/raw/import.mjs +99 -0
- package/src/menu/raw/normalize.mjs +120 -0
- package/src/menu/raw/source.mjs +116 -0
- package/src/menu/raw/vision.mjs +252 -0
- package/src/research/README.md +69 -0
- package/src/research/index.mjs +147 -0
- package/src/research/lib/google.mjs +93 -0
- package/src/research/lib/hours.mjs +109 -0
- package/src/research/lib/merge.mjs +119 -0
- package/src/research/lib/osm.mjs +49 -0
- package/src/research/lib/report.mjs +118 -0
- package/src/research/lib/social.mjs +49 -0
- package/src/research/lib/util.mjs +104 -0
- package/src/research/lib/website.mjs +285 -0
- package/src/research/research.mjs +67 -0
- package/src/tripadvisor/README.md +78 -0
- package/src/tripadvisor/index.mjs +178 -0
- package/src/tripadvisor/links.mjs +78 -0
- package/src/tripadvisor/photos.mjs +55 -0
- package/types/index.d.mts +65 -0
- package/types/instagram/download.d.mts +1 -0
- package/types/instagram/index.d.mts +13 -0
- package/types/instagram/links.d.mts +14 -0
- package/types/instagram/record.d.mts +1 -0
- package/types/lib/edge.d.mts +14 -0
- package/types/lib/env.d.mts +12 -0
- package/types/lib/errors.d.mts +25 -0
- package/types/lib/files.d.mts +1 -0
- package/types/lib/images.d.mts +6 -0
- package/types/lib/log.d.mts +5 -0
- package/types/lib/photos.d.mts +30 -0
- package/types/lib/playwright.d.mts +1677 -0
- package/types/lib/project.d.mts +32 -0
- package/types/lib/text.d.mts +4 -0
- package/types/lib/types.d.mts +668 -0
- package/types/menu/cluvi/config.d.mts +13 -0
- package/types/menu/cluvi/extract.d.mts +2 -0
- package/types/menu/cluvi/import.d.mts +35 -0
- package/types/menu/cluvi/source.d.mts +37 -0
- package/types/menu/index.d.mts +6 -0
- package/types/menu/lib/db.d.mts +23 -0
- package/types/menu/lib/import.d.mts +11 -0
- package/types/menu/lib/menu.d.mts +24 -0
- package/types/menu/lib/run.d.mts +27 -0
- package/types/menu/raw/config.d.mts +18 -0
- package/types/menu/raw/extract.d.mts +2 -0
- package/types/menu/raw/import.d.mts +64 -0
- package/types/menu/raw/normalize.d.mts +29 -0
- package/types/menu/raw/source.d.mts +19 -0
- package/types/menu/raw/vision.d.mts +13 -0
- package/types/research/index.d.mts +9 -0
- package/types/research/lib/google.d.mts +12 -0
- package/types/research/lib/hours.d.mts +22 -0
- package/types/research/lib/merge.d.mts +87 -0
- package/types/research/lib/osm.d.mts +36 -0
- package/types/research/lib/report.d.mts +6 -0
- package/types/research/lib/social.d.mts +118 -0
- package/types/research/lib/util.d.mts +45 -0
- package/types/research/lib/website.d.mts +283 -0
- package/types/research/research.d.mts +1 -0
- package/types/tripadvisor/index.d.mts +11 -0
- package/types/tripadvisor/links.d.mts +13 -0
- package/types/tripadvisor/photos.d.mts +1 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// The pure parts of photos.mjs: the command line, the restaurant link and the photo URLs.
|
|
2
|
+
// Split out so they can be tested without starting a browser.
|
|
3
|
+
|
|
4
|
+
import { usageError } from '../lib/errors.mjs'
|
|
5
|
+
|
|
6
|
+
export function parseArgs(argv) {
|
|
7
|
+
const opts = { links: [], dryRun: false, debug: false, max: Infinity }
|
|
8
|
+
for (let i = 0; i < argv.length; i++) {
|
|
9
|
+
const a = argv[i]
|
|
10
|
+
if (a === '--out') opts.out = argv[++i]
|
|
11
|
+
else if (a === '--cdp') opts.cdp = argv[++i]
|
|
12
|
+
else if (a === '--edge-dir') opts.edgeDir = argv[++i]
|
|
13
|
+
else if (a === '--max') {
|
|
14
|
+
opts.max = Number(argv[++i])
|
|
15
|
+
if (!(opts.max > 0)) throw usageError('--max needs a number above 0')
|
|
16
|
+
} else if (a === '--dry-run') opts.dryRun = true
|
|
17
|
+
else if (a === '--debug') opts.debug = true
|
|
18
|
+
else if (a === '--help' || a === '-h') opts.help = true
|
|
19
|
+
else if (a.startsWith('--')) throw usageError(`Unknown option ${a}`)
|
|
20
|
+
else opts.links.push(a)
|
|
21
|
+
}
|
|
22
|
+
return opts
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A restaurant page looks like /Restaurant_Review-g297478-d1234567-Reviews-Name-City.html on
|
|
26
|
+
// any country domain (tripadvisor.com, .co, .es...). The query and hash are dropped, so the same
|
|
27
|
+
// restaurant gives one URL.
|
|
28
|
+
export function normalizeRestaurant(raw) {
|
|
29
|
+
let url
|
|
30
|
+
try {
|
|
31
|
+
url = new URL(raw.trim())
|
|
32
|
+
} catch {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
if (!/(^|\.)tripadvisor\.[a-z.]+$/.test(url.hostname)) return null
|
|
36
|
+
const m = url.pathname.match(/\/Restaurant_Review-g\d+-(d\d+)[^/]*\.html$/)
|
|
37
|
+
if (!m) return null
|
|
38
|
+
return { id: m[1], url: `${url.origin}${url.pathname}` }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const MEDIA = /https?:\/\/(?:dynamic-media-cdn|media-cdn)\.tripadvisor\.com\/media\/photo-([a-z])\/(?:\d{3,}\/)?([\w-]+(?:\/[\w-]+){2,5}\/[^\s"'\\)?<>]+?\.(?:jpe?g|png|webp))/gi
|
|
42
|
+
|
|
43
|
+
// Page HTML often carries URLs inside JSON, with slashes escaped as \/ or \u002F.
|
|
44
|
+
function unescape(text) {
|
|
45
|
+
return text.replace(/\\u002F/gi, '/').replace(/\\\//g, '/').replace(/&/g, '&')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Every TripAdvisor photo URL in some text, once per photo (the same photo appears in several
|
|
49
|
+
// sizes: photo-s, photo-l, photo-o...). Reviewer avatars, maps and logos are left out.
|
|
50
|
+
// Returns [{ key, size, url }] in the order first seen; `key` is the path after the size.
|
|
51
|
+
export function findPhotos(text) {
|
|
52
|
+
const found = new Map()
|
|
53
|
+
for (const m of unescape(text).matchAll(MEDIA)) {
|
|
54
|
+
const [url, size, key] = m
|
|
55
|
+
if (/avatar|\/map|logo/i.test(key)) continue
|
|
56
|
+
if (!found.has(key)) found.set(key, { key, size, url })
|
|
57
|
+
}
|
|
58
|
+
return [...found.values()]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Candidate URLs for one photo, biggest first: the original, then the next sizes, then the size the
|
|
62
|
+
// page itself showed. Not every size exists for every photo, so the caller tries them in turn.
|
|
63
|
+
export function sizeCandidates(photo) {
|
|
64
|
+
const base = 'https://dynamic-media-cdn.tripadvisor.com/media'
|
|
65
|
+
const urls = ['o', 'w', 'l'].map((size) => `${base}/photo-${size}/${photo.key}`)
|
|
66
|
+
const own = photo.url.replace(/\?.*$/, '')
|
|
67
|
+
if (!urls.includes(own)) urls.push(own)
|
|
68
|
+
return urls
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// '1a/2b/3c/4d/name.jpg' -> '1a2b3c4d-name.jpg': unique per photo and stable between runs.
|
|
72
|
+
export function fileName(photo) {
|
|
73
|
+
const parts = photo.key.split('/')
|
|
74
|
+
const name = parts.pop()
|
|
75
|
+
const ext = (name.match(/\.(jpe?g|png|webp)$/i)?.[1] ?? 'jpg').toLowerCase().replace('jpeg', 'jpg')
|
|
76
|
+
const stem = name.replace(/\.\w+$/, '')
|
|
77
|
+
return `${parts.join('')}-${stem}.${ext}`.replace(/[^\w.-]/g, '_')
|
|
78
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Downloads the photos of a restaurant's TripAdvisor page, in the user's own Edge window.
|
|
2
|
+
// Usage: tablefacts photos tripadvisor --out <folder> [--cdp <url>] <restaurant link>...
|
|
3
|
+
// The work is in index.mjs (downloadTripadvisor); this file is only the command line.
|
|
4
|
+
import { TablefactsError, cliMessage, exitCodeFor } from '../lib/errors.mjs'
|
|
5
|
+
import { consoleLog } from '../lib/log.mjs'
|
|
6
|
+
import { downloadTripadvisor } from './index.mjs'
|
|
7
|
+
import { parseArgs } from './links.mjs'
|
|
8
|
+
|
|
9
|
+
const HELP = `Download the photos of a restaurant's TripAdvisor page.
|
|
10
|
+
|
|
11
|
+
tablefacts photos tripadvisor --out <folder> [options] <restaurant link>...
|
|
12
|
+
|
|
13
|
+
--out <folder> where the images are saved (required, created if missing)
|
|
14
|
+
--cdp <url> attach to Edge on this debugging address (default http://localhost:9222). If nothing
|
|
15
|
+
is listening there the script starts Edge itself
|
|
16
|
+
--edge-dir <dir> profile folder of that Edge (default C:\\ig-edge, shared with photos instagram)
|
|
17
|
+
--max <n> stop after n photos per restaurant (default: all it can reach)
|
|
18
|
+
--dry-run print the photo URLs found, save nothing
|
|
19
|
+
--debug save the page HTML into <folder>/_debug, to fix the selectors
|
|
20
|
+
--help this text
|
|
21
|
+
|
|
22
|
+
The link is the restaurant's page, like https://www.tripadvisor.com/Restaurant_Review-g297478-d123-Reviews-Name.html
|
|
23
|
+
TripAdvisor shows a "verify you are human" check to automated browsers: if it appears in the window,
|
|
24
|
+
tick it yourself. This script waits for it and never tries to get around it.
|
|
25
|
+
|
|
26
|
+
Only download photos the restaurant owns or has allowed you to use. Most photos on TripAdvisor were
|
|
27
|
+
uploaded by guests, who keep the rights to them.`
|
|
28
|
+
|
|
29
|
+
const FLAGS = {
|
|
30
|
+
out: '--out <folder>',
|
|
31
|
+
links: 'the arguments',
|
|
32
|
+
cdp: '--cdp',
|
|
33
|
+
debug: '--debug',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let summary
|
|
37
|
+
try {
|
|
38
|
+
const opts = parseArgs(process.argv.slice(2))
|
|
39
|
+
if (opts.help) {
|
|
40
|
+
console.log(HELP)
|
|
41
|
+
process.exit(0)
|
|
42
|
+
}
|
|
43
|
+
if (!opts.out) {
|
|
44
|
+
console.error('Missing --out <folder>.\n\n' + HELP)
|
|
45
|
+
process.exit(2)
|
|
46
|
+
}
|
|
47
|
+
summary = await downloadTripadvisor({ ...opts, log: consoleLog })
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (!(err instanceof TablefactsError)) throw err
|
|
50
|
+
console.error(cliMessage(err, FLAGS))
|
|
51
|
+
process.exit(exitCodeFor(err))
|
|
52
|
+
}
|
|
53
|
+
console.log(`\nSaved ${summary.saved}, already there ${summary.skipped}, failed ${summary.failed.length}.`)
|
|
54
|
+
for (const f of summary.failed) console.log(` ${f.item}: ${cliMessage({ message: f.reason }, FLAGS)}`)
|
|
55
|
+
process.exit(summary.failed.length ? 1 : 0)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** @typedef {import('./lib/types.mjs').Log} Log */
|
|
2
|
+
/** @typedef {import('./lib/types.mjs').Env} Env */
|
|
3
|
+
/** @typedef {import('./lib/types.mjs').ResearchOptions} ResearchOptions */
|
|
4
|
+
/** @typedef {import('./lib/types.mjs').ResearchResult} ResearchResult */
|
|
5
|
+
/** @typedef {import('./lib/types.mjs').ResearchPhoto} ResearchPhoto */
|
|
6
|
+
/** @typedef {import('./lib/types.mjs').ResearchProfile} ResearchProfile */
|
|
7
|
+
/** @typedef {import('./lib/types.mjs').InstagramOptions} InstagramOptions */
|
|
8
|
+
/** @typedef {import('./lib/types.mjs').TripadvisorOptions} TripadvisorOptions */
|
|
9
|
+
/** @typedef {import('./lib/types.mjs').PhotoSummary} PhotoSummary */
|
|
10
|
+
/** @typedef {import('./lib/types.mjs').PhotoFailure} PhotoFailure */
|
|
11
|
+
/** @typedef {import('./lib/types.mjs').Menu} Menu */
|
|
12
|
+
/** @typedef {import('./lib/types.mjs').MenuCategory} MenuCategory */
|
|
13
|
+
/** @typedef {import('./lib/types.mjs').MenuSection} MenuSection */
|
|
14
|
+
/** @typedef {import('./lib/types.mjs').MenuProduct} MenuProduct */
|
|
15
|
+
/** @typedef {import('./lib/types.mjs').MenuTotals} MenuTotals */
|
|
16
|
+
/** @typedef {import('./lib/types.mjs').MenuImage} MenuImage */
|
|
17
|
+
/** @typedef {import('./lib/types.mjs').MenuSourceConfig} MenuSourceConfig */
|
|
18
|
+
/** @typedef {import('./lib/types.mjs').CluviConfig} CluviConfig */
|
|
19
|
+
/** @typedef {import('./lib/types.mjs').RawConfig} RawConfig */
|
|
20
|
+
/** @typedef {import('./lib/types.mjs').ImportOptions} ImportOptions */
|
|
21
|
+
/** @typedef {import('./lib/types.mjs').ImportMenuOptions} ImportMenuOptions */
|
|
22
|
+
/** @typedef {import('./lib/types.mjs').ImportCluviOptions} ImportCluviOptions */
|
|
23
|
+
/** @typedef {import('./lib/types.mjs').ImportImageMenuOptions} ImportImageMenuOptions */
|
|
24
|
+
/** @typedef {import('./lib/types.mjs').ListMenuImagesOptions} ListMenuImagesOptions */
|
|
25
|
+
/** @typedef {import('./lib/types.mjs').ImportResult} ImportResult */
|
|
26
|
+
/** @typedef {import('./lib/types.mjs').PriceFormat} PriceFormat */
|
|
27
|
+
/** @typedef {import('./lib/types.mjs').VisionProvider} VisionProvider */
|
|
28
|
+
/** @typedef {import('./lib/types.mjs').LoadEnvOptions} LoadEnvOptions */
|
|
29
|
+
/** @typedef {import('./lib/types.mjs').TablefactsErrorCode} TablefactsErrorCode */
|
|
30
|
+
export type Log = import('./lib/types.mjs').Log;
|
|
31
|
+
export type Env = import('./lib/types.mjs').Env;
|
|
32
|
+
export type ResearchOptions = import('./lib/types.mjs').ResearchOptions;
|
|
33
|
+
export type ResearchResult = import('./lib/types.mjs').ResearchResult;
|
|
34
|
+
export type ResearchPhoto = import('./lib/types.mjs').ResearchPhoto;
|
|
35
|
+
export type ResearchProfile = import('./lib/types.mjs').ResearchProfile;
|
|
36
|
+
export type InstagramOptions = import('./lib/types.mjs').InstagramOptions;
|
|
37
|
+
export type TripadvisorOptions = import('./lib/types.mjs').TripadvisorOptions;
|
|
38
|
+
export type PhotoSummary = import('./lib/types.mjs').PhotoSummary;
|
|
39
|
+
export type PhotoFailure = import('./lib/types.mjs').PhotoFailure;
|
|
40
|
+
export type Menu = import('./lib/types.mjs').Menu;
|
|
41
|
+
export type MenuCategory = import('./lib/types.mjs').MenuCategory;
|
|
42
|
+
export type MenuSection = import('./lib/types.mjs').MenuSection;
|
|
43
|
+
export type MenuProduct = import('./lib/types.mjs').MenuProduct;
|
|
44
|
+
export type MenuTotals = import('./lib/types.mjs').MenuTotals;
|
|
45
|
+
export type MenuImage = import('./lib/types.mjs').MenuImage;
|
|
46
|
+
export type MenuSourceConfig = import('./lib/types.mjs').MenuSourceConfig;
|
|
47
|
+
export type CluviConfig = import('./lib/types.mjs').CluviConfig;
|
|
48
|
+
export type RawConfig = import('./lib/types.mjs').RawConfig;
|
|
49
|
+
export type ImportOptions = import('./lib/types.mjs').ImportOptions;
|
|
50
|
+
export type ImportMenuOptions = import('./lib/types.mjs').ImportMenuOptions;
|
|
51
|
+
export type ImportCluviOptions = import('./lib/types.mjs').ImportCluviOptions;
|
|
52
|
+
export type ImportImageMenuOptions = import('./lib/types.mjs').ImportImageMenuOptions;
|
|
53
|
+
export type ListMenuImagesOptions = import('./lib/types.mjs').ListMenuImagesOptions;
|
|
54
|
+
export type ImportResult = import('./lib/types.mjs').ImportResult;
|
|
55
|
+
export type PriceFormat = import('./lib/types.mjs').PriceFormat;
|
|
56
|
+
export type VisionProvider = import('./lib/types.mjs').VisionProvider;
|
|
57
|
+
export type LoadEnvOptions = import('./lib/types.mjs').LoadEnvOptions;
|
|
58
|
+
export type TablefactsErrorCode = import('./lib/types.mjs').TablefactsErrorCode;
|
|
59
|
+
export { research } from './research/index.mjs';
|
|
60
|
+
export { downloadInstagram } from './instagram/index.mjs';
|
|
61
|
+
export { downloadTripadvisor } from './tripadvisor/index.mjs';
|
|
62
|
+
export { importMenu, importCluvi, importImageMenu, listMenuImages, validateMenu, countMenu, normalizePages, parsePrice, providers, defaultProvider, } from './menu/index.mjs';
|
|
63
|
+
export { projectRoot, workDir, workDirIn, envFiles } from './lib/project.mjs';
|
|
64
|
+
export { TablefactsError } from './lib/errors.mjs';
|
|
65
|
+
export { loadEnv } from './lib/env.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads the photos of Instagram posts (and optionally a whole profile) through toolzu.com.
|
|
3
|
+
* At least one of `links`, `file` or `profile` is required. Resolves to a summary whose `failed`
|
|
4
|
+
* lists `{ item, reason }` and whose `found` is present only in a dry run. Throws a TablefactsError
|
|
5
|
+
* on invalid arguments (code 'EUSAGE', with `option`), a missing playwright ('EDEPENDENCY') or a
|
|
6
|
+
* browser that cannot be reached.
|
|
7
|
+
* `out`, `file` and `userDataDir` resolve against `projectDir` (default: TABLEFACTS_PROJECT or the
|
|
8
|
+
* current folder). `viaGoogle` reaches toolzu through a Google search. `log(message, level)` gets
|
|
9
|
+
* level 'info', 'warn' or 'error'.
|
|
10
|
+
* @param {import('../lib/types.mjs').InstagramOptions} options
|
|
11
|
+
* @returns {Promise<import('../lib/types.mjs').PhotoSummary>}
|
|
12
|
+
*/
|
|
13
|
+
export declare function downloadInstagram({ links, file, out, profile, pages, cdp, edgeDir, viaGoogle, browser, userDataDir, projectDir, headed, dryRun, debug, log: logOption, }?: import('../lib/types.mjs').InstagramOptions): Promise<import('../lib/types.mjs').PhotoSummary>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function parseArgs(argv: any): {
|
|
2
|
+
links: never[];
|
|
3
|
+
headed: boolean;
|
|
4
|
+
dryRun: boolean;
|
|
5
|
+
debug: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalize(raw: any): {
|
|
8
|
+
shortcode: string;
|
|
9
|
+
url: string;
|
|
10
|
+
} | null;
|
|
11
|
+
export declare function parseProfile(raw: any): {
|
|
12
|
+
user: any;
|
|
13
|
+
url: string;
|
|
14
|
+
} | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const DEFAULT_CDP = "http://localhost:9222";
|
|
2
|
+
export declare const DEFAULT_EDGE_DIR = "C:\\ig-edge";
|
|
3
|
+
/**
|
|
4
|
+
* Whether a browser answers on the DevTools address.
|
|
5
|
+
* @param {string} cdp e.g. http://localhost:9222
|
|
6
|
+
* @returns {Promise<boolean>}
|
|
7
|
+
*/
|
|
8
|
+
export declare function cdpUp(cdp: string): Promise<boolean>;
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} cdp DevTools address; only localhost can be started
|
|
11
|
+
* @param {string} [edgeDir] Edge profile folder. Default C:\ig-edge
|
|
12
|
+
* @returns {Promise<void>}
|
|
13
|
+
*/
|
|
14
|
+
export declare function ensureEdge(cdp: string, edgeDir?: string): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** `env` when given, else process.env. */
|
|
2
|
+
export declare const resolveEnv: (env: any) => any;
|
|
3
|
+
/** Loads `files` (default: the project's .env files) into `env`. The first file that sets a variable wins. Returns the files it read. */
|
|
4
|
+
export declare function loadEnvFiles(files?: string[], env?: NodeJS.ProcessEnv): string[];
|
|
5
|
+
/**
|
|
6
|
+
* Loads the project's env files (`projectDir`, else TABLEFACTS_PROJECT or the current folder) or
|
|
7
|
+
* exactly `files` into `env` (default process.env). Never overrides a variable that is already set.
|
|
8
|
+
* Returns the files it read.
|
|
9
|
+
* @param {import('./types.mjs').LoadEnvOptions} [options]
|
|
10
|
+
* @returns {string[]}
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadEnv({ projectDir, files, env }?: import('./types.mjs').LoadEnvOptions): string[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare class TablefactsError extends Error {
|
|
2
|
+
/** @type {import('./types.mjs').TablefactsErrorCode} */
|
|
3
|
+
code: import('./types.mjs').TablefactsErrorCode;
|
|
4
|
+
/** The library option the error is about, when it is about one. @type {string=} */
|
|
5
|
+
option: string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} message
|
|
8
|
+
* @param {import('./types.mjs').TablefactsErrorCode} code
|
|
9
|
+
* @param {{ cause?: unknown, option?: string }} [options] `option` names the library option the message is about.
|
|
10
|
+
*/
|
|
11
|
+
constructor(message: string, code: import('./types.mjs').TablefactsErrorCode, options?: {
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
option?: string;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** Bad or missing arguments: the CLIs exit with 2. */
|
|
17
|
+
export declare const usageError: (message: any) => TablefactsError;
|
|
18
|
+
/** The exit code a CLI uses for an error it reports: 2 for bad arguments (EUSAGE), 1 for anything else. */
|
|
19
|
+
export declare const exitCodeFor: (err: any) => 1 | 2;
|
|
20
|
+
/** "fooBar" -> "foo-bar". */
|
|
21
|
+
export declare const kebab: (name: any) => string;
|
|
22
|
+
/** An error about a library option. Write the option in backticks in the message, e.g. "`out` is required". */
|
|
23
|
+
export declare const optionError: (option: any, message: any, code?: string) => TablefactsError;
|
|
24
|
+
/** The message with `option` tokens replaced by CLI flag text from `flags` ({ out: '--out <folder>' }). */
|
|
25
|
+
export declare const cliMessage: (err: any, flags?: {}) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function exists(path: any): Promise<boolean>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Throws EFAILED unless the response is a good image: ok, an image/* content type and at least `minBytes` bytes. */
|
|
2
|
+
export declare function assertImageResponse({ ok, status, contentType, bytes, minBytes, label }?: {
|
|
3
|
+
minBytes?: number | undefined;
|
|
4
|
+
}): void;
|
|
5
|
+
/** Writes the bytes to `file`, creating its folder first. */
|
|
6
|
+
export declare function writeImage(file: any, bytes: any): Promise<void>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const noopLog: () => void;
|
|
2
|
+
/** For CLIs: info goes to stdout, warn and error to stderr. */
|
|
3
|
+
export declare const consoleLog: (message: any, level?: string) => void;
|
|
4
|
+
/** Always returns a function that is called as log(message, level); tolerates a missing log or one that takes one argument. */
|
|
5
|
+
export declare function normalizeLog(log: any): (message: any, level?: string) => void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export declare const DELAY_MS = 2500;
|
|
2
|
+
/** { saved, skipped, failed: [{ item, reason }], found? } where `found` ([{ name, url }]) exists only in a dry run. */
|
|
3
|
+
export declare function newSummary({ dryRun }?: {
|
|
4
|
+
dryRun?: boolean | undefined;
|
|
5
|
+
}): {
|
|
6
|
+
saved: number;
|
|
7
|
+
skipped: number;
|
|
8
|
+
failed: never[];
|
|
9
|
+
};
|
|
10
|
+
/** Resolves the output folder and creates it (not in a dry run), plus <out>/_debug when `debug`. */
|
|
11
|
+
export declare function prepareOut({ out, dryRun, debug, projectDir }?: {
|
|
12
|
+
debug?: boolean | undefined;
|
|
13
|
+
dryRun?: boolean | undefined;
|
|
14
|
+
}): Promise<{
|
|
15
|
+
outDir: string;
|
|
16
|
+
debugDir: string | null;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Dry run: records { name, url } in summary.found. File already there: counts it as skipped.
|
|
20
|
+
* Otherwise awaits `save({ file, name, url })` and counts it as saved. A save error is not caught.
|
|
21
|
+
*/
|
|
22
|
+
export declare function storeFile({ outDir, name, url, dryRun, summary, log, save }: {
|
|
23
|
+
dryRun: any;
|
|
24
|
+
log: any;
|
|
25
|
+
name: any;
|
|
26
|
+
outDir: any;
|
|
27
|
+
save: any;
|
|
28
|
+
summary: any;
|
|
29
|
+
url: any;
|
|
30
|
+
}): Promise<void>;
|