@spinajs/queue-templates-pdf 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.
Files changed (54) hide show
  1. package/README.md +101 -0
  2. package/lib/cjs/RenderPdfJob.d.ts +38 -0
  3. package/lib/cjs/RenderPdfJob.d.ts.map +1 -0
  4. package/lib/cjs/RenderPdfJob.js +114 -0
  5. package/lib/cjs/RenderPdfJob.js.map +1 -0
  6. package/lib/cjs/index.d.ts +5 -0
  7. package/lib/cjs/index.d.ts.map +1 -0
  8. package/lib/cjs/index.js +25 -0
  9. package/lib/cjs/index.js.map +1 -0
  10. package/lib/cjs/package.json +1 -0
  11. package/lib/cjs/protocol.d.ts +49 -0
  12. package/lib/cjs/protocol.d.ts.map +1 -0
  13. package/lib/cjs/protocol.js +3 -0
  14. package/lib/cjs/protocol.js.map +1 -0
  15. package/lib/cjs/render-worker.d.ts +2 -0
  16. package/lib/cjs/render-worker.d.ts.map +1 -0
  17. package/lib/cjs/render-worker.js +32 -0
  18. package/lib/cjs/render-worker.js.map +1 -0
  19. package/lib/cjs/render.d.ts +17 -0
  20. package/lib/cjs/render.d.ts.map +1 -0
  21. package/lib/cjs/render.js +67 -0
  22. package/lib/cjs/render.js.map +1 -0
  23. package/lib/cjs/schedule.d.ts +48 -0
  24. package/lib/cjs/schedule.d.ts.map +1 -0
  25. package/lib/cjs/schedule.js +37 -0
  26. package/lib/cjs/schedule.js.map +1 -0
  27. package/lib/mjs/RenderPdfJob.d.ts +38 -0
  28. package/lib/mjs/RenderPdfJob.d.ts.map +1 -0
  29. package/lib/mjs/RenderPdfJob.js +111 -0
  30. package/lib/mjs/RenderPdfJob.js.map +1 -0
  31. package/lib/mjs/index.d.ts +5 -0
  32. package/lib/mjs/index.d.ts.map +1 -0
  33. package/lib/mjs/index.js +9 -0
  34. package/lib/mjs/index.js.map +1 -0
  35. package/lib/mjs/package.json +1 -0
  36. package/lib/mjs/protocol.d.ts +49 -0
  37. package/lib/mjs/protocol.d.ts.map +1 -0
  38. package/lib/mjs/protocol.js +2 -0
  39. package/lib/mjs/protocol.js.map +1 -0
  40. package/lib/mjs/render-worker.d.ts +2 -0
  41. package/lib/mjs/render-worker.d.ts.map +1 -0
  42. package/lib/mjs/render-worker.js +30 -0
  43. package/lib/mjs/render-worker.js.map +1 -0
  44. package/lib/mjs/render.d.ts +17 -0
  45. package/lib/mjs/render.d.ts.map +1 -0
  46. package/lib/mjs/render.js +64 -0
  47. package/lib/mjs/render.js.map +1 -0
  48. package/lib/mjs/schedule.d.ts +48 -0
  49. package/lib/mjs/schedule.d.ts.map +1 -0
  50. package/lib/mjs/schedule.js +34 -0
  51. package/lib/mjs/schedule.js.map +1 -0
  52. package/lib/tsconfig.cjs.tsbuildinfo +1 -0
  53. package/lib/tsconfig.mjs.tsbuildinfo +1 -0
  54. package/package.json +65 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderPdfJob.d.ts","sourceRoot":"","sources":["../../src/RenderPdfJob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,YAAY,EAAE,MAAM,eAAe,CAAC;AAGnD,OAAO,EAAE,QAAQ,EAAO,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAiB,MAAM,eAAe,CAAC;AAqB7E;;;;;GAKG;AACH,qBACa,YAAa,SAAQ,QAAQ;IAExC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB,wDAAwD;IACjD,KAAK,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IACxC,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IAC9B,kFAAkF;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;IACxB,4DAA4D;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEb,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrE,6DAA6D;IAC7D,SAAS,CAAC,WAAW,IAAI,YAAY;IAIrC,SAAS,CAAC,YAAY,IAAI,cAAc;IAaxC;;;;OAIG;IACH,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;CA6CrG"}
@@ -0,0 +1,111 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { fork } from 'child_process';
11
+ import { dirname, join } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { QueueJob, Job } from '@spinajs/queue';
14
+ import { Log, Logger } from '@spinajs/log';
15
+ /** Default max render duration before the worker is force-killed (ms). */
16
+ const DEFAULT_RENDER_TIMEOUT_MS = 120000;
17
+ /**
18
+ * This module's directory, resolved across the dual ESM/CJS build. Under CJS
19
+ * `__dirname` exists; under ESM it does not, so we fall back to `import.meta.url`
20
+ * - accessed via `eval` so the `import.meta` token never appears in the
21
+ * commonjs-compiled output (which would otherwise fail to compile).
22
+ */
23
+ function moduleDir() {
24
+ if (typeof __dirname !== 'undefined') {
25
+ return __dirname;
26
+ }
27
+ // eslint-disable-next-line no-eval
28
+ const url = eval('import.meta.url');
29
+ return dirname(fileURLToPath(url));
30
+ }
31
+ /**
32
+ * Queue job that renders a PDF in an isolated worker process and reports render
33
+ * progress (percent + phase + message) back to the queue. All heavy/crash-prone
34
+ * work (Chromium, template compile, fs upload) runs in the forked worker; this
35
+ * class only supervises it.
36
+ */
37
+ let RenderPdfJob = class RenderPdfJob extends QueueJob {
38
+ async execute(progress) {
39
+ const child = this.spawnWorker();
40
+ return this.superviseChild(child, progress);
41
+ }
42
+ /** Spawn the render worker process. Overridable in tests. */
43
+ spawnWorker() {
44
+ return fork(join(moduleDir(), 'render-worker.js'));
45
+ }
46
+ buildRequest() {
47
+ return {
48
+ input: this.input,
49
+ template: this.template,
50
+ model: this.model,
51
+ lang: this.lang,
52
+ output: this.output,
53
+ pdfOptions: this.pdfOptions,
54
+ assetBasePath: this.assetBasePath,
55
+ config: this.config,
56
+ };
57
+ }
58
+ /**
59
+ * Drive one worker to completion: relay progress to the queue, resolve on
60
+ * `done`, reject on `error` / unexpected exit, and force-kill on timeout.
61
+ * Every terminal path clears the timer and detaches listeners exactly once.
62
+ */
63
+ superviseChild(child, progress) {
64
+ const timeoutMs = this.timeoutMs ?? DEFAULT_RENDER_TIMEOUT_MS;
65
+ return new Promise((resolve, reject) => {
66
+ let settled = false;
67
+ const settle = (action) => {
68
+ if (settled) {
69
+ return;
70
+ }
71
+ settled = true;
72
+ clearTimeout(timer);
73
+ child.removeAllListeners('message');
74
+ child.removeAllListeners('error');
75
+ child.removeAllListeners('exit');
76
+ action();
77
+ };
78
+ const timer = setTimeout(() => {
79
+ settle(() => reject(new Error(`Render worker timed out after ${timeoutMs}ms`)));
80
+ child.kill('SIGKILL');
81
+ }, timeoutMs);
82
+ child.on('message', (m) => {
83
+ switch (m.type) {
84
+ case 'progress':
85
+ // fire-and-forget: never block the worker on progress persistence
86
+ progress(m.percent, { phase: m.phase, message: m.message }).catch((err) => this.Log?.warn(`Failed to persist progress: ${err.message}`));
87
+ break;
88
+ case 'done':
89
+ settle(() => resolve(m.result));
90
+ break;
91
+ case 'error':
92
+ settle(() => reject(new Error(m.message)));
93
+ child.kill();
94
+ break;
95
+ }
96
+ });
97
+ child.on('error', (err) => settle(() => reject(err)));
98
+ child.on('exit', (code) => settle(() => reject(new Error(`Render worker exited unexpectedly with code ${code}`))));
99
+ child.send(this.buildRequest());
100
+ });
101
+ }
102
+ };
103
+ __decorate([
104
+ Logger('RenderPdfJob'),
105
+ __metadata("design:type", Log)
106
+ ], RenderPdfJob.prototype, "Log", void 0);
107
+ RenderPdfJob = __decorate([
108
+ Job()
109
+ ], RenderPdfJob);
110
+ export { RenderPdfJob };
111
+ //# sourceMappingURL=RenderPdfJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderPdfJob.js","sourceRoot":"","sources":["../../src/RenderPdfJob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './protocol.js';
2
+ export * from './render.js';
3
+ export * from './RenderPdfJob.js';
4
+ export * from './schedule.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
@@ -0,0 +1,9 @@
1
+ // @spinajs/queue-templates-pdf
2
+ //
3
+ // A queue job that renders a PDF template in an isolated worker process and
4
+ // reports render progress (percent + phase + message) back to the queue.
5
+ export * from './protocol.js';
6
+ export * from './render.js';
7
+ export * from './RenderPdfJob.js';
8
+ export * from './schedule.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,4EAA4E;AAC5E,yEAAyE;AAEzE,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where a rendered PDF is written - a named @spinajs/fs provider and a path
3
+ * relative to that provider's base.
4
+ */
5
+ export interface IRenderOutput {
6
+ /** Configured @spinajs/fs provider name (local / temp / s3 / ...). */
7
+ provider: string;
8
+ /** Destination path relative to the provider base. */
9
+ path: string;
10
+ }
11
+ /**
12
+ * A single PDF render request handed to the worker process.
13
+ */
14
+ export interface IRenderRequest {
15
+ /** Template path (template mode) or a raw HTML string. */
16
+ input: string;
17
+ /** When true, `input` is a template rendered via the text engine by extension; otherwise it is raw HTML. */
18
+ template?: boolean;
19
+ /** Model passed to the template (template mode). */
20
+ model?: unknown;
21
+ /** Optional language (template mode). */
22
+ lang?: string;
23
+ /** Where to write the resulting PDF. */
24
+ output: IRenderOutput;
25
+ /** Puppeteer PDF options (format, landscape, scale, ...). */
26
+ pdfOptions?: Record<string, unknown>;
27
+ /** Directory served over the local http server so relative asset URLs resolve (raw HTML mode). */
28
+ assetBasePath?: string;
29
+ /**
30
+ * Optional inline configuration for the worker. When present it is used to
31
+ * bootstrap the worker's Configuration; otherwise the worker resolves the
32
+ * ambient app Configuration (re-bootstrap model).
33
+ */
34
+ config?: unknown;
35
+ }
36
+ /** Messages the worker sends back to its parent over the IPC channel. */
37
+ export type WorkerMessage = {
38
+ type: 'progress';
39
+ percent: number;
40
+ phase: string;
41
+ message?: string;
42
+ } | {
43
+ type: 'done';
44
+ result: IRenderOutput;
45
+ } | {
46
+ type: 'error';
47
+ message: string;
48
+ };
49
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,aAAa,CAAC;IACtB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,kGAAkG;IAClG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,yEAAyE;AACzE,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/protocol.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=render-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-worker.d.ts","sourceRoot":"","sources":["../../src/render-worker.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import { DI } from '@spinajs/di';
2
+ import { renderPdfToProvider } from './render.js';
3
+ /**
4
+ * Render worker entrypoint. Forked by {@link RenderPdfJob} with an IPC channel:
5
+ * the parent sends an {@link IRenderRequest}, the worker streams `progress`
6
+ * messages and finishes with `done` or `error`, then exits so its process (and
7
+ * the pooled Chromium) is fully reclaimed.
8
+ */
9
+ function send(message) {
10
+ process.send?.(message);
11
+ }
12
+ process.on('message', async (req) => {
13
+ try {
14
+ const result = await renderPdfToProvider(req, (m) => send(m));
15
+ send({ type: 'done', result });
16
+ await DI.dispose();
17
+ process.exit(0);
18
+ }
19
+ catch (err) {
20
+ send({ type: 'error', message: err?.message ?? String(err) });
21
+ try {
22
+ await DI.dispose();
23
+ }
24
+ catch {
25
+ /* best-effort cleanup on the error path */
26
+ }
27
+ process.exit(1);
28
+ }
29
+ });
30
+ //# sourceMappingURL=render-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-worker.js","sourceRoot":"","sources":["../../src/render-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGlD;;;;;GAKG;AACH,SAAS,IAAI,CAAC,OAAsB;IAClC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,GAAmB,EAAE,EAAE;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC/B,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,2CAA2C;QAC7C,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { IRenderRequest, IRenderOutput, WorkerMessage } from './protocol.js';
2
+ import '@spinajs/templates-pdf';
3
+ import '@spinajs/fs';
4
+ type ProgressMessage = Extract<WorkerMessage, {
5
+ type: 'progress';
6
+ }>;
7
+ /**
8
+ * Render a PDF for `req` and upload it to the requested @spinajs/fs provider,
9
+ * reporting progress via `onProgress`. This is the testable core shared by the
10
+ * worker entrypoint - it performs the heavy work (template compile, Chromium
11
+ * render, upload) and cleans up its temp file.
12
+ *
13
+ * @returns the output descriptor (provider + path) on success.
14
+ */
15
+ export declare function renderPdfToProvider(req: IRenderRequest, onProgress: (m: ProgressMessage) => void): Promise<IRenderOutput>;
16
+ export {};
17
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG7E,OAAO,wBAAwB,CAAC;AAChC,OAAO,aAAa,CAAC;AAErB,KAAK,eAAe,GAAG,OAAO,CAAC,aAAa,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AA4BpE;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,CA0B/H"}
@@ -0,0 +1,64 @@
1
+ import { DI } from '@spinajs/di';
2
+ import { Configuration, FrameworkConfiguration } from '@spinajs/configuration';
3
+ import { Templates } from '@spinajs/templates';
4
+ import { PdfRenderer } from '@spinajs/templates-pdf';
5
+ import { fsService, FsBootsrapper } from '@spinajs/fs';
6
+ import { join } from 'path';
7
+ import { tmpdir } from 'os';
8
+ import { unlink } from 'fs/promises';
9
+ // registers the PDF renderer (+ puppeteer/pug) and the fs providers as a side effect
10
+ import '@spinajs/templates-pdf';
11
+ import '@spinajs/fs';
12
+ let tmpCounter = 0;
13
+ /**
14
+ * Bootstrap the worker's DI/config. With an inline `config` a Configuration is
15
+ * registered from it; otherwise the ambient Configuration is resolved (the
16
+ * re-bootstrap model - the worker shares the app's config environment). The fs
17
+ * providers are then initialized.
18
+ */
19
+ async function bootstrap(config) {
20
+ if (config) {
21
+ class WorkerConfiguration extends FrameworkConfiguration {
22
+ onLoad() {
23
+ return config;
24
+ }
25
+ }
26
+ DI.register(WorkerConfiguration).as(Configuration);
27
+ }
28
+ await DI.resolve(Configuration);
29
+ // register the `__file_provider__` factory, then build the configured providers
30
+ const fsBootstrapper = await DI.resolve(FsBootsrapper);
31
+ fsBootstrapper.bootstrap();
32
+ await DI.resolve(fsService);
33
+ }
34
+ /**
35
+ * Render a PDF for `req` and upload it to the requested @spinajs/fs provider,
36
+ * reporting progress via `onProgress`. This is the testable core shared by the
37
+ * worker entrypoint - it performs the heavy work (template compile, Chromium
38
+ * render, upload) and cleans up its temp file.
39
+ *
40
+ * @returns the output descriptor (provider + path) on success.
41
+ */
42
+ export async function renderPdfToProvider(req, onProgress) {
43
+ await bootstrap(req.config);
44
+ const templates = await DI.resolve(Templates);
45
+ const renderer = await DI.resolve(PdfRenderer, [req.pdfOptions ?? {}]);
46
+ const provider = await DI.resolve('__file_provider__', [req.output.provider]);
47
+ const html = req.template ? await templates.render(req.input, req.model, req.lang) : req.input;
48
+ const tmp = join(tmpdir(), `spinajs-pdf-${process.pid}-${Date.now()}-${tmpCounter++}.pdf`);
49
+ try {
50
+ await renderer.renderHtmlToFile(html, tmp, {
51
+ assetBasePath: req.assetBasePath,
52
+ onProgress: (p) => onProgress({ type: 'progress', percent: p.percent, phase: p.phase, message: p.message }),
53
+ });
54
+ // push the locally-rendered temp file to the configured destination provider
55
+ await provider.upload(tmp, req.output.path);
56
+ return req.output;
57
+ }
58
+ finally {
59
+ await unlink(tmp).catch(() => {
60
+ /* temp may not exist if the render failed before writing */
61
+ });
62
+ }
63
+ }
64
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { IRenderOutput } from './protocol.js';
2
+ /** Payload for a {@link RenderPdfJob} - the fields describing what to render and where. */
3
+ export interface IRenderPdfJobPayload {
4
+ /** Template path (template mode) or raw HTML string. */
5
+ input: string;
6
+ /** When true, `input` is a template rendered by extension; otherwise raw HTML. */
7
+ template?: boolean;
8
+ model?: unknown;
9
+ lang?: string;
10
+ /** Destination @spinajs/fs provider + path. */
11
+ output: IRenderOutput;
12
+ pdfOptions?: Record<string, unknown>;
13
+ assetBasePath?: string;
14
+ /** Optional inline worker config; omit to re-bootstrap the ambient app config. */
15
+ config?: unknown;
16
+ /** Max render duration before the worker is killed (ms). */
17
+ timeoutMs?: number;
18
+ }
19
+ /** Scheduling options for {@link scheduleRenderPdf}. */
20
+ export interface IScheduleRenderPdfOptions {
21
+ /** Delay before the job is delivered (ms). */
22
+ delay?: number;
23
+ /** Cron expression for recurring rendering (e.g. `0 6 * * *` for a daily report). */
24
+ cron?: string;
25
+ /** Period between repeats (ms), used with `repeat`. */
26
+ period?: number;
27
+ /** Number of times to repeat the schedule. */
28
+ repeat?: number;
29
+ }
30
+ /**
31
+ * Enqueue a PDF render job. With no options it runs as soon as a consumer picks
32
+ * it up; `delay` schedules it later; `cron` (or `period` + `repeat`) makes it
33
+ * recurring.
34
+ *
35
+ * Progress is tracked on the queue job model and exposed via
36
+ * `@spinajs/queue-http-progress` at `GET jobs/v1/:jobId/status`
37
+ * (percent + phase + message).
38
+ *
39
+ * @returns the JobId, for correlating the render with its progress/status endpoint.
40
+ *
41
+ * @example
42
+ * // render now, then poll GET jobs/v1/:jobId/status
43
+ * const jobId = await scheduleRenderPdf({ input: 'invoice.pug', template: true, model, output: { provider: 's3', path: 'invoices/1.pdf' } });
44
+ * // daily at 6am
45
+ * await scheduleRenderPdf({ input: 'report.pug', template: true, output: { provider: 'local', path: 'daily.pdf' } }, { cron: '0 6 * * *' });
46
+ */
47
+ export declare function scheduleRenderPdf(payload: IRenderPdfJobPayload, options?: IScheduleRenderPdfOptions): Promise<string | undefined>;
48
+ //# sourceMappingURL=schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/schedule.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,2FAA2F;AAC3F,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wDAAwD;AACxD,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAWvI"}
@@ -0,0 +1,34 @@
1
+ import { RenderPdfJob } from './RenderPdfJob.js';
2
+ /**
3
+ * Enqueue a PDF render job. With no options it runs as soon as a consumer picks
4
+ * it up; `delay` schedules it later; `cron` (or `period` + `repeat`) makes it
5
+ * recurring.
6
+ *
7
+ * Progress is tracked on the queue job model and exposed via
8
+ * `@spinajs/queue-http-progress` at `GET jobs/v1/:jobId/status`
9
+ * (percent + phase + message).
10
+ *
11
+ * @returns the JobId, for correlating the render with its progress/status endpoint.
12
+ *
13
+ * @example
14
+ * // render now, then poll GET jobs/v1/:jobId/status
15
+ * const jobId = await scheduleRenderPdf({ input: 'invoice.pug', template: true, model, output: { provider: 's3', path: 'invoices/1.pdf' } });
16
+ * // daily at 6am
17
+ * await scheduleRenderPdf({ input: 'report.pug', template: true, output: { provider: 'local', path: 'daily.pdf' } }, { cron: '0 6 * * *' });
18
+ */
19
+ export async function scheduleRenderPdf(payload, options) {
20
+ // Only include schedule fields that are set. IMessageOptions declares all fields
21
+ // required, but emit() spreads options into the message, so absent fields behave
22
+ // exactly like calling emit with no options.
23
+ const scheduleOptions = {};
24
+ if (options?.delay !== undefined)
25
+ scheduleOptions.ScheduleDelay = options.delay;
26
+ if (options?.cron !== undefined)
27
+ scheduleOptions.ScheduleCron = options.cron;
28
+ if (options?.period !== undefined)
29
+ scheduleOptions.SchedulePeriod = options.period;
30
+ if (options?.repeat !== undefined)
31
+ scheduleOptions.ScheduleRepeat = options.repeat;
32
+ return RenderPdfJob.emit(payload, scheduleOptions);
33
+ }
34
+ //# sourceMappingURL=schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../src/schedule.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAiCjD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAA6B,EAAE,OAAmC;IACxG,iFAAiF;IACjF,iFAAiF;IACjF,6CAA6C;IAC7C,MAAM,eAAe,GAA6B,EAAE,CAAC;IACrD,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS;QAAE,eAAe,CAAC,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;IAChF,IAAI,OAAO,EAAE,IAAI,KAAK,SAAS;QAAE,eAAe,CAAC,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7E,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS;QAAE,eAAe,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IACnF,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS;QAAE,eAAe,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAEnF,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,eAAkC,CAAC,CAAC;AACxE,CAAC"}