@websitescreenshotworld/sdk 0.0.0-stage → 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/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +399 -2
- package/dist/cjs/auto-schedule.d.ts +20 -0
- package/dist/cjs/auto-schedule.js +34 -0
- package/dist/cjs/client.d.ts +41 -0
- package/dist/cjs/client.js +58 -0
- package/dist/cjs/error-codes.d.ts +54 -0
- package/dist/cjs/error-codes.js +94 -0
- package/dist/cjs/errors.d.ts +56 -0
- package/dist/cjs/errors.js +112 -0
- package/dist/cjs/files.d.ts +6 -0
- package/dist/cjs/files.js +45 -0
- package/dist/cjs/index.d.ts +16 -0
- package/dist/cjs/index.js +37 -0
- package/dist/cjs/instant-snap.d.ts +99 -0
- package/dist/cjs/instant-snap.js +205 -0
- package/dist/cjs/models/capture-file.d.ts +35 -0
- package/dist/cjs/models/capture-file.js +34 -0
- package/dist/cjs/models/capture-options.d.ts +144 -0
- package/dist/cjs/models/capture-options.js +2 -0
- package/dist/cjs/models/capture.d.ts +108 -0
- package/dist/cjs/models/capture.js +63 -0
- package/dist/cjs/models/defaults.d.ts +82 -0
- package/dist/cjs/models/defaults.js +46 -0
- package/dist/cjs/models/tokens.d.ts +92 -0
- package/dist/cjs/models/tokens.js +85 -0
- package/dist/cjs/models/trigger-result.d.ts +20 -0
- package/dist/cjs/models/trigger-result.js +13 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/transport.d.ts +55 -0
- package/dist/cjs/transport.js +206 -0
- package/dist/cjs/version.d.ts +2 -0
- package/dist/cjs/version.js +5 -0
- package/dist/cjs/webhooks/events.d.ts +128 -0
- package/dist/cjs/webhooks/events.js +13 -0
- package/dist/cjs/webhooks/parse-event.d.ts +10 -0
- package/dist/cjs/webhooks/parse-event.js +149 -0
- package/dist/cjs/webhooks/webhook.d.ts +26 -0
- package/dist/cjs/webhooks/webhook.js +78 -0
- package/dist/cjs/wire.d.ts +15 -0
- package/dist/cjs/wire.js +71 -0
- package/dist/esm/auto-schedule.d.ts +20 -0
- package/dist/esm/auto-schedule.js +30 -0
- package/dist/esm/client.d.ts +41 -0
- package/dist/esm/client.js +54 -0
- package/dist/esm/error-codes.d.ts +54 -0
- package/dist/esm/error-codes.js +91 -0
- package/dist/esm/errors.d.ts +56 -0
- package/dist/esm/errors.js +98 -0
- package/dist/esm/files.d.ts +6 -0
- package/dist/esm/files.js +41 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/instant-snap.d.ts +99 -0
- package/dist/esm/instant-snap.js +201 -0
- package/dist/esm/models/capture-file.d.ts +35 -0
- package/dist/esm/models/capture-file.js +31 -0
- package/dist/esm/models/capture-options.d.ts +144 -0
- package/dist/esm/models/capture-options.js +1 -0
- package/dist/esm/models/capture.d.ts +108 -0
- package/dist/esm/models/capture.js +57 -0
- package/dist/esm/models/defaults.d.ts +82 -0
- package/dist/esm/models/defaults.js +42 -0
- package/dist/esm/models/tokens.d.ts +92 -0
- package/dist/esm/models/tokens.js +82 -0
- package/dist/esm/models/trigger-result.d.ts +20 -0
- package/dist/esm/models/trigger-result.js +10 -0
- package/dist/esm/transport.d.ts +55 -0
- package/dist/esm/transport.js +201 -0
- package/dist/esm/version.d.ts +2 -0
- package/dist/esm/version.js +2 -0
- package/dist/esm/webhooks/events.d.ts +128 -0
- package/dist/esm/webhooks/events.js +10 -0
- package/dist/esm/webhooks/parse-event.d.ts +10 -0
- package/dist/esm/webhooks/parse-event.js +146 -0
- package/dist/esm/webhooks/webhook.d.ts +26 -0
- package/dist/esm/webhooks/webhook.js +75 -0
- package/dist/esm/wire.d.ts +15 -0
- package/dist/esm/wire.js +66 -0
- package/package.json +68 -4
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InstantSnap = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const errors_js_1 = require("./errors.js");
|
|
6
|
+
const files_js_1 = require("./files.js");
|
|
7
|
+
const capture_file_js_1 = require("./models/capture-file.js");
|
|
8
|
+
const capture_js_1 = require("./models/capture.js");
|
|
9
|
+
const defaults_js_1 = require("./models/defaults.js");
|
|
10
|
+
const wire_js_1 = require("./wire.js");
|
|
11
|
+
const CAPTURES_PATH = "/instant-snap/v1/captures";
|
|
12
|
+
const DEFAULT_WAIT_TIMEOUT_SECONDS = 360;
|
|
13
|
+
const DEFAULT_POLL_INTERVAL_SECONDS = 3;
|
|
14
|
+
/** The statuses of a capture that has not finished yet. */
|
|
15
|
+
const UNFINISHED_STATUSES = new Set([
|
|
16
|
+
"queued",
|
|
17
|
+
"running",
|
|
18
|
+
"pending",
|
|
19
|
+
]);
|
|
20
|
+
/** On-demand captures: an image, a video or a PDF of a web page. */
|
|
21
|
+
class InstantSnap {
|
|
22
|
+
#config;
|
|
23
|
+
/** @internal Use `client.instantSnap`. */
|
|
24
|
+
constructor(config) {
|
|
25
|
+
this.#config = config;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Captures a page and returns the capture, with a `downloadUrl` valid for 60 minutes.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const capture = await client.instantSnap.capture({ url: "https://example.com", format: "webp" });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
async capture(options, requestOptions = {}) {
|
|
36
|
+
const wire = await this.#transport.json(this.#captureCall(options, "json", requestOptions));
|
|
37
|
+
return (0, capture_js_1.parseCapture)(wire);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Captures a page and returns the file itself as a stream, with its metadata. The API does not
|
|
41
|
+
* keep the file, so read or save it before discarding the result.
|
|
42
|
+
*/
|
|
43
|
+
async captureFile(options, requestOptions = {}) {
|
|
44
|
+
const response = await this.#transport.file(this.#captureCall(options, "by_format", requestOptions));
|
|
45
|
+
return (0, capture_file_js_1.captureFileFrom)(response);
|
|
46
|
+
}
|
|
47
|
+
/** Captures a page and uploads the file to your S3-compatible bucket. */
|
|
48
|
+
async captureToBucket(options, requestOptions = {}) {
|
|
49
|
+
const wire = await this.#transport.json(this.#captureCall(options, "cloud_storage", requestOptions));
|
|
50
|
+
return (0, capture_js_1.parseCapture)(wire);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Queues a capture and returns at once with it, `status` `queued`; it runs when a browser is
|
|
54
|
+
* free. Read it with {@link InstantSnap.wait} or {@link InstantSnap.get}, or receive it at
|
|
55
|
+
* `callbackUrl`.
|
|
56
|
+
*
|
|
57
|
+
* `options.timeout` sets the browser time it may take, 30 to 300 seconds. The call itself uses
|
|
58
|
+
* the client's `timeout`, not `captureTimeout`, since nothing is captured while it waits. A
|
|
59
|
+
* capture still queued 15 minutes later ends `failed` with `capture_expired`, not charged.
|
|
60
|
+
*
|
|
61
|
+
* @throws `RateLimitError` with `errorCode` `queue_full` when too many async captures are
|
|
62
|
+
* already waiting or running. It is not retried; `retryAfterSeconds` says when to try again.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const queued = await client.instantSnap.submit({ url: "https://example.com", timeout: 240 });
|
|
67
|
+
* const capture = await client.instantSnap.wait(queued.id);
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
async submit(options, requestOptions = {}) {
|
|
71
|
+
const wire = await this.#transport.json(this.#captureCall(options, "json", requestOptions, true));
|
|
72
|
+
return (0, capture_js_1.parseCapture)(wire);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Queues a capture that uploads the file to your S3-compatible bucket, and returns at once with
|
|
76
|
+
* it, `status` `queued`. Otherwise as {@link InstantSnap.submit}.
|
|
77
|
+
*/
|
|
78
|
+
async submitToBucket(options, requestOptions = {}) {
|
|
79
|
+
const wire = await this.#transport.json(this.#captureCall(options, "cloud_storage", requestOptions, true));
|
|
80
|
+
return (0, capture_js_1.parseCapture)(wire);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Reads a capture with `get` every `pollIntervalSeconds` until its status is no longer `queued`,
|
|
84
|
+
* `running` or `pending`, and returns it. When `timeoutSeconds` runs out first, returns the
|
|
85
|
+
* capture as last read, still unfinished, so you can decide whether to wait again. An error from
|
|
86
|
+
* `get` is thrown as it is.
|
|
87
|
+
*
|
|
88
|
+
* @param capture - A capture's id, or the capture itself, such as the one `submit` returned.
|
|
89
|
+
*/
|
|
90
|
+
async wait(capture, options = {}) {
|
|
91
|
+
const id = typeof capture === "string" ? capture : capture.id;
|
|
92
|
+
const timeoutMs = (options.timeoutSeconds ?? DEFAULT_WAIT_TIMEOUT_SECONDS) * 1000;
|
|
93
|
+
const intervalMs = (options.pollIntervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS) * 1000;
|
|
94
|
+
const { signal } = options;
|
|
95
|
+
const expiresAt = performance.now() + timeoutMs;
|
|
96
|
+
for (;;) {
|
|
97
|
+
const current = await this.get(id, { signal });
|
|
98
|
+
const remaining = expiresAt - performance.now();
|
|
99
|
+
if (!UNFINISHED_STATUSES.has(current.status) || remaining <= 0) {
|
|
100
|
+
return current;
|
|
101
|
+
}
|
|
102
|
+
if (intervalMs > 0)
|
|
103
|
+
await sleep(Math.min(intervalMs, remaining), signal);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Reads a capture back, with a fresh `downloadUrl`. */
|
|
107
|
+
async get(id, requestOptions = {}) {
|
|
108
|
+
const wire = await this.#transport.json({
|
|
109
|
+
method: "GET",
|
|
110
|
+
url: this.#url(`${CAPTURES_PATH}/${encodeURIComponent(id)}`),
|
|
111
|
+
apiKey: this.#apiKey(),
|
|
112
|
+
timeout: requestOptions.timeout ?? this.#config.timeout,
|
|
113
|
+
signal: requestOptions.signal,
|
|
114
|
+
});
|
|
115
|
+
return (0, capture_js_1.parseCapture)(wire);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Downloads a capture's file, or its page source, to a path or a stream.
|
|
119
|
+
*
|
|
120
|
+
* @param source - A capture from `capture` or `get`, or its `pageSource`.
|
|
121
|
+
* @param destination - A file path, a Node.js writable stream or a web `WritableStream`.
|
|
122
|
+
*/
|
|
123
|
+
async download(source, destination, requestOptions = {}) {
|
|
124
|
+
if (source.downloadUrl === null) {
|
|
125
|
+
throw new TypeError("This capture has no downloadUrl. Read it again with get() for a fresh one.");
|
|
126
|
+
}
|
|
127
|
+
// The link is signed, so the API key stays with the API and is not sent to the file host.
|
|
128
|
+
const response = await this.#transport.file({
|
|
129
|
+
method: "GET",
|
|
130
|
+
url: source.downloadUrl,
|
|
131
|
+
file: true,
|
|
132
|
+
timeout: requestOptions.timeout ?? this.#config.timeout,
|
|
133
|
+
signal: requestOptions.signal,
|
|
134
|
+
});
|
|
135
|
+
const body = response.body ?? new Blob([]).stream();
|
|
136
|
+
await (0, files_js_1.writeBody)(body, destination, requestOptions.signal);
|
|
137
|
+
}
|
|
138
|
+
/** The values a capture takes for fields it does not set, and the limits. Needs no API key. */
|
|
139
|
+
async defaults(requestOptions = {}) {
|
|
140
|
+
const wire = await this.#transport.json({
|
|
141
|
+
method: "GET",
|
|
142
|
+
url: this.#url("/instant-snap/v1/defaults"),
|
|
143
|
+
timeout: requestOptions.timeout ?? this.#config.timeout,
|
|
144
|
+
signal: requestOptions.signal,
|
|
145
|
+
});
|
|
146
|
+
return (0, defaults_js_1.parseDefaults)(wire);
|
|
147
|
+
}
|
|
148
|
+
/** The devices `emulatedDevice` accepts and the countries `proxy.country` accepts. */
|
|
149
|
+
async catalog(requestOptions = {}) {
|
|
150
|
+
const wire = await this.#transport.json({
|
|
151
|
+
method: "GET",
|
|
152
|
+
url: this.#url("/instant-snap/v1/catalog"),
|
|
153
|
+
apiKey: this.#apiKey(),
|
|
154
|
+
timeout: requestOptions.timeout ?? this.#config.timeout,
|
|
155
|
+
signal: requestOptions.signal,
|
|
156
|
+
});
|
|
157
|
+
return (0, defaults_js_1.parseCatalog)(wire);
|
|
158
|
+
}
|
|
159
|
+
get #transport() {
|
|
160
|
+
return this.#config.transport;
|
|
161
|
+
}
|
|
162
|
+
#captureCall(options, responseType, requestOptions, async = false) {
|
|
163
|
+
const body = { ...(0, wire_js_1.toWire)(options), response_type: responseType };
|
|
164
|
+
return {
|
|
165
|
+
method: "POST",
|
|
166
|
+
url: this.#url(CAPTURES_PATH),
|
|
167
|
+
apiKey: this.#apiKey(),
|
|
168
|
+
body: async ? { ...body, async: true } : body,
|
|
169
|
+
idempotencyKey: requestOptions.idempotencyKey ?? (0, node_crypto_1.randomUUID)(),
|
|
170
|
+
file: responseType === "by_format",
|
|
171
|
+
// An async capture is answered at once, so it takes the ordinary timeout.
|
|
172
|
+
timeout: requestOptions.timeout ??
|
|
173
|
+
(async ? this.#config.timeout : this.#config.captureTimeout),
|
|
174
|
+
signal: requestOptions.signal,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
#url(path) {
|
|
178
|
+
return `${this.#config.baseUrl}${path}`;
|
|
179
|
+
}
|
|
180
|
+
#apiKey() {
|
|
181
|
+
if (this.#config.apiKey === undefined) {
|
|
182
|
+
throw new errors_js_1.AuthenticationError("No Instant Snap API key: pass instantSnapApiKey to the client or set WSW_INSTANT_SNAP_API_KEY.");
|
|
183
|
+
}
|
|
184
|
+
return this.#config.apiKey;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.InstantSnap = InstantSnap;
|
|
188
|
+
/** Waits `milliseconds`, or rejects with the signal's reason as soon as it aborts. */
|
|
189
|
+
function sleep(milliseconds, signal) {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
if (signal?.aborted === true) {
|
|
192
|
+
reject(signal.reason);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const onAbort = () => {
|
|
196
|
+
clearTimeout(timer);
|
|
197
|
+
reject(signal?.reason);
|
|
198
|
+
};
|
|
199
|
+
const timer = setTimeout(() => {
|
|
200
|
+
signal?.removeEventListener("abort", onAbort);
|
|
201
|
+
resolve();
|
|
202
|
+
}, milliseconds);
|
|
203
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
204
|
+
});
|
|
205
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CaptureStatus } from "./tokens.js";
|
|
2
|
+
/**
|
|
3
|
+
* A file returned by `instantSnap.captureFile`, with its metadata. The body is a stream that can be
|
|
4
|
+
* read once: through `body`, `arrayBuffer()` or `saveTo()`.
|
|
5
|
+
*/
|
|
6
|
+
export interface CaptureFile {
|
|
7
|
+
id: string;
|
|
8
|
+
/** `delivered`: the file is in this answer and the API does not keep it. */
|
|
9
|
+
status: CaptureStatus;
|
|
10
|
+
contentType: string | null;
|
|
11
|
+
/** The name the API suggests, from `Content-Disposition`. */
|
|
12
|
+
filename: string | null;
|
|
13
|
+
width: number | null;
|
|
14
|
+
height: number | null;
|
|
15
|
+
sizeBytes: number | null;
|
|
16
|
+
/** How long the capture took. */
|
|
17
|
+
durationMs: number | null;
|
|
18
|
+
/** The status the website answered with. */
|
|
19
|
+
pageHttpStatus: number | null;
|
|
20
|
+
specVersion: string | null;
|
|
21
|
+
capturedAt: Date | null;
|
|
22
|
+
/** Where the browser ended up. `null` when the page did not redirect or the URL is not plain ASCII. */
|
|
23
|
+
finalUrl: string | null;
|
|
24
|
+
/** Lowercase hex SHA-256 of the file. */
|
|
25
|
+
sha256: string | null;
|
|
26
|
+
/** How many parts the answer has, when it has more than one. */
|
|
27
|
+
partCount: number | null;
|
|
28
|
+
/** The file itself. */
|
|
29
|
+
readonly body: ReadableStream<Uint8Array>;
|
|
30
|
+
/** Reads the whole file into memory. */
|
|
31
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
32
|
+
/** Streams the file to `path`, replacing any file already there. */
|
|
33
|
+
saveTo(path: string): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export declare function captureFileFrom(response: Response): CaptureFile;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.captureFileFrom = captureFileFrom;
|
|
4
|
+
const files_js_1 = require("../files.js");
|
|
5
|
+
const wire_js_1 = require("../wire.js");
|
|
6
|
+
function captureFileFrom(response) {
|
|
7
|
+
const headers = response.headers;
|
|
8
|
+
const header = (name) => headers.get(`x-instantsnap-${name}`);
|
|
9
|
+
const body = response.body ?? new Blob([]).stream();
|
|
10
|
+
return {
|
|
11
|
+
id: header("id") ?? "",
|
|
12
|
+
status: header("status") ?? "delivered",
|
|
13
|
+
contentType: headers.get("content-type"),
|
|
14
|
+
filename: (0, files_js_1.filenameFrom)(headers.get("content-disposition")),
|
|
15
|
+
width: integerOrNull(header("width")),
|
|
16
|
+
height: integerOrNull(header("height")),
|
|
17
|
+
sizeBytes: integerOrNull(header("size-bytes")),
|
|
18
|
+
durationMs: integerOrNull(header("duration-ms")),
|
|
19
|
+
pageHttpStatus: integerOrNull(header("page-http-status")),
|
|
20
|
+
specVersion: header("spec-version"),
|
|
21
|
+
capturedAt: (0, wire_js_1.parseDate)(header("captured-at")),
|
|
22
|
+
finalUrl: header("final-url"),
|
|
23
|
+
sha256: header("sha256"),
|
|
24
|
+
partCount: integerOrNull(header("part-count")),
|
|
25
|
+
body,
|
|
26
|
+
arrayBuffer: () => new Response(body).arrayBuffer(),
|
|
27
|
+
saveTo: (path) => (0, files_js_1.writeBody)(body, path),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function integerOrNull(value) {
|
|
31
|
+
if (value === null || !/^\s*-?\d+\s*$/.test(value))
|
|
32
|
+
return null;
|
|
33
|
+
return Number(value);
|
|
34
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { CaptureFormat, DeviceCategory, MediaCategory, PageSourceFormat, ScrollMode, WatermarkLocation } from "./tokens.js";
|
|
2
|
+
/** The browser window the page is rendered in. */
|
|
3
|
+
export interface ViewportOptions {
|
|
4
|
+
/** Width in CSS pixels, 250 to 7680. Default 1920. */
|
|
5
|
+
width?: number;
|
|
6
|
+
/** Height in CSS pixels, 250 to 7680. Default 1080. */
|
|
7
|
+
height?: number;
|
|
8
|
+
/** Device pixel ratio, 1 to 3. Default 1. */
|
|
9
|
+
deviceScaleFactor?: number;
|
|
10
|
+
}
|
|
11
|
+
/** The position the browser reports. Latitude and longitude are sent together or not at all. */
|
|
12
|
+
export interface GeolocationOptions {
|
|
13
|
+
/** -90 to 90. */
|
|
14
|
+
latitude?: number;
|
|
15
|
+
/** -180 to 180. */
|
|
16
|
+
longitude?: number;
|
|
17
|
+
/** In metres, 1 to 10000. Default 100. */
|
|
18
|
+
accuracy?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Elements to act on before the capture. */
|
|
21
|
+
export interface SelectorOptions {
|
|
22
|
+
/** Capture only the element this XPath selects. At most 512 characters. */
|
|
23
|
+
xpath?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Elements to hide: up to 25 comma-separated CSS selectors (an XPath starting with `//` works
|
|
26
|
+
* too), at most 2000 characters. Each is applied on its own; one the browser rejects is skipped.
|
|
27
|
+
*/
|
|
28
|
+
hide?: string;
|
|
29
|
+
/** CSS selector of an element to click. At most 512 characters. */
|
|
30
|
+
click?: string;
|
|
31
|
+
/** CSS selector of an element to hover. At most 512 characters. */
|
|
32
|
+
hover?: string;
|
|
33
|
+
}
|
|
34
|
+
/** Route the capture through a proxy. Requires the ProxyLocation plan feature. */
|
|
35
|
+
export interface ProxyOptions {
|
|
36
|
+
/** A country from the `proxyCountries` of `instantSnap.catalog()`. */
|
|
37
|
+
country: string;
|
|
38
|
+
}
|
|
39
|
+
/** A strip with the capture time and URL. Passing this turns it on. Not available for video. */
|
|
40
|
+
export interface TimestampWatermarkOptions {
|
|
41
|
+
/** Default `true`. */
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
/** Default `"top"`. */
|
|
44
|
+
location?: WatermarkLocation;
|
|
45
|
+
/** Hex colour. Default `"#000000"`. */
|
|
46
|
+
fontColor?: string;
|
|
47
|
+
/** Hex colour. Default `"#FFFFFF"`. */
|
|
48
|
+
backgroundColor?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Also keep the page source. Charged as a second capture, capped at 10 MB, not available with
|
|
52
|
+
* `captureFile`.
|
|
53
|
+
*/
|
|
54
|
+
export interface PageSourceOptions {
|
|
55
|
+
/** Default `"html"`. */
|
|
56
|
+
format?: PageSourceFormat;
|
|
57
|
+
}
|
|
58
|
+
/** The bucket `captureToBucket` uploads to. Needs `region` or `endpointUrl`. */
|
|
59
|
+
export interface S3DeliveryOptions {
|
|
60
|
+
accessKeyId: string;
|
|
61
|
+
/** Sent to the API but never returned. */
|
|
62
|
+
secretAccessKey: string;
|
|
63
|
+
bucket: string;
|
|
64
|
+
region?: string;
|
|
65
|
+
/** An https endpoint for S3-compatible storage. */
|
|
66
|
+
endpointUrl?: string;
|
|
67
|
+
/** Default `false`. */
|
|
68
|
+
forcePathStyle?: boolean;
|
|
69
|
+
keyPrefix?: string;
|
|
70
|
+
/** Wait up to 30 seconds for the upload before answering. Default `false`. */
|
|
71
|
+
waitForSync?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* What to capture and how. Only `url` is required; anything left out takes the API's default,
|
|
75
|
+
* which `instantSnap.defaults()` lists.
|
|
76
|
+
*/
|
|
77
|
+
export interface CaptureOptions {
|
|
78
|
+
/** The page to capture: absolute http or https, without credentials, at most 2048 characters. */
|
|
79
|
+
url: string;
|
|
80
|
+
/** Default `"image"`. */
|
|
81
|
+
mediaCategory?: MediaCategory;
|
|
82
|
+
/** Must belong to `mediaCategory`. */
|
|
83
|
+
format?: CaptureFormat;
|
|
84
|
+
viewport?: ViewportOptions;
|
|
85
|
+
/** Default `"desktop"`. */
|
|
86
|
+
deviceCategory?: DeviceCategory;
|
|
87
|
+
/** A device from `instantSnap.catalog()`, case-insensitive. Mobile defaults to "iPhone 13". */
|
|
88
|
+
emulatedDevice?: string;
|
|
89
|
+
/** Default `"viewport"`, or `"scroll"` for video. */
|
|
90
|
+
scrollMode?: ScrollMode;
|
|
91
|
+
/** Default `false`. */
|
|
92
|
+
autoHeight?: boolean;
|
|
93
|
+
/** 250 to 100000, and within the format's own ceiling. */
|
|
94
|
+
maxHeight?: number;
|
|
95
|
+
/** Percent, 50 to 100. Default 100. */
|
|
96
|
+
zoom?: number;
|
|
97
|
+
/** Default `true`. */
|
|
98
|
+
blockAds?: boolean;
|
|
99
|
+
/** Default `true`. */
|
|
100
|
+
blockPopUps?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Press Escape after the pop-up pass and again after scrolling (once in a video), for a modal
|
|
103
|
+
* that closes on Escape. Default `false`.
|
|
104
|
+
*/
|
|
105
|
+
pressEscape?: boolean;
|
|
106
|
+
/** 1000 to 60000. Default 30000. */
|
|
107
|
+
pageTimeoutMs?: number;
|
|
108
|
+
/** The pause after each scroll step of a full-page capture, 0 to 10000. Default 500. */
|
|
109
|
+
scrollingDelayMs?: number;
|
|
110
|
+
userAgent?: string;
|
|
111
|
+
/** A BCP 47 language tag. */
|
|
112
|
+
locale?: string;
|
|
113
|
+
/** An IANA time zone. */
|
|
114
|
+
timeZone?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Sent with every request the page makes, with names exactly as given. Requires the
|
|
117
|
+
* RequestHeadersCookies plan feature.
|
|
118
|
+
*/
|
|
119
|
+
headers?: Record<string, string>;
|
|
120
|
+
/**
|
|
121
|
+
* Set for the URL's origin, with names exactly as given. Requires the RequestHeadersCookies plan
|
|
122
|
+
* feature.
|
|
123
|
+
*/
|
|
124
|
+
cookies?: Record<string, string>;
|
|
125
|
+
geolocation?: GeolocationOptions;
|
|
126
|
+
selectors?: SelectorOptions;
|
|
127
|
+
proxy?: ProxyOptions;
|
|
128
|
+
timestampWatermark?: TimestampWatermarkOptions;
|
|
129
|
+
pageSource?: PageSourceOptions;
|
|
130
|
+
/** Also receives the `capture.succeeded` or `capture.failed` webhook. Requires the Webhooks plan feature. */
|
|
131
|
+
callbackUrl?: string;
|
|
132
|
+
/**
|
|
133
|
+
* Async only (`submit` and `submitToBucket`): the seconds the capture may take in the browser,
|
|
134
|
+
* 30 to 300; time in the queue does not count. Server default 120. The server refuses it on the
|
|
135
|
+
* other capture operations.
|
|
136
|
+
*/
|
|
137
|
+
timeout?: number;
|
|
138
|
+
}
|
|
139
|
+
/** Options for `instantSnap.captureFile()`, which cannot keep the page source. */
|
|
140
|
+
export type CaptureFileOptions = Omit<CaptureOptions, "pageSource">;
|
|
141
|
+
/** Options for `instantSnap.captureToBucket()`. */
|
|
142
|
+
export interface CaptureToBucketOptions extends CaptureOptions {
|
|
143
|
+
s3Delivery: S3DeliveryOptions;
|
|
144
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { ErrorCode } from "../error-codes.js";
|
|
2
|
+
import type { CaptureFormat, CaptureStatus, MediaCategory, PageSourceFormat, ResponseType, S3DeliveryStatus } from "./tokens.js";
|
|
3
|
+
/** The page source kept alongside a capture. */
|
|
4
|
+
export interface PageSource {
|
|
5
|
+
format: PageSourceFormat;
|
|
6
|
+
contentType: string | null;
|
|
7
|
+
sizeBytes: number | null;
|
|
8
|
+
/** Lowercase hex SHA-256 of the source. */
|
|
9
|
+
sha256: string | null;
|
|
10
|
+
/** Short-lived; reissued on every read. */
|
|
11
|
+
downloadUrl: string | null;
|
|
12
|
+
}
|
|
13
|
+
/** How the upload to your bucket went. Present for `captureToBucket` only. */
|
|
14
|
+
export interface S3Delivery {
|
|
15
|
+
status: S3DeliveryStatus;
|
|
16
|
+
bucket: string;
|
|
17
|
+
key: string | null;
|
|
18
|
+
/** Masked to its last four characters. */
|
|
19
|
+
accessKeyId: string | null;
|
|
20
|
+
error: string | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A capture, as `capture`, `captureToBucket`, `submit`, `submitToBucket`, `get` and `wait` return
|
|
24
|
+
* it.
|
|
25
|
+
*/
|
|
26
|
+
export interface Capture {
|
|
27
|
+
id: string;
|
|
28
|
+
/**
|
|
29
|
+
* `succeeded`, `failed` and the other end states; an async capture from `submit` or
|
|
30
|
+
* `submitToBucket` reads `queued` until a browser takes it and `running` while it is captured.
|
|
31
|
+
*/
|
|
32
|
+
status: CaptureStatus;
|
|
33
|
+
responseType: ResponseType;
|
|
34
|
+
mediaCategory: MediaCategory;
|
|
35
|
+
/** The format written. */
|
|
36
|
+
format: CaptureFormat | null;
|
|
37
|
+
contentType: string | null;
|
|
38
|
+
sizeBytes: number | null;
|
|
39
|
+
width: number | null;
|
|
40
|
+
height: number | null;
|
|
41
|
+
/** How long the capture took. */
|
|
42
|
+
durationMs: number | null;
|
|
43
|
+
/** The status the website answered with. */
|
|
44
|
+
pageHttpStatus: number | null;
|
|
45
|
+
capturedAt: Date | null;
|
|
46
|
+
/** Where the browser ended up, when the page redirected. */
|
|
47
|
+
finalUrl: string | null;
|
|
48
|
+
/** Lowercase hex SHA-256 of the file. */
|
|
49
|
+
mediaSha256: string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Valid for 60 minutes and reissued on every `get`. `null` for `captureToBucket` and for captures
|
|
52
|
+
* without a file.
|
|
53
|
+
*/
|
|
54
|
+
downloadUrl: string | null;
|
|
55
|
+
/** When the file is deleted, 24 hours after the capture. */
|
|
56
|
+
expiresAt: Date | null;
|
|
57
|
+
pageSource: PageSource | null;
|
|
58
|
+
s3Delivery: S3Delivery | null;
|
|
59
|
+
callbackUrl: string | null;
|
|
60
|
+
/** Why a `failed` or `timed_out` capture ended. */
|
|
61
|
+
errorCode: ErrorCode | null;
|
|
62
|
+
/** The default set the capture was taken under, such as `v1`. */
|
|
63
|
+
specVersion: string;
|
|
64
|
+
}
|
|
65
|
+
/** A capture as the API sends it. */
|
|
66
|
+
export interface WireCapture {
|
|
67
|
+
id: string;
|
|
68
|
+
status: string;
|
|
69
|
+
response_type: string;
|
|
70
|
+
media_category: string;
|
|
71
|
+
format?: string | null;
|
|
72
|
+
content_type?: string | null;
|
|
73
|
+
size_bytes?: number | null;
|
|
74
|
+
width?: number | null;
|
|
75
|
+
height?: number | null;
|
|
76
|
+
duration_ms?: number | null;
|
|
77
|
+
page_http_status?: number | null;
|
|
78
|
+
captured_at?: string | null;
|
|
79
|
+
final_url?: string | null;
|
|
80
|
+
media_sha256?: string | null;
|
|
81
|
+
download_url?: string | null;
|
|
82
|
+
expires_at?: string | null;
|
|
83
|
+
page_source?: WirePageSource | null;
|
|
84
|
+
s3_delivery?: WireS3Delivery | null;
|
|
85
|
+
callback_url?: string | null;
|
|
86
|
+
error_code?: string | null;
|
|
87
|
+
spec_version: string;
|
|
88
|
+
}
|
|
89
|
+
export interface WirePageSource {
|
|
90
|
+
format: string;
|
|
91
|
+
content_type?: string | null;
|
|
92
|
+
size_bytes?: number | null;
|
|
93
|
+
sha256?: string | null;
|
|
94
|
+
download_url?: string | null;
|
|
95
|
+
}
|
|
96
|
+
export interface WireS3Delivery {
|
|
97
|
+
status: string;
|
|
98
|
+
bucket: string;
|
|
99
|
+
key?: string | null;
|
|
100
|
+
access_key_id?: string | null;
|
|
101
|
+
error?: string | null;
|
|
102
|
+
}
|
|
103
|
+
/** The fields every form of a capture shares, webhooks included. */
|
|
104
|
+
export type CaptureFields = Omit<Capture, "downloadUrl" | "pageSource" | "s3Delivery">;
|
|
105
|
+
export declare function parseCaptureFields(wire: WireCapture): CaptureFields;
|
|
106
|
+
export declare function parseCapture(wire: WireCapture): Capture;
|
|
107
|
+
export declare function parsePageSourceFields(wire: WirePageSource): Omit<PageSource, "downloadUrl">;
|
|
108
|
+
export declare function parseS3DeliveryFields(wire: WireS3Delivery): Omit<S3Delivery, "accessKeyId">;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseCaptureFields = parseCaptureFields;
|
|
4
|
+
exports.parseCapture = parseCapture;
|
|
5
|
+
exports.parsePageSourceFields = parsePageSourceFields;
|
|
6
|
+
exports.parseS3DeliveryFields = parseS3DeliveryFields;
|
|
7
|
+
const wire_js_1 = require("../wire.js");
|
|
8
|
+
function parseCaptureFields(wire) {
|
|
9
|
+
return {
|
|
10
|
+
id: wire.id,
|
|
11
|
+
status: wire.status,
|
|
12
|
+
responseType: wire.response_type,
|
|
13
|
+
mediaCategory: wire.media_category,
|
|
14
|
+
format: wire.format ?? null,
|
|
15
|
+
contentType: wire.content_type ?? null,
|
|
16
|
+
sizeBytes: wire.size_bytes ?? null,
|
|
17
|
+
width: wire.width ?? null,
|
|
18
|
+
height: wire.height ?? null,
|
|
19
|
+
durationMs: wire.duration_ms ?? null,
|
|
20
|
+
pageHttpStatus: wire.page_http_status ?? null,
|
|
21
|
+
capturedAt: (0, wire_js_1.parseDate)(wire.captured_at),
|
|
22
|
+
finalUrl: wire.final_url ?? null,
|
|
23
|
+
mediaSha256: wire.media_sha256 ?? null,
|
|
24
|
+
expiresAt: (0, wire_js_1.parseDate)(wire.expires_at),
|
|
25
|
+
callbackUrl: wire.callback_url ?? null,
|
|
26
|
+
errorCode: wire.error_code ?? null,
|
|
27
|
+
specVersion: wire.spec_version,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function parseCapture(wire) {
|
|
31
|
+
return {
|
|
32
|
+
...parseCaptureFields(wire),
|
|
33
|
+
downloadUrl: wire.download_url ?? null,
|
|
34
|
+
pageSource: wire.page_source
|
|
35
|
+
? {
|
|
36
|
+
...parsePageSourceFields(wire.page_source),
|
|
37
|
+
downloadUrl: wire.page_source.download_url ?? null,
|
|
38
|
+
}
|
|
39
|
+
: null,
|
|
40
|
+
s3Delivery: wire.s3_delivery
|
|
41
|
+
? {
|
|
42
|
+
...parseS3DeliveryFields(wire.s3_delivery),
|
|
43
|
+
accessKeyId: wire.s3_delivery.access_key_id ?? null,
|
|
44
|
+
}
|
|
45
|
+
: null,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function parsePageSourceFields(wire) {
|
|
49
|
+
return {
|
|
50
|
+
format: wire.format,
|
|
51
|
+
contentType: wire.content_type ?? null,
|
|
52
|
+
sizeBytes: wire.size_bytes ?? null,
|
|
53
|
+
sha256: wire.sha256 ?? null,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseS3DeliveryFields(wire) {
|
|
57
|
+
return {
|
|
58
|
+
status: wire.status,
|
|
59
|
+
bucket: wire.bucket,
|
|
60
|
+
key: wire.key ?? null,
|
|
61
|
+
error: wire.error ?? null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** The viewport a capture uses when it does not set one. */
|
|
2
|
+
export interface DefaultViewport {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
deviceScaleFactor: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The values a capture takes for fields it does not send, and the limits it is checked against.
|
|
9
|
+
* Every field is optional because the set is versioned by `specVersion` and may change.
|
|
10
|
+
*/
|
|
11
|
+
export interface Defaults {
|
|
12
|
+
specVersion?: string;
|
|
13
|
+
responseType?: string;
|
|
14
|
+
mediaCategory?: string;
|
|
15
|
+
/** The default format of each media category. */
|
|
16
|
+
format?: Record<string, string>;
|
|
17
|
+
/** The formats each media category accepts. */
|
|
18
|
+
formats?: Record<string, string[]>;
|
|
19
|
+
deviceCategory?: string;
|
|
20
|
+
emulatedDevice?: string;
|
|
21
|
+
viewport?: DefaultViewport;
|
|
22
|
+
zoom?: number;
|
|
23
|
+
scrollMode?: string;
|
|
24
|
+
autoHeight?: boolean;
|
|
25
|
+
autoHeightMaxHeight?: number | null;
|
|
26
|
+
autoHeightMaxHeightWebp?: number;
|
|
27
|
+
blockAds?: boolean;
|
|
28
|
+
blockPopUps?: boolean;
|
|
29
|
+
pressEscape?: boolean;
|
|
30
|
+
pageTimeoutMs?: number;
|
|
31
|
+
scrollingDelayMs?: number;
|
|
32
|
+
geolocation?: boolean;
|
|
33
|
+
geolocationAccuracy?: number;
|
|
34
|
+
waitForCloudSync?: boolean;
|
|
35
|
+
timestampWatermark?: Record<string, unknown>;
|
|
36
|
+
pageSource?: Record<string, unknown>;
|
|
37
|
+
limits?: Record<string, unknown>;
|
|
38
|
+
/** Settings the API leaves to the browser rather than fixing a value. */
|
|
39
|
+
leftToTheBrowser?: string[];
|
|
40
|
+
}
|
|
41
|
+
/** What `emulatedDevice` and `proxy.country` accept. */
|
|
42
|
+
export interface Catalog {
|
|
43
|
+
emulatedDevices: string[];
|
|
44
|
+
proxyCountries: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface WireDefaults {
|
|
47
|
+
spec_version?: string;
|
|
48
|
+
response_type?: string;
|
|
49
|
+
media_category?: string;
|
|
50
|
+
format?: Record<string, string>;
|
|
51
|
+
formats?: Record<string, string[]>;
|
|
52
|
+
device_category?: string;
|
|
53
|
+
emulated_device?: string;
|
|
54
|
+
viewport?: {
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
device_scale_factor: number;
|
|
58
|
+
};
|
|
59
|
+
zoom?: number;
|
|
60
|
+
scroll_mode?: string;
|
|
61
|
+
auto_height?: boolean;
|
|
62
|
+
auto_height_max_height?: number | null;
|
|
63
|
+
auto_height_max_height_webp?: number;
|
|
64
|
+
block_ads?: boolean;
|
|
65
|
+
block_pop_ups?: boolean;
|
|
66
|
+
press_escape?: boolean;
|
|
67
|
+
page_timeout_ms?: number;
|
|
68
|
+
scrolling_delay_ms?: number;
|
|
69
|
+
geolocation?: boolean;
|
|
70
|
+
geolocation_accuracy?: number;
|
|
71
|
+
wait_for_cloud_sync?: boolean;
|
|
72
|
+
timestamp_watermark?: Record<string, unknown>;
|
|
73
|
+
page_source?: Record<string, unknown>;
|
|
74
|
+
limits?: Record<string, unknown>;
|
|
75
|
+
left_to_the_browser?: string[];
|
|
76
|
+
}
|
|
77
|
+
export interface WireCatalog {
|
|
78
|
+
emulated_devices: string[];
|
|
79
|
+
proxy_countries: string[];
|
|
80
|
+
}
|
|
81
|
+
export declare function parseDefaults(wire: WireDefaults): Defaults;
|
|
82
|
+
export declare function parseCatalog(wire: WireCatalog): Catalog;
|