@ironfang/render 3.0.2 → 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 +27 -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 +224 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,11 @@ verify Render webhooks with it.
|
|
|
23
23
|
the type check and then came back as a 400. Code that passed one no longer
|
|
24
24
|
compiles; pass it as text (`String(n)`).
|
|
25
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
|
+
|
|
26
31
|
```sh
|
|
27
32
|
npm install @ironfang/render
|
|
28
33
|
```
|
|
@@ -70,6 +75,28 @@ to 1,024 UTF-8 bytes and share the render timeout. Invalid input returns
|
|
|
70
75
|
messages. A screenshot cache hit does not re-execute controls; use `no_cache`
|
|
71
76
|
for fresh execution. [Full reference](https://ironfang.com/render/docs#page-controls).
|
|
72
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
|
+
|
|
73
100
|
## Durable jobs
|
|
74
101
|
|
|
75
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;
|
|
@@ -104,6 +152,57 @@ export interface ClipRequest {
|
|
|
104
152
|
end?: number;
|
|
105
153
|
}[];
|
|
106
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
|
+
}
|
|
107
206
|
export interface SitePreviewRequest extends RenderCommon {
|
|
108
207
|
url: string;
|
|
109
208
|
width?: number;
|
|
@@ -113,7 +212,7 @@ export interface SitePreviewRequest extends RenderCommon {
|
|
|
113
212
|
device?: 'desktop' | 'tablet' | 'mobile';
|
|
114
213
|
delay_ms?: number;
|
|
115
214
|
}
|
|
116
|
-
export type JobKind = 'screenshot' | 'pdf' | 'qr' | 'image' | 'clip' | 'site_preview';
|
|
215
|
+
export type JobKind = 'screenshot' | 'pdf' | 'qr' | 'image' | 'clip' | 'site_preview' | 'document' | 'bundle';
|
|
117
216
|
export type JobStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
|
|
118
217
|
export interface JobDelivery {
|
|
119
218
|
webhook_destination?: string;
|
|
@@ -137,6 +236,43 @@ export interface JobSubmission {
|
|
|
137
236
|
external_id?: string;
|
|
138
237
|
delivery?: JobDelivery;
|
|
139
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
|
+
}
|
|
140
276
|
export interface Job {
|
|
141
277
|
id: string;
|
|
142
278
|
kind: JobKind;
|
|
@@ -156,6 +292,8 @@ export interface Job {
|
|
|
156
292
|
code: string;
|
|
157
293
|
message: string;
|
|
158
294
|
};
|
|
295
|
+
/** Once an attempt has loaded the page, failed attempts included. */
|
|
296
|
+
diagnostics?: RenderDiagnostics;
|
|
159
297
|
result?: {
|
|
160
298
|
available: boolean;
|
|
161
299
|
content_type: string;
|
|
@@ -175,6 +313,13 @@ export interface SignRequest {
|
|
|
175
313
|
width?: number;
|
|
176
314
|
height?: number;
|
|
177
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;
|
|
178
323
|
ttl_hours?: number;
|
|
179
324
|
}
|
|
180
325
|
export interface SignedUrl {
|
|
@@ -256,6 +401,8 @@ export interface RequestRecord {
|
|
|
256
401
|
option_fields: string[];
|
|
257
402
|
client?: string;
|
|
258
403
|
created_at: string;
|
|
404
|
+
/** For a render that loaded a page; a cache hit has none. */
|
|
405
|
+
diagnostics?: RenderDiagnostics;
|
|
259
406
|
}
|
|
260
407
|
export interface RequestFilter {
|
|
261
408
|
since?: string;
|
|
@@ -299,3 +446,79 @@ export interface Destination {
|
|
|
299
446
|
/** Present on a webhook destination's creation response only. */
|
|
300
447
|
signing_secret?: string;
|
|
301
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