nexdf 0.1.0 → 0.1.1

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 CHANGED
@@ -9,6 +9,7 @@ Plug-and-play PDF generation for Next.js using customizable HTML/HTMX templates
9
9
  - Tailwind-friendly templates you can edit directly
10
10
  - A4-ready default template
11
11
  - Fast runtime path: browser prewarm + page pool reuse + template compile cache
12
+ - Optional DB template resolver with file-template fallback
12
13
 
13
14
  ## Install
14
15
 
@@ -50,6 +51,7 @@ curl -X POST http://localhost:3000/api/pdf \
50
51
  -H "content-type: application/json" \
51
52
  -d '{
52
53
  "filename": "invoice.pdf",
54
+ "templateKey": "basic",
53
55
  "templatePath": "basic.html",
54
56
  "data": {
55
57
  "title": "Invoice #001",
@@ -86,6 +88,7 @@ Body:
86
88
  ```json
87
89
  {
88
90
  "filename": "document.pdf",
91
+ "templateKey": "basic",
89
92
  "templatePath": "basic.html",
90
93
  "data": {
91
94
  "title": "Any value"
@@ -93,6 +96,14 @@ Body:
93
96
  }
94
97
  ```
95
98
 
99
+ `templateKey` is available when you use a custom template resolver.
100
+
101
+ Template resolution behavior:
102
+
103
+ 1. If `resolveTemplate` returns HTML, that HTML is rendered.
104
+ 2. If `resolveTemplate` returns `null`, file-template rendering is used.
105
+ 3. File-template path is `templatePath` or `defaultTemplate`.
106
+
96
107
  ## Runtime requirements
97
108
 
98
109
  - Next route runtime must be `nodejs`
@@ -123,4 +134,36 @@ export const POST = createPdfRouteHandler({
123
134
  defaultTemplate: "basic.html",
124
135
  poolSize: 2
125
136
  });
137
+ ```
138
+
139
+ ## Database-backed templates
140
+
141
+ Use `resolveTemplate` to fetch HTML from DB and render server-side:
142
+
143
+ ```ts
144
+ export const POST = createPdfRouteHandler({
145
+ templatesDir: process.cwd() + "/templates/pdf",
146
+ defaultTemplate: "basic.html",
147
+ poolSize: 2,
148
+ resolveTemplate: async ({ body }) => {
149
+ const templateKey = body.templateKey ?? "basic";
150
+ const html = await loadTemplateHtmlFromDb(templateKey);
151
+ return html;
152
+ }
153
+ });
154
+ ```
155
+
156
+ If `resolveTemplate` returns `null`, file-based template loading is used as fallback.
157
+
158
+ Example request body with DB-first + file fallback support:
159
+
160
+ ```json
161
+ {
162
+ "filename": "document.pdf",
163
+ "templateKey": "basic",
164
+ "templatePath": "basic.html",
165
+ "data": {
166
+ "title": "Any value"
167
+ }
168
+ }
126
169
  ```
package/dist/index.d.ts CHANGED
@@ -8,13 +8,20 @@ declare function listPlaceholders(template: string): string[];
8
8
  type PdfRequestBody = {
9
9
  data?: TemplateData;
10
10
  templatePath?: string;
11
+ templateKey?: string;
11
12
  filename?: string;
12
13
  };
14
+ type ResolveTemplateContext = {
15
+ body: PdfRequestBody;
16
+ templatesDir: string;
17
+ defaultTemplate: string;
18
+ };
13
19
  type CreatePdfRouteHandlerOptions = {
14
20
  templatesDir?: string;
15
21
  defaultTemplate?: string;
16
22
  prewarm?: boolean;
17
23
  poolSize?: number;
24
+ resolveTemplate?: (context: ResolveTemplateContext) => Promise<string | null>;
18
25
  };
19
26
  declare function createPdfRouteHandler(options?: CreatePdfRouteHandlerOptions): (request: Request) => Promise<Response>;
20
27
 
package/dist/index.js CHANGED
@@ -189,16 +189,26 @@ function createPdfRouteHandler(options = {}) {
189
189
  try {
190
190
  const body = await request.json();
191
191
  const filename = body.filename || "document.pdf";
192
- const templateName = body.templatePath || defaultTemplate;
193
- const safeTemplatePath = path.resolve(templatesDir, templateName);
194
- if (!safeTemplatePath.startsWith(path.resolve(templatesDir))) {
195
- return Response.json(
196
- { error: "Invalid templatePath. Template must be inside templates directory." },
197
- { status: 400 }
198
- );
192
+ const resolvedTemplate = options.resolveTemplate ? await options.resolveTemplate({
193
+ body,
194
+ templatesDir,
195
+ defaultTemplate
196
+ }) : null;
197
+ let html;
198
+ if (resolvedTemplate) {
199
+ html = renderTemplateString(resolvedTemplate, body.data ?? {});
200
+ } else {
201
+ const templateName = body.templatePath || defaultTemplate;
202
+ const safeTemplatePath = path.resolve(templatesDir, templateName);
203
+ if (!safeTemplatePath.startsWith(path.resolve(templatesDir))) {
204
+ return Response.json(
205
+ { error: "Invalid templatePath. Template must be inside templates directory." },
206
+ { status: 400 }
207
+ );
208
+ }
209
+ await access(safeTemplatePath, fsConstants.R_OK);
210
+ html = await renderTemplateFile(safeTemplatePath, body.data ?? {});
199
211
  }
200
- await access(safeTemplatePath, fsConstants.R_OK);
201
- const html = await renderTemplateFile(safeTemplatePath, body.data ?? {});
202
212
  const pdfBuffer = await generatePdfBuffer({ html });
203
213
  return new Response(new Uint8Array(pdfBuffer), {
204
214
  status: 200,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexdf",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Plug-and-play Next.js PDF endpoint with customizable HTML/HTMX templates",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -48,4 +48,4 @@
48
48
  "tsup": "^8.3.5",
49
49
  "typescript": "^5.7.3"
50
50
  }
51
- }
51
+ }