@shotkit/shotium 0.1.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,12 +1,418 @@
1
- import { a as PageGotoParams, c as StartOptions, l as Viewport, n as DaemonOptions, o as PurgeOptions, r as DaemonStatus, s as ScreenshotOptions, t as Clip, u as WorkerEvent } from "./types-x9HtkzeE.js";
2
1
  import { EventEmitter } from "node:events";
3
2
  import net from "node:net";
3
+ //#region src/types.d.ts
4
+ /** A region of the document, in CSS pixels. */
5
+ interface Clip {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ }
11
+ interface PageGotoParams {
12
+ /** Milliseconds before the load is abandoned. Default 30000. */
13
+ timeout?: number;
14
+ /**
15
+ * `load` waits for parsing to finish, the load event to fire and every
16
+ * request to complete. `networkidle` additionally waits for a 500ms window
17
+ * with nothing in flight, which matters for documents that keep fetching
18
+ * after the load event -- CSS that pulls in more CSS, or a font a late style
19
+ * change brought in.
20
+ */
21
+ waitUntil?: 'load' | 'networkidle';
22
+ }
23
+ /** The viewport the document is laid out in. */
24
+ interface Viewport {
25
+ /** CSS pixels. Default 1280. */
26
+ width?: number;
27
+ /** CSS pixels. Default 720. */
28
+ height?: number;
29
+ }
30
+ /**
31
+ * What a capture may do with the HTTP cache, spelled the way `fetch` spells
32
+ * it.
33
+ *
34
+ * - `default`: ordinary HTTP semantics. A fresh entry is used without asking,
35
+ * a stale one is revalidated, and the response updates the cache.
36
+ * - `reload`: read nothing, write everything -- the browser's reload button.
37
+ * The next capture is fast again.
38
+ * - `no-store`: neither read nor write. For a page that should not be left on
39
+ * this machine's disk, which an authenticated one usually should not.
40
+ * - `only-if-cached`: the network may not be touched and a miss is an error.
41
+ * Useful for a deterministic re-render of something already fetched.
42
+ */
43
+ type CacheMode = 'default' | 'reload' | 'no-store' | 'only-if-cached';
44
+ /** Where the milliseconds went. */
45
+ interface CaptureTiming {
46
+ /**
47
+ * Fetching the top-level document. For a cold `https:` URL this is DNS, TCP,
48
+ * TLS and a round trip, and it is routinely larger than everything below --
49
+ * which is the single most useful thing this object says.
50
+ */
51
+ fetch: number;
52
+ /** Parse, subresources, style, layout, prepaint, paint. */
53
+ render: number;
54
+ encode: number;
55
+ /** Wall clock for the whole capture, so the three above can be checked. */
56
+ total: number;
57
+ }
58
+ /** What one capture cost, and where its bytes came from. */
59
+ interface CaptureStats {
60
+ /** Every resource the document asked for, itself included. */
61
+ requests: number;
62
+ /**
63
+ * Answered from the HTTP cache -- the body came from disk.
64
+ *
65
+ * Not the same as "no network was touched". A stale entry that can be
66
+ * revalidated costs a conditional request and a 304, and counts here too;
67
+ * what the cache saved is the download rather than the round trip. That is
68
+ * why `timing.fetch` can be tens of milliseconds with this set.
69
+ */
70
+ fromCache: number;
71
+ failed: number;
72
+ /** Decoded body bytes, summed -- not the transfer size. */
73
+ bytes: number;
74
+ /** The document's own status. 0 for a `file:` URL. */
75
+ httpStatus: number;
76
+ /** After redirects, which is what relative URLs resolved against. */
77
+ finalUrl: string;
78
+ timing: CaptureTiming;
79
+ }
80
+ /** One screenshot, and what taking it cost. */
81
+ interface ScreenshotResult {
82
+ /**
83
+ * The encoded image, or `null` when `path` was given: the engine wrote the
84
+ * file itself and there is nothing left to hand back.
85
+ */
86
+ image: Buffer | null;
87
+ stats: CaptureStats;
88
+ }
89
+ interface ScreenshotOptions {
90
+ /** An http/https/file URL, or a local path. */
91
+ file: string;
92
+ /** Default `png`. */
93
+ type?: 'png' | 'jpeg' | 'webp';
94
+ /** Capture the whole document rather than the viewport. */
95
+ fullPage?: boolean;
96
+ /**
97
+ * Capture the box of the first element matching this CSS selector. Resolved
98
+ * inside the renderer with Document::querySelector -- there is no JavaScript
99
+ * engine, so nothing is injected into the page.
100
+ */
101
+ selector?: string;
102
+ /** 1-100, `jpeg` and `webp` only. Default 90. */
103
+ quality?: number;
104
+ /** Device scale factor, 0.01-8. Default 1. */
105
+ scale?: number;
106
+ /**
107
+ * Keep the alpha channel instead of painting the page's white backdrop.
108
+ * Rejected for `jpeg`, which has no alpha channel.
109
+ */
110
+ omitBackground?: boolean;
111
+ /** Write the image here instead of returning it, saving a round trip. */
112
+ path?: string;
113
+ pageGotoParams?: PageGotoParams;
114
+ /** A region of the document, in CSS pixels. */
115
+ clip?: Clip;
116
+ /** The viewport the document is laid out in. */
117
+ viewport?: Viewport;
118
+ /**
119
+ * Let the document read `file:` subresources. Off by default: a library does
120
+ * not get to decide for its caller that a document may read the filesystem it
121
+ * is rendered on.
122
+ */
123
+ allowFileAccess?: boolean;
124
+ /**
125
+ * What this capture may do with the HTTP cache. Default `default`.
126
+ *
127
+ * It applies to the subresources as well as the document: a `reload` that
128
+ * refreshed the HTML and reused yesterday's stylesheet would be a confusing
129
+ * thing to have asked for.
130
+ */
131
+ cache?: CacheMode;
132
+ /**
133
+ * Extra request headers, sent with the document and with the subresources
134
+ * that are same-origin with it.
135
+ *
136
+ * Same-origin is the whole rule and it is not configurable. A caller passing
137
+ * `Authorization` or `Cookie` means it for the site being photographed; a
138
+ * page that pulls a script from a CDN must not have the credential
139
+ * forwarded there.
140
+ */
141
+ headers?: Record<string, string>;
142
+ }
143
+ interface StartOptions {
144
+ /**
145
+ * Root of the HTTP disk cache. `null` disables caching entirely.
146
+ *
147
+ * The default is a per-project directory under the system temporary
148
+ * directory -- see `cache.getDir()`. Caching is on by default because the
149
+ * alternative turned out to be worse: without it every capture of an
150
+ * `https:` URL pays for DNS, TLS and a round trip, which for a small page is
151
+ * most of the time the call takes and all of the time the caller did not
152
+ * expect to spend.
153
+ */
154
+ cacheDir?: string | null;
155
+ /**
156
+ * Ceiling on the cache directory, in bytes. Default 256 MB.
157
+ *
158
+ * Zero is not "unlimited" -- it hands the decision to the backend, which
159
+ * sizes itself against the volume's free space. That was a reasonable
160
+ * default when every user of the cache had named a directory on purpose; for
161
+ * one that appears by default under `~/.shotium` because somebody imported a
162
+ * library, a number somebody chose is better than a number nobody did.
163
+ */
164
+ cacheMaxBytes?: number;
165
+ /** Overrides the built-in user agent string. */
166
+ userAgent?: string;
167
+ /**
168
+ * Where `shotium_data.pak` and `shotium_strings.pak` are. Defaults to the
169
+ * directory the engine was loaded from, which is where they ship.
170
+ */
171
+ resourceDir?: string;
172
+ }
173
+ /** What `start()` reports about the engine it brought up. */
174
+ interface StartResult {
175
+ /**
176
+ * Whether this lifecycle is started.
177
+ *
178
+ * A process has at most one engine, so `false` here does not mean there is
179
+ * nothing running -- it means this `Runtime` is stood down. `cacheDir` below
180
+ * is still answered from the engine, because a stood-down engine keeps its
181
+ * cache directory and reporting `null` would say the cache had gone away
182
+ * when what went away was the willingness to render.
183
+ */
184
+ running: boolean;
185
+ /** The directory in use, or `null` when caching is off. */
186
+ cacheDir: string | null;
187
+ /**
188
+ * Whether that directory is actually being cached into.
189
+ *
190
+ * A directory that cannot be created or written to costs nothing visible:
191
+ * the engine renders exactly as well without a cache, only slower, and every
192
+ * capture pays for the network again for a reason nothing reports. `false`
193
+ * with a `cacheDir` set means the open failed; `false` with `cacheDir: null`
194
+ * means no cache was asked for.
195
+ *
196
+ * It is not about sharing. Several processes may use one directory and all
197
+ * of them cache -- the backend takes no cross-process lock -- so `true` in
198
+ * two processes at once is the ordinary answer.
199
+ */
200
+ cacheActive: boolean;
201
+ }
202
+ interface DaemonOptions extends StartOptions {
203
+ /**
204
+ * Address the daemon by name instead of by configuration. Without it the
205
+ * endpoint is a hash of `cacheDir`, `userAgent` and `resourceDir`, so a
206
+ * client never attaches to a daemon that renders with something other than
207
+ * what it asked for.
208
+ */
209
+ name?: string;
210
+ /** The pipe or socket to use, overriding both the name and the hash. */
211
+ endpoint?: string;
212
+ /**
213
+ * Exit after this long with no connections and nothing rendering. Default
214
+ * 300000; `0` never exits.
215
+ */
216
+ idleTimeoutMs?: number;
217
+ /**
218
+ * Render one throwaway document at startup, so the first real request does
219
+ * not pay for whatever the engine initialises lazily. Default true.
220
+ */
221
+ prewarm?: boolean;
222
+ /** Fail instead of starting a daemon when none is listening. */
223
+ spawn?: boolean;
224
+ /** Where a spawned daemon's diagnostics go. Default `$SHOTIUM_DAEMON_LOG`. */
225
+ logFile?: string;
226
+ /** How long to wait for a daemon this process started to bind. */
227
+ startTimeoutMs?: number;
228
+ }
229
+ interface DaemonStatus {
230
+ ok?: boolean;
231
+ running?: boolean;
232
+ spawned?: boolean;
233
+ pid: number;
234
+ endpoint: string;
235
+ cacheDir: string | null;
236
+ userAgent?: string;
237
+ resourceDir?: string;
238
+ /** The engine has rendered at least once. */
239
+ warm: boolean;
240
+ uptimeMs: number;
241
+ connections: number;
242
+ inFlight: number;
243
+ served: number;
244
+ idleTimeoutMs: number;
245
+ version: string;
246
+ }
247
+ /**
248
+ * Options for `releaseMemory()`.
249
+ *
250
+ * Named for what it does rather than for `purge`, which it was called until
251
+ * 0.3. With `cache.clear()` in the API the old name reads as though it clears
252
+ * the cache, and it does not: it hands back blink's heap, skia's caches and
253
+ * PartitionAlloc's free lists, all of which the engine rebuilds on demand.
254
+ * Nothing on disk is touched.
255
+ */
256
+ interface ReleaseMemoryOptions {
257
+ /**
258
+ * Also ask the OS to take the engine's pages back. The next screenshot pays
259
+ * them back in soft page faults -- a few milliseconds -- so this is for when
260
+ * there may not be a next one soon.
261
+ */
262
+ releaseWorkingSet?: boolean;
263
+ }
264
+ /** Which cache directory an operation is about. */
265
+ interface CacheTarget {
266
+ /**
267
+ * `current` (the default) is this project's directory, `all` is every
268
+ * directory shotium has created under the shared root -- `~/.shotium/cache`
269
+ * -- and a string is either an absolute path or one project hash as
270
+ * `getDir()` reports it.
271
+ *
272
+ * The absolute path is there because `start({cacheDir})` accepts any
273
+ * directory: without it, a caller who chose their own cache would have the
274
+ * one cache these methods could not see.
275
+ *
276
+ * `all` exists because the directories are per-project by default, so
277
+ * "clear shotium's caches" is otherwise something a caller cannot express
278
+ * without already knowing where the other projects were.
279
+ */
280
+ target?: 'current' | 'all' | (string & {});
281
+ }
282
+ /** One resource the cache is holding. */
283
+ interface CacheEntry {
284
+ /** The resource, not the backend's key -- see `cache.getFiles()`. */
285
+ url: string;
286
+ /** Milliseconds since the Unix epoch. */
287
+ lastUsedMs: number;
288
+ bytes: number;
289
+ /** Which cache directory it was found in. */
290
+ dir: string;
291
+ }
292
+ interface CacheClearOptions extends CacheTarget {
293
+ /**
294
+ * Glob patterns matched against entry URLs -- not against filenames, which
295
+ * are hashes and would match nothing anybody would think to write.
296
+ *
297
+ * Supports `*` (within a path segment), `**` (across segments), `?` and
298
+ * `{a,b}`. Matching happens here rather than in the engine: the entries come
299
+ * back first, the patterns are applied to their URLs, and the ones that
300
+ * matched are what gets removed.
301
+ */
302
+ glob?: string[];
303
+ /**
304
+ * Remove entries not used for this many seconds. `0`, the default, means no
305
+ * age limit.
306
+ */
307
+ maxAge?: number;
308
+ /**
309
+ * Evict least-recently-used entries until the directory is at or below this
310
+ * many bytes. `0`, the default, means no size limit.
311
+ */
312
+ maxSize?: number;
313
+ }
314
+ interface CacheClearResult {
315
+ /**
316
+ * How many entries went. `-1` when the whole directory was dropped in one
317
+ * operation, which the backend does without counting them.
318
+ */
319
+ removed: number;
320
+ bytesBefore: number;
321
+ bytesAfter: number;
322
+ /** Which directory this result is for. */
323
+ dir: string;
324
+ }
325
+ //#endregion
326
+ //#region src/lib/binding.d.ts
327
+ /**
328
+ * The engine handle the addon hands back. Opaque on purpose: everything that
329
+ * can be done with it is a call on the binding below.
330
+ */
331
+ type Engine = unknown;
332
+ //#endregion
333
+ //#region src/lib/cache.d.ts
334
+ /**
335
+ * The cache, from the outside.
336
+ *
337
+ * Every method takes the engine handle if there is one, and that is not an
338
+ * optimisation. Within one process a cache directory has one backend: asking
339
+ * for a second one on the directory the engine holds waits for the engine's to
340
+ * go away, which it will not do while the engine is up. Borrowing is the only
341
+ * thing that returns.
342
+ *
343
+ * "If there is one" means the process, not the lifecycle. `stop()` stands the
344
+ * engine down without tearing it down, so an engine that has been stopped
345
+ * still holds its directory and still has to be borrowed from -- which is also
346
+ * what makes the cache survive a stop, and outlive one, and be worth having.
347
+ *
348
+ * Across processes there is no such constraint -- several of them may share a
349
+ * directory and all of them cache.
350
+ *
351
+ * The engine is fetched through a callback rather than held, because this
352
+ * object is built once at import time and the engine comes and goes.
353
+ */
354
+ declare class Cache {
355
+ private readonly engineHandle;
356
+ constructor(engineHandle: () => Engine | null);
357
+ /**
358
+ * This project's cache directory, absolute and with forward slashes.
359
+ *
360
+ * It exists whether or not anything has been written to it -- the answer is
361
+ * "where the cache goes", not "where a cache is".
362
+ */
363
+ getDir(options?: CacheTarget): string;
364
+ /** Every directory the target names. `all` can be several; the rest, one. */
365
+ getDirs(options?: CacheTarget): string[];
366
+ /**
367
+ * What the cache is holding, by URL.
368
+ *
369
+ * Named `getFiles` for the operation callers reach for, and deliberately not
370
+ * returning filenames: the files in a cache directory are called things like
371
+ * `5349fbae98c6d9a1_0`, because the name is a hash of the entry key. A list
372
+ * of those answers no question anybody has. The URLs are what the entries
373
+ * are, and they are what `clear({glob})` matches against.
374
+ *
375
+ * This opens every entry to read its key and size, so it is a diagnostic
376
+ * rather than something to put on a request path.
377
+ */
378
+ getFiles(options?: CacheTarget): Promise<CacheEntry[]>;
379
+ /**
380
+ * Removes what the options select. With no options, everything.
381
+ *
382
+ * The three filters compose, and `glob` is applied here rather than in the
383
+ * engine: the entries are listed, their URLs are matched, and the ones that
384
+ * matched are what the engine is asked to remove. That keeps the pattern
385
+ * dialect in the layer whose users have opinions about pattern dialects, and
386
+ * keeps the engine's interface to exact URLs.
387
+ *
388
+ * Removal goes through the cache backend, never through the filesystem.
389
+ * Deleting the files directly would leave the backend's index naming entries
390
+ * that are no longer there, and the next process to open the directory
391
+ * either rebuilds the index from disk or, having found it inconsistent,
392
+ * discards it. That is the difference between clearing a cache and
393
+ * corrupting one.
394
+ */
395
+ clear(options?: CacheClearOptions): Promise<CacheClearResult[]>;
396
+ /**
397
+ * The engine handle, when there is an engine.
398
+ *
399
+ * Passed for every directory and not only the engine's own. It is never
400
+ * wrong to pass it -- the engine's thread can open any directory, and for
401
+ * the one it already has open, borrowing its backend is the only thing that
402
+ * returns. It is passing `null` while an engine is up that hangs, which is
403
+ * why this is conditional on neither the directory asked for nor on whether
404
+ * the engine is currently accepting captures.
405
+ */
406
+ private handleFor;
407
+ }
408
+ //#endregion
4
409
  //#region src/lib/client.d.ts
5
410
  interface ClientReply {
6
411
  id: number;
7
412
  ok?: boolean;
8
413
  error?: string;
9
414
  path?: string;
415
+ stats?: CaptureStats;
10
416
  }
11
417
  interface ClientResult {
12
418
  header: ClientReply;
@@ -26,8 +432,13 @@ declare class DaemonClient extends EventEmitter {
26
432
  private settle;
27
433
  private failAll;
28
434
  send(message: Record<string, unknown>): Promise<ClientResult>;
29
- /** Resolves to the image, or to null when `path` was given. */
30
- screenshot(options: ScreenshotOptions): Promise<Buffer | null>;
435
+ /**
436
+ * One screenshot, and what taking it cost.
437
+ *
438
+ * The same shape the in-process engine returns, so that moving a program
439
+ * between the two is an import change and nothing else.
440
+ */
441
+ screenshot(options: ScreenshotOptions): Promise<ScreenshotResult>;
31
442
  status(): Promise<DaemonStatus>;
32
443
  shutdown(): Promise<{
33
444
  ok: boolean;
@@ -36,14 +447,14 @@ declare class DaemonClient extends EventEmitter {
36
447
  }
37
448
  //#endregion
38
449
  //#region src/index.d.ts
39
- /** The five things a caller does with the resident pool. */
450
+ /** The five things a caller does with the resident engine. */
40
451
  interface Daemon {
41
452
  /** Connects, starting a daemon if none is listening. */
42
453
  connect(options?: DaemonOptions): Promise<DaemonClient>;
43
454
  /** One screenshot through the daemon, connection and all. */
44
455
  screenshot(options: ScreenshotOptions & {
45
456
  daemon?: DaemonOptions;
46
- }): Promise<Buffer | null>;
457
+ }): Promise<ScreenshotResult>;
47
458
  /** Starts one if it is not up, and reports what is there either way. */
48
459
  start(options?: DaemonOptions): Promise<DaemonStatus & {
49
460
  spawned: boolean;
@@ -57,79 +468,162 @@ interface Daemon {
57
468
  endpoint: string;
58
469
  }>;
59
470
  }
60
- interface Runtime {
61
- on(event: 'ready', listener: (info: {
62
- workers: number;
63
- }) => void): this;
64
- on(event: 'exit', listener: (event: WorkerEvent) => void): this;
65
- on(event: 'crash', listener: (event: WorkerEvent) => void): this;
66
- on(event: 'timeout', listener: (event: {
67
- worker: number;
68
- timeout: number;
69
- }) => void): this;
70
- on(event: 'worker-restart', listener: (event: {
71
- worker: number;
72
- reason: string;
73
- delay: number;
74
- }) => void): this;
75
- /** A worker could not be started at all -- a missing or unusable binary. */
76
- on(event: 'worker-error', listener: (event: {
77
- worker: number;
78
- error: Error;
79
- }) => void): this;
80
- on(event: 'stderr', listener: (event: {
81
- worker: number;
82
- line: string;
83
- }) => void): this;
84
- }
85
471
  /**
86
- * The library's one runtime: a pool of worker processes plus its lifecycle.
472
+ * The engine, and its lifecycle, in this process.
473
+ *
474
+ * import shotium from '@shotkit/shotium';
475
+ *
476
+ * shotium.start();
477
+ * const {image, stats} = await shotium.screenshot({
478
+ * file: 'https://example.com',
479
+ * });
480
+ * await shotium.stop();
481
+ *
482
+ * `start` and `stop` are explicit because starting Blink is the expensive part
483
+ * -- tens of milliseconds and a working set that stays resident -- and only
484
+ * the caller knows whether the next screenshot is coming in a moment or never.
485
+ * Neither call is required: a screenshot starts the engine if it is not up.
486
+ * What they buy is control over when that cost is paid, and the certainty that
487
+ * it has been given back.
87
488
  *
88
- * `runtime` below is the singleton, because the expensive part is the
89
- * processes and a second runtime would double them for no gain. Anyone who
90
- * genuinely wants two constructs a Runtime directly.
489
+ * Neither is rationed, either. They may be called in any order and as often as
490
+ * a program likes: `stop()` stands the engine down and `start()` picks the
491
+ * same one back up, warm cache and all. What cannot happen is a *second*
492
+ * engine -- Blink is initialised once per process and there is no undo -- but
493
+ * that is a fact about how many there are, not about how many times the one
494
+ * may be asked for.
91
495
  *
92
- * Its pool lives and dies with this process. `daemon` is the same pool behind
93
- * a socket, for callers whose process does not live long enough to be worth
94
- * starting one.
496
+ * The methods are on the module rather than under a `runtime` namespace, which
497
+ * they were until 0.3. There was never anything else to start, so the word
498
+ * carried nothing; and `runtime.cache` would have been the wrong place for the
499
+ * cache besides, since a cache directory outlives every engine that writes to
500
+ * it and can be read when no engine is running at all.
501
+ *
502
+ * `Runtime` is still exported for a caller who wants to own a lifecycle rather
503
+ * than share the module's. It is a lifecycle and not an engine: there is one
504
+ * engine per process, and a second Runtime that starts adopts the same one
505
+ * rather than building another. Parallelism is more processes, not more
506
+ * Runtimes.
507
+ *
508
+ * `daemon` is the same engine in a process of its own, behind a socket, for
509
+ * callers whose own process does not live long enough to be worth starting
510
+ * one.
95
511
  */
96
- declare class Runtime extends EventEmitter {
97
- private pool;
512
+ declare class Runtime {
513
+ private engine;
514
+ /**
515
+ * The HTTP cache: where it is, what is in it, and how to empty it.
516
+ *
517
+ * On the Runtime as well as on the module because a caller holding their own
518
+ * Runtime needs the engine handle to reach a directory that engine has open:
519
+ * within one process a directory has one backend, so borrowing is the only
520
+ * way in.
521
+ */
522
+ readonly cache: Cache;
98
523
  get running(): boolean;
99
524
  /**
100
- * Starts the pool. Safe to call twice; the second call is a no-op, so that
101
- * library code can call it defensively.
525
+ * Starts the engine, or picks the running one back up.
526
+ *
527
+ * Callable as often as you like, in any order with `stop()`; library code
528
+ * can call it defensively. The first call in a process builds the engine and
529
+ * every later one adopts it -- the same engine, the same warm cache. The one
530
+ * thing it will refuse is a *different* configuration: the options below are
531
+ * fixed when the engine is built, and there is no second build, so naming
532
+ * one that disagrees with what is running throws rather than rendering with
533
+ * a value you did not ask for.
534
+ *
535
+ * Every option has a default. `cacheDir` is the HTTP disk cache and defaults
536
+ * to a per-project directory under `~/.shotium/cache`, and not under the
537
+ * temporary directory, which is defined by not surviving. `null` turns it
538
+ * off. `resourceDir` is where `shotium_data.pak` and
539
+ * `shotium_strings.pak` are, and defaults to the directory the engine was
540
+ * loaded from, which is where they ship.
102
541
  *
103
- * Every option has a default: the binary is `$SHOTIUM_BINARY`, then the
104
- * platform package, then `./bin/shotium.exe`; the worker count is half the
105
- * cores, at least one and at most four; the cache root is a directory under
106
- * the system temp, and `null` disables caching.
542
+ * The return value is worth reading once. `cacheActive: false` with a
543
+ * `cacheDir` set means the directory could not be opened and this engine is
544
+ * running without a cache -- correctly, silently, and a round trip slower on
545
+ * everything.
546
+ */
547
+ start(options?: StartOptions): StartResult;
548
+ /** What `start()` returned, asked again. */
549
+ status(): StartResult;
550
+ /**
551
+ * Stands the engine down, after whatever is queued.
552
+ *
553
+ * The queue drains, the memory the engine can rebuild goes back to the OS,
554
+ * and `running` becomes false. Blink itself stays initialised, because there
555
+ * is no way to un-initialise it -- so the disk cache stays where it is, and
556
+ * `start()` or the next `screenshot()` picks the same engine back up.
557
+ *
558
+ * Which makes this a caller saying they are done for now rather than a
559
+ * destructor. It does the same work as `releaseMemory({releaseWorkingSet:
560
+ * true})` and additionally stops accepting captures.
107
561
  */
108
- start(options?: StartOptions): this;
109
- /** Stops every worker. The pool can be started again afterwards. */
110
562
  stop(): Promise<void>;
111
563
  /**
112
- * Renders one screenshot. Resolves to the encoded image, or to `null` when
113
- * `path` was given and the worker wrote the file itself.
564
+ * Hands back what the engine is holding but can rebuild: Blink's heap,
565
+ * skia's caches, PartitionAlloc's free lists. Worth calling when a batch has
566
+ * ended and the next one may be a while away.
567
+ *
568
+ * This is memory and nothing else. It was called `purge()` until 0.3, which
569
+ * next to `cache.clear()` read as though it emptied the HTTP cache; it does
570
+ * not touch the disk at all.
114
571
  */
115
- screenshot(options: ScreenshotOptions): Promise<Buffer | null>;
572
+ releaseMemory(options?: ReleaseMemoryOptions): void;
573
+ /**
574
+ * Renders one screenshot, and reports what it cost.
575
+ *
576
+ * `image` is the encoded bytes, or `null` when `path` was given and the
577
+ * engine wrote the file itself. `stats` says how many resources were
578
+ * fetched, how many came from the cache, and where the milliseconds went --
579
+ * which for an `https:` URL is usually the answer to "why did this take so
580
+ * long", because a cold connection costs more than the render does.
581
+ */
582
+ screenshot(options: ScreenshotOptions): Promise<ScreenshotResult>;
116
583
  }
117
- /** The shared pool: one per process, started on first use. */
584
+ /** The shared engine: one per process, started on first use. */
118
585
  declare const runtime: Runtime;
119
- /** One screenshot through the shared pool, starting it if it is not up. */
120
- declare const screenshot: (options: ScreenshotOptions) => Promise<Buffer | null>;
586
+ /** One screenshot through the shared engine, starting it if it is not up. */
587
+ declare const screenshot: (options: ScreenshotOptions) => Promise<ScreenshotResult>;
588
+ declare const start: (options?: StartOptions) => StartResult;
589
+ declare const status: () => StartResult;
590
+ declare const stop: () => Promise<void>;
591
+ declare const releaseMemory: (options?: ReleaseMemoryOptions) => void;
592
+ /**
593
+ * The HTTP cache.
594
+ *
595
+ * At the top level rather than under the engine because it outlives one: the
596
+ * directory is on disk whether or not anything is running, `getDir()` answers
597
+ * before the first `start()`, and clearing it is something a program may want
598
+ * to do without bringing Blink up at all. When an engine *is* up, these
599
+ * borrow its cache backend, because within one process a directory has one
600
+ * backend and that is the only way in.
601
+ */
602
+ declare const cache: Cache;
121
603
  /**
122
- * The resident pool: workers that outlive the process that started them,
604
+ * The resident engine: a process that outlives the one that started it,
123
605
  * reachable over a named pipe on Windows and a unix socket elsewhere. For
124
606
  * callers that are short-lived themselves. See lib/daemon.ts.
607
+ *
608
+ * It has no `cache` of its own. A daemon's cache directory is reported by
609
+ * `daemon.status()`, and clearing it is done by pointing `cache.clear()` at
610
+ * that directory or by stopping the daemon -- a cross-process cache protocol
611
+ * would be a second implementation of this module for something nobody does on
612
+ * a request path.
125
613
  */
126
614
  declare const daemon: Daemon;
127
615
  declare const _default: {
128
616
  Runtime: typeof Runtime;
129
- runtime: Runtime;
130
- screenshot: (options: ScreenshotOptions) => Promise<Buffer | null>;
617
+ cache: Cache;
131
618
  daemon: Daemon;
619
+ releaseMemory: (options?: ReleaseMemoryOptions) => void;
620
+ runtime: Runtime;
621
+ screenshot: (options: ScreenshotOptions) => Promise<ScreenshotResult>;
622
+ start: (options?: StartOptions) => StartResult;
623
+ status: () => StartResult;
624
+ stop: () => Promise<void>;
625
+ readonly running: boolean;
132
626
  };
133
627
  //#endregion
134
- export { type Clip, Daemon, type DaemonClient, type DaemonOptions, type DaemonStatus, type PageGotoParams, type PurgeOptions, Runtime, type ScreenshotOptions, type StartOptions, type Viewport, type WorkerEvent, daemon, _default as default, runtime, screenshot };
628
+ export { Cache, type CacheClearOptions, type CacheClearResult, type CacheEntry, type CacheMode, type CacheTarget, type CaptureStats, type CaptureTiming, type Clip, Daemon, type DaemonClient, type DaemonOptions, type DaemonStatus, type PageGotoParams, type ReleaseMemoryOptions, Runtime, type ScreenshotOptions, type ScreenshotResult, type StartOptions, type StartResult, type Viewport, cache, daemon, _default as default, releaseMemory, runtime, screenshot, start, status, stop };
135
629
  //# sourceMappingURL=index.d.ts.map