@codebucket/puppet-master 1.0.6 → 1.0.7
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/AGENTS.md +69 -0
- package/README.md +173 -0
- package/dist/index.d.ts +47 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -14
- package/package.json +26 -5
- package/schemas/pdf-task-options.schema.json +92 -0
- package/.idea/Puppet-Master-Module.iml +0 -12
- package/.idea/material_theme_project_new.xml +0 -17
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
- package/src/index.ts +0 -64
- package/tsconfig.json +0 -19
package/AGENTS.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
Use this package when code needs to render HTML to a PDF through the Puppet Master server.
|
|
4
|
+
|
|
5
|
+
## Package
|
|
6
|
+
|
|
7
|
+
- Install: `npm install @codebucket/puppet-master`
|
|
8
|
+
- Import: `import { PuppetMaster } from "@codebucket/puppet-master";`
|
|
9
|
+
|
|
10
|
+
## Public API
|
|
11
|
+
|
|
12
|
+
This package currently exposes one public method only:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
new PuppetMaster({
|
|
16
|
+
baseUrl: string,
|
|
17
|
+
apiKey: string,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
await client.pdf({
|
|
21
|
+
pdfPath: string,
|
|
22
|
+
content: string,
|
|
23
|
+
launchOptions?: Record<string, unknown>,
|
|
24
|
+
pageOptions?: Record<string, unknown>,
|
|
25
|
+
pdfOptions?: Record<string, unknown>,
|
|
26
|
+
otherPageFunctions?: Array<
|
|
27
|
+
[methodName: string] | [methodName: string, args: unknown[]]
|
|
28
|
+
>,
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Required Behavior
|
|
33
|
+
|
|
34
|
+
- Always pass `baseUrl` and `apiKey` when constructing the client.
|
|
35
|
+
- Always pass both `pdfPath` and `content` when calling `pdf`.
|
|
36
|
+
- Treat `pdfPath` as a local output path only. It is not part of the server task payload.
|
|
37
|
+
- Treat `launchOptions`, `pageOptions`, and `pdfOptions` as pass-through Puppeteer option objects.
|
|
38
|
+
- Model `otherPageFunctions` as an ordered tuple array of page method calls.
|
|
39
|
+
|
|
40
|
+
## Wire Contract
|
|
41
|
+
|
|
42
|
+
The client sends:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"task": {
|
|
47
|
+
"content": "<html>...</html>",
|
|
48
|
+
"launchOptions": {},
|
|
49
|
+
"pageOptions": {},
|
|
50
|
+
"pdfOptions": {},
|
|
51
|
+
"otherPageFunctions": [
|
|
52
|
+
["waitForNetworkIdle", [{ "idleTime": 500 }]]
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The client also sends the `x-api-key` header.
|
|
59
|
+
|
|
60
|
+
## Constraints
|
|
61
|
+
|
|
62
|
+
- Do not invent extra public methods.
|
|
63
|
+
- Do not model `otherPageFunctions` as an object.
|
|
64
|
+
- Do not call the server directly if this package is already available.
|
|
65
|
+
- Prefer the exported `PuppetMasterClientOptions`, `RemotePdfTaskOptions`, `PdfTaskOptions`, and `PageFunctionCall` types in TypeScript.
|
|
66
|
+
|
|
67
|
+
## Current Server Caveat
|
|
68
|
+
|
|
69
|
+
- The uploaded server currently executes `otherPageFunctions` after `page.pdf()`. Do not rely on those calls to affect the generated PDF unless the server implementation changes.
|
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# `@codebucket/puppet-master`
|
|
2
|
+
|
|
3
|
+
TypeScript client for the Puppet Master HTML-to-PDF server.
|
|
4
|
+
|
|
5
|
+
The uploaded server code shows that this client talks to a single `POST /pdf` endpoint, sends a JSON body shaped like `{ task: ... }`, and downloads the generated PDF as a file stream.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @codebucket/puppet-master
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { PuppetMaster } from "@codebucket/puppet-master";
|
|
17
|
+
|
|
18
|
+
const client = new PuppetMaster({
|
|
19
|
+
baseUrl: process.env.PUPPET_MASTER_BASE_URL!,
|
|
20
|
+
apiKey: process.env.PUPPET_MASTER_API_KEY!,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await client.pdf({
|
|
24
|
+
pdfPath: "./invoice.pdf",
|
|
25
|
+
content: `
|
|
26
|
+
<!doctype html>
|
|
27
|
+
<html>
|
|
28
|
+
<body>
|
|
29
|
+
<h1>Invoice</h1>
|
|
30
|
+
<p>Hello from Puppet Master.</p>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
`,
|
|
34
|
+
pageOptions: {
|
|
35
|
+
waitUntil: "networkidle0",
|
|
36
|
+
},
|
|
37
|
+
pdfOptions: {
|
|
38
|
+
format: "A4",
|
|
39
|
+
printBackground: true,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Public API
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import {
|
|
48
|
+
PuppetMaster,
|
|
49
|
+
type PuppetMasterClientOptions,
|
|
50
|
+
type PdfTaskOptions,
|
|
51
|
+
type RemotePdfTaskOptions,
|
|
52
|
+
type PageFunctionCall,
|
|
53
|
+
} from "@codebucket/puppet-master";
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `new PuppetMaster(options)`
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
interface PuppetMasterClientOptions {
|
|
60
|
+
baseUrl: string;
|
|
61
|
+
apiKey: string;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `await client.pdf(task)`
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
type PageFunctionCall =
|
|
69
|
+
| [methodName: string]
|
|
70
|
+
| [methodName: string, args: unknown[]];
|
|
71
|
+
|
|
72
|
+
interface RemotePdfTaskOptions {
|
|
73
|
+
launchOptions?: Record<string, unknown>;
|
|
74
|
+
pageOptions?: Record<string, unknown>;
|
|
75
|
+
pdfOptions?: Record<string, unknown>;
|
|
76
|
+
otherPageFunctions?: PageFunctionCall[];
|
|
77
|
+
content: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface PdfTaskOptions extends RemotePdfTaskOptions {
|
|
81
|
+
pdfPath: string;
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## What The Server Actually Does
|
|
86
|
+
|
|
87
|
+
Based on the uploaded server code:
|
|
88
|
+
|
|
89
|
+
1. `POST /pdf` receives `{ task }`.
|
|
90
|
+
2. The worker launches Puppeteer with `task.launchOptions`.
|
|
91
|
+
3. It creates a page and calls `page.setContent(task.content, task.pageOptions)`.
|
|
92
|
+
4. It generates a PDF with `page.pdf({ ...task.pdfOptions, path })`.
|
|
93
|
+
5. It optionally runs `task.otherPageFunctions`.
|
|
94
|
+
6. The server downloads the generated file back to the client.
|
|
95
|
+
|
|
96
|
+
Important caveat:
|
|
97
|
+
|
|
98
|
+
- In the current server implementation, `otherPageFunctions` runs after `page.pdf()`. That means those calls do not affect the PDF that was already generated.
|
|
99
|
+
|
|
100
|
+
## Wire Format
|
|
101
|
+
|
|
102
|
+
The client sends this request body to the server:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"task": {
|
|
107
|
+
"content": "<html>...</html>",
|
|
108
|
+
"launchOptions": {},
|
|
109
|
+
"pageOptions": {},
|
|
110
|
+
"pdfOptions": {},
|
|
111
|
+
"otherPageFunctions": [
|
|
112
|
+
["waitForNetworkIdle", [{ "idleTime": 500 }]]
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Notes:
|
|
119
|
+
|
|
120
|
+
- `pdfPath` is local-only. It is where this client writes the downloaded PDF on the caller's machine.
|
|
121
|
+
- The client always sends the `x-api-key` header.
|
|
122
|
+
- The uploaded server route currently has API-key validation commented out, but the contract still reserves that header.
|
|
123
|
+
|
|
124
|
+
## `otherPageFunctions` Format
|
|
125
|
+
|
|
126
|
+
Use an ordered array of page method calls, not an object.
|
|
127
|
+
|
|
128
|
+
Examples:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
const waitForIdle: PageFunctionCall = ["waitForNetworkIdle", [{ idleTime: 500 }]];
|
|
132
|
+
const emulateScreen: PageFunctionCall = ["emulateMediaType", ["screen"]];
|
|
133
|
+
const bringToFront: PageFunctionCall = ["bringToFront"];
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Agent Guidance
|
|
137
|
+
|
|
138
|
+
If you are generating code with AI:
|
|
139
|
+
|
|
140
|
+
1. Install `@codebucket/puppet-master`.
|
|
141
|
+
2. Import from the package root.
|
|
142
|
+
3. Create one `PuppetMaster` client with `baseUrl` and `apiKey`.
|
|
143
|
+
4. Call only `await client.pdf(...)` because this package currently exposes only one public method.
|
|
144
|
+
5. Provide `content` and `pdfPath` every time.
|
|
145
|
+
6. Model `otherPageFunctions` as tuple arrays, not as a map or object.
|
|
146
|
+
7. Do not send `pdfPath` in hand-written HTTP requests to the server; that field is client-side only.
|
|
147
|
+
|
|
148
|
+
## Minimal Agent Example
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { PuppetMaster, type PageFunctionCall } from "@codebucket/puppet-master";
|
|
152
|
+
|
|
153
|
+
async function renderPdf(html: string, outputPath: string) {
|
|
154
|
+
const otherPageFunctions: PageFunctionCall[] = [
|
|
155
|
+
["waitForNetworkIdle", [{ idleTime: 500 }]],
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const client = new PuppetMaster({
|
|
159
|
+
baseUrl: process.env.PUPPET_MASTER_BASE_URL!,
|
|
160
|
+
apiKey: process.env.PUPPET_MASTER_API_KEY!,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
await client.pdf({
|
|
164
|
+
content: html,
|
|
165
|
+
pdfPath: outputPath,
|
|
166
|
+
otherPageFunctions,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Machine-Readable Schema
|
|
172
|
+
|
|
173
|
+
For tooling and agent integration, see `schemas/pdf-task-options.schema.json`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
export interface PuppetMasterClientOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Base URL of the Puppet Master API.
|
|
4
|
+
* Example: https://your-hosted-service.example.com
|
|
5
|
+
*/
|
|
2
6
|
baseUrl: string;
|
|
7
|
+
/**
|
|
8
|
+
* API key sent as the x-api-key header on every request.
|
|
9
|
+
*/
|
|
3
10
|
apiKey: string;
|
|
4
|
-
}
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
}
|
|
12
|
+
export type PageFunctionCall = [methodName: string] | [methodName: string, args: unknown[]];
|
|
13
|
+
export interface RemotePdfTaskOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Options forwarded to the remote Puppeteer launch call.
|
|
16
|
+
*/
|
|
17
|
+
launchOptions?: Record<string, unknown>;
|
|
18
|
+
/**
|
|
19
|
+
* Options applied to the remote page before rendering.
|
|
20
|
+
*/
|
|
21
|
+
pageOptions?: Record<string, unknown>;
|
|
22
|
+
/**
|
|
23
|
+
* PDF generation options passed to the remote page.pdf call.
|
|
24
|
+
*/
|
|
25
|
+
pdfOptions?: Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Ordered page method calls forwarded to the remote Puppeteer page instance.
|
|
28
|
+
* Example: [["waitForNetworkIdle", [{ idleTime: 500 }]], ["emulateMediaType", ["screen"]]]
|
|
29
|
+
*
|
|
30
|
+
* Note: the uploaded server currently executes these calls after page.pdf().
|
|
31
|
+
*/
|
|
32
|
+
otherPageFunctions?: PageFunctionCall[];
|
|
33
|
+
/**
|
|
34
|
+
* HTML content rendered by the remote service before creating the PDF.
|
|
35
|
+
*/
|
|
11
36
|
content: string;
|
|
12
|
-
}
|
|
37
|
+
}
|
|
38
|
+
export interface PdfTaskOptions extends RemotePdfTaskOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Absolute or relative path on the local machine where the downloaded PDF should be written.
|
|
41
|
+
* This field is used only by the client and is not forwarded to the server.
|
|
42
|
+
*/
|
|
43
|
+
pdfPath: string;
|
|
44
|
+
}
|
|
13
45
|
export declare class PuppetMaster {
|
|
14
46
|
private readonly axios;
|
|
15
|
-
constructor(opts:
|
|
16
|
-
|
|
47
|
+
constructor(opts: PuppetMasterClientOptions);
|
|
48
|
+
/**
|
|
49
|
+
* Render HTML content to a PDF via the remote Puppet Master service
|
|
50
|
+
* and stream the generated file to pdfPath on the local filesystem.
|
|
51
|
+
*/
|
|
52
|
+
pdf(opts: PdfTaskOptions): Promise<void>;
|
|
17
53
|
}
|
|
18
|
-
export {};
|
|
19
54
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,yBAAyB;IACtC;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GACtB,CAAC,UAAU,EAAE,MAAM,CAAC,GACpB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,aAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC;;OAEG;IACH,WAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC;;OAEG;IACH,UAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC;;;;;OAKG;IACH,kBAAmB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACzC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,oBAAoB;IACxD;;;OAGG;IACH,OAAO,EAAG,MAAM,CAAC;CACpB;AA4DD,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;gBAE1B,IAAI,EAAE,yBAAyB;IAW3C;;;OAGG;IACG,GAAG,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBjD"}
|
package/dist/index.js
CHANGED
|
@@ -32,8 +32,58 @@ const util_1 = __importDefault(require("util"));
|
|
|
32
32
|
const stream_1 = __importDefault(require("stream"));
|
|
33
33
|
const fs_1 = __importDefault(require("fs"));
|
|
34
34
|
const pipeline = util_1.default.promisify(stream_1.default.pipeline);
|
|
35
|
+
function assertNonEmptyString(value, fieldName) {
|
|
36
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
37
|
+
throw new Error(`${fieldName} must be a non-empty string.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function streamToString(readable) {
|
|
41
|
+
const chunks = [];
|
|
42
|
+
return await new Promise((resolve, reject) => {
|
|
43
|
+
readable.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
44
|
+
readable.on("error", reject);
|
|
45
|
+
readable.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function extractAxiosErrorMessage(err) {
|
|
49
|
+
const responseData = err.response?.data;
|
|
50
|
+
if (!responseData) {
|
|
51
|
+
return err.message;
|
|
52
|
+
}
|
|
53
|
+
if (typeof responseData === "string") {
|
|
54
|
+
try {
|
|
55
|
+
const parsed = JSON.parse(responseData);
|
|
56
|
+
return parsed.message ?? err.message;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return responseData;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (typeof responseData.on === "function") {
|
|
63
|
+
const text = await streamToString(responseData);
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(text);
|
|
66
|
+
return parsed.message ?? err.message;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (typeof responseData === "object" && responseData !== null && "message" in responseData) {
|
|
73
|
+
const { message } = responseData;
|
|
74
|
+
if (typeof message === "string" && message.length > 0) {
|
|
75
|
+
return message;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return err.message;
|
|
79
|
+
}
|
|
80
|
+
function createRemotePdfTask(opts) {
|
|
81
|
+
const { pdfPath, ...task } = opts;
|
|
82
|
+
return task;
|
|
83
|
+
}
|
|
35
84
|
class PuppetMaster {
|
|
36
85
|
constructor(opts) {
|
|
86
|
+
assertNonEmptyString(opts.baseUrl, "baseUrl");
|
|
37
87
|
this.axios = axios_1.default.create({
|
|
38
88
|
baseURL: opts.baseUrl,
|
|
39
89
|
headers: {
|
|
@@ -41,28 +91,22 @@ class PuppetMaster {
|
|
|
41
91
|
}
|
|
42
92
|
});
|
|
43
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Render HTML content to a PDF via the remote Puppet Master service
|
|
96
|
+
* and stream the generated file to pdfPath on the local filesystem.
|
|
97
|
+
*/
|
|
44
98
|
async pdf(opts) {
|
|
99
|
+
assertNonEmptyString(opts.pdfPath, "pdfPath");
|
|
100
|
+
assertNonEmptyString(opts.content, "content");
|
|
45
101
|
try {
|
|
46
|
-
const request = await this.axios.post("/pdf", { task: opts }, {
|
|
102
|
+
const request = await this.axios.post("/pdf", { task: createRemotePdfTask(opts) }, {
|
|
47
103
|
responseType: "stream"
|
|
48
104
|
});
|
|
49
105
|
await pipeline(request.data, fs_1.default.createWriteStream(opts.pdfPath));
|
|
50
106
|
}
|
|
51
107
|
catch (err) {
|
|
52
108
|
if (err instanceof axios_1.AxiosError) {
|
|
53
|
-
err
|
|
54
|
-
function streamToString(stream) {
|
|
55
|
-
const chunks = [];
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
|
58
|
-
stream.on('error', (err) => reject(err));
|
|
59
|
-
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
const streamString = await streamToString(err.response?.data);
|
|
63
|
-
let data = JSON.parse(streamString);
|
|
64
|
-
console.log(data);
|
|
65
|
-
throw new Error(data.message);
|
|
109
|
+
throw new Error(await extractAxiosErrorMessage(err));
|
|
66
110
|
}
|
|
67
111
|
throw err;
|
|
68
112
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codebucket/puppet-master",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "TypeScript client for the Puppet Master HTML-to-PDF API",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"AGENTS.md",
|
|
17
|
+
"schemas"
|
|
18
|
+
],
|
|
6
19
|
"scripts": {
|
|
7
|
-
"
|
|
8
|
-
"start": "tsc"
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"start": "tsc",
|
|
22
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
23
|
},
|
|
10
24
|
"devDependencies": {
|
|
11
25
|
"ts-node": "^10.9.2",
|
|
@@ -14,6 +28,13 @@
|
|
|
14
28
|
"dependencies": {
|
|
15
29
|
"axios": "^1.7.7"
|
|
16
30
|
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"puppeteer",
|
|
33
|
+
"pdf",
|
|
34
|
+
"html-to-pdf",
|
|
35
|
+
"typescript",
|
|
36
|
+
"client"
|
|
37
|
+
],
|
|
17
38
|
"repository": {
|
|
18
39
|
"type": "git",
|
|
19
40
|
"url": "https://github.com/Codebucket-Solutions/puppet-master-module.git"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://codebucket.solutions/schemas/pdf-task-options.schema.json",
|
|
4
|
+
"title": "PuppetMaster PdfTaskOptions",
|
|
5
|
+
"description": "Client input accepted by PuppetMaster.pdf(). pdfPath is local-only; the remaining fields form the remote task payload.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"pdfPath",
|
|
10
|
+
"content"
|
|
11
|
+
],
|
|
12
|
+
"properties": {
|
|
13
|
+
"pdfPath": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Local filesystem path where the downloaded PDF should be written."
|
|
16
|
+
},
|
|
17
|
+
"content": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "HTML content passed to the remote server."
|
|
20
|
+
},
|
|
21
|
+
"launchOptions": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Options forwarded to puppeteer.launch on the server.",
|
|
24
|
+
"additionalProperties": true
|
|
25
|
+
},
|
|
26
|
+
"pageOptions": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"description": "Options forwarded to page.setContent on the server.",
|
|
29
|
+
"additionalProperties": true
|
|
30
|
+
},
|
|
31
|
+
"pdfOptions": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"description": "Options forwarded to page.pdf on the server.",
|
|
34
|
+
"additionalProperties": true
|
|
35
|
+
},
|
|
36
|
+
"otherPageFunctions": {
|
|
37
|
+
"type": "array",
|
|
38
|
+
"description": "Ordered page method calls. Each item is either [methodName] or [methodName, argsArray].",
|
|
39
|
+
"items": {
|
|
40
|
+
"oneOf": [
|
|
41
|
+
{
|
|
42
|
+
"type": "array",
|
|
43
|
+
"prefixItems": [
|
|
44
|
+
{
|
|
45
|
+
"type": "string"
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"maxItems": 1
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"type": "array",
|
|
53
|
+
"prefixItems": [
|
|
54
|
+
{
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": true
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"minItems": 2,
|
|
63
|
+
"maxItems": 2
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"examples": [
|
|
70
|
+
{
|
|
71
|
+
"pdfPath": "./invoice.pdf",
|
|
72
|
+
"content": "<html><body><h1>Invoice</h1></body></html>",
|
|
73
|
+
"pageOptions": {
|
|
74
|
+
"waitUntil": "networkidle0"
|
|
75
|
+
},
|
|
76
|
+
"pdfOptions": {
|
|
77
|
+
"format": "A4",
|
|
78
|
+
"printBackground": true
|
|
79
|
+
},
|
|
80
|
+
"otherPageFunctions": [
|
|
81
|
+
[
|
|
82
|
+
"waitForNetworkIdle",
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
"idleTime": 500
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
]
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<module type="WEB_MODULE" version="4">
|
|
3
|
-
<component name="NewModuleRootManager">
|
|
4
|
-
<content url="file://$MODULE_DIR$">
|
|
5
|
-
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
|
6
|
-
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
|
7
|
-
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
|
8
|
-
</content>
|
|
9
|
-
<orderEntry type="inheritedJdk" />
|
|
10
|
-
<orderEntry type="sourceFolder" forTests="false" />
|
|
11
|
-
</component>
|
|
12
|
-
</module>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="MaterialThemeProjectNewConfig">
|
|
4
|
-
<option name="metadata">
|
|
5
|
-
<MTProjectMetadataState>
|
|
6
|
-
<option name="migrated" value="true" />
|
|
7
|
-
<option name="pristineConfig" value="false" />
|
|
8
|
-
<option name="userId" value="2ad778c:1922f284558:-7ffd" />
|
|
9
|
-
</MTProjectMetadataState>
|
|
10
|
-
</option>
|
|
11
|
-
<option name="titleBarState">
|
|
12
|
-
<MTProjectTitleBarConfigState>
|
|
13
|
-
<option name="overrideColor" value="false" />
|
|
14
|
-
</MTProjectTitleBarConfigState>
|
|
15
|
-
</option>
|
|
16
|
-
</component>
|
|
17
|
-
</project>
|
package/.idea/modules.xml
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="ProjectModuleManager">
|
|
4
|
-
<modules>
|
|
5
|
-
<module fileurl="file://$PROJECT_DIR$/.idea/Puppet-Master-Module.iml" filepath="$PROJECT_DIR$/.idea/Puppet-Master-Module.iml" />
|
|
6
|
-
</modules>
|
|
7
|
-
</component>
|
|
8
|
-
</project>
|
package/.idea/vcs.xml
DELETED
package/src/index.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import axios, { Axios, AxiosError, AxiosResponse } from "axios";
|
|
2
|
-
import util from "util";
|
|
3
|
-
import stream from "stream";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
|
|
6
|
-
const pipeline = util.promisify(stream.pipeline);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type opts = {
|
|
10
|
-
baseUrl: string;
|
|
11
|
-
apiKey: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type puppeteerOpts = {
|
|
15
|
-
launchOptions ?: any;
|
|
16
|
-
pageOptions ?: any;
|
|
17
|
-
pdfOptions ?: any;
|
|
18
|
-
otherPageFunctions ?: any;
|
|
19
|
-
pdfPath : string;
|
|
20
|
-
content: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class PuppetMaster {
|
|
24
|
-
private readonly axios: Axios;
|
|
25
|
-
|
|
26
|
-
constructor(opts: opts) {
|
|
27
|
-
this.axios = axios.create({
|
|
28
|
-
baseURL: opts.baseUrl,
|
|
29
|
-
headers: {
|
|
30
|
-
"x-api-key": opts.apiKey
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async pdf(opts: puppeteerOpts) {
|
|
36
|
-
try {
|
|
37
|
-
const request = await this.axios.post("/pdf", {task:opts}, {
|
|
38
|
-
responseType: "stream"
|
|
39
|
-
});
|
|
40
|
-
await pipeline(request.data, fs.createWriteStream(opts.pdfPath));
|
|
41
|
-
} catch (err) {
|
|
42
|
-
if (err instanceof AxiosError) {
|
|
43
|
-
err.response?.data.setEncoding('utf8')
|
|
44
|
-
|
|
45
|
-
function streamToString (stream:any): Promise<string> {
|
|
46
|
-
const chunks: any[] = [];
|
|
47
|
-
return new Promise((resolve, reject) => {
|
|
48
|
-
stream.on('data', (chunk:any) => chunks.push(Buffer.from(chunk)));
|
|
49
|
-
stream.on('error', (err:any) => reject(err));
|
|
50
|
-
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const streamString = await streamToString(err.response?.data);
|
|
55
|
-
|
|
56
|
-
let data = JSON.parse(streamString);
|
|
57
|
-
|
|
58
|
-
console.log(data);
|
|
59
|
-
throw new Error(data.message);
|
|
60
|
-
}
|
|
61
|
-
throw err;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"strict": true,
|
|
6
|
-
"useUnknownInCatchVariables": false,
|
|
7
|
-
"declaration": true,
|
|
8
|
-
"declarationMap": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"outDir": "./dist",
|
|
11
|
-
"rootDir": "./src"
|
|
12
|
-
},
|
|
13
|
-
"include": [
|
|
14
|
-
"src/**/*.ts"
|
|
15
|
-
],
|
|
16
|
-
"exclude": [
|
|
17
|
-
"node_modules"
|
|
18
|
-
]
|
|
19
|
-
}
|