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,45 @@
|
|
|
1
|
+
import { fold } from "../../lib/text.mjs";
|
|
2
|
+
export { fold };
|
|
3
|
+
export declare const BROWSER_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36 cannario-research";
|
|
4
|
+
export declare const sleep: (ms: any) => Promise<any>;
|
|
5
|
+
/** Runs `fn(item, index)` over `items` with at most `size` in flight; results keep the order of `items`. */
|
|
6
|
+
export declare function mapPool(items: any, size: any, fn: any): Promise<any[]>;
|
|
7
|
+
export declare const MAX_BODY_BYTES: number;
|
|
8
|
+
/**
|
|
9
|
+
* GETs a URL as text. The body is capped at 2 MB (`truncated: true` when it was cut, never an error) and a
|
|
10
|
+
* clearly binary content-type (image, pdf, zip...) is not read at all (`skipped: true`, empty text).
|
|
11
|
+
*/
|
|
12
|
+
export declare function fetchText(url: any, { headers, timeout, maxBytes }?: {
|
|
13
|
+
headers?: {} | undefined;
|
|
14
|
+
maxBytes?: number | undefined;
|
|
15
|
+
timeout?: number | undefined;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
ok: boolean;
|
|
18
|
+
status: number;
|
|
19
|
+
url: string;
|
|
20
|
+
type: string;
|
|
21
|
+
text: string;
|
|
22
|
+
skipped: boolean;
|
|
23
|
+
} | {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
status: number;
|
|
26
|
+
url: string;
|
|
27
|
+
type: string;
|
|
28
|
+
text: any;
|
|
29
|
+
truncated: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
ok: boolean;
|
|
32
|
+
status: number;
|
|
33
|
+
url: string;
|
|
34
|
+
type: string;
|
|
35
|
+
text: any;
|
|
36
|
+
}>;
|
|
37
|
+
export declare const tokens: (s: any) => string[];
|
|
38
|
+
/** Loose text match: at least 60% of the shorter side's words appear in the other. */
|
|
39
|
+
export declare function sameText(a: any, b: any): boolean;
|
|
40
|
+
export declare const slugify: (s: any) => string;
|
|
41
|
+
export declare const digits: (s: any) => string;
|
|
42
|
+
/** Same phone number written differently (spaces, +country, leading 0). */
|
|
43
|
+
export declare const samePhone: (a: any, b: any) => boolean;
|
|
44
|
+
/** Distance in km between two { lat, lng } points. */
|
|
45
|
+
export declare function km(a: any, b: any): number;
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/** Pulls every fact a page holds. `base` resolves relative links. */
|
|
2
|
+
export declare function analyzeHtml(html: any, base: any, lines?: any): {
|
|
3
|
+
url: any;
|
|
4
|
+
title: any;
|
|
5
|
+
lang: any;
|
|
6
|
+
meta: {
|
|
7
|
+
siteName: any;
|
|
8
|
+
title: any;
|
|
9
|
+
description: any;
|
|
10
|
+
themeColor: any;
|
|
11
|
+
};
|
|
12
|
+
jsonld: {
|
|
13
|
+
name: any;
|
|
14
|
+
telephone: any;
|
|
15
|
+
email: any;
|
|
16
|
+
priceRange: any;
|
|
17
|
+
cuisines: string[];
|
|
18
|
+
address: {
|
|
19
|
+
street: any;
|
|
20
|
+
locality: any;
|
|
21
|
+
region: any;
|
|
22
|
+
postcode: any;
|
|
23
|
+
country: any;
|
|
24
|
+
};
|
|
25
|
+
geo: {
|
|
26
|
+
lat: number;
|
|
27
|
+
lng: number;
|
|
28
|
+
} | null;
|
|
29
|
+
sameAs: string[];
|
|
30
|
+
logo: any;
|
|
31
|
+
rating: {
|
|
32
|
+
value: number;
|
|
33
|
+
count: number | null;
|
|
34
|
+
} | null;
|
|
35
|
+
hours: {
|
|
36
|
+
week: string[][][];
|
|
37
|
+
partial?: undefined;
|
|
38
|
+
} | {
|
|
39
|
+
week: never[][];
|
|
40
|
+
} | null;
|
|
41
|
+
} | null;
|
|
42
|
+
links: {
|
|
43
|
+
tel: never[];
|
|
44
|
+
mail: never[];
|
|
45
|
+
whatsapp: never[];
|
|
46
|
+
reserve: never[];
|
|
47
|
+
menu: never[];
|
|
48
|
+
delivery: never[];
|
|
49
|
+
maps: never[];
|
|
50
|
+
waze: never[];
|
|
51
|
+
facebook: never[];
|
|
52
|
+
tiktok: never[];
|
|
53
|
+
tripadvisor: never[];
|
|
54
|
+
hubs: never[];
|
|
55
|
+
instagram: never[];
|
|
56
|
+
};
|
|
57
|
+
images: any[];
|
|
58
|
+
logos: {
|
|
59
|
+
url: string | null;
|
|
60
|
+
kind: any;
|
|
61
|
+
}[];
|
|
62
|
+
textLength: any;
|
|
63
|
+
headings: any[];
|
|
64
|
+
paragraphs: any;
|
|
65
|
+
hoursText: any;
|
|
66
|
+
anchors: {
|
|
67
|
+
href: string | null;
|
|
68
|
+
text: any;
|
|
69
|
+
}[];
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* One lazily launched Chromium shared by every render of a run. `get()` launches on first use;
|
|
73
|
+
* `close()` is safe to call when nothing was launched.
|
|
74
|
+
*/
|
|
75
|
+
export declare function createBrowser(): {
|
|
76
|
+
get(): any;
|
|
77
|
+
close(): Promise<void>;
|
|
78
|
+
};
|
|
79
|
+
/** One page, analysed. Returns { page, blocked } and never throws on HTTP errors. `browser` is a shared createBrowser(); without one a private browser is used and closed. */
|
|
80
|
+
export declare function readPage(url: any, { renderJs, browser }?: {
|
|
81
|
+
renderJs?: boolean | undefined;
|
|
82
|
+
}): Promise<{
|
|
83
|
+
page: null;
|
|
84
|
+
blocked: string;
|
|
85
|
+
} | {
|
|
86
|
+
blocked?: undefined;
|
|
87
|
+
page: {
|
|
88
|
+
url: any;
|
|
89
|
+
title: any;
|
|
90
|
+
lang: any;
|
|
91
|
+
meta: {
|
|
92
|
+
siteName: any;
|
|
93
|
+
title: any;
|
|
94
|
+
description: any;
|
|
95
|
+
themeColor: any;
|
|
96
|
+
};
|
|
97
|
+
jsonld: {
|
|
98
|
+
name: any;
|
|
99
|
+
telephone: any;
|
|
100
|
+
email: any;
|
|
101
|
+
priceRange: any;
|
|
102
|
+
cuisines: string[];
|
|
103
|
+
address: {
|
|
104
|
+
street: any;
|
|
105
|
+
locality: any;
|
|
106
|
+
region: any;
|
|
107
|
+
postcode: any;
|
|
108
|
+
country: any;
|
|
109
|
+
};
|
|
110
|
+
geo: {
|
|
111
|
+
lat: number;
|
|
112
|
+
lng: number;
|
|
113
|
+
} | null;
|
|
114
|
+
sameAs: string[];
|
|
115
|
+
logo: any;
|
|
116
|
+
rating: {
|
|
117
|
+
value: number;
|
|
118
|
+
count: number | null;
|
|
119
|
+
} | null;
|
|
120
|
+
hours: {
|
|
121
|
+
week: string[][][];
|
|
122
|
+
partial?: undefined;
|
|
123
|
+
} | {
|
|
124
|
+
week: never[][];
|
|
125
|
+
} | null;
|
|
126
|
+
} | null;
|
|
127
|
+
links: {
|
|
128
|
+
tel: never[];
|
|
129
|
+
mail: never[];
|
|
130
|
+
whatsapp: never[];
|
|
131
|
+
reserve: never[];
|
|
132
|
+
menu: never[];
|
|
133
|
+
delivery: never[];
|
|
134
|
+
maps: never[];
|
|
135
|
+
waze: never[];
|
|
136
|
+
facebook: never[];
|
|
137
|
+
tiktok: never[];
|
|
138
|
+
tripadvisor: never[];
|
|
139
|
+
hubs: never[];
|
|
140
|
+
instagram: never[];
|
|
141
|
+
};
|
|
142
|
+
images: any[];
|
|
143
|
+
logos: {
|
|
144
|
+
url: string | null;
|
|
145
|
+
kind: any;
|
|
146
|
+
}[];
|
|
147
|
+
textLength: any;
|
|
148
|
+
headings: any[];
|
|
149
|
+
paragraphs: any;
|
|
150
|
+
hoursText: any;
|
|
151
|
+
anchors: {
|
|
152
|
+
href: string | null;
|
|
153
|
+
text: any;
|
|
154
|
+
}[];
|
|
155
|
+
rendered: boolean;
|
|
156
|
+
};
|
|
157
|
+
} | {
|
|
158
|
+
blocked?: undefined;
|
|
159
|
+
page: {
|
|
160
|
+
url: any;
|
|
161
|
+
title: any;
|
|
162
|
+
lang: any;
|
|
163
|
+
meta: {
|
|
164
|
+
siteName: any;
|
|
165
|
+
title: any;
|
|
166
|
+
description: any;
|
|
167
|
+
themeColor: any;
|
|
168
|
+
};
|
|
169
|
+
jsonld: {
|
|
170
|
+
name: any;
|
|
171
|
+
telephone: any;
|
|
172
|
+
email: any;
|
|
173
|
+
priceRange: any;
|
|
174
|
+
cuisines: string[];
|
|
175
|
+
address: {
|
|
176
|
+
street: any;
|
|
177
|
+
locality: any;
|
|
178
|
+
region: any;
|
|
179
|
+
postcode: any;
|
|
180
|
+
country: any;
|
|
181
|
+
};
|
|
182
|
+
geo: {
|
|
183
|
+
lat: number;
|
|
184
|
+
lng: number;
|
|
185
|
+
} | null;
|
|
186
|
+
sameAs: string[];
|
|
187
|
+
logo: any;
|
|
188
|
+
rating: {
|
|
189
|
+
value: number;
|
|
190
|
+
count: number | null;
|
|
191
|
+
} | null;
|
|
192
|
+
hours: {
|
|
193
|
+
week: string[][][];
|
|
194
|
+
partial?: undefined;
|
|
195
|
+
} | {
|
|
196
|
+
week: never[][];
|
|
197
|
+
} | null;
|
|
198
|
+
} | null;
|
|
199
|
+
links: {
|
|
200
|
+
tel: never[];
|
|
201
|
+
mail: never[];
|
|
202
|
+
whatsapp: never[];
|
|
203
|
+
reserve: never[];
|
|
204
|
+
menu: never[];
|
|
205
|
+
delivery: never[];
|
|
206
|
+
maps: never[];
|
|
207
|
+
waze: never[];
|
|
208
|
+
facebook: never[];
|
|
209
|
+
tiktok: never[];
|
|
210
|
+
tripadvisor: never[];
|
|
211
|
+
hubs: never[];
|
|
212
|
+
instagram: never[];
|
|
213
|
+
};
|
|
214
|
+
images: any[];
|
|
215
|
+
logos: {
|
|
216
|
+
url: string | null;
|
|
217
|
+
kind: any;
|
|
218
|
+
}[];
|
|
219
|
+
textLength: any;
|
|
220
|
+
headings: any[];
|
|
221
|
+
paragraphs: any;
|
|
222
|
+
hoursText: any;
|
|
223
|
+
anchors: {
|
|
224
|
+
href: string | null;
|
|
225
|
+
text: any;
|
|
226
|
+
}[];
|
|
227
|
+
};
|
|
228
|
+
}>;
|
|
229
|
+
/** The restaurant's own site: the home page plus a few contact/about/menu pages, merged. */
|
|
230
|
+
export declare function scrapeSite(startUrl: any, { renderJs, maxPages, browser }?: {
|
|
231
|
+
maxPages?: number | undefined;
|
|
232
|
+
renderJs?: boolean | undefined;
|
|
233
|
+
}): Promise<{
|
|
234
|
+
url: any;
|
|
235
|
+
pages: {
|
|
236
|
+
url: any;
|
|
237
|
+
title: any;
|
|
238
|
+
rendered: boolean;
|
|
239
|
+
}[];
|
|
240
|
+
lang: any;
|
|
241
|
+
meta: {
|
|
242
|
+
siteName: any;
|
|
243
|
+
title: any;
|
|
244
|
+
description: any;
|
|
245
|
+
themeColor: any;
|
|
246
|
+
};
|
|
247
|
+
jsonld: {
|
|
248
|
+
name: any;
|
|
249
|
+
telephone: any;
|
|
250
|
+
email: any;
|
|
251
|
+
priceRange: any;
|
|
252
|
+
cuisines: string[];
|
|
253
|
+
address: {
|
|
254
|
+
street: any;
|
|
255
|
+
locality: any;
|
|
256
|
+
region: any;
|
|
257
|
+
postcode: any;
|
|
258
|
+
country: any;
|
|
259
|
+
};
|
|
260
|
+
geo: {
|
|
261
|
+
lat: number;
|
|
262
|
+
lng: number;
|
|
263
|
+
} | null;
|
|
264
|
+
sameAs: string[];
|
|
265
|
+
logo: any;
|
|
266
|
+
rating: {
|
|
267
|
+
value: number;
|
|
268
|
+
count: number | null;
|
|
269
|
+
} | null;
|
|
270
|
+
hours: {
|
|
271
|
+
week: string[][][];
|
|
272
|
+
partial?: undefined;
|
|
273
|
+
} | {
|
|
274
|
+
week: never[][];
|
|
275
|
+
} | null;
|
|
276
|
+
} | null;
|
|
277
|
+
links: {};
|
|
278
|
+
images: any;
|
|
279
|
+
logos: any;
|
|
280
|
+
headings: any;
|
|
281
|
+
paragraphs: any;
|
|
282
|
+
hoursText: any;
|
|
283
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloads the photos of TripAdvisor restaurant pages through the user's own Edge.
|
|
3
|
+
* Resolves to a summary whose `failed` lists `{ item, reason }` and whose `found` is present only
|
|
4
|
+
* in a dry run. Throws a TablefactsError on invalid arguments (code 'EUSAGE', with `option`), a
|
|
5
|
+
* missing playwright ('EDEPENDENCY') or a browser that cannot be reached.
|
|
6
|
+
* `out` resolves against `projectDir` (default: TABLEFACTS_PROJECT or the current folder).
|
|
7
|
+
* `log(message, level)` gets level 'info', 'warn' or 'error'.
|
|
8
|
+
* @param {import('../lib/types.mjs').TripadvisorOptions} options
|
|
9
|
+
* @returns {Promise<import('../lib/types.mjs').PhotoSummary>}
|
|
10
|
+
*/
|
|
11
|
+
export declare function downloadTripadvisor({ links, out, cdp, edgeDir, max, projectDir, dryRun, debug, log: logOption, }?: import('../lib/types.mjs').TripadvisorOptions): Promise<import('../lib/types.mjs').PhotoSummary>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function parseArgs(argv: any): {
|
|
2
|
+
links: never[];
|
|
3
|
+
dryRun: boolean;
|
|
4
|
+
debug: boolean;
|
|
5
|
+
max: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function normalizeRestaurant(raw: any): {
|
|
8
|
+
id: string;
|
|
9
|
+
url: string;
|
|
10
|
+
} | null;
|
|
11
|
+
export declare function findPhotos(text: any): any[];
|
|
12
|
+
export declare function sizeCandidates(photo: any): string[];
|
|
13
|
+
export declare function fileName(photo: any): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|