safeer-pdf-generator
Version:
Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery
341 lines • 10.9 kB
TypeScript
/**
* Core type definitions for PDF Reporter
*/
import type { PdfEventEmitter } from './core/PdfEventEmitter.js';
export interface LoggingAdapter {
debug(message: string, ...args: any[]): void;
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string, ...args: any[]): void;
}
export interface ColumnDefinition {
key: string;
title: string;
dataIndex: string;
flex?: number;
type?: 'image' | 'boolean' | 'text' | 'link';
}
export interface UserInfo {
/** Company/tenant name. Shown in the header and the footer's center line. */
name?: string;
email?: string;
mobile?: string;
companyLogo?: string;
companyName?: string;
companyLogoBase64?: string;
/**
* Person who generated the report. Shown only in the footer's left
* "Generated by …" line. When unset, that line is omitted entirely (no
* placeholder text). Decoupled from `name` so the company name no longer
* appears as the author.
*/
generatedBy?: string;
}
/**
* Configuration for the PDF footer (rendered by MergeService.addPageNumbers).
* All toggles default to true. Pass `footer: false` on PdfGenerationOptions to
* disable the entire footer (including the separator line).
*/
export interface FooterConfig {
/** Master toggle. Default: true. */
show?: boolean;
/** Toggle the "Generated by …" line (footer left). Default: true. */
showGeneratedBy?: boolean;
/** Toggle the "<company> • <year>" line (footer center). Default: true. */
showCompany?: boolean;
/** Toggle the "Page X of Y" line (footer right). Default: true. */
showPageNumbers?: boolean;
/** String/format overrides. EN/AR defaults still apply when unset. */
labels?: {
/** Override the "Generated by" prefix (e.g. "Préparé par"). */
generatedBy?: string;
/** Override the "Page X of Y" formatter. */
page?: (current: number, total: number) => string;
};
}
export interface InfoSection {
label: string;
value: string;
}
export interface ChunkingOptions {
enabled?: boolean;
chunkSize?: number;
maxConcurrency?: number;
}
export interface PuppeteerOptions {
executablePath?: string;
args?: string[];
headless?: boolean;
protocolTimeout?: number;
/**
* Pass an existing Puppeteer Browser instance (e.g., from puppeteer-cluster or generic-pool).
* When provided, the package will use this browser instead of launching a new one,
* and will NOT close it when done — only pages will be closed.
*/
browserInstance?: any;
}
export interface PdfOptions {
format?: string;
landscape?: boolean;
printBackground?: boolean;
margin?: {
top?: string;
left?: string;
right?: string;
bottom?: string;
};
displayHeaderFooter?: boolean;
}
export interface LogoFetchOptions {
inlineAsBase64?: boolean;
fetcher?: (url: string) => Promise<string>;
}
export interface TotalsConfig {
columns: string[];
aggregator?: (rows: any[], col: string) => any;
}
export interface HooksConfig {
beforeChunkRender?: (params: any) => void | Promise<void>;
afterChunkRender?: (result: Buffer, params: any) => void | Promise<void>;
beforeMerge?: (chunks: Buffer[]) => void | Promise<void>;
afterMerge?: (merged: Buffer) => void | Promise<void>;
transformHtml?: (html: string, stage: 'pre' | 'post') => string;
transformColumnValue?: (value: any, column: ColumnDefinition, row: any) => any;
}
export interface S3UploadConfig {
bucket: string;
region: string;
accessKeyId?: string;
secretAccessKey?: string;
folder?: string;
acl?: string;
contentType?: string;
endpoint?: string;
}
/**
* Configuration for writing the generated PDF to local disk.
* Composes with other destinations (s3, email, webhook) — all run independently.
*/
export interface LocalFsConfig {
/** Destination directory. Relative paths resolve from process.cwd(). */
path: string;
/** Override the auto-generated filename (defaults to result.fileName). */
filename?: string;
/** Create parent directories recursively if missing. Default: true. */
createDir?: boolean;
/** Overwrite existing files. When false, throws if the target exists. Default: true. */
overwrite?: boolean;
}
export interface LocalFsResult {
/** Absolute path to the written file. */
path: string;
/** Size of the written file in bytes (equal to result.sizeBytes). */
sizeBytes: number;
}
/**
* Configuration for webhook dispatch after PDF generation
*/
export interface WebhookConfig {
/** URL to send the POST request to */
url: string;
/** Secret for HMAC-SHA256 signature (sent as X-Webhook-Signature header) */
secret?: string;
/** Pass-through metadata included in the webhook payload (e.g., tenantId, userId) */
metadata?: Record<string, any>;
/** Request timeout in milliseconds (default: 10000) */
timeoutMs?: number;
}
export interface EmailSendConfig {
to: string;
from?: string;
subject?: string;
transport?: any;
smtp?: {
host: string;
port: number;
secure?: boolean;
auth: {
user: string;
pass: string;
};
};
attachmentMode?: 'attachment' | 'link';
template?: string | EmailTemplateFunction;
}
export type EmailTemplateFunction = (context: EmailTemplateContext) => string;
export interface EmailTemplateContext {
title: string;
generatedAt: string;
downloadUrl?: string;
fileSizeMB: number;
user?: UserInfo;
company?: {
name?: string;
};
}
export interface TemplateCompilerFunction {
(params: TemplateCompilerParams): TemplateCompilerResult;
}
export interface TemplateCompilerParams {
title: string;
data: any[];
columns: ColumnDefinition[];
locale?: string;
reportDate?: string | Date;
userInfo?: UserInfo;
infoSection?: InfoSection[];
pdfFilteredColumns?: string[];
translationFn?: (key: string) => string;
chunkInfo?: {
current: number;
total: number;
startRow: number;
endRow: number;
};
/**
* Custom CSS to inject into the template's <style> block.
* Use this to override table styling, colors, fonts, etc.
*/
customCss?: string;
/**
* Custom HTML to inject before or after the data table.
* Useful for adding summaries, charts, disclaimers, etc.
*/
customHtml?: {
beforeTable?: string;
afterTable?: string;
};
}
export interface TemplateCompilerResult {
html: string;
header: string;
footer: string;
}
export interface PdfGenerationOptions {
title: string;
data: any[];
columns: ColumnDefinition[];
locale?: string;
reportDate?: string | Date;
userInfo?: UserInfo;
infoSection?: InfoSection[];
filteredColumnKeys?: string[];
translationFn?: (key: string) => string;
chunking?: ChunkingOptions;
template?: string | TemplateCompilerFunction;
headerTemplate?: string | ((ctx: any) => string);
footerTemplate?: string | ((ctx: any) => string);
includeTotals?: boolean;
totalsConfig?: TotalsConfig;
email?: EmailSendConfig | false;
s3?: S3UploadConfig | false;
/** Write the generated PDF to a local file. Composes with `s3`, `email`, and `webhook`. */
localFs?: LocalFsConfig | false;
/**
* Footer rendering controls. Pass `false` to disable the entire footer
* (separator + all lines). Pass an object to toggle individual lines or
* override labels. Default: all lines visible, with the "Generated by"
* line omitted when `userInfo.generatedBy` is unset.
*/
footer?: FooterConfig | false;
/**
* @deprecated Not implemented. The package always returns a Buffer in `result.buffer`.
* For local-file output use `localFs: { path }`. Slated for removal in 2.0.
*/
return?: 'buffer' | 'stream' | 'file';
/**
* @deprecated Not implemented. Use `localFs: { path }` instead.
* Slated for removal in 2.0.
*/
outputPath?: string;
logoFetch?: LogoFetchOptions;
hooks?: HooksConfig;
timeoutMs?: number;
puppeteer?: PuppeteerOptions;
pdf?: PdfOptions;
logging?: LoggingAdapter;
/**
* Custom CSS injected into the template's style block (overrides default styles)
*/
customCss?: string;
/**
* Custom HTML injected before/after the data table in the template
*/
customHtml?: {
beforeTable?: string;
afterTable?: string;
};
/**
* Optional event emitter for lifecycle events (generation:started, s3:upload:complete, error, etc.)
*/
events?: PdfEventEmitter;
/**
* Optional webhook configuration to dispatch HTTP POST upon completion.
* Set to false to explicitly disable.
*/
webhook?: WebhookConfig | false;
}
export interface PdfGenerationResult {
buffer: Buffer;
sizeBytes: number;
pageCount: number;
durationMs: number;
fileName: string;
metadata: {
title: string;
chunkCount: number;
rows: number;
generatedAt: string;
};
s3?: {
url: string;
key: string;
};
email?: {
sent: boolean;
to?: string;
messageId?: string;
error?: string;
};
localFs?: LocalFsResult;
}
export interface MergePdfsOptions {
logging?: LoggingAdapter;
}
export interface S3UploadResult {
url: string;
key: string;
}
export interface EmailResult {
sent: boolean;
messageId?: string;
error?: string;
}
export interface UploaderAdapter {
upload(buffer: Buffer, filename: string, mimeType: string, options?: any): Promise<S3UploadResult>;
}
export interface EmailSenderAdapter {
send(options: EmailSendConfig, buffer: Buffer, filename: string): Promise<EmailResult>;
}
export declare class PdfGenerationError extends Error {
cause?: Error | undefined;
constructor(message: string, cause?: Error | undefined);
}
export declare class TemplateError extends Error {
cause?: Error | undefined;
constructor(message: string, cause?: Error | undefined);
}
export declare class S3UploadError extends Error {
cause?: Error | undefined;
constructor(message: string, cause?: Error | undefined);
}
export declare class EmailError extends Error {
cause?: Error | undefined;
constructor(message: string, cause?: Error | undefined);
}
export declare class ChunkProcessingError extends Error {
chunkIndex?: number | undefined;
cause?: Error | undefined;
constructor(message: string, chunkIndex?: number | undefined, cause?: Error | undefined);
}
//# sourceMappingURL=types.d.ts.map