@ironfang/render 3.0.1 → 3.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/README.md +32 -0
- package/dist/client.d.ts +61 -2
- package/dist/client.js +95 -2
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +229 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,16 @@ the HMAC key, while a Render secret is the hex of the 32-byte key deliveries
|
|
|
18
18
|
are signed with, so it rejected every genuine delivery. Upgrade if you
|
|
19
19
|
verify Render webhooks with it.
|
|
20
20
|
|
|
21
|
+
3.0.2 types a template's `vars` as `Record<string, string>` in `image()` and
|
|
22
|
+
`sign()`, as the API has always required: a number, boolean or object passed
|
|
23
|
+
the type check and then came back as a 400. Code that passed one no longer
|
|
24
|
+
compiles; pass it as text (`String(n)`).
|
|
25
|
+
|
|
26
|
+
3.1.0 adds capture bundles (`bundle()`), stored document templates
|
|
27
|
+
(`documentTemplates`), and the request fields for lazy content, success
|
|
28
|
+
rules, PDF controls, language and time zone, with each render's `diagnostics`.
|
|
29
|
+
Additions only; see the [changelog](CHANGELOG.md).
|
|
30
|
+
|
|
21
31
|
```sh
|
|
22
32
|
npm install @ironfang/render
|
|
23
33
|
```
|
|
@@ -65,6 +75,28 @@ to 1,024 UTF-8 bytes and share the render timeout. Invalid input returns
|
|
|
65
75
|
messages. A screenshot cache hit does not re-execute controls; use `no_cache`
|
|
66
76
|
for fresh execution. [Full reference](https://ironfang.com/render/docs#page-controls).
|
|
67
77
|
|
|
78
|
+
## Capture bundles
|
|
79
|
+
|
|
80
|
+
One navigation, the outputs you choose: a screenshot, the rendered HTML, the
|
|
81
|
+
page's text, Markdown and its metadata. HTML, text and metadata are read
|
|
82
|
+
together after the page's readiness and actions; the screenshot follows.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const b = await client.bundle({
|
|
86
|
+
url: 'https://example.com/blog/post',
|
|
87
|
+
outputs: ['screenshot', 'markdown', 'metadata'],
|
|
88
|
+
});
|
|
89
|
+
console.log(b.manifest.metadata?.title, b.markdown);
|
|
90
|
+
for (const o of b.manifest.outputs) {
|
|
91
|
+
if (o.status !== 'included') console.warn(o.name, o.status, o.reason);
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
An output over its bound is `truncated` or `omitted` in the manifest, never
|
|
96
|
+
dropped silently. Iframe content is not in the text outputs. A bundle counts one
|
|
97
|
+
`render.screenshot`. Captured HTML is the page's own: never insert it into a
|
|
98
|
+
page of yours.
|
|
99
|
+
|
|
68
100
|
## Durable jobs
|
|
69
101
|
|
|
70
102
|
```ts
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ClipRequest, Destination, DestinationDraft, ImageRequest, Job, JobSubmission, PdfRequest, QrRequest, RequestFilter, RequestRecord, ScreenshotRequest, SignRequest, SignedUrl, SitePreviewRequest, UnmeteredUsage, Usage } from './types.js';
|
|
2
|
-
export declare const VERSION = "3.0
|
|
1
|
+
import type { ClipRequest, Destination, DestinationDraft, DocumentRenderRequest, DocumentRevision, DocumentTemplate, DocumentTemplateInput, DocumentValidation, ImageRequest, Job, JobSubmission, PdfRequest, QrRequest, RequestFilter, RequestRecord, BundleManifest, BundleRequest, ScreenshotRequest, SignRequest, SignedUrl, SitePreviewRequest, UnmeteredUsage, Usage } from './types.js';
|
|
2
|
+
export declare const VERSION = "3.1.0";
|
|
3
3
|
export interface ClientOptions {
|
|
4
4
|
/** Your `rw_live_...` key. Never ship this to a browser. */
|
|
5
5
|
apiKey: string;
|
|
@@ -41,6 +41,24 @@ export interface RenderResult {
|
|
|
41
41
|
/** Write the bytes to a file. Node only; throws elsewhere. */
|
|
42
42
|
saveTo(path: string): Promise<void>;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* A capture bundle. The text outputs are the page's own content: untrusted,
|
|
46
|
+
* never to be inserted into a page as HTML.
|
|
47
|
+
*/
|
|
48
|
+
export interface BundleResult {
|
|
49
|
+
manifest: BundleManifest;
|
|
50
|
+
/** Each output is undefined when the bundle does not include it. */
|
|
51
|
+
screenshot: Uint8Array | undefined;
|
|
52
|
+
renderedHtml: string | undefined;
|
|
53
|
+
text: string | undefined;
|
|
54
|
+
markdown: string | undefined;
|
|
55
|
+
/** `render.screenshot`. */
|
|
56
|
+
meter: string | undefined;
|
|
57
|
+
quantity: number;
|
|
58
|
+
renderMs: number;
|
|
59
|
+
requestId: string;
|
|
60
|
+
headers: Headers;
|
|
61
|
+
}
|
|
44
62
|
/** A site preview: the poster frame and the scrolling video. */
|
|
45
63
|
export interface SitePreviewResult {
|
|
46
64
|
poster: Uint8Array;
|
|
@@ -73,8 +91,49 @@ export declare class IronfangRender {
|
|
|
73
91
|
/** Render one of your stored templates with variables filled in. */
|
|
74
92
|
image(templateId: string, req?: ImageRequest, opts?: RequestOptions): Promise<RenderResult>;
|
|
75
93
|
clip(req: ClipRequest, opts?: RequestOptions): Promise<RenderResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Capture a bundle: the outputs you select - screenshot, rendered HTML,
|
|
96
|
+
* text, Markdown, metadata - from one navigation of the page.
|
|
97
|
+
*/
|
|
98
|
+
bundle(req: BundleRequest, opts?: RequestOptions): Promise<BundleResult>;
|
|
76
99
|
sitePreview(req: SitePreviewRequest, opts?: RequestOptions): Promise<SitePreviewResult>;
|
|
77
100
|
private render;
|
|
101
|
+
/**
|
|
102
|
+
* Stored PDF templates with structured JSON data: a draft, published as
|
|
103
|
+
* immutable numbered revisions. Render a published revision with `render`,
|
|
104
|
+
* or as a job of kind `document`.
|
|
105
|
+
*/
|
|
106
|
+
readonly documentTemplates: {
|
|
107
|
+
create: (tpl: DocumentTemplateInput, opts?: RequestOptions) => Promise<DocumentTemplate>;
|
|
108
|
+
list: (opts?: RequestOptions & {
|
|
109
|
+
archived?: boolean;
|
|
110
|
+
}) => Promise<{
|
|
111
|
+
templates: DocumentTemplate[];
|
|
112
|
+
}>;
|
|
113
|
+
get: (id: string, opts?: RequestOptions) => Promise<DocumentTemplate>;
|
|
114
|
+
/** Replace the draft. What is published changes only on `publish`. */
|
|
115
|
+
update: (id: string, tpl: DocumentTemplateInput, opts?: RequestOptions) => Promise<DocumentTemplate>;
|
|
116
|
+
/** Archive: no more edits or unversioned renders; revisions and accepted jobs are kept. */
|
|
117
|
+
archive: (id: string, opts?: RequestOptions) => Promise<void>;
|
|
118
|
+
/** Check the draft against data (or its sample data) without printing or billing. */
|
|
119
|
+
validate: (id: string, data?: Record<string, unknown>, opts?: RequestOptions) => Promise<DocumentValidation>;
|
|
120
|
+
/** Print the draft with data (or its sample data). A PDF render; throws on any failure. */
|
|
121
|
+
preview: (id: string, data?: Record<string, unknown>, opts?: RequestOptions) => Promise<RenderResult>;
|
|
122
|
+
/** Publish the draft as a new revision (or the latest again, if unchanged). */
|
|
123
|
+
publish: (id: string, opts?: RequestOptions) => Promise<DocumentRevision>;
|
|
124
|
+
/** Publish an existing revision again: a rollback. */
|
|
125
|
+
publishRevision: (id: string, revision: number, opts?: RequestOptions) => Promise<DocumentRevision>;
|
|
126
|
+
revisions: (id: string, opts?: RequestOptions) => Promise<{
|
|
127
|
+
revisions: DocumentRevision[];
|
|
128
|
+
}>;
|
|
129
|
+
revision: (id: string, revision: number, opts?: RequestOptions) => Promise<DocumentRevision>;
|
|
130
|
+
/**
|
|
131
|
+
* Print a revision - the published one unless `req.revision` names
|
|
132
|
+
* another - with `req.data`. Data problems throw `data_invalid` before
|
|
133
|
+
* anything is charged; the error's `fields` names each path.
|
|
134
|
+
*/
|
|
135
|
+
render: (id: string, req: DocumentRenderRequest, opts?: RequestOptions) => Promise<RenderResult>;
|
|
136
|
+
};
|
|
78
137
|
readonly jobs: {
|
|
79
138
|
/**
|
|
80
139
|
* Submit a durable job. Pass `idempotencyKey` to make a retry of the same
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BILLING_CODES, IronfangBillingError, IronfangRenderError } from './errors.js';
|
|
2
|
-
export const VERSION = '3.0
|
|
2
|
+
export const VERSION = '3.1.0';
|
|
3
3
|
export class IronfangRender {
|
|
4
4
|
apiKey;
|
|
5
5
|
baseUrl;
|
|
@@ -31,6 +31,36 @@ export class IronfangRender {
|
|
|
31
31
|
clip(req, opts = {}) {
|
|
32
32
|
return this.render('/v1/video', req, opts);
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Capture a bundle: the outputs you select - screenshot, rendered HTML,
|
|
36
|
+
* text, Markdown, metadata - from one navigation of the page.
|
|
37
|
+
*/
|
|
38
|
+
async bundle(req, opts = {}) {
|
|
39
|
+
const resp = await this.send('POST', '/v1/bundle', req, opts, idempotency(opts));
|
|
40
|
+
const form = await resp.formData();
|
|
41
|
+
const manifest = form.get('manifest');
|
|
42
|
+
if (manifest === null) {
|
|
43
|
+
throw new IronfangRenderError('the response did not include a manifest', {
|
|
44
|
+
status: resp.status, code: 'bad_response', requestId: requestIdOf(resp),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const text = async (name) => {
|
|
48
|
+
const part = form.get(name);
|
|
49
|
+
return part instanceof Blob ? await part.text() : undefined;
|
|
50
|
+
};
|
|
51
|
+
const shot = form.get('screenshot');
|
|
52
|
+
return {
|
|
53
|
+
manifest: JSON.parse(typeof manifest === 'string' ? manifest : await manifest.text()),
|
|
54
|
+
screenshot: shot instanceof Blob ? new Uint8Array(await shot.arrayBuffer()) : undefined,
|
|
55
|
+
renderedHtml: await text('rendered_html'),
|
|
56
|
+
text: await text('text'),
|
|
57
|
+
markdown: await text('markdown'),
|
|
58
|
+
...usageOf(resp),
|
|
59
|
+
renderMs: num(resp.headers.get('X-Ironfang-Render-Ms')),
|
|
60
|
+
requestId: requestIdOf(resp),
|
|
61
|
+
headers: resp.headers,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
34
64
|
async sitePreview(req, opts = {}) {
|
|
35
65
|
const resp = await this.send('POST', '/v1/site-preview', req, opts, idempotency(opts));
|
|
36
66
|
const form = await resp.formData();
|
|
@@ -66,6 +96,66 @@ export class IronfangRender {
|
|
|
66
96
|
saveTo: (target) => saveBytes(target, bytes),
|
|
67
97
|
};
|
|
68
98
|
}
|
|
99
|
+
/* -------------------------- document templates ------------------------ */
|
|
100
|
+
/**
|
|
101
|
+
* Stored PDF templates with structured JSON data: a draft, published as
|
|
102
|
+
* immutable numbered revisions. Render a published revision with `render`,
|
|
103
|
+
* or as a job of kind `document`.
|
|
104
|
+
*/
|
|
105
|
+
documentTemplates = {
|
|
106
|
+
create: async (tpl, opts = {}) => {
|
|
107
|
+
const resp = await this.send('POST', '/v1/document-templates', tpl, opts);
|
|
108
|
+
return (await resp.json());
|
|
109
|
+
},
|
|
110
|
+
list: async (opts = {}) => {
|
|
111
|
+
const resp = await this.send('GET', `/v1/document-templates${opts.archived ? '?archived=true' : ''}`, undefined, opts);
|
|
112
|
+
return (await resp.json());
|
|
113
|
+
},
|
|
114
|
+
get: async (id, opts = {}) => {
|
|
115
|
+
const resp = await this.send('GET', `/v1/document-templates/${encodeURIComponent(id)}`, undefined, opts);
|
|
116
|
+
return (await resp.json());
|
|
117
|
+
},
|
|
118
|
+
/** Replace the draft. What is published changes only on `publish`. */
|
|
119
|
+
update: async (id, tpl, opts = {}) => {
|
|
120
|
+
const resp = await this.send('PUT', `/v1/document-templates/${encodeURIComponent(id)}`, tpl, opts);
|
|
121
|
+
return (await resp.json());
|
|
122
|
+
},
|
|
123
|
+
/** Archive: no more edits or unversioned renders; revisions and accepted jobs are kept. */
|
|
124
|
+
archive: async (id, opts = {}) => {
|
|
125
|
+
await this.send('DELETE', `/v1/document-templates/${encodeURIComponent(id)}`, undefined, opts);
|
|
126
|
+
},
|
|
127
|
+
/** Check the draft against data (or its sample data) without printing or billing. */
|
|
128
|
+
validate: async (id, data, opts = {}) => {
|
|
129
|
+
const resp = await this.send('POST', `/v1/document-templates/${encodeURIComponent(id)}/validate`, data ? { data } : {}, opts);
|
|
130
|
+
return (await resp.json());
|
|
131
|
+
},
|
|
132
|
+
/** Print the draft with data (or its sample data). A PDF render; throws on any failure. */
|
|
133
|
+
preview: (id, data, opts = {}) => this.render(`/v1/document-templates/${encodeURIComponent(id)}/preview`, data ? { data } : {}, opts),
|
|
134
|
+
/** Publish the draft as a new revision (or the latest again, if unchanged). */
|
|
135
|
+
publish: async (id, opts = {}) => {
|
|
136
|
+
const resp = await this.send('POST', `/v1/document-templates/${encodeURIComponent(id)}/publish`, undefined, opts);
|
|
137
|
+
return (await resp.json());
|
|
138
|
+
},
|
|
139
|
+
/** Publish an existing revision again: a rollback. */
|
|
140
|
+
publishRevision: async (id, revision, opts = {}) => {
|
|
141
|
+
const resp = await this.send('POST', `/v1/document-templates/${encodeURIComponent(id)}/revisions/${revision}/publish`, undefined, opts);
|
|
142
|
+
return (await resp.json());
|
|
143
|
+
},
|
|
144
|
+
revisions: async (id, opts = {}) => {
|
|
145
|
+
const resp = await this.send('GET', `/v1/document-templates/${encodeURIComponent(id)}/revisions`, undefined, opts);
|
|
146
|
+
return (await resp.json());
|
|
147
|
+
},
|
|
148
|
+
revision: async (id, revision, opts = {}) => {
|
|
149
|
+
const resp = await this.send('GET', `/v1/document-templates/${encodeURIComponent(id)}/revisions/${revision}`, undefined, opts);
|
|
150
|
+
return (await resp.json());
|
|
151
|
+
},
|
|
152
|
+
/**
|
|
153
|
+
* Print a revision - the published one unless `req.revision` names
|
|
154
|
+
* another - with `req.data`. Data problems throw `data_invalid` before
|
|
155
|
+
* anything is charged; the error's `fields` names each path.
|
|
156
|
+
*/
|
|
157
|
+
render: (id, req, opts = {}) => this.render(`/v1/document-templates/${encodeURIComponent(id)}/render`, req, opts),
|
|
158
|
+
};
|
|
69
159
|
/* -------------------------------- jobs -------------------------------- */
|
|
70
160
|
jobs = {
|
|
71
161
|
/**
|
|
@@ -216,6 +306,7 @@ async function errorFrom(resp) {
|
|
|
216
306
|
let code = `http_${resp.status}`;
|
|
217
307
|
let message = `request failed with HTTP ${resp.status}`;
|
|
218
308
|
let detail = {};
|
|
309
|
+
let fields;
|
|
219
310
|
try {
|
|
220
311
|
const body = (await resp.json());
|
|
221
312
|
if (body?.error?.code)
|
|
@@ -224,11 +315,13 @@ async function errorFrom(resp) {
|
|
|
224
315
|
message = body.error.message;
|
|
225
316
|
if (body?.error)
|
|
226
317
|
detail = body.error;
|
|
318
|
+
if (Array.isArray(body?.fields))
|
|
319
|
+
fields = body.fields;
|
|
227
320
|
}
|
|
228
321
|
catch {
|
|
229
322
|
// Not JSON; the status is all there is.
|
|
230
323
|
}
|
|
231
|
-
const opts = { status: resp.status, code, requestId: requestIdOf(resp), retryAfter: retryAfterOf(resp) };
|
|
324
|
+
const opts = { status: resp.status, code, requestId: requestIdOf(resp), retryAfter: retryAfterOf(resp), fields };
|
|
232
325
|
if (BILLING_CODES.includes(code)) {
|
|
233
326
|
return new IronfangBillingError(message, {
|
|
234
327
|
...opts, product: text(detail.product), meter: text(detail.meter), resetAt: text(detail.reset_at),
|
package/dist/errors.d.ts
CHANGED
|
@@ -12,11 +12,25 @@ export declare class IronfangRenderError extends Error {
|
|
|
12
12
|
readonly code: string;
|
|
13
13
|
readonly requestId: string | undefined;
|
|
14
14
|
readonly retryAfter: number | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Per-field problems, when the request failed on particular fields: for
|
|
17
|
+
* `data_invalid`, each path in a document template's data and what is wrong.
|
|
18
|
+
*/
|
|
19
|
+
readonly fields: {
|
|
20
|
+
field: string;
|
|
21
|
+
code: string;
|
|
22
|
+
message: string;
|
|
23
|
+
}[] | undefined;
|
|
15
24
|
constructor(message: string, opts: {
|
|
16
25
|
status: number;
|
|
17
26
|
code: string;
|
|
18
27
|
requestId?: string | undefined;
|
|
19
28
|
retryAfter?: number | undefined;
|
|
29
|
+
fields?: {
|
|
30
|
+
field: string;
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
}[] | undefined;
|
|
20
34
|
});
|
|
21
35
|
/** The request was refused before any work was done. */
|
|
22
36
|
get isClientError(): boolean;
|
package/dist/errors.js
CHANGED
|
@@ -12,6 +12,11 @@ export class IronfangRenderError extends Error {
|
|
|
12
12
|
code;
|
|
13
13
|
requestId;
|
|
14
14
|
retryAfter;
|
|
15
|
+
/**
|
|
16
|
+
* Per-field problems, when the request failed on particular fields: for
|
|
17
|
+
* `data_invalid`, each path in a document template's data and what is wrong.
|
|
18
|
+
*/
|
|
19
|
+
fields;
|
|
15
20
|
constructor(message, opts) {
|
|
16
21
|
super(message);
|
|
17
22
|
this.name = 'IronfangRenderError';
|
|
@@ -19,6 +24,7 @@ export class IronfangRenderError extends Error {
|
|
|
19
24
|
this.code = opts.code;
|
|
20
25
|
this.requestId = opts.requestId;
|
|
21
26
|
this.retryAfter = opts.retryAfter;
|
|
27
|
+
this.fields = opts.fields;
|
|
22
28
|
}
|
|
23
29
|
/** The request was refused before any work was done. */
|
|
24
30
|
get isClientError() {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { IronfangRender, VERSION } from './client.js';
|
|
2
|
-
export type { ClientOptions, RequestOptions, RenderResult, SitePreviewResult, WaitOptions } from './client.js';
|
|
2
|
+
export type { ClientOptions, RequestOptions, BundleResult, RenderResult, SitePreviewResult, WaitOptions } from './client.js';
|
|
3
3
|
export { IronfangBillingError, IronfangRenderError } from './errors.js';
|
|
4
4
|
export { verifyWebhook, signWebhook } from './webhook.js';
|
|
5
5
|
export type { WebhookInput } from './webhook.js';
|
package/dist/types.d.ts
CHANGED
|
@@ -31,6 +31,31 @@ export interface RenderCommon {
|
|
|
31
31
|
wait_for_selector?: string;
|
|
32
32
|
timeout_ms?: number;
|
|
33
33
|
device_scale_factor?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The browser's language, a BCP 47 tag such as `de-DE`: Intl formatting,
|
|
36
|
+
* `navigator.language(s)` and `Accept-Language`. An `Accept-Language` in
|
|
37
|
+
* `headers` is sent exactly as given instead. Not an IP address in that country.
|
|
38
|
+
*/
|
|
39
|
+
locale?: string;
|
|
40
|
+
/** The browser's clock, an IANA zone such as `Europe/Berlin`. Default UTC. */
|
|
41
|
+
timezone?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Opt-in rules for output that should count as a failure: a render one fails
|
|
45
|
+
* is a 422 `success_rule_failed` naming the rule (`error.rule`), and is not
|
|
46
|
+
* charged. Without rules a render behaves as before.
|
|
47
|
+
*/
|
|
48
|
+
export interface SuccessRules {
|
|
49
|
+
/** Target statuses that fail the render: `404`, `'5xx'` or `'400-499'`. Needs `url`. */
|
|
50
|
+
fail_on_status?: (number | string)[];
|
|
51
|
+
/** A CSS selector that must match an element in the ready page. Does not wait. */
|
|
52
|
+
require_selector?: string;
|
|
53
|
+
/** Text that must be displayed on the page: literal, any case, whitespace collapsed. */
|
|
54
|
+
require_text?: string[];
|
|
55
|
+
/** Text that must not be displayed on the page, matched as `require_text`. */
|
|
56
|
+
forbid_text?: string[];
|
|
57
|
+
/** Screenshots only: fail a capture that is a single flat colour. */
|
|
58
|
+
fail_on_blank?: boolean;
|
|
34
59
|
}
|
|
35
60
|
export interface ScreenshotRequest extends RenderCommon {
|
|
36
61
|
url?: string;
|
|
@@ -39,6 +64,12 @@ export interface ScreenshotRequest extends RenderCommon {
|
|
|
39
64
|
height?: number;
|
|
40
65
|
full_page?: boolean;
|
|
41
66
|
full_page_max_height?: number;
|
|
67
|
+
/**
|
|
68
|
+
* `scroll` walks a `full_page` capture down first, so lazy images and
|
|
69
|
+
* IntersectionObserver content are in it. Bounded (at most 10 s); omitted,
|
|
70
|
+
* the capture is taken as before. Requires `full_page`.
|
|
71
|
+
*/
|
|
72
|
+
full_page_readiness?: 'scroll';
|
|
42
73
|
selector?: string;
|
|
43
74
|
clip?: {
|
|
44
75
|
x: number;
|
|
@@ -52,6 +83,7 @@ export interface ScreenshotRequest extends RenderCommon {
|
|
|
52
83
|
quality?: number;
|
|
53
84
|
device?: 'desktop' | 'tablet' | 'mobile';
|
|
54
85
|
delay_ms?: number;
|
|
86
|
+
success_rules?: SuccessRules;
|
|
55
87
|
}
|
|
56
88
|
export interface PdfRequest extends RenderCommon {
|
|
57
89
|
url?: string;
|
|
@@ -68,6 +100,22 @@ export interface PdfRequest extends RenderCommon {
|
|
|
68
100
|
header_html?: string;
|
|
69
101
|
footer_html?: string;
|
|
70
102
|
scale?: number;
|
|
103
|
+
/** A custom size with units, such as `210mm`; both or neither, never with `paper_format`. */
|
|
104
|
+
paper_width?: string;
|
|
105
|
+
paper_height?: string;
|
|
106
|
+
/** Let the page's own `@page { size }` decide the page size. */
|
|
107
|
+
prefer_css_page_size?: boolean;
|
|
108
|
+
/** Pages to keep, such as `1-3, 5, 8-`; the whole document is still bounded. */
|
|
109
|
+
page_ranges?: string;
|
|
110
|
+
/** The PDF's document properties. */
|
|
111
|
+
metadata?: {
|
|
112
|
+
title?: string;
|
|
113
|
+
author?: string;
|
|
114
|
+
subject?: string;
|
|
115
|
+
keywords?: string;
|
|
116
|
+
};
|
|
117
|
+
/** As on a screenshot, less `fail_on_blank`. */
|
|
118
|
+
success_rules?: Omit<SuccessRules, 'fail_on_blank'>;
|
|
71
119
|
}
|
|
72
120
|
export interface QrRequest {
|
|
73
121
|
data: string;
|
|
@@ -85,7 +133,9 @@ export interface QrRequest {
|
|
|
85
133
|
logo_pad?: boolean;
|
|
86
134
|
}
|
|
87
135
|
export interface ImageRequest {
|
|
88
|
-
|
|
136
|
+
/** The template's placeholder values. Text only: the API refuses a number,
|
|
137
|
+
* boolean or object with 400, so pass `String(n)` for a number. */
|
|
138
|
+
vars?: Record<string, string>;
|
|
89
139
|
format?: 'png' | 'jpeg' | 'webp';
|
|
90
140
|
}
|
|
91
141
|
export interface ClipRequest {
|
|
@@ -102,6 +152,57 @@ export interface ClipRequest {
|
|
|
102
152
|
end?: number;
|
|
103
153
|
}[];
|
|
104
154
|
}
|
|
155
|
+
/** The outputs a capture bundle can include. */
|
|
156
|
+
export type BundleOutputName = 'screenshot' | 'rendered_html' | 'text' | 'markdown' | 'metadata';
|
|
157
|
+
/**
|
|
158
|
+
* A capture bundle: every screenshot option, and the outputs wanted from the
|
|
159
|
+
* one navigation. `selector`, `clip`, `format`, `quality`, `omit_background`
|
|
160
|
+
* and `fail_on_blank` need `screenshot` among the outputs.
|
|
161
|
+
*/
|
|
162
|
+
export interface BundleRequest extends ScreenshotRequest {
|
|
163
|
+
outputs: BundleOutputName[];
|
|
164
|
+
}
|
|
165
|
+
/** One output's line in a bundle's manifest. */
|
|
166
|
+
export interface BundleOutput {
|
|
167
|
+
name: BundleOutputName;
|
|
168
|
+
/** `truncated` is the start of it, to its limit; `omitted` is none of it. */
|
|
169
|
+
status: 'included' | 'truncated' | 'omitted';
|
|
170
|
+
reason?: 'too_large' | 'source_too_large' | 'unparseable';
|
|
171
|
+
part?: string;
|
|
172
|
+
filename?: string;
|
|
173
|
+
content_type?: string;
|
|
174
|
+
bytes?: number;
|
|
175
|
+
sha256?: string;
|
|
176
|
+
source_bytes?: number;
|
|
177
|
+
limit_bytes?: number;
|
|
178
|
+
}
|
|
179
|
+
/** A bundle's manifest: what it holds and how it was captured. */
|
|
180
|
+
export interface BundleManifest {
|
|
181
|
+
version: 1;
|
|
182
|
+
/** The main document's final response; null for `html` input. */
|
|
183
|
+
target: {
|
|
184
|
+
status: number;
|
|
185
|
+
url: string;
|
|
186
|
+
redirects: number;
|
|
187
|
+
} | null;
|
|
188
|
+
capture: {
|
|
189
|
+
started_at: string;
|
|
190
|
+
page_read_at: string;
|
|
191
|
+
screenshot_at?: string;
|
|
192
|
+
finished_at: string;
|
|
193
|
+
};
|
|
194
|
+
outputs: BundleOutput[];
|
|
195
|
+
metadata?: {
|
|
196
|
+
title: string;
|
|
197
|
+
description?: string;
|
|
198
|
+
lang?: string;
|
|
199
|
+
canonical_url?: string;
|
|
200
|
+
robots?: string;
|
|
201
|
+
open_graph?: Record<string, string>;
|
|
202
|
+
};
|
|
203
|
+
/** Frames whose content is not in the HTML, text or Markdown. */
|
|
204
|
+
child_frames: number;
|
|
205
|
+
}
|
|
105
206
|
export interface SitePreviewRequest extends RenderCommon {
|
|
106
207
|
url: string;
|
|
107
208
|
width?: number;
|
|
@@ -111,7 +212,7 @@ export interface SitePreviewRequest extends RenderCommon {
|
|
|
111
212
|
device?: 'desktop' | 'tablet' | 'mobile';
|
|
112
213
|
delay_ms?: number;
|
|
113
214
|
}
|
|
114
|
-
export type JobKind = 'screenshot' | 'pdf' | 'qr' | 'image' | 'clip' | 'site_preview';
|
|
215
|
+
export type JobKind = 'screenshot' | 'pdf' | 'qr' | 'image' | 'clip' | 'site_preview' | 'document' | 'bundle';
|
|
115
216
|
export type JobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
|
|
116
217
|
export interface JobDelivery {
|
|
117
218
|
webhook_destination?: string;
|
|
@@ -135,6 +236,43 @@ export interface JobSubmission {
|
|
|
135
236
|
external_id?: string;
|
|
136
237
|
delivery?: JobDelivery;
|
|
137
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* What a render learned about the page: on a job, its webhook and a request
|
|
241
|
+
* history row. A synchronous render gives the same as `X-Ironfang-*` headers.
|
|
242
|
+
*/
|
|
243
|
+
export interface RenderDiagnostics {
|
|
244
|
+
settle?: 'stable' | 'ceiling' | 'fixed' | 'skipped';
|
|
245
|
+
settle_ms?: number;
|
|
246
|
+
/** The page's own final HTTP status after redirects; absent for raw HTML. */
|
|
247
|
+
target_status?: number;
|
|
248
|
+
/** The final address, without credentials, query or fragment. */
|
|
249
|
+
target_url?: string;
|
|
250
|
+
target_redirects?: number;
|
|
251
|
+
navigate_ms?: number;
|
|
252
|
+
capture_ms?: number;
|
|
253
|
+
viewport?: {
|
|
254
|
+
width: number;
|
|
255
|
+
height: number;
|
|
256
|
+
scale: number;
|
|
257
|
+
mobile: boolean;
|
|
258
|
+
};
|
|
259
|
+
/** What a `full_page_readiness` pass did. */
|
|
260
|
+
readiness?: {
|
|
261
|
+
profile: 'scroll';
|
|
262
|
+
steps: number;
|
|
263
|
+
ms: number;
|
|
264
|
+
images_loading?: number;
|
|
265
|
+
images_broken?: number;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* Output complete as a render but not the whole page: `height_capped`,
|
|
269
|
+
* `scroll_budget_exhausted`, `images_incomplete`, `images_broken`; and
|
|
270
|
+
* `bot_challenge` when the page is recognisably a challenge interstitial.
|
|
271
|
+
*/
|
|
272
|
+
warnings?: string[];
|
|
273
|
+
/** The `success_rules` field that failed the render. */
|
|
274
|
+
failed_rule?: 'fail_on_status' | 'require_selector' | 'require_text' | 'forbid_text' | 'fail_on_blank';
|
|
275
|
+
}
|
|
138
276
|
export interface Job {
|
|
139
277
|
id: string;
|
|
140
278
|
kind: JobKind;
|
|
@@ -154,6 +292,8 @@ export interface Job {
|
|
|
154
292
|
code: string;
|
|
155
293
|
message: string;
|
|
156
294
|
};
|
|
295
|
+
/** Once an attempt has loaded the page, failed attempts included. */
|
|
296
|
+
diagnostics?: RenderDiagnostics;
|
|
157
297
|
result?: {
|
|
158
298
|
available: boolean;
|
|
159
299
|
content_type: string;
|
|
@@ -168,10 +308,18 @@ export interface SignRequest {
|
|
|
168
308
|
kind: 'screenshot' | 'image';
|
|
169
309
|
url?: string;
|
|
170
310
|
template?: string;
|
|
171
|
-
|
|
311
|
+
/** Placeholder values for an `image` URL: text only, as for `image()`. */
|
|
312
|
+
vars?: Record<string, string>;
|
|
172
313
|
width?: number;
|
|
173
314
|
height?: number;
|
|
174
315
|
full_page?: boolean;
|
|
316
|
+
/** Screenshot only, with `full_page`: as on `ScreenshotRequest`. */
|
|
317
|
+
full_page_readiness?: 'scroll';
|
|
318
|
+
/** Screenshot only: as on `ScreenshotRequest`, signed into the URL. */
|
|
319
|
+
success_rules?: SuccessRules;
|
|
320
|
+
/** Screenshot only: as on `ScreenshotRequest`, signed into the URL. */
|
|
321
|
+
locale?: string;
|
|
322
|
+
timezone?: string;
|
|
175
323
|
ttl_hours?: number;
|
|
176
324
|
}
|
|
177
325
|
export interface SignedUrl {
|
|
@@ -253,6 +401,8 @@ export interface RequestRecord {
|
|
|
253
401
|
option_fields: string[];
|
|
254
402
|
client?: string;
|
|
255
403
|
created_at: string;
|
|
404
|
+
/** For a render that loaded a page; a cache hit has none. */
|
|
405
|
+
diagnostics?: RenderDiagnostics;
|
|
256
406
|
}
|
|
257
407
|
export interface RequestFilter {
|
|
258
408
|
since?: string;
|
|
@@ -296,3 +446,79 @@ export interface Destination {
|
|
|
296
446
|
/** Present on a webhook destination's creation response only. */
|
|
297
447
|
signing_secret?: string;
|
|
298
448
|
}
|
|
449
|
+
/** How a document template prints, kept with each revision. As on `PdfRequest`. */
|
|
450
|
+
export interface DocumentOptions {
|
|
451
|
+
landscape?: boolean;
|
|
452
|
+
paper_format?: 'a3' | 'a4' | 'a5' | 'letter' | 'legal' | 'tabloid';
|
|
453
|
+
margin?: {
|
|
454
|
+
top?: number;
|
|
455
|
+
right?: number;
|
|
456
|
+
bottom?: number;
|
|
457
|
+
left?: number;
|
|
458
|
+
};
|
|
459
|
+
print_background?: boolean;
|
|
460
|
+
header_html?: string;
|
|
461
|
+
footer_html?: string;
|
|
462
|
+
scale?: number;
|
|
463
|
+
/** A custom size with units, such as `210mm`; both or neither, never with `paper_format`. */
|
|
464
|
+
paper_width?: string;
|
|
465
|
+
paper_height?: string;
|
|
466
|
+
/** Let the page's own `@page { size }` decide the page size. */
|
|
467
|
+
prefer_css_page_size?: boolean;
|
|
468
|
+
/** Pages to keep, such as `1-3, 5, 8-`; the whole document is still bounded. */
|
|
469
|
+
page_ranges?: string;
|
|
470
|
+
/** The PDF's document properties. */
|
|
471
|
+
metadata?: {
|
|
472
|
+
title?: string;
|
|
473
|
+
author?: string;
|
|
474
|
+
subject?: string;
|
|
475
|
+
keywords?: string;
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* A stored PDF template's draft: html/template source over JSON data, a
|
|
480
|
+
* strict JSON Schema subset for the data, print options and sample data.
|
|
481
|
+
*/
|
|
482
|
+
export interface DocumentTemplateInput {
|
|
483
|
+
name: string;
|
|
484
|
+
source: string;
|
|
485
|
+
schema?: Record<string, unknown>;
|
|
486
|
+
options?: DocumentOptions;
|
|
487
|
+
sample_data?: Record<string, unknown>;
|
|
488
|
+
}
|
|
489
|
+
export interface DocumentTemplate extends DocumentTemplateInput {
|
|
490
|
+
id: string;
|
|
491
|
+
/** The revision unversioned renders use; null until first published. */
|
|
492
|
+
published_revision: number | null;
|
|
493
|
+
archived_at?: string;
|
|
494
|
+
created_at: string;
|
|
495
|
+
updated_at: string;
|
|
496
|
+
}
|
|
497
|
+
/** An immutable published version of a document template. */
|
|
498
|
+
export interface DocumentRevision {
|
|
499
|
+
id: string;
|
|
500
|
+
template_id: string;
|
|
501
|
+
number: number;
|
|
502
|
+
/** Absent from lists. */
|
|
503
|
+
source?: string;
|
|
504
|
+
schema?: Record<string, unknown>;
|
|
505
|
+
options: DocumentOptions;
|
|
506
|
+
sha256: string;
|
|
507
|
+
created_at: string;
|
|
508
|
+
}
|
|
509
|
+
export interface DocumentRenderRequest {
|
|
510
|
+
data?: Record<string, unknown>;
|
|
511
|
+
/** A revision number; absent or 0 for the published one. */
|
|
512
|
+
revision?: number;
|
|
513
|
+
}
|
|
514
|
+
/** A field the data got wrong, for `data_invalid` (the error's `fields`). */
|
|
515
|
+
export interface DocumentFieldError {
|
|
516
|
+
field: string;
|
|
517
|
+
code: 'required' | 'invalid';
|
|
518
|
+
message: string;
|
|
519
|
+
}
|
|
520
|
+
export interface DocumentValidation {
|
|
521
|
+
valid: boolean;
|
|
522
|
+
fields?: DocumentFieldError[];
|
|
523
|
+
error?: string;
|
|
524
|
+
}
|
package/package.json
CHANGED