@spinajs/templates-puppeteer 2.0.481 → 2.0.482
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 +35 -0
- package/lib/cjs/asset-server.d.ts +32 -0
- package/lib/cjs/asset-server.d.ts.map +1 -0
- package/lib/cjs/asset-server.js +125 -0
- package/lib/cjs/asset-server.js.map +1 -0
- package/lib/cjs/browser-pool.d.ts +31 -0
- package/lib/cjs/browser-pool.d.ts.map +1 -0
- package/lib/cjs/browser-pool.js +83 -0
- package/lib/cjs/browser-pool.js.map +1 -0
- package/lib/cjs/index.d.ts +84 -19
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +203 -182
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/progress.d.ts +41 -0
- package/lib/cjs/progress.d.ts.map +1 -0
- package/lib/cjs/progress.js +179 -0
- package/lib/cjs/progress.js.map +1 -0
- package/lib/mjs/asset-server.d.ts +32 -0
- package/lib/mjs/asset-server.d.ts.map +1 -0
- package/lib/mjs/asset-server.js +118 -0
- package/lib/mjs/asset-server.js.map +1 -0
- package/lib/mjs/browser-pool.d.ts +31 -0
- package/lib/mjs/browser-pool.d.ts.map +1 -0
- package/lib/mjs/browser-pool.js +76 -0
- package/lib/mjs/browser-pool.js.map +1 -0
- package/lib/mjs/index.d.ts +84 -19
- package/lib/mjs/index.d.ts.map +1 -1
- package/lib/mjs/index.js +190 -180
- package/lib/mjs/index.js.map +1 -1
- package/lib/mjs/progress.d.ts +41 -0
- package/lib/mjs/progress.d.ts.map +1 -0
- package/lib/mjs/progress.js +175 -0
- package/lib/mjs/progress.js.map +1 -0
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +8 -7
package/lib/mjs/index.js
CHANGED
|
@@ -7,234 +7,244 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
import { default as puppeteer } from 'puppeteer';
|
|
11
10
|
import { NotSupported } from '@spinajs/exceptions';
|
|
12
|
-
import { TemplateRenderer, Templates } from '@spinajs/templates';
|
|
11
|
+
import { TemplateRenderer, Templates, RenderPhase } from '@spinajs/templates';
|
|
13
12
|
import { LazyInject } from '@spinajs/di';
|
|
14
13
|
import { basename, dirname, join } from 'path';
|
|
15
14
|
import { Log, Logger } from '@spinajs/log';
|
|
16
|
-
import Express from 'express';
|
|
17
|
-
import cors from 'cors';
|
|
18
15
|
import '@spinajs/templates-pug';
|
|
19
|
-
import
|
|
16
|
+
import { RenderProgressReporter } from './progress.js';
|
|
17
|
+
import { LocalAssetServer } from './asset-server.js';
|
|
18
|
+
import { BrowserPool } from './browser-pool.js';
|
|
19
|
+
export * from './progress.js';
|
|
20
|
+
export * from './asset-server.js';
|
|
21
|
+
export * from './browser-pool.js';
|
|
22
|
+
/** Default navigation/render timeout when none is configured (ms). */
|
|
23
|
+
const DEFAULT_RENDER_TIMEOUT_MS = 30000;
|
|
24
|
+
/** Watchdog canceller used when timeouts are disabled (debug mode). */
|
|
25
|
+
const noop = () => {
|
|
26
|
+
/* nothing to cancel */
|
|
27
|
+
};
|
|
20
28
|
export class PuppeteerRenderer extends TemplateRenderer {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Per-instance options that give this renderer its identity for the DI
|
|
31
|
+
* @PerInstanceCheck pooling (see subclasses). Renderers that are not pooled
|
|
32
|
+
* per-instance inherit the `undefined` default.
|
|
33
|
+
*/
|
|
34
|
+
get instanceOptions() {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* DI @PerInstanceCheck hook: an existing instance is reused only when it was
|
|
39
|
+
* created with the same options.
|
|
40
|
+
*/
|
|
41
|
+
__checkInstance__(creationOptions) {
|
|
42
|
+
return JSON.stringify(this.instanceOptions) === JSON.stringify(creationOptions);
|
|
43
|
+
}
|
|
44
|
+
/** Local static server used so Chromium can load relative asset URLs. Lazily created. */
|
|
45
|
+
get assetServer() {
|
|
46
|
+
return (this._assetServer ??= new LocalAssetServer(this.Log, () => this.Options?.static?.portRange));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Pooled browser, launched lazily and reused across renders, closed on dispose().
|
|
50
|
+
* One pool per options-set (see @PerInstanceCheck on subclasses). Lazily created.
|
|
51
|
+
*/
|
|
52
|
+
get browserPool() {
|
|
53
|
+
return (this._browserPool ??= new BrowserPool(this.Log, () => this.buildLaunchOptions()));
|
|
54
|
+
}
|
|
55
|
+
buildLaunchOptions() {
|
|
56
|
+
return {
|
|
57
|
+
...(this.Options?.args || {}),
|
|
58
|
+
...(this.Options?.executablePath && {
|
|
59
|
+
executablePath: this.Options.executablePath,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async renderToFile(template, model, filePath, language, options) {
|
|
64
|
+
const templateBasePath = dirname(template);
|
|
65
|
+
await this.renderContentToFile(filePath, { assetBasePath: templateBasePath, onProgress: options?.onProgress }, async (page, httpPort, markLoading) => {
|
|
33
66
|
const compiledTemplate = await this.TemplatingService.render(join(templateBasePath, basename(template, this.Extension)) + '.pug', {
|
|
34
|
-
// add template temporary server port
|
|
35
|
-
// so we can use it to render images in template
|
|
67
|
+
// add template temporary server port so templates can reference local images
|
|
36
68
|
__http_template_port__: httpPort,
|
|
37
|
-
// for
|
|
69
|
+
// for convenience add full url to local http server
|
|
38
70
|
__http_template_address__: `http://localhost:${httpPort}`,
|
|
39
71
|
...model,
|
|
40
72
|
}, language);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
// template is compiled; resource loading starts with setContent
|
|
74
|
+
markLoading();
|
|
75
|
+
await page.setContent(compiledTemplate);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Render a raw HTML string to a file (image/pdf per the concrete renderer).
|
|
80
|
+
* Intended for CI (e.g. screenshot comparison) where HTML is already produced.
|
|
81
|
+
*
|
|
82
|
+
* @param html - the HTML content to render
|
|
83
|
+
* @param filePath - output file path
|
|
84
|
+
* @param options.assetBasePath - directory served over the local http server so
|
|
85
|
+
* relative asset URLs in the HTML resolve (defaults to the output file's dir)
|
|
86
|
+
* @param options.viewport - fixed viewport for deterministic captures
|
|
87
|
+
*/
|
|
88
|
+
async renderHtmlToFile(html, filePath, options) {
|
|
89
|
+
await this.renderContentToFile(filePath, { assetBasePath: options?.assetBasePath ?? dirname(filePath), onProgress: options?.onProgress }, async (page, httpPort, markLoading) => {
|
|
90
|
+
if (options?.viewport) {
|
|
91
|
+
await page.setViewport(options.viewport);
|
|
53
92
|
}
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
93
|
+
// resource loading starts with setContent
|
|
94
|
+
markLoading();
|
|
95
|
+
// inject a <base href> so relative asset URLs resolve via the local server
|
|
96
|
+
await page.setContent(this.injectBaseHref(html, `http://127.0.0.1:${httpPort}/`));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Shared render orchestration: local static server + pooled browser + page +
|
|
101
|
+
* timeout watchdog + cleanup. `prepare` populates the page content (template-compiled
|
|
102
|
+
* HTML, or raw HTML). The pooled browser survives; only the page is closed.
|
|
103
|
+
*/
|
|
104
|
+
async renderContentToFile(filePath, opts, prepare) {
|
|
105
|
+
const timerLabel = `puppeteer-template-rendering-${filePath}`;
|
|
106
|
+
const progress = new RenderProgressReporter(filePath, opts.onProgress);
|
|
107
|
+
let server = null;
|
|
108
|
+
let page = null;
|
|
109
|
+
// When debug.close === false the page is intentionally kept open for
|
|
110
|
+
// inspection, so timeouts must not be armed (the watchdog would close it).
|
|
111
|
+
// The browser itself is pooled and only closed on dispose().
|
|
112
|
+
const keepPageOpen = this.Options?.debug?.close === false;
|
|
113
|
+
try {
|
|
114
|
+
this.Log.timeStart(timerLabel);
|
|
115
|
+
this.Log.trace(`Rendering to file ${filePath}`);
|
|
116
|
+
progress.phase(RenderPhase.Starting);
|
|
117
|
+
// fire up local http server for serving images etc, because chromium
|
|
118
|
+
// prevents reading local files when not using the file:// protocol
|
|
119
|
+
server = await this.assetServer.serve(opts.assetBasePath ?? process.cwd());
|
|
120
|
+
const httpPort = server.address().port;
|
|
121
|
+
// reuse the pooled browser; open a fresh page per render
|
|
122
|
+
const browser = await this.browserPool.acquire();
|
|
123
|
+
page = await browser.newPage();
|
|
124
|
+
progress.attach(page);
|
|
125
|
+
// In debug mode neither timeouts nor the watchdog are armed, so the page
|
|
126
|
+
// can be inspected indefinitely.
|
|
127
|
+
const cancelWatchdog = keepPageOpen ? noop : this.armRenderWatchdog(page);
|
|
128
|
+
if (!keepPageOpen) {
|
|
129
|
+
this.configurePageTimeouts(page);
|
|
70
130
|
}
|
|
71
|
-
|
|
72
|
-
const eventCleanup = this.addPageEventListeners(page);
|
|
131
|
+
const removeListeners = this.addPageEventListeners(page);
|
|
73
132
|
try {
|
|
133
|
+
progress.phase(RenderPhase.Preparing);
|
|
74
134
|
await page.setBypassCSP(true);
|
|
75
|
-
|
|
76
|
-
//
|
|
135
|
+
// prepare compiles/builds the HTML then calls markLoading() right before
|
|
136
|
+
// setContent, so template-compile time is separated from resource loading.
|
|
137
|
+
await prepare(page, httpPort, () => progress.phase(RenderPhase.Loading));
|
|
138
|
+
// perform the concrete rendering (PDF or image)
|
|
139
|
+
progress.phase(RenderPhase.Rendering);
|
|
77
140
|
await this.performRender(page, filePath);
|
|
78
|
-
|
|
79
|
-
if (renderTimeout) {
|
|
80
|
-
clearTimeout(renderTimeout);
|
|
81
|
-
renderTimeout = undefined;
|
|
82
|
-
}
|
|
83
|
-
// Clean up event listeners
|
|
84
|
-
eventCleanup();
|
|
141
|
+
progress.phase(RenderPhase.Done);
|
|
85
142
|
}
|
|
86
143
|
catch (renderError) {
|
|
87
|
-
|
|
88
|
-
if (renderTimeout) {
|
|
89
|
-
clearTimeout(renderTimeout);
|
|
90
|
-
renderTimeout = undefined;
|
|
91
|
-
}
|
|
92
|
-
this.Log.error(renderError, `Error during rendering for template ${template}`);
|
|
144
|
+
this.Log.error(renderError, `Error during rendering for ${filePath}`);
|
|
93
145
|
throw renderError;
|
|
94
146
|
}
|
|
147
|
+
finally {
|
|
148
|
+
cancelWatchdog();
|
|
149
|
+
removeListeners();
|
|
150
|
+
}
|
|
95
151
|
}
|
|
96
152
|
catch (err) {
|
|
97
|
-
|
|
153
|
+
progress.phase(RenderPhase.Failed, err?.message);
|
|
154
|
+
this.Log.error(err, `Error rendering to file ${filePath}`);
|
|
98
155
|
throw err;
|
|
99
156
|
}
|
|
100
157
|
finally {
|
|
101
|
-
|
|
102
|
-
this.Log.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
if (
|
|
108
|
-
await this.
|
|
158
|
+
progress.dispose();
|
|
159
|
+
const duration = this.Log.timeEnd(timerLabel);
|
|
160
|
+
this.Log.trace(`Ended rendering to file ${filePath}, took: ${duration}ms`);
|
|
161
|
+
this.warnIfSlow(filePath, duration);
|
|
162
|
+
// Close the page (not the pooled browser). Skip if debug.close is false
|
|
163
|
+
// so the rendered page can be inspected.
|
|
164
|
+
if (page && !keepPageOpen) {
|
|
165
|
+
await this.closePage(page);
|
|
109
166
|
}
|
|
110
|
-
else if (
|
|
111
|
-
this.Log.info('
|
|
167
|
+
else if (page) {
|
|
168
|
+
this.Log.info('Page kept open for debugging (debug.close=false)');
|
|
112
169
|
}
|
|
113
170
|
if (server) {
|
|
114
|
-
await this.
|
|
171
|
+
await this.assetServer.close(server);
|
|
115
172
|
}
|
|
116
173
|
}
|
|
117
174
|
}
|
|
118
|
-
|
|
119
|
-
|
|
175
|
+
/** Effective render timeout: configured value, else the default. */
|
|
176
|
+
get renderTimeoutMs() {
|
|
177
|
+
return this.Options?.renderTimeout || DEFAULT_RENDER_TIMEOUT_MS;
|
|
120
178
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const app = Express();
|
|
126
|
-
app.use(cors());
|
|
127
|
-
app.use(Express.static(basePath));
|
|
128
|
-
return new Promise((resolve, reject) => {
|
|
129
|
-
const server = app
|
|
130
|
-
// if no port is provided express will choose random port to start (available)
|
|
131
|
-
// if not, we will get random from range in config
|
|
132
|
-
.listen(!this.Options?.static?.portRange || this.Options.static.portRange.length === 0
|
|
133
|
-
? 0
|
|
134
|
-
: _.random(this.Options.static.portRange[0], this.Options.static.portRange[1]))
|
|
135
|
-
.on('listening', function () {
|
|
136
|
-
self.Log.trace(`Puppeteer image server started on port ${this.address().port}`);
|
|
137
|
-
self.Log.trace(`Puppeteer static file dir at ${basePath}`);
|
|
138
|
-
resolve(this);
|
|
139
|
-
})
|
|
140
|
-
.on('error', (err) => {
|
|
141
|
-
self.Log.error(err, `Puppeteer image server cannot start`);
|
|
142
|
-
// Clean up the failed server
|
|
143
|
-
if (server) {
|
|
144
|
-
server.close(() => {
|
|
145
|
-
reject(err);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
reject(err);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
// Set a timeout for server startup
|
|
153
|
-
setTimeout(() => {
|
|
154
|
-
if (!server.listening) {
|
|
155
|
-
server.close();
|
|
156
|
-
reject(new Error('Server startup timeout'));
|
|
157
|
-
}
|
|
158
|
-
}, 10000);
|
|
159
|
-
});
|
|
179
|
+
/** Apply the configured (or default) navigation and render timeouts to a page. */
|
|
180
|
+
configurePageTimeouts(page) {
|
|
181
|
+
page.setDefaultNavigationTimeout(this.Options?.navigationTimeout || DEFAULT_RENDER_TIMEOUT_MS);
|
|
182
|
+
page.setDefaultTimeout(this.renderTimeoutMs);
|
|
160
183
|
}
|
|
161
184
|
/**
|
|
162
|
-
*
|
|
185
|
+
* Arm a watchdog that closes the page if a render exceeds the configured
|
|
186
|
+
* timeout - only the page is closed, the pooled browser may be serving other
|
|
187
|
+
* concurrent renders. Returns a function that cancels the watchdog.
|
|
163
188
|
*/
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
await this.forceCloseBrowser(browser);
|
|
177
|
-
}
|
|
178
|
-
catch (killErr) {
|
|
179
|
-
this.Log.error(`Failed to force kill browser: ${killErr.message}`);
|
|
180
|
-
}
|
|
189
|
+
armRenderWatchdog(page) {
|
|
190
|
+
const timeoutMs = this.renderTimeoutMs;
|
|
191
|
+
const handle = setTimeout(() => {
|
|
192
|
+
this.Log.warn(`Render timeout (${timeoutMs}ms) - closing page`);
|
|
193
|
+
page.close().catch(() => { });
|
|
194
|
+
}, timeoutMs);
|
|
195
|
+
return () => clearTimeout(handle);
|
|
196
|
+
}
|
|
197
|
+
/** Warn if a render took longer than the configured warning threshold. */
|
|
198
|
+
warnIfSlow(filePath, duration) {
|
|
199
|
+
if (this.Options && duration > this.Options.renderDurationWarning) {
|
|
200
|
+
this.Log.warn(`Rendering to file ${filePath} took too long.`);
|
|
181
201
|
}
|
|
182
202
|
}
|
|
203
|
+
/** Close a page, downgrading any close error to a warning (best-effort cleanup). */
|
|
204
|
+
async closePage(page) {
|
|
205
|
+
await page.close().catch((err) => this.Log.warn(`Error closing page: ${err.message}`));
|
|
206
|
+
}
|
|
183
207
|
/**
|
|
184
|
-
*
|
|
208
|
+
* Insert a <base href> into the HTML <head> so relative asset URLs resolve
|
|
209
|
+
* against the local static server. No-op if the HTML already declares a <base>.
|
|
185
210
|
*/
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (process) {
|
|
190
|
-
process.kill('SIGKILL');
|
|
191
|
-
this.Log.warn('Browser process force killed');
|
|
192
|
-
}
|
|
211
|
+
injectBaseHref(html, baseUrl) {
|
|
212
|
+
if (/<base\s/i.test(html)) {
|
|
213
|
+
return html;
|
|
193
214
|
}
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
if (/<head[^>]*>/i.test(html)) {
|
|
216
|
+
return html.replace(/<head([^>]*)>/i, `<head$1><base href="${baseUrl}">`);
|
|
196
217
|
}
|
|
218
|
+
if (/<html[^>]*>/i.test(html)) {
|
|
219
|
+
return html.replace(/<html([^>]*)>/i, `<html$1><head><base href="${baseUrl}"></head>`);
|
|
220
|
+
}
|
|
221
|
+
return `<base href="${baseUrl}">${html}`;
|
|
197
222
|
}
|
|
198
223
|
/**
|
|
199
|
-
*
|
|
224
|
+
* Close the pooled browser when the service is disposed (e.g. DI.dispose() at shutdown).
|
|
225
|
+
* Callers that render one-shot (CLI, single email) should dispose so the process can exit.
|
|
200
226
|
*/
|
|
201
|
-
async
|
|
202
|
-
|
|
203
|
-
await
|
|
204
|
-
const timeout = setTimeout(() => {
|
|
205
|
-
reject(new Error('Server close timeout'));
|
|
206
|
-
}, 5000);
|
|
207
|
-
server.close((err) => {
|
|
208
|
-
clearTimeout(timeout);
|
|
209
|
-
if (err)
|
|
210
|
-
reject(err);
|
|
211
|
-
else
|
|
212
|
-
resolve();
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
this.Log.warn(`Error closing server: ${err.message}`);
|
|
218
|
-
// Force close connections if available
|
|
219
|
-
try {
|
|
220
|
-
if ('closeAllConnections' in server) {
|
|
221
|
-
server.closeAllConnections();
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
catch (forceErr) {
|
|
225
|
-
this.Log.error(`Error force closing server connections: ${forceErr.message}`);
|
|
226
|
-
}
|
|
227
|
+
async dispose() {
|
|
228
|
+
if (this._browserPool) {
|
|
229
|
+
await this._browserPool.dispose();
|
|
227
230
|
}
|
|
231
|
+
await super.dispose();
|
|
232
|
+
}
|
|
233
|
+
async render(_templateName, _model, _language) {
|
|
234
|
+
throw new NotSupported('cannot render puppeteer template to string');
|
|
228
235
|
}
|
|
229
236
|
/**
|
|
230
237
|
* Add page event listeners with cleanup function
|
|
231
238
|
*/
|
|
232
239
|
addPageEventListeners(page) {
|
|
233
240
|
const listeners = {
|
|
234
|
-
console: (message) => this.Log.trace(`${message.type().
|
|
235
|
-
pageerror: (
|
|
241
|
+
console: (message) => this.Log.trace(`${message.type().slice(0, 3).toUpperCase()} ${message.text()}`),
|
|
242
|
+
pageerror: (error) => this.Log.error(error.message),
|
|
236
243
|
response: (response) => this.Log.trace(`${response.status()} ${response.url()}`),
|
|
237
|
-
requestfailed: (request) =>
|
|
244
|
+
requestfailed: (request) => {
|
|
245
|
+
const failure = request.failure();
|
|
246
|
+
this.Log.error(`${failure ? failure.errorText : 'request failed'} ${request.url()}`);
|
|
247
|
+
}
|
|
238
248
|
};
|
|
239
249
|
// Add listeners
|
|
240
250
|
page.on('console', listeners.console);
|
|
@@ -244,10 +254,10 @@ export class PuppeteerRenderer extends TemplateRenderer {
|
|
|
244
254
|
// Return cleanup function
|
|
245
255
|
return () => {
|
|
246
256
|
try {
|
|
247
|
-
page.
|
|
248
|
-
page.
|
|
249
|
-
page.
|
|
250
|
-
page.
|
|
257
|
+
page.off('console', listeners.console);
|
|
258
|
+
page.off('pageerror', listeners.pageerror);
|
|
259
|
+
page.off('response', listeners.response);
|
|
260
|
+
page.off('requestfailed', listeners.requestfailed);
|
|
251
261
|
}
|
|
252
262
|
catch (err) {
|
|
253
263
|
this.Log.warn(`Error removing page listeners: ${err.message}`);
|
package/lib/mjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Page } from 'puppeteer';
|
|
2
|
+
import { RenderPhase, RenderProgressCallback } from '@spinajs/templates';
|
|
3
|
+
/**
|
|
4
|
+
* Tracks a single puppeteer render and delivers throttled {@link IRenderProgress}
|
|
5
|
+
* snapshots to a callback. Resource counts come from the page's request lifecycle
|
|
6
|
+
* events; the percentage is phase-weighted (see {@link PHASE_BANDS}), and a
|
|
7
|
+
* heartbeat keeps it ticking during the long Loading/Rendering phases.
|
|
8
|
+
*
|
|
9
|
+
* When constructed without a callback it is entirely inert - no listeners, no
|
|
10
|
+
* timers, no allocations on the hot path - so callers can always create one.
|
|
11
|
+
*/
|
|
12
|
+
export declare class RenderProgressReporter {
|
|
13
|
+
private readonly filePath;
|
|
14
|
+
private readonly enabled;
|
|
15
|
+
private readonly callback;
|
|
16
|
+
private readonly startedAt;
|
|
17
|
+
private phaseNow;
|
|
18
|
+
private loaded;
|
|
19
|
+
private pending;
|
|
20
|
+
private failed;
|
|
21
|
+
private lastEmitAt;
|
|
22
|
+
private lastPercent;
|
|
23
|
+
private heartbeat;
|
|
24
|
+
private page;
|
|
25
|
+
private readonly onRequest;
|
|
26
|
+
private readonly onRequestFinished;
|
|
27
|
+
private readonly onRequestFailed;
|
|
28
|
+
constructor(filePath: string, callback?: RenderProgressCallback);
|
|
29
|
+
/** Attach resource-lifecycle counters to the page. Call once, after newPage(). */
|
|
30
|
+
attach(page: Page): void;
|
|
31
|
+
/** Transition to a new phase and emit immediately (bypassing the throttle). */
|
|
32
|
+
phase(phase: RenderPhase, message?: string): void;
|
|
33
|
+
/** Stop the heartbeat and detach page listeners. Safe to call multiple times. */
|
|
34
|
+
dispose(): void;
|
|
35
|
+
private startHeartbeat;
|
|
36
|
+
private stopHeartbeat;
|
|
37
|
+
private computePercent;
|
|
38
|
+
private emit;
|
|
39
|
+
private defaultMessage;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAmB,WAAW,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAiB1F;;;;;;;;GAQG;AACH,qBAAa,sBAAsB;IA+BrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA9BrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAEhD,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAK;IAEnB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;IAExB,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,CAAC,IAAI,CAAqB;IAGjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAExB;IACF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAIhC;IACF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAI9B;gBAE2B,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB;IAKhF,kFAAkF;IAC3E,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAW/B,+EAA+E;IACxE,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAiBxD,iFAAiF;IAC1E,OAAO,IAAI,IAAI;IAWtB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,IAAI;IAsCZ,OAAO,CAAC,cAAc;CAsBvB"}
|