pdfnative-cli
Advanced tools
+555
| #!/usr/bin/env node | ||
| 'use strict'; | ||
| var pdfnative = require('pdfnative'); | ||
| var fs = require('fs'); | ||
| var promises = require('fs/promises'); | ||
| var module$1 = require('module'); | ||
| var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __esm = (fn, res) => function __init() { | ||
| return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; | ||
| }; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| // src/utils/error.ts | ||
| var CliError; | ||
| var init_error = __esm({ | ||
| "src/utils/error.ts"() { | ||
| CliError = class extends Error { | ||
| exitCode; | ||
| constructor(message, exitCode = 1) { | ||
| super(message); | ||
| this.name = "CliError"; | ||
| this.exitCode = exitCode; | ||
| } | ||
| }; | ||
| } | ||
| }); | ||
| // src/utils/args.ts | ||
| function parseArgs(argv) { | ||
| const flags = {}; | ||
| const positionals = []; | ||
| let i = 0; | ||
| while (i < argv.length) { | ||
| const token = argv[i]; | ||
| if (token === "--") { | ||
| i++; | ||
| while (i < argv.length) { | ||
| positionals.push(argv[i]); | ||
| i++; | ||
| } | ||
| break; | ||
| } | ||
| if (token.startsWith("--")) { | ||
| const eqIdx = token.indexOf("="); | ||
| if (eqIdx !== -1) { | ||
| const key = token.slice(2, eqIdx); | ||
| const value = token.slice(eqIdx + 1); | ||
| flags[key] = value; | ||
| } else { | ||
| const key = token.slice(2); | ||
| const next = argv[i + 1]; | ||
| if (next !== void 0 && !next.startsWith("-")) { | ||
| flags[key] = next; | ||
| i++; | ||
| } else { | ||
| flags[key] = true; | ||
| } | ||
| } | ||
| } else if (token.startsWith("-") && token.length === 2) { | ||
| const key = token.slice(1); | ||
| const next = argv[i + 1]; | ||
| if (next !== void 0 && !next.startsWith("-")) { | ||
| flags[key] = next; | ||
| i++; | ||
| } else { | ||
| flags[key] = true; | ||
| } | ||
| } else { | ||
| positionals.push(token); | ||
| } | ||
| i++; | ||
| } | ||
| return { flags, positionals }; | ||
| } | ||
| function getStringFlag(flags, ...names) { | ||
| for (const name of names) { | ||
| const value = flags[name]; | ||
| if (value !== void 0) { | ||
| if (typeof value !== "string") { | ||
| throw new CliError(`Flag --${name} requires a value.`, 2); | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| function hasFlag(flags, ...names) { | ||
| return names.some((n) => flags[n] !== void 0); | ||
| } | ||
| var init_args = __esm({ | ||
| "src/utils/args.ts"() { | ||
| init_error(); | ||
| } | ||
| }); | ||
| var init_core_bridge = __esm({ | ||
| "src/core-bridge/index.ts"() { | ||
| } | ||
| }); | ||
| function validatePath(filePath) { | ||
| const normalised = filePath.replace(/\\/g, "/"); | ||
| if (normalised.includes("../") || normalised === "..") { | ||
| throw new CliError(`Path traversal detected in path: ${filePath}`, 1); | ||
| } | ||
| } | ||
| function readStdin() { | ||
| return new Promise((resolve, reject) => { | ||
| const chunks = []; | ||
| process.stdin.on("data", (chunk) => chunks.push(chunk)); | ||
| process.stdin.on("end", () => resolve(Buffer.concat(chunks))); | ||
| process.stdin.on("error", reject); | ||
| }); | ||
| } | ||
| async function readFileOrStdin(filePath) { | ||
| if (filePath === void 0) { | ||
| return readStdin(); | ||
| } | ||
| validatePath(filePath); | ||
| return promises.readFile(filePath); | ||
| } | ||
| function assertJsonSizeLimit(buf) { | ||
| if (buf.length > JSON_SIZE_LIMIT) { | ||
| throw new CliError( | ||
| `JSON input exceeds the 50 MB limit (got ${(buf.length / 1024 / 1024).toFixed(1)} MB).`, | ||
| 1 | ||
| ); | ||
| } | ||
| } | ||
| async function writeOutput(data, filePath) { | ||
| if (filePath === void 0) { | ||
| await new Promise((resolve, reject) => { | ||
| process.stdout.write(data, (err) => { | ||
| if (err) reject(err); | ||
| else resolve(); | ||
| }); | ||
| }); | ||
| return; | ||
| } | ||
| validatePath(filePath); | ||
| await promises.writeFile(filePath, data); | ||
| } | ||
| async function writeStreamingOutput(chunks, filePath) { | ||
| if (filePath === void 0) { | ||
| for await (const chunk of chunks) { | ||
| await new Promise((resolve, reject) => { | ||
| process.stdout.write(chunk, (err) => { | ||
| if (err) reject(err); | ||
| else resolve(); | ||
| }); | ||
| }); | ||
| } | ||
| return; | ||
| } | ||
| validatePath(filePath); | ||
| const stream = fs.createWriteStream(filePath); | ||
| await new Promise((resolve, reject) => { | ||
| stream.on("error", reject); | ||
| stream.on("finish", resolve); | ||
| (async () => { | ||
| for await (const chunk of chunks) { | ||
| const ok = stream.write(chunk); | ||
| if (!ok) { | ||
| await new Promise((r) => stream.once("drain", r)); | ||
| } | ||
| } | ||
| stream.end(); | ||
| })().catch(reject); | ||
| }); | ||
| } | ||
| var JSON_SIZE_LIMIT; | ||
| var init_io = __esm({ | ||
| "src/utils/io.ts"() { | ||
| init_error(); | ||
| JSON_SIZE_LIMIT = 50 * 1024 * 1024; | ||
| } | ||
| }); | ||
| // src/commands/render.ts | ||
| var render_exports = {}; | ||
| __export(render_exports, { | ||
| render: () => render | ||
| }); | ||
| async function render(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const outputPath = getStringFlag(args.flags, "output", "o"); | ||
| const useStream = hasFlag(args.flags, "stream"); | ||
| const conformance = getStringFlag(args.flags, "conformance"); | ||
| if (conformance !== void 0 && !VALID_CONFORMANCE.has(conformance)) { | ||
| throw new CliError( | ||
| `Invalid --conformance value "${conformance}". Valid values: 1b, 2b, 3b.`, | ||
| 2 | ||
| ); | ||
| } | ||
| const inputBuf = await readFileOrStdin(inputPath); | ||
| assertJsonSizeLimit(inputBuf); | ||
| let params; | ||
| try { | ||
| params = JSON.parse(inputBuf.toString("utf8")); | ||
| } catch (e) { | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| throw new CliError(`Failed to parse JSON input: ${message}`, 1); | ||
| } | ||
| if (typeof params !== "object" || params === null) { | ||
| throw new CliError("JSON input must be a DocumentParams object.", 1); | ||
| } | ||
| if (conformance !== void 0) { | ||
| params["pdfaConformance"] = conformance; | ||
| } | ||
| if (useStream) { | ||
| const generator = pdfnative.buildDocumentPDFStream(params); | ||
| await writeStreamingOutput(generator, outputPath); | ||
| } else { | ||
| const pdfBytes = pdfnative.buildDocumentPDFBytes(params); | ||
| await writeOutput(pdfBytes, outputPath); | ||
| } | ||
| } | ||
| var VALID_CONFORMANCE; | ||
| var init_render = __esm({ | ||
| "src/commands/render.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| VALID_CONFORMANCE = /* @__PURE__ */ new Set(["1b", "2b", "3b"]); | ||
| } | ||
| }); | ||
| // src/commands/sign.ts | ||
| var sign_exports = {}; | ||
| __export(sign_exports, { | ||
| sign: () => sign | ||
| }); | ||
| function pemToDer(pem) { | ||
| const body = pem.replace(/-----BEGIN [^-]+-----/g, "").replace(/-----END [^-]+-----/g, "").replace(/\s+/g, ""); | ||
| const binaryStr = atob(body); | ||
| const bytes = new Uint8Array(binaryStr.length); | ||
| for (let i = 0; i < binaryStr.length; i++) { | ||
| bytes[i] = binaryStr.charCodeAt(i); | ||
| } | ||
| return bytes; | ||
| } | ||
| async function loadPem(envVar, filePath, label) { | ||
| const fromEnv = process.env[envVar]; | ||
| if (fromEnv !== void 0 && fromEnv.trim().length > 0) { | ||
| return fromEnv; | ||
| } | ||
| if (filePath !== void 0) { | ||
| validatePath(filePath); | ||
| const buf = await promises.readFile(filePath, "utf8"); | ||
| return buf; | ||
| } | ||
| throw new CliError( | ||
| `Missing ${label}. Provide $${envVar} (env) or --${label.replace(/ /g, "-")} <path>.`, | ||
| 2 | ||
| ); | ||
| } | ||
| async function sign(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const outputPath = getStringFlag(args.flags, "output", "o"); | ||
| const keyPath = getStringFlag(args.flags, "key"); | ||
| const certPath = getStringFlag(args.flags, "cert"); | ||
| const pdfBuf = await readFileOrStdin(inputPath); | ||
| const pdfBytes = new Uint8Array(pdfBuf); | ||
| const privateKeyPem = await loadPem("PDFNATIVE_SIGN_KEY", keyPath, "private key"); | ||
| const certPem = await loadPem("PDFNATIVE_SIGN_CERT", certPath, "certificate"); | ||
| let options; | ||
| try { | ||
| const keyDer = pemToDer(privateKeyPem); | ||
| const certDer = pemToDer(certPem); | ||
| const rsaKey = pdfnative.parseRsaPrivateKey(keyDer); | ||
| const signerCert = pdfnative.parseCertificate(certDer); | ||
| options = { rsaKey, signerCert, algorithm: "rsa-sha256" }; | ||
| } catch { | ||
| throw new CliError("Failed to parse signing credentials. Verify key and certificate are valid PEM-encoded files.", 1); | ||
| } | ||
| const signedBytes = pdfnative.signPdfBytes(pdfBytes, options); | ||
| await writeOutput(signedBytes, outputPath); | ||
| } | ||
| var init_sign = __esm({ | ||
| "src/commands/sign.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| } | ||
| }); | ||
| // src/commands/inspect.ts | ||
| var inspect_exports = {}; | ||
| __export(inspect_exports, { | ||
| inspect: () => inspect | ||
| }); | ||
| function safeInfoString(value) { | ||
| if (typeof value !== "string") return null; | ||
| const trimmed = value.trim(); | ||
| if (trimmed.startsWith("(") && trimmed.endsWith(")")) { | ||
| return trimmed.slice(1, -1); | ||
| } | ||
| if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(trimmed)) { | ||
| return null; | ||
| } | ||
| return trimmed || null; | ||
| } | ||
| function extractVersion(reader) { | ||
| const header = new TextDecoder("ascii", { fatal: false }).decode( | ||
| reader.bytes.slice(0, 20) | ||
| ); | ||
| const match = /^%PDF-(\d+\.\d+)/.exec(header); | ||
| return match !== null ? match[1] : "unknown"; | ||
| } | ||
| function extractEncrypted(reader) { | ||
| const trailer = reader.trailer; | ||
| return trailer.get("Encrypt") !== void 0; | ||
| } | ||
| function extractPdfaConformance(reader) { | ||
| try { | ||
| const catalog = reader.getCatalog(); | ||
| const metaRef = catalog.get("Metadata"); | ||
| if (metaRef === void 0) return null; | ||
| const metaObj = reader.resolveValue(metaRef); | ||
| if (typeof metaObj !== "object" || metaObj === null || !("streamData" in metaObj)) { | ||
| return null; | ||
| } | ||
| const decoded = reader.decodeStream( | ||
| metaObj | ||
| ); | ||
| const xmp = new TextDecoder("utf-8", { fatal: false }).decode(decoded); | ||
| const partMatch = /pdfaid:part[^>]*>(\d+)</.exec(xmp); | ||
| const confMatch = /pdfaid:conformance[^>]*>([A-Za-z]+)</.exec(xmp); | ||
| if (partMatch !== null && confMatch !== null) { | ||
| return `${partMatch[1]}${confMatch[1].toLowerCase()}`; | ||
| } | ||
| } catch { | ||
| } | ||
| return null; | ||
| } | ||
| function countSignatures(reader) { | ||
| try { | ||
| const catalog = reader.getCatalog(); | ||
| const acroRef = catalog.get("AcroForm"); | ||
| if (acroRef === void 0) return 0; | ||
| const acro = reader.resolveValue(acroRef); | ||
| if (!(acro instanceof Map)) return 0; | ||
| const fieldsVal = acro.get("Fields"); | ||
| if (!Array.isArray(fieldsVal)) return 0; | ||
| let count = 0; | ||
| for (const ref of fieldsVal) { | ||
| const field = reader.resolveValue(ref); | ||
| if (field instanceof Map && field.get("FT") === "/Sig") { | ||
| count++; | ||
| } | ||
| } | ||
| return count; | ||
| } catch { | ||
| return 0; | ||
| } | ||
| } | ||
| async function inspect(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const format = getStringFlag(args.flags, "format", "f") ?? "json"; | ||
| if (format !== "json" && format !== "text") { | ||
| throw new CliError(`Invalid --format value "${format}". Valid values: json, text.`, 2); | ||
| } | ||
| const inputBuf = await readFileOrStdin(inputPath); | ||
| const pdfBytes = new Uint8Array(inputBuf); | ||
| let reader; | ||
| try { | ||
| reader = pdfnative.openPdf(pdfBytes); | ||
| } catch (e) { | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| throw new CliError(`Failed to read PDF: ${message}`, 1); | ||
| } | ||
| const info = reader.getInfo(); | ||
| const result = { | ||
| version: extractVersion(reader), | ||
| pageCount: reader.pageCount, | ||
| encrypted: extractEncrypted(reader), | ||
| pdfaConformance: extractPdfaConformance(reader), | ||
| signatures: countSignatures(reader), | ||
| metadata: { | ||
| title: info !== null ? safeInfoString(info.get("Title")) : null, | ||
| author: info !== null ? safeInfoString(info.get("Author")) : null, | ||
| creationDate: info !== null ? safeInfoString(info.get("CreationDate")) : null, | ||
| subject: info !== null ? safeInfoString(info.get("Subject")) : null, | ||
| producer: info !== null ? safeInfoString(info.get("Producer")) : null | ||
| } | ||
| }; | ||
| if (format === "json") { | ||
| process.stdout.write(JSON.stringify(result, null, 2) + "\n"); | ||
| } else { | ||
| const lines = [ | ||
| `Version: ${result.version}`, | ||
| `Pages: ${result.pageCount}`, | ||
| `Encrypted: ${result.encrypted ? "yes" : "no"}`, | ||
| `PDF/A: ${result.pdfaConformance ?? "none"}`, | ||
| `Signatures: ${result.signatures}`, | ||
| `Title: ${result.metadata.title ?? "\u2014"}`, | ||
| `Author: ${result.metadata.author ?? "\u2014"}`, | ||
| `Created: ${result.metadata.creationDate ?? "\u2014"}`, | ||
| `Subject: ${result.metadata.subject ?? "\u2014"}`, | ||
| `Producer: ${result.metadata.producer ?? "\u2014"}` | ||
| ]; | ||
| process.stdout.write(lines.join("\n") + "\n"); | ||
| } | ||
| } | ||
| var init_inspect = __esm({ | ||
| "src/commands/inspect.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| } | ||
| }); | ||
| // src/index.ts | ||
| init_args(); | ||
| init_error(); | ||
| var USAGE = `pdfnative-cli \u2014 Official CLI for pdfnative | ||
| Usage: | ||
| pdfnative <command> [options] | ||
| Commands: | ||
| render Render a JSON document definition to PDF | ||
| sign Apply a digital signature to an existing PDF | ||
| inspect Analyse a PDF and output metadata / conformance info | ||
| Options: | ||
| --help, -h Show this help message | ||
| --version, -V Show version | ||
| Run \`pdfnative <command> --help\` for per-command options. | ||
| `; | ||
| var RENDER_USAGE = `pdfnative render \u2014 Render a JSON DocumentParams to PDF | ||
| Usage: | ||
| pdfnative render [--input <file.json>] [--output <out.pdf>] [--stream] [--conformance <level>] | ||
| Options: | ||
| --input, -i Path to JSON input file (default: stdin) | ||
| --output, -o Output PDF path (default: stdout) | ||
| --stream Use streaming output for large documents | ||
| --conformance PDF/A conformance level: 1b, 2b, or 3b | ||
| --help, -h Show this help message | ||
| `; | ||
| var SIGN_USAGE = `pdfnative sign \u2014 Apply a digital signature to an existing PDF | ||
| Usage: | ||
| pdfnative sign [--input <file.pdf>] [--output <out.pdf>] [--key <key.pem>] [--cert <cert.pem>] | ||
| Options: | ||
| --input, -i Path to input PDF (default: stdin) | ||
| --output, -o Signed PDF output path (default: stdout) | ||
| --key Path to PEM private key file | ||
| (overridden by $PDFNATIVE_SIGN_KEY env var) | ||
| --cert Path to PEM certificate file | ||
| (overridden by $PDFNATIVE_SIGN_CERT env var) | ||
| --help, -h Show this help message | ||
| Security: $PDFNATIVE_SIGN_KEY and $PDFNATIVE_SIGN_CERT env vars take precedence over file flags. | ||
| `; | ||
| var INSPECT_USAGE = `pdfnative inspect \u2014 Analyse a PDF and output metadata | ||
| Usage: | ||
| pdfnative inspect [--input <file.pdf>] [--format <fmt>] | ||
| Options: | ||
| --input, -i Path to input PDF (default: stdin) | ||
| --format, -f Output format: json (default) or text | ||
| --help, -h Show this help message | ||
| `; | ||
| function getVersion() { | ||
| const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))); | ||
| const pkg = require2("../package.json"); | ||
| return pkg.version; | ||
| } | ||
| async function loadCommand(name) { | ||
| switch (name) { | ||
| case "render": { | ||
| const m = await Promise.resolve().then(() => (init_render(), render_exports)); | ||
| return m.render; | ||
| } | ||
| case "sign": { | ||
| const m = await Promise.resolve().then(() => (init_sign(), sign_exports)); | ||
| return m.sign; | ||
| } | ||
| case "inspect": { | ||
| const m = await Promise.resolve().then(() => (init_inspect(), inspect_exports)); | ||
| return m.inspect; | ||
| } | ||
| default: | ||
| return Promise.reject(new CliError(`Unknown command: ${name}. Run pdfnative --help for usage.`, 1)); | ||
| } | ||
| } | ||
| async function main() { | ||
| const argv = process.argv.slice(2); | ||
| const args = parseArgs(argv); | ||
| if (hasFlag(args.flags, "help", "h") && args.positionals.length === 0) { | ||
| process.stdout.write(USAGE); | ||
| process.exit(0); | ||
| } | ||
| if (hasFlag(args.flags, "version", "V")) { | ||
| process.stdout.write(getVersion() + "\n"); | ||
| process.exit(0); | ||
| } | ||
| const commandName = args.positionals[0]; | ||
| if (commandName === void 0) { | ||
| process.stdout.write(USAGE); | ||
| process.exit(0); | ||
| } | ||
| if (hasFlag(args.flags, "help", "h")) { | ||
| switch (commandName) { | ||
| case "render": | ||
| process.stdout.write(RENDER_USAGE); | ||
| break; | ||
| case "sign": | ||
| process.stdout.write(SIGN_USAGE); | ||
| break; | ||
| case "inspect": | ||
| process.stdout.write(INSPECT_USAGE); | ||
| break; | ||
| default: | ||
| process.stderr.write(`Unknown command: ${commandName}. Run pdfnative --help for usage. | ||
| `); | ||
| process.exit(1); | ||
| } | ||
| process.exit(0); | ||
| } | ||
| const commandArgs = parseArgs(argv.filter((_t, _i) => { | ||
| if (_t === commandName && args.positionals[0] === commandName) { | ||
| return false; | ||
| } | ||
| return true; | ||
| })); | ||
| const command = await loadCommand(commandName); | ||
| await command(commandArgs); | ||
| } | ||
| main().catch((e) => { | ||
| if (e instanceof CliError) { | ||
| process.stderr.write(e.message + "\n"); | ||
| process.exit(e.exitCode); | ||
| } | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| process.stderr.write(`Error: ${message} | ||
| `); | ||
| process.exit(1); | ||
| }); | ||
| //# sourceMappingURL=cli.cjs.map | ||
| //# sourceMappingURL=cli.cjs.map |
| {"version":3,"sources":["../src/utils/error.ts","../src/utils/args.ts","../src/core-bridge/index.ts","../src/utils/io.ts","../src/commands/render.ts","../src/commands/sign.ts","../src/commands/inspect.ts","../src/index.ts"],"names":["readFile","writeFile","createWriteStream","buildDocumentPDFStream","buildDocumentPDFBytes","parseRsaPrivateKey","parseCertificate","signPdfBytes","openPdf","require","createRequire"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\r\n * CLI Error — thrown by commands when a user-facing error occurs.\r\n *\r\n * Exit code conventions:\r\n * 1 = runtime error (invalid input, I/O failure)\r\n * 2 = usage error (missing required argument)\r\n */\r\nexport class CliError extends Error {\r\n public readonly exitCode: number;\r\n\r\n constructor(message: string, exitCode = 1) {\r\n super(message);\r\n this.name = 'CliError';\r\n this.exitCode = exitCode;\r\n }\r\n}\r\n\r\n/**\r\n * Print a message to stderr and terminate the process.\r\n * Never returns — declared as `never` for type narrowing.\r\n */\r\nexport function die(message: string, exitCode = 1): never {\r\n process.stderr.write(message + '\\n');\r\n process.exit(exitCode);\r\n}\r\n","import { CliError } from './error.js';\r\n\r\nexport interface ParsedArgs {\r\n readonly flags: Record<string, string | boolean>;\r\n readonly positionals: readonly string[];\r\n}\r\n\r\n/**\r\n * Zero-dependency argument parser.\r\n *\r\n * Supported forms:\r\n * --flag value flags.flag = 'value'\r\n * --flag=value flags.flag = 'value'\r\n * -f value flags.f = 'value'\r\n * --flag flags.flag = true (boolean)\r\n * -- stop flag parsing; rest → positionals\r\n * bare token positionals[]\r\n */\r\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\r\n const flags: Record<string, string | boolean> = {};\r\n const positionals: string[] = [];\r\n let i = 0;\r\n\r\n while (i < argv.length) {\r\n const token = argv[i] as string;\r\n\r\n if (token === '--') {\r\n // Everything after -- goes into positionals\r\n i++;\r\n while (i < argv.length) {\r\n positionals.push(argv[i] as string);\r\n i++;\r\n }\r\n break;\r\n }\r\n\r\n if (token.startsWith('--')) {\r\n const eqIdx = token.indexOf('=');\r\n if (eqIdx !== -1) {\r\n // --flag=value\r\n const key = token.slice(2, eqIdx);\r\n const value = token.slice(eqIdx + 1);\r\n flags[key] = value;\r\n } else {\r\n const key = token.slice(2);\r\n const next = argv[i + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n // --flag value\r\n flags[key] = next;\r\n i++;\r\n } else {\r\n // --flag (boolean)\r\n flags[key] = true;\r\n }\r\n }\r\n } else if (token.startsWith('-') && token.length === 2) {\r\n // -f value\r\n const key = token.slice(1);\r\n const next = argv[i + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n flags[key] = next;\r\n i++;\r\n } else {\r\n flags[key] = true;\r\n }\r\n } else {\r\n positionals.push(token);\r\n }\r\n\r\n i++;\r\n }\r\n\r\n return { flags, positionals };\r\n}\r\n\r\n/**\r\n * Return the string value of the first matching flag name, or undefined.\r\n * Throws CliError if the flag is present but its value is a boolean (i.e. no value provided).\r\n */\r\nexport function getStringFlag(\r\n flags: Record<string, string | boolean>,\r\n ...names: string[]\r\n): string | undefined {\r\n for (const name of names) {\r\n const value = flags[name];\r\n if (value !== undefined) {\r\n if (typeof value !== 'string') {\r\n throw new CliError(`Flag --${name} requires a value.`, 2);\r\n }\r\n return value;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** Return true if any of the given flag names is present (boolean or string value). */\r\nexport function hasFlag(flags: Record<string, string | boolean>, ...names: string[]): boolean {\r\n return names.some((n) => flags[n] !== undefined);\r\n}\r\n","// Selective re-exports from pdfnative — keeps the CLI surface minimal.\r\n// All PDF logic lives in pdfnative; this module is the only import point.\r\n\r\n// ── Render ───────────────────────────────────────────────────────────\r\nexport { buildDocumentPDFBytes } from 'pdfnative';\r\nexport { buildDocumentPDFStream } from 'pdfnative';\r\n\r\n// ── Sign ─────────────────────────────────────────────────────────────\r\nexport { signPdfBytes } from 'pdfnative';\r\nexport { parseCertificate } from 'pdfnative';\r\nexport { parseRsaPrivateKey } from 'pdfnative';\r\n\r\n// ── Inspect ──────────────────────────────────────────────────────────\r\nexport { openPdf } from 'pdfnative';\r\n\r\n// ── Types ────────────────────────────────────────────────────────────\r\nexport type { DocumentParams } from 'pdfnative';\r\nexport type { PdfSignOptions, SignatureAlgorithm } from 'pdfnative';\r\nexport type { X509Certificate, X509Name } from 'pdfnative';\r\nexport type { RsaPrivateKey } from 'pdfnative';\r\nexport type { EcPrivateKey } from 'pdfnative';\r\nexport type { PdfReader } from 'pdfnative';\r\nexport type { StreamOptions } from 'pdfnative';\r\n","import { createWriteStream } from 'node:fs';\r\nimport { readFile, writeFile } from 'node:fs/promises';\r\nimport { CliError } from './error.js';\r\n\r\nconst JSON_SIZE_LIMIT = 50 * 1024 * 1024; // 50 MB\r\n\r\n/**\r\n * Validate a file path against directory traversal.\r\n * Throws CliError if the path contains `../` or `..\\\\` sequences.\r\n */\r\nexport function validatePath(filePath: string): void {\r\n // Normalise backslashes for Windows paths and check for traversal\r\n const normalised = filePath.replace(/\\\\/g, '/');\r\n if (normalised.includes('../') || normalised === '..') {\r\n throw new CliError(`Path traversal detected in path: ${filePath}`, 1);\r\n }\r\n}\r\n\r\n/**\r\n * Read all bytes from stdin.\r\n */\r\nexport function readStdin(): Promise<Buffer> {\r\n return new Promise((resolve, reject) => {\r\n const chunks: Buffer[] = [];\r\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\r\n process.stdin.on('end', () => resolve(Buffer.concat(chunks)));\r\n process.stdin.on('error', reject);\r\n });\r\n}\r\n\r\n/**\r\n * Read a file by path, or fall back to stdin if `filePath` is undefined.\r\n */\r\nexport async function readFileOrStdin(filePath: string | undefined): Promise<Buffer> {\r\n if (filePath === undefined) {\r\n return readStdin();\r\n }\r\n validatePath(filePath);\r\n return readFile(filePath);\r\n}\r\n\r\n/**\r\n * Enforce the 50 MB JSON input size limit on a buffer.\r\n * Throws CliError if the buffer exceeds the limit.\r\n */\r\nexport function assertJsonSizeLimit(buf: Buffer): void {\r\n if (buf.length > JSON_SIZE_LIMIT) {\r\n throw new CliError(\r\n `JSON input exceeds the 50 MB limit (got ${(buf.length / 1024 / 1024).toFixed(1)} MB).`,\r\n 1,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write binary data to a file path, or to stdout if `filePath` is undefined.\r\n */\r\nexport async function writeOutput(data: Uint8Array, filePath: string | undefined): Promise<void> {\r\n if (filePath === undefined) {\r\n await new Promise<void>((resolve, reject) => {\r\n process.stdout.write(data, (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n });\r\n });\r\n return;\r\n }\r\n validatePath(filePath);\r\n await writeFile(filePath, data);\r\n}\r\n\r\n/**\r\n * Pipe streaming PDF chunks to a file or stdout.\r\n * Used by `render --stream`.\r\n */\r\nexport async function writeStreamingOutput(\r\n chunks: AsyncGenerator<Uint8Array>,\r\n filePath: string | undefined,\r\n): Promise<void> {\r\n if (filePath === undefined) {\r\n for await (const chunk of chunks) {\r\n await new Promise<void>((resolve, reject) => {\r\n process.stdout.write(chunk, (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n });\r\n });\r\n }\r\n return;\r\n }\r\n\r\n validatePath(filePath);\r\n const stream = createWriteStream(filePath);\r\n await new Promise<void>((resolve, reject) => {\r\n stream.on('error', reject);\r\n stream.on('finish', resolve);\r\n (async () => {\r\n for await (const chunk of chunks) {\r\n const ok = stream.write(chunk);\r\n if (!ok) {\r\n await new Promise<void>((r) => stream.once('drain', r));\r\n }\r\n }\r\n stream.end();\r\n })().catch(reject);\r\n });\r\n}\r\n\r\n","import { buildDocumentPDFBytes, buildDocumentPDFStream } from '../core-bridge/index.js';\r\nimport type { DocumentParams } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag, hasFlag } from '../utils/args.js';\r\nimport { readFileOrStdin, writeOutput, writeStreamingOutput, assertJsonSizeLimit } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\nconst VALID_CONFORMANCE = new Set(['1b', '2b', '3b']);\r\n\r\nexport async function render(args: ParsedArgs): Promise<void> {\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const outputPath = getStringFlag(args.flags, 'output', 'o');\r\n const useStream = hasFlag(args.flags, 'stream');\r\n const conformance = getStringFlag(args.flags, 'conformance');\r\n\r\n if (conformance !== undefined && !VALID_CONFORMANCE.has(conformance)) {\r\n throw new CliError(\r\n `Invalid --conformance value \"${conformance}\". Valid values: 1b, 2b, 3b.`,\r\n 2,\r\n );\r\n }\r\n\r\n const inputBuf = await readFileOrStdin(inputPath);\r\n assertJsonSizeLimit(inputBuf);\r\n\r\n let params: DocumentParams;\r\n try {\r\n params = JSON.parse(inputBuf.toString('utf8')) as DocumentParams;\r\n } catch (e) {\r\n const message = e instanceof Error ? e.message : String(e);\r\n throw new CliError(`Failed to parse JSON input: ${message}`, 1);\r\n }\r\n\r\n if (typeof params !== 'object' || params === null) {\r\n throw new CliError('JSON input must be a DocumentParams object.', 1);\r\n }\r\n\r\n // Inject --conformance flag into params if provided\r\n if (conformance !== undefined) {\r\n (params as unknown as Record<string, unknown>)['pdfaConformance'] = conformance;\r\n }\r\n\r\n if (useStream) {\r\n const generator = buildDocumentPDFStream(params);\r\n await writeStreamingOutput(generator, outputPath);\r\n } else {\r\n const pdfBytes = buildDocumentPDFBytes(params);\r\n await writeOutput(pdfBytes, outputPath);\r\n }\r\n}\r\n","import { readFile } from 'node:fs/promises';\r\nimport {\r\n signPdfBytes,\r\n parseCertificate,\r\n parseRsaPrivateKey,\r\n} from '../core-bridge/index.js';\r\nimport type { PdfSignOptions } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag } from '../utils/args.js';\r\nimport { readFileOrStdin, writeOutput, validatePath } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\n/**\r\n * Decode a PEM-encoded block to DER bytes.\r\n * Strips -----BEGIN ...-----/-----END ...----- headers and base64-decodes.\r\n */\r\nfunction pemToDer(pem: string): Uint8Array {\r\n const body = pem\r\n .replace(/-----BEGIN [^-]+-----/g, '')\r\n .replace(/-----END [^-]+-----/g, '')\r\n .replace(/\\s+/g, '');\r\n const binaryStr = atob(body);\r\n const bytes = new Uint8Array(binaryStr.length);\r\n for (let i = 0; i < binaryStr.length; i++) {\r\n bytes[i] = binaryStr.charCodeAt(i);\r\n }\r\n return bytes;\r\n}\r\n\r\n/**\r\n * Load a PEM string from an environment variable or a file path.\r\n * Environment variable takes precedence over the file flag (security best practice).\r\n *\r\n * @param envVar - Name of the env var containing the PEM string.\r\n * @param filePath - File path from a CLI flag.\r\n * @param label - Human-readable label for error messages (e.g. \"private key\").\r\n */\r\nasync function loadPem(\r\n envVar: string,\r\n filePath: string | undefined,\r\n label: string,\r\n): Promise<string> {\r\n const fromEnv = process.env[envVar];\r\n if (fromEnv !== undefined && fromEnv.trim().length > 0) {\r\n return fromEnv;\r\n }\r\n if (filePath !== undefined) {\r\n validatePath(filePath);\r\n const buf = await readFile(filePath, 'utf8');\r\n return buf;\r\n }\r\n throw new CliError(\r\n `Missing ${label}. Provide $${envVar} (env) or --${label.replace(/ /g, '-')} <path>.`,\r\n 2,\r\n );\r\n}\r\n\r\nexport async function sign(args: ParsedArgs): Promise<void> {\r\n // --input is required for sign (no stdin for PDF to avoid accidental pipe issues)\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const outputPath = getStringFlag(args.flags, 'output', 'o');\r\n const keyPath = getStringFlag(args.flags, 'key');\r\n const certPath = getStringFlag(args.flags, 'cert');\r\n\r\n const pdfBuf = await readFileOrStdin(inputPath);\r\n const pdfBytes = new Uint8Array(pdfBuf);\r\n\r\n // Load secrets — env vars take precedence over file flags (OWASP: avoid key exposure)\r\n const privateKeyPem = await loadPem('PDFNATIVE_SIGN_KEY', keyPath, 'private key');\r\n const certPem = await loadPem('PDFNATIVE_SIGN_CERT', certPath, 'certificate');\r\n\r\n // Parse keys — never include raw key material in error messages\r\n let options: PdfSignOptions;\r\n try {\r\n const keyDer = pemToDer(privateKeyPem);\r\n const certDer = pemToDer(certPem);\r\n const rsaKey = parseRsaPrivateKey(keyDer);\r\n const signerCert = parseCertificate(certDer);\r\n options = { rsaKey, signerCert, algorithm: 'rsa-sha256' };\r\n } catch {\r\n // Do NOT include key material or PEM content in the error message\r\n throw new CliError('Failed to parse signing credentials. Verify key and certificate are valid PEM-encoded files.', 1);\r\n }\r\n\r\n const signedBytes = signPdfBytes(pdfBytes, options);\r\n await writeOutput(signedBytes, outputPath);\r\n}\r\n","import { openPdf } from '../core-bridge/index.js';\r\nimport type { PdfReader } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag } from '../utils/args.js';\r\nimport { readFileOrStdin } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\ninterface InspectResult {\r\n readonly version: string;\r\n readonly pageCount: number;\r\n readonly encrypted: boolean;\r\n readonly pdfaConformance: string | null;\r\n readonly signatures: number;\r\n readonly metadata: {\r\n readonly title: string | null;\r\n readonly author: string | null;\r\n readonly creationDate: string | null;\r\n readonly subject: string | null;\r\n readonly producer: string | null;\r\n };\r\n}\r\n\r\n/**\r\n * Safely extract a string value from a PDF info dictionary entry.\r\n * Returns null if the value is not a plain string (e.g. hex strings, refs, binary blobs).\r\n */\r\nfunction safeInfoString(value: unknown): string | null {\r\n if (typeof value !== 'string') return null;\r\n // Strip surrounding PDF literal string parens if present\r\n const trimmed = value.trim();\r\n if (trimmed.startsWith('(') && trimmed.endsWith(')')) {\r\n return trimmed.slice(1, -1);\r\n }\r\n // Filter out binary/non-printable content (no raw bytes in output — OWASP)\r\n // eslint-disable-next-line no-control-regex\r\n if (/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(trimmed)) {\r\n return null;\r\n }\r\n return trimmed || null;\r\n}\r\n\r\nfunction extractVersion(reader: PdfReader): string {\r\n // Version is in the %PDF-X.Y header (first bytes of the file)\r\n const header = new TextDecoder('ascii', { fatal: false }).decode(\r\n reader.bytes.slice(0, 20),\r\n );\r\n const match = /^%PDF-(\\d+\\.\\d+)/.exec(header);\r\n return match !== null ? match[1] : 'unknown';\r\n}\r\n\r\nfunction extractEncrypted(reader: PdfReader): boolean {\r\n const trailer = reader.trailer;\r\n return trailer.get('Encrypt') !== undefined;\r\n}\r\n\r\nfunction extractPdfaConformance(reader: PdfReader): string | null {\r\n // PDF/A conformance is declared in XMP metadata.\r\n // We check the catalog -> Metadata stream for pdfaid:conformance and pdfaid:part.\r\n try {\r\n const catalog = reader.getCatalog();\r\n const metaRef = catalog.get('Metadata');\r\n if (metaRef === undefined) return null;\r\n const metaObj = reader.resolveValue(metaRef);\r\n if (\r\n typeof metaObj !== 'object' ||\r\n metaObj === null ||\r\n !('streamData' in metaObj)\r\n ) {\r\n return null;\r\n }\r\n const decoded = reader.decodeStream(\r\n metaObj as Parameters<PdfReader['decodeStream']>[0],\r\n );\r\n const xmp = new TextDecoder('utf-8', { fatal: false }).decode(decoded);\r\n const partMatch = /pdfaid:part[^>]*>(\\d+)</.exec(xmp);\r\n const confMatch = /pdfaid:conformance[^>]*>([A-Za-z]+)</.exec(xmp);\r\n if (partMatch !== null && confMatch !== null) {\r\n return `${partMatch[1]}${confMatch[1].toLowerCase()}`;\r\n }\r\n } catch {\r\n // Non-critical — conformance detection is best-effort\r\n }\r\n return null;\r\n}\r\n\r\nfunction countSignatures(reader: PdfReader): number {\r\n // Signatures are AcroForm fields with /FT /Sig\r\n try {\r\n const catalog = reader.getCatalog();\r\n const acroRef = catalog.get('AcroForm');\r\n if (acroRef === undefined) return 0;\r\n const acro = reader.resolveValue(acroRef);\r\n if (!(acro instanceof Map)) return 0;\r\n const fieldsVal = acro.get('Fields');\r\n if (!Array.isArray(fieldsVal)) return 0;\r\n let count = 0;\r\n for (const ref of fieldsVal) {\r\n const field = reader.resolveValue(ref as Parameters<PdfReader['resolveValue']>[0]);\r\n if (field instanceof Map && field.get('FT') === '/Sig') {\r\n count++;\r\n }\r\n }\r\n return count;\r\n } catch {\r\n return 0;\r\n }\r\n}\r\n\r\nexport async function inspect(args: ParsedArgs): Promise<void> {\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const format = getStringFlag(args.flags, 'format', 'f') ?? 'json';\r\n\r\n if (format !== 'json' && format !== 'text') {\r\n throw new CliError(`Invalid --format value \"${format}\". Valid values: json, text.`, 2);\r\n }\r\n\r\n const inputBuf = await readFileOrStdin(inputPath);\r\n const pdfBytes = new Uint8Array(inputBuf);\r\n\r\n let reader: PdfReader;\r\n try {\r\n reader = openPdf(pdfBytes);\r\n } catch (e) {\r\n const message = e instanceof Error ? e.message : String(e);\r\n throw new CliError(`Failed to read PDF: ${message}`, 1);\r\n }\r\n\r\n const info = reader.getInfo();\r\n const result: InspectResult = {\r\n version: extractVersion(reader),\r\n pageCount: reader.pageCount,\r\n encrypted: extractEncrypted(reader),\r\n pdfaConformance: extractPdfaConformance(reader),\r\n signatures: countSignatures(reader),\r\n metadata: {\r\n title: info !== null ? safeInfoString(info.get('Title')) : null,\r\n author: info !== null ? safeInfoString(info.get('Author')) : null,\r\n creationDate: info !== null ? safeInfoString(info.get('CreationDate')) : null,\r\n subject: info !== null ? safeInfoString(info.get('Subject')) : null,\r\n producer: info !== null ? safeInfoString(info.get('Producer')) : null,\r\n },\r\n };\r\n\r\n if (format === 'json') {\r\n process.stdout.write(JSON.stringify(result, null, 2) + '\\n');\r\n } else {\r\n // Human-readable text table\r\n const lines = [\r\n `Version: ${result.version}`,\r\n `Pages: ${result.pageCount}`,\r\n `Encrypted: ${result.encrypted ? 'yes' : 'no'}`,\r\n `PDF/A: ${result.pdfaConformance ?? 'none'}`,\r\n `Signatures: ${result.signatures}`,\r\n `Title: ${result.metadata.title ?? '—'}`,\r\n `Author: ${result.metadata.author ?? '—'}`,\r\n `Created: ${result.metadata.creationDate ?? '—'}`,\r\n `Subject: ${result.metadata.subject ?? '—'}`,\r\n `Producer: ${result.metadata.producer ?? '—'}`,\r\n ];\r\n process.stdout.write(lines.join('\\n') + '\\n');\r\n }\r\n}\r\n","import { createRequire } from 'node:module';\r\nimport { parseArgs, hasFlag } from './utils/args.js';\r\nimport { CliError } from './utils/error.js';\r\n\r\n// Lazy-import commands to keep startup fast for --help / --version\r\ntype CommandFn = (args: ReturnType<typeof parseArgs>) => Promise<void>;\r\n\r\nconst USAGE = `\\\r\npdfnative-cli — Official CLI for pdfnative\r\n\r\nUsage:\r\n pdfnative <command> [options]\r\n\r\nCommands:\r\n render Render a JSON document definition to PDF\r\n sign Apply a digital signature to an existing PDF\r\n inspect Analyse a PDF and output metadata / conformance info\r\n\r\nOptions:\r\n --help, -h Show this help message\r\n --version, -V Show version\r\n\r\nRun \\`pdfnative <command> --help\\` for per-command options.\r\n`;\r\n\r\nconst RENDER_USAGE = `\\\r\npdfnative render — Render a JSON DocumentParams to PDF\r\n\r\nUsage:\r\n pdfnative render [--input <file.json>] [--output <out.pdf>] [--stream] [--conformance <level>]\r\n\r\nOptions:\r\n --input, -i Path to JSON input file (default: stdin)\r\n --output, -o Output PDF path (default: stdout)\r\n --stream Use streaming output for large documents\r\n --conformance PDF/A conformance level: 1b, 2b, or 3b\r\n --help, -h Show this help message\r\n`;\r\n\r\nconst SIGN_USAGE = `\\\r\npdfnative sign — Apply a digital signature to an existing PDF\r\n\r\nUsage:\r\n pdfnative sign [--input <file.pdf>] [--output <out.pdf>] [--key <key.pem>] [--cert <cert.pem>]\r\n\r\nOptions:\r\n --input, -i Path to input PDF (default: stdin)\r\n --output, -o Signed PDF output path (default: stdout)\r\n --key Path to PEM private key file\r\n (overridden by $PDFNATIVE_SIGN_KEY env var)\r\n --cert Path to PEM certificate file\r\n (overridden by $PDFNATIVE_SIGN_CERT env var)\r\n --help, -h Show this help message\r\n\r\nSecurity: $PDFNATIVE_SIGN_KEY and $PDFNATIVE_SIGN_CERT env vars take precedence over file flags.\r\n`;\r\n\r\nconst INSPECT_USAGE = `\\\r\npdfnative inspect — Analyse a PDF and output metadata\r\n\r\nUsage:\r\n pdfnative inspect [--input <file.pdf>] [--format <fmt>]\r\n\r\nOptions:\r\n --input, -i Path to input PDF (default: stdin)\r\n --format, -f Output format: json (default) or text\r\n --help, -h Show this help message\r\n`;\r\n\r\nfunction getVersion(): string {\r\n // Use createRequire to load package.json in an ESM-compatible way\r\n const require = createRequire(import.meta.url);\r\n const pkg = require('../package.json') as { version: string };\r\n return pkg.version;\r\n}\r\n\r\nasync function loadCommand(name: string): Promise<CommandFn> {\r\n switch (name) {\r\n case 'render': {\r\n const m = await import('./commands/render.js');\r\n return m.render;\r\n }\r\n case 'sign': {\r\n const m = await import('./commands/sign.js');\r\n return m.sign;\r\n }\r\n case 'inspect': {\r\n const m = await import('./commands/inspect.js');\r\n return m.inspect;\r\n }\r\n default:\r\n return Promise.reject(new CliError(`Unknown command: ${name}. Run pdfnative --help for usage.`, 1));\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const argv = process.argv.slice(2);\r\n const args = parseArgs(argv);\r\n\r\n // Global --help / -h\r\n if (hasFlag(args.flags, 'help', 'h') && args.positionals.length === 0) {\r\n process.stdout.write(USAGE);\r\n process.exit(0);\r\n }\r\n\r\n // Global --version / -V\r\n if (hasFlag(args.flags, 'version', 'V')) {\r\n process.stdout.write(getVersion() + '\\n');\r\n process.exit(0);\r\n }\r\n\r\n const commandName = args.positionals[0];\r\n\r\n if (commandName === undefined) {\r\n process.stdout.write(USAGE);\r\n process.exit(0);\r\n }\r\n\r\n // Per-command --help\r\n if (hasFlag(args.flags, 'help', 'h')) {\r\n switch (commandName) {\r\n case 'render': process.stdout.write(RENDER_USAGE); break;\r\n case 'sign': process.stdout.write(SIGN_USAGE); break;\r\n case 'inspect': process.stdout.write(INSPECT_USAGE); break;\r\n default:\r\n process.stderr.write(`Unknown command: ${commandName}. Run pdfnative --help for usage.\\n`);\r\n process.exit(1);\r\n }\r\n process.exit(0);\r\n }\r\n\r\n // Strip the command name from positionals before passing to the command\r\n const commandArgs = parseArgs(argv.filter((_t, _i) => {\r\n // Remove only the first positional (the command name itself)\r\n if (_t === commandName && args.positionals[0] === commandName) {\r\n return false;\r\n }\r\n return true;\r\n }));\r\n\r\n const command = await loadCommand(commandName);\r\n await command(commandArgs);\r\n}\r\n\r\nmain().catch((e: unknown) => {\r\n if (e instanceof CliError) {\r\n process.stderr.write(e.message + '\\n');\r\n process.exit(e.exitCode);\r\n }\r\n const message = e instanceof Error ? e.message : String(e);\r\n process.stderr.write(`Error: ${message}\\n`);\r\n process.exit(1);\r\n});\r\n"]} |
| export { } |
| export { } |
+552
| #!/usr/bin/env node | ||
| import { buildDocumentPDFStream, buildDocumentPDFBytes, parseRsaPrivateKey, parseCertificate, signPdfBytes, openPdf } from 'pdfnative'; | ||
| import { createWriteStream } from 'fs'; | ||
| import { readFile, writeFile } from 'fs/promises'; | ||
| import { createRequire } from 'module'; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __esm = (fn, res) => function __init() { | ||
| return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; | ||
| }; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| // src/utils/error.ts | ||
| var CliError; | ||
| var init_error = __esm({ | ||
| "src/utils/error.ts"() { | ||
| CliError = class extends Error { | ||
| exitCode; | ||
| constructor(message, exitCode = 1) { | ||
| super(message); | ||
| this.name = "CliError"; | ||
| this.exitCode = exitCode; | ||
| } | ||
| }; | ||
| } | ||
| }); | ||
| // src/utils/args.ts | ||
| function parseArgs(argv) { | ||
| const flags = {}; | ||
| const positionals = []; | ||
| let i = 0; | ||
| while (i < argv.length) { | ||
| const token = argv[i]; | ||
| if (token === "--") { | ||
| i++; | ||
| while (i < argv.length) { | ||
| positionals.push(argv[i]); | ||
| i++; | ||
| } | ||
| break; | ||
| } | ||
| if (token.startsWith("--")) { | ||
| const eqIdx = token.indexOf("="); | ||
| if (eqIdx !== -1) { | ||
| const key = token.slice(2, eqIdx); | ||
| const value = token.slice(eqIdx + 1); | ||
| flags[key] = value; | ||
| } else { | ||
| const key = token.slice(2); | ||
| const next = argv[i + 1]; | ||
| if (next !== void 0 && !next.startsWith("-")) { | ||
| flags[key] = next; | ||
| i++; | ||
| } else { | ||
| flags[key] = true; | ||
| } | ||
| } | ||
| } else if (token.startsWith("-") && token.length === 2) { | ||
| const key = token.slice(1); | ||
| const next = argv[i + 1]; | ||
| if (next !== void 0 && !next.startsWith("-")) { | ||
| flags[key] = next; | ||
| i++; | ||
| } else { | ||
| flags[key] = true; | ||
| } | ||
| } else { | ||
| positionals.push(token); | ||
| } | ||
| i++; | ||
| } | ||
| return { flags, positionals }; | ||
| } | ||
| function getStringFlag(flags, ...names) { | ||
| for (const name of names) { | ||
| const value = flags[name]; | ||
| if (value !== void 0) { | ||
| if (typeof value !== "string") { | ||
| throw new CliError(`Flag --${name} requires a value.`, 2); | ||
| } | ||
| return value; | ||
| } | ||
| } | ||
| return void 0; | ||
| } | ||
| function hasFlag(flags, ...names) { | ||
| return names.some((n) => flags[n] !== void 0); | ||
| } | ||
| var init_args = __esm({ | ||
| "src/utils/args.ts"() { | ||
| init_error(); | ||
| } | ||
| }); | ||
| var init_core_bridge = __esm({ | ||
| "src/core-bridge/index.ts"() { | ||
| } | ||
| }); | ||
| function validatePath(filePath) { | ||
| const normalised = filePath.replace(/\\/g, "/"); | ||
| if (normalised.includes("../") || normalised === "..") { | ||
| throw new CliError(`Path traversal detected in path: ${filePath}`, 1); | ||
| } | ||
| } | ||
| function readStdin() { | ||
| return new Promise((resolve, reject) => { | ||
| const chunks = []; | ||
| process.stdin.on("data", (chunk) => chunks.push(chunk)); | ||
| process.stdin.on("end", () => resolve(Buffer.concat(chunks))); | ||
| process.stdin.on("error", reject); | ||
| }); | ||
| } | ||
| async function readFileOrStdin(filePath) { | ||
| if (filePath === void 0) { | ||
| return readStdin(); | ||
| } | ||
| validatePath(filePath); | ||
| return readFile(filePath); | ||
| } | ||
| function assertJsonSizeLimit(buf) { | ||
| if (buf.length > JSON_SIZE_LIMIT) { | ||
| throw new CliError( | ||
| `JSON input exceeds the 50 MB limit (got ${(buf.length / 1024 / 1024).toFixed(1)} MB).`, | ||
| 1 | ||
| ); | ||
| } | ||
| } | ||
| async function writeOutput(data, filePath) { | ||
| if (filePath === void 0) { | ||
| await new Promise((resolve, reject) => { | ||
| process.stdout.write(data, (err) => { | ||
| if (err) reject(err); | ||
| else resolve(); | ||
| }); | ||
| }); | ||
| return; | ||
| } | ||
| validatePath(filePath); | ||
| await writeFile(filePath, data); | ||
| } | ||
| async function writeStreamingOutput(chunks, filePath) { | ||
| if (filePath === void 0) { | ||
| for await (const chunk of chunks) { | ||
| await new Promise((resolve, reject) => { | ||
| process.stdout.write(chunk, (err) => { | ||
| if (err) reject(err); | ||
| else resolve(); | ||
| }); | ||
| }); | ||
| } | ||
| return; | ||
| } | ||
| validatePath(filePath); | ||
| const stream = createWriteStream(filePath); | ||
| await new Promise((resolve, reject) => { | ||
| stream.on("error", reject); | ||
| stream.on("finish", resolve); | ||
| (async () => { | ||
| for await (const chunk of chunks) { | ||
| const ok = stream.write(chunk); | ||
| if (!ok) { | ||
| await new Promise((r) => stream.once("drain", r)); | ||
| } | ||
| } | ||
| stream.end(); | ||
| })().catch(reject); | ||
| }); | ||
| } | ||
| var JSON_SIZE_LIMIT; | ||
| var init_io = __esm({ | ||
| "src/utils/io.ts"() { | ||
| init_error(); | ||
| JSON_SIZE_LIMIT = 50 * 1024 * 1024; | ||
| } | ||
| }); | ||
| // src/commands/render.ts | ||
| var render_exports = {}; | ||
| __export(render_exports, { | ||
| render: () => render | ||
| }); | ||
| async function render(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const outputPath = getStringFlag(args.flags, "output", "o"); | ||
| const useStream = hasFlag(args.flags, "stream"); | ||
| const conformance = getStringFlag(args.flags, "conformance"); | ||
| if (conformance !== void 0 && !VALID_CONFORMANCE.has(conformance)) { | ||
| throw new CliError( | ||
| `Invalid --conformance value "${conformance}". Valid values: 1b, 2b, 3b.`, | ||
| 2 | ||
| ); | ||
| } | ||
| const inputBuf = await readFileOrStdin(inputPath); | ||
| assertJsonSizeLimit(inputBuf); | ||
| let params; | ||
| try { | ||
| params = JSON.parse(inputBuf.toString("utf8")); | ||
| } catch (e) { | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| throw new CliError(`Failed to parse JSON input: ${message}`, 1); | ||
| } | ||
| if (typeof params !== "object" || params === null) { | ||
| throw new CliError("JSON input must be a DocumentParams object.", 1); | ||
| } | ||
| if (conformance !== void 0) { | ||
| params["pdfaConformance"] = conformance; | ||
| } | ||
| if (useStream) { | ||
| const generator = buildDocumentPDFStream(params); | ||
| await writeStreamingOutput(generator, outputPath); | ||
| } else { | ||
| const pdfBytes = buildDocumentPDFBytes(params); | ||
| await writeOutput(pdfBytes, outputPath); | ||
| } | ||
| } | ||
| var VALID_CONFORMANCE; | ||
| var init_render = __esm({ | ||
| "src/commands/render.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| VALID_CONFORMANCE = /* @__PURE__ */ new Set(["1b", "2b", "3b"]); | ||
| } | ||
| }); | ||
| // src/commands/sign.ts | ||
| var sign_exports = {}; | ||
| __export(sign_exports, { | ||
| sign: () => sign | ||
| }); | ||
| function pemToDer(pem) { | ||
| const body = pem.replace(/-----BEGIN [^-]+-----/g, "").replace(/-----END [^-]+-----/g, "").replace(/\s+/g, ""); | ||
| const binaryStr = atob(body); | ||
| const bytes = new Uint8Array(binaryStr.length); | ||
| for (let i = 0; i < binaryStr.length; i++) { | ||
| bytes[i] = binaryStr.charCodeAt(i); | ||
| } | ||
| return bytes; | ||
| } | ||
| async function loadPem(envVar, filePath, label) { | ||
| const fromEnv = process.env[envVar]; | ||
| if (fromEnv !== void 0 && fromEnv.trim().length > 0) { | ||
| return fromEnv; | ||
| } | ||
| if (filePath !== void 0) { | ||
| validatePath(filePath); | ||
| const buf = await readFile(filePath, "utf8"); | ||
| return buf; | ||
| } | ||
| throw new CliError( | ||
| `Missing ${label}. Provide $${envVar} (env) or --${label.replace(/ /g, "-")} <path>.`, | ||
| 2 | ||
| ); | ||
| } | ||
| async function sign(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const outputPath = getStringFlag(args.flags, "output", "o"); | ||
| const keyPath = getStringFlag(args.flags, "key"); | ||
| const certPath = getStringFlag(args.flags, "cert"); | ||
| const pdfBuf = await readFileOrStdin(inputPath); | ||
| const pdfBytes = new Uint8Array(pdfBuf); | ||
| const privateKeyPem = await loadPem("PDFNATIVE_SIGN_KEY", keyPath, "private key"); | ||
| const certPem = await loadPem("PDFNATIVE_SIGN_CERT", certPath, "certificate"); | ||
| let options; | ||
| try { | ||
| const keyDer = pemToDer(privateKeyPem); | ||
| const certDer = pemToDer(certPem); | ||
| const rsaKey = parseRsaPrivateKey(keyDer); | ||
| const signerCert = parseCertificate(certDer); | ||
| options = { rsaKey, signerCert, algorithm: "rsa-sha256" }; | ||
| } catch { | ||
| throw new CliError("Failed to parse signing credentials. Verify key and certificate are valid PEM-encoded files.", 1); | ||
| } | ||
| const signedBytes = signPdfBytes(pdfBytes, options); | ||
| await writeOutput(signedBytes, outputPath); | ||
| } | ||
| var init_sign = __esm({ | ||
| "src/commands/sign.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| } | ||
| }); | ||
| // src/commands/inspect.ts | ||
| var inspect_exports = {}; | ||
| __export(inspect_exports, { | ||
| inspect: () => inspect | ||
| }); | ||
| function safeInfoString(value) { | ||
| if (typeof value !== "string") return null; | ||
| const trimmed = value.trim(); | ||
| if (trimmed.startsWith("(") && trimmed.endsWith(")")) { | ||
| return trimmed.slice(1, -1); | ||
| } | ||
| if (/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(trimmed)) { | ||
| return null; | ||
| } | ||
| return trimmed || null; | ||
| } | ||
| function extractVersion(reader) { | ||
| const header = new TextDecoder("ascii", { fatal: false }).decode( | ||
| reader.bytes.slice(0, 20) | ||
| ); | ||
| const match = /^%PDF-(\d+\.\d+)/.exec(header); | ||
| return match !== null ? match[1] : "unknown"; | ||
| } | ||
| function extractEncrypted(reader) { | ||
| const trailer = reader.trailer; | ||
| return trailer.get("Encrypt") !== void 0; | ||
| } | ||
| function extractPdfaConformance(reader) { | ||
| try { | ||
| const catalog = reader.getCatalog(); | ||
| const metaRef = catalog.get("Metadata"); | ||
| if (metaRef === void 0) return null; | ||
| const metaObj = reader.resolveValue(metaRef); | ||
| if (typeof metaObj !== "object" || metaObj === null || !("streamData" in metaObj)) { | ||
| return null; | ||
| } | ||
| const decoded = reader.decodeStream( | ||
| metaObj | ||
| ); | ||
| const xmp = new TextDecoder("utf-8", { fatal: false }).decode(decoded); | ||
| const partMatch = /pdfaid:part[^>]*>(\d+)</.exec(xmp); | ||
| const confMatch = /pdfaid:conformance[^>]*>([A-Za-z]+)</.exec(xmp); | ||
| if (partMatch !== null && confMatch !== null) { | ||
| return `${partMatch[1]}${confMatch[1].toLowerCase()}`; | ||
| } | ||
| } catch { | ||
| } | ||
| return null; | ||
| } | ||
| function countSignatures(reader) { | ||
| try { | ||
| const catalog = reader.getCatalog(); | ||
| const acroRef = catalog.get("AcroForm"); | ||
| if (acroRef === void 0) return 0; | ||
| const acro = reader.resolveValue(acroRef); | ||
| if (!(acro instanceof Map)) return 0; | ||
| const fieldsVal = acro.get("Fields"); | ||
| if (!Array.isArray(fieldsVal)) return 0; | ||
| let count = 0; | ||
| for (const ref of fieldsVal) { | ||
| const field = reader.resolveValue(ref); | ||
| if (field instanceof Map && field.get("FT") === "/Sig") { | ||
| count++; | ||
| } | ||
| } | ||
| return count; | ||
| } catch { | ||
| return 0; | ||
| } | ||
| } | ||
| async function inspect(args) { | ||
| const inputPath = getStringFlag(args.flags, "input", "i"); | ||
| const format = getStringFlag(args.flags, "format", "f") ?? "json"; | ||
| if (format !== "json" && format !== "text") { | ||
| throw new CliError(`Invalid --format value "${format}". Valid values: json, text.`, 2); | ||
| } | ||
| const inputBuf = await readFileOrStdin(inputPath); | ||
| const pdfBytes = new Uint8Array(inputBuf); | ||
| let reader; | ||
| try { | ||
| reader = openPdf(pdfBytes); | ||
| } catch (e) { | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| throw new CliError(`Failed to read PDF: ${message}`, 1); | ||
| } | ||
| const info = reader.getInfo(); | ||
| const result = { | ||
| version: extractVersion(reader), | ||
| pageCount: reader.pageCount, | ||
| encrypted: extractEncrypted(reader), | ||
| pdfaConformance: extractPdfaConformance(reader), | ||
| signatures: countSignatures(reader), | ||
| metadata: { | ||
| title: info !== null ? safeInfoString(info.get("Title")) : null, | ||
| author: info !== null ? safeInfoString(info.get("Author")) : null, | ||
| creationDate: info !== null ? safeInfoString(info.get("CreationDate")) : null, | ||
| subject: info !== null ? safeInfoString(info.get("Subject")) : null, | ||
| producer: info !== null ? safeInfoString(info.get("Producer")) : null | ||
| } | ||
| }; | ||
| if (format === "json") { | ||
| process.stdout.write(JSON.stringify(result, null, 2) + "\n"); | ||
| } else { | ||
| const lines = [ | ||
| `Version: ${result.version}`, | ||
| `Pages: ${result.pageCount}`, | ||
| `Encrypted: ${result.encrypted ? "yes" : "no"}`, | ||
| `PDF/A: ${result.pdfaConformance ?? "none"}`, | ||
| `Signatures: ${result.signatures}`, | ||
| `Title: ${result.metadata.title ?? "\u2014"}`, | ||
| `Author: ${result.metadata.author ?? "\u2014"}`, | ||
| `Created: ${result.metadata.creationDate ?? "\u2014"}`, | ||
| `Subject: ${result.metadata.subject ?? "\u2014"}`, | ||
| `Producer: ${result.metadata.producer ?? "\u2014"}` | ||
| ]; | ||
| process.stdout.write(lines.join("\n") + "\n"); | ||
| } | ||
| } | ||
| var init_inspect = __esm({ | ||
| "src/commands/inspect.ts"() { | ||
| init_core_bridge(); | ||
| init_args(); | ||
| init_io(); | ||
| init_error(); | ||
| } | ||
| }); | ||
| // src/index.ts | ||
| init_args(); | ||
| init_error(); | ||
| var USAGE = `pdfnative-cli \u2014 Official CLI for pdfnative | ||
| Usage: | ||
| pdfnative <command> [options] | ||
| Commands: | ||
| render Render a JSON document definition to PDF | ||
| sign Apply a digital signature to an existing PDF | ||
| inspect Analyse a PDF and output metadata / conformance info | ||
| Options: | ||
| --help, -h Show this help message | ||
| --version, -V Show version | ||
| Run \`pdfnative <command> --help\` for per-command options. | ||
| `; | ||
| var RENDER_USAGE = `pdfnative render \u2014 Render a JSON DocumentParams to PDF | ||
| Usage: | ||
| pdfnative render [--input <file.json>] [--output <out.pdf>] [--stream] [--conformance <level>] | ||
| Options: | ||
| --input, -i Path to JSON input file (default: stdin) | ||
| --output, -o Output PDF path (default: stdout) | ||
| --stream Use streaming output for large documents | ||
| --conformance PDF/A conformance level: 1b, 2b, or 3b | ||
| --help, -h Show this help message | ||
| `; | ||
| var SIGN_USAGE = `pdfnative sign \u2014 Apply a digital signature to an existing PDF | ||
| Usage: | ||
| pdfnative sign [--input <file.pdf>] [--output <out.pdf>] [--key <key.pem>] [--cert <cert.pem>] | ||
| Options: | ||
| --input, -i Path to input PDF (default: stdin) | ||
| --output, -o Signed PDF output path (default: stdout) | ||
| --key Path to PEM private key file | ||
| (overridden by $PDFNATIVE_SIGN_KEY env var) | ||
| --cert Path to PEM certificate file | ||
| (overridden by $PDFNATIVE_SIGN_CERT env var) | ||
| --help, -h Show this help message | ||
| Security: $PDFNATIVE_SIGN_KEY and $PDFNATIVE_SIGN_CERT env vars take precedence over file flags. | ||
| `; | ||
| var INSPECT_USAGE = `pdfnative inspect \u2014 Analyse a PDF and output metadata | ||
| Usage: | ||
| pdfnative inspect [--input <file.pdf>] [--format <fmt>] | ||
| Options: | ||
| --input, -i Path to input PDF (default: stdin) | ||
| --format, -f Output format: json (default) or text | ||
| --help, -h Show this help message | ||
| `; | ||
| function getVersion() { | ||
| const require2 = createRequire(import.meta.url); | ||
| const pkg = require2("../package.json"); | ||
| return pkg.version; | ||
| } | ||
| async function loadCommand(name) { | ||
| switch (name) { | ||
| case "render": { | ||
| const m = await Promise.resolve().then(() => (init_render(), render_exports)); | ||
| return m.render; | ||
| } | ||
| case "sign": { | ||
| const m = await Promise.resolve().then(() => (init_sign(), sign_exports)); | ||
| return m.sign; | ||
| } | ||
| case "inspect": { | ||
| const m = await Promise.resolve().then(() => (init_inspect(), inspect_exports)); | ||
| return m.inspect; | ||
| } | ||
| default: | ||
| return Promise.reject(new CliError(`Unknown command: ${name}. Run pdfnative --help for usage.`, 1)); | ||
| } | ||
| } | ||
| async function main() { | ||
| const argv = process.argv.slice(2); | ||
| const args = parseArgs(argv); | ||
| if (hasFlag(args.flags, "help", "h") && args.positionals.length === 0) { | ||
| process.stdout.write(USAGE); | ||
| process.exit(0); | ||
| } | ||
| if (hasFlag(args.flags, "version", "V")) { | ||
| process.stdout.write(getVersion() + "\n"); | ||
| process.exit(0); | ||
| } | ||
| const commandName = args.positionals[0]; | ||
| if (commandName === void 0) { | ||
| process.stdout.write(USAGE); | ||
| process.exit(0); | ||
| } | ||
| if (hasFlag(args.flags, "help", "h")) { | ||
| switch (commandName) { | ||
| case "render": | ||
| process.stdout.write(RENDER_USAGE); | ||
| break; | ||
| case "sign": | ||
| process.stdout.write(SIGN_USAGE); | ||
| break; | ||
| case "inspect": | ||
| process.stdout.write(INSPECT_USAGE); | ||
| break; | ||
| default: | ||
| process.stderr.write(`Unknown command: ${commandName}. Run pdfnative --help for usage. | ||
| `); | ||
| process.exit(1); | ||
| } | ||
| process.exit(0); | ||
| } | ||
| const commandArgs = parseArgs(argv.filter((_t, _i) => { | ||
| if (_t === commandName && args.positionals[0] === commandName) { | ||
| return false; | ||
| } | ||
| return true; | ||
| })); | ||
| const command = await loadCommand(commandName); | ||
| await command(commandArgs); | ||
| } | ||
| main().catch((e) => { | ||
| if (e instanceof CliError) { | ||
| process.stderr.write(e.message + "\n"); | ||
| process.exit(e.exitCode); | ||
| } | ||
| const message = e instanceof Error ? e.message : String(e); | ||
| process.stderr.write(`Error: ${message} | ||
| `); | ||
| process.exit(1); | ||
| }); | ||
| //# sourceMappingURL=cli.js.map | ||
| //# sourceMappingURL=cli.js.map |
| {"version":3,"sources":["../src/utils/error.ts","../src/utils/args.ts","../src/core-bridge/index.ts","../src/utils/io.ts","../src/commands/render.ts","../src/commands/sign.ts","../src/commands/inspect.ts","../src/index.ts"],"names":["readFile","require"],"mappings":"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","file":"cli.js","sourcesContent":["/**\r\n * CLI Error — thrown by commands when a user-facing error occurs.\r\n *\r\n * Exit code conventions:\r\n * 1 = runtime error (invalid input, I/O failure)\r\n * 2 = usage error (missing required argument)\r\n */\r\nexport class CliError extends Error {\r\n public readonly exitCode: number;\r\n\r\n constructor(message: string, exitCode = 1) {\r\n super(message);\r\n this.name = 'CliError';\r\n this.exitCode = exitCode;\r\n }\r\n}\r\n\r\n/**\r\n * Print a message to stderr and terminate the process.\r\n * Never returns — declared as `never` for type narrowing.\r\n */\r\nexport function die(message: string, exitCode = 1): never {\r\n process.stderr.write(message + '\\n');\r\n process.exit(exitCode);\r\n}\r\n","import { CliError } from './error.js';\r\n\r\nexport interface ParsedArgs {\r\n readonly flags: Record<string, string | boolean>;\r\n readonly positionals: readonly string[];\r\n}\r\n\r\n/**\r\n * Zero-dependency argument parser.\r\n *\r\n * Supported forms:\r\n * --flag value flags.flag = 'value'\r\n * --flag=value flags.flag = 'value'\r\n * -f value flags.f = 'value'\r\n * --flag flags.flag = true (boolean)\r\n * -- stop flag parsing; rest → positionals\r\n * bare token positionals[]\r\n */\r\nexport function parseArgs(argv: readonly string[]): ParsedArgs {\r\n const flags: Record<string, string | boolean> = {};\r\n const positionals: string[] = [];\r\n let i = 0;\r\n\r\n while (i < argv.length) {\r\n const token = argv[i] as string;\r\n\r\n if (token === '--') {\r\n // Everything after -- goes into positionals\r\n i++;\r\n while (i < argv.length) {\r\n positionals.push(argv[i] as string);\r\n i++;\r\n }\r\n break;\r\n }\r\n\r\n if (token.startsWith('--')) {\r\n const eqIdx = token.indexOf('=');\r\n if (eqIdx !== -1) {\r\n // --flag=value\r\n const key = token.slice(2, eqIdx);\r\n const value = token.slice(eqIdx + 1);\r\n flags[key] = value;\r\n } else {\r\n const key = token.slice(2);\r\n const next = argv[i + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n // --flag value\r\n flags[key] = next;\r\n i++;\r\n } else {\r\n // --flag (boolean)\r\n flags[key] = true;\r\n }\r\n }\r\n } else if (token.startsWith('-') && token.length === 2) {\r\n // -f value\r\n const key = token.slice(1);\r\n const next = argv[i + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n flags[key] = next;\r\n i++;\r\n } else {\r\n flags[key] = true;\r\n }\r\n } else {\r\n positionals.push(token);\r\n }\r\n\r\n i++;\r\n }\r\n\r\n return { flags, positionals };\r\n}\r\n\r\n/**\r\n * Return the string value of the first matching flag name, or undefined.\r\n * Throws CliError if the flag is present but its value is a boolean (i.e. no value provided).\r\n */\r\nexport function getStringFlag(\r\n flags: Record<string, string | boolean>,\r\n ...names: string[]\r\n): string | undefined {\r\n for (const name of names) {\r\n const value = flags[name];\r\n if (value !== undefined) {\r\n if (typeof value !== 'string') {\r\n throw new CliError(`Flag --${name} requires a value.`, 2);\r\n }\r\n return value;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\n/** Return true if any of the given flag names is present (boolean or string value). */\r\nexport function hasFlag(flags: Record<string, string | boolean>, ...names: string[]): boolean {\r\n return names.some((n) => flags[n] !== undefined);\r\n}\r\n","// Selective re-exports from pdfnative — keeps the CLI surface minimal.\r\n// All PDF logic lives in pdfnative; this module is the only import point.\r\n\r\n// ── Render ───────────────────────────────────────────────────────────\r\nexport { buildDocumentPDFBytes } from 'pdfnative';\r\nexport { buildDocumentPDFStream } from 'pdfnative';\r\n\r\n// ── Sign ─────────────────────────────────────────────────────────────\r\nexport { signPdfBytes } from 'pdfnative';\r\nexport { parseCertificate } from 'pdfnative';\r\nexport { parseRsaPrivateKey } from 'pdfnative';\r\n\r\n// ── Inspect ──────────────────────────────────────────────────────────\r\nexport { openPdf } from 'pdfnative';\r\n\r\n// ── Types ────────────────────────────────────────────────────────────\r\nexport type { DocumentParams } from 'pdfnative';\r\nexport type { PdfSignOptions, SignatureAlgorithm } from 'pdfnative';\r\nexport type { X509Certificate, X509Name } from 'pdfnative';\r\nexport type { RsaPrivateKey } from 'pdfnative';\r\nexport type { EcPrivateKey } from 'pdfnative';\r\nexport type { PdfReader } from 'pdfnative';\r\nexport type { StreamOptions } from 'pdfnative';\r\n","import { createWriteStream } from 'node:fs';\r\nimport { readFile, writeFile } from 'node:fs/promises';\r\nimport { CliError } from './error.js';\r\n\r\nconst JSON_SIZE_LIMIT = 50 * 1024 * 1024; // 50 MB\r\n\r\n/**\r\n * Validate a file path against directory traversal.\r\n * Throws CliError if the path contains `../` or `..\\\\` sequences.\r\n */\r\nexport function validatePath(filePath: string): void {\r\n // Normalise backslashes for Windows paths and check for traversal\r\n const normalised = filePath.replace(/\\\\/g, '/');\r\n if (normalised.includes('../') || normalised === '..') {\r\n throw new CliError(`Path traversal detected in path: ${filePath}`, 1);\r\n }\r\n}\r\n\r\n/**\r\n * Read all bytes from stdin.\r\n */\r\nexport function readStdin(): Promise<Buffer> {\r\n return new Promise((resolve, reject) => {\r\n const chunks: Buffer[] = [];\r\n process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk));\r\n process.stdin.on('end', () => resolve(Buffer.concat(chunks)));\r\n process.stdin.on('error', reject);\r\n });\r\n}\r\n\r\n/**\r\n * Read a file by path, or fall back to stdin if `filePath` is undefined.\r\n */\r\nexport async function readFileOrStdin(filePath: string | undefined): Promise<Buffer> {\r\n if (filePath === undefined) {\r\n return readStdin();\r\n }\r\n validatePath(filePath);\r\n return readFile(filePath);\r\n}\r\n\r\n/**\r\n * Enforce the 50 MB JSON input size limit on a buffer.\r\n * Throws CliError if the buffer exceeds the limit.\r\n */\r\nexport function assertJsonSizeLimit(buf: Buffer): void {\r\n if (buf.length > JSON_SIZE_LIMIT) {\r\n throw new CliError(\r\n `JSON input exceeds the 50 MB limit (got ${(buf.length / 1024 / 1024).toFixed(1)} MB).`,\r\n 1,\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Write binary data to a file path, or to stdout if `filePath` is undefined.\r\n */\r\nexport async function writeOutput(data: Uint8Array, filePath: string | undefined): Promise<void> {\r\n if (filePath === undefined) {\r\n await new Promise<void>((resolve, reject) => {\r\n process.stdout.write(data, (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n });\r\n });\r\n return;\r\n }\r\n validatePath(filePath);\r\n await writeFile(filePath, data);\r\n}\r\n\r\n/**\r\n * Pipe streaming PDF chunks to a file or stdout.\r\n * Used by `render --stream`.\r\n */\r\nexport async function writeStreamingOutput(\r\n chunks: AsyncGenerator<Uint8Array>,\r\n filePath: string | undefined,\r\n): Promise<void> {\r\n if (filePath === undefined) {\r\n for await (const chunk of chunks) {\r\n await new Promise<void>((resolve, reject) => {\r\n process.stdout.write(chunk, (err) => {\r\n if (err) reject(err);\r\n else resolve();\r\n });\r\n });\r\n }\r\n return;\r\n }\r\n\r\n validatePath(filePath);\r\n const stream = createWriteStream(filePath);\r\n await new Promise<void>((resolve, reject) => {\r\n stream.on('error', reject);\r\n stream.on('finish', resolve);\r\n (async () => {\r\n for await (const chunk of chunks) {\r\n const ok = stream.write(chunk);\r\n if (!ok) {\r\n await new Promise<void>((r) => stream.once('drain', r));\r\n }\r\n }\r\n stream.end();\r\n })().catch(reject);\r\n });\r\n}\r\n\r\n","import { buildDocumentPDFBytes, buildDocumentPDFStream } from '../core-bridge/index.js';\r\nimport type { DocumentParams } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag, hasFlag } from '../utils/args.js';\r\nimport { readFileOrStdin, writeOutput, writeStreamingOutput, assertJsonSizeLimit } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\nconst VALID_CONFORMANCE = new Set(['1b', '2b', '3b']);\r\n\r\nexport async function render(args: ParsedArgs): Promise<void> {\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const outputPath = getStringFlag(args.flags, 'output', 'o');\r\n const useStream = hasFlag(args.flags, 'stream');\r\n const conformance = getStringFlag(args.flags, 'conformance');\r\n\r\n if (conformance !== undefined && !VALID_CONFORMANCE.has(conformance)) {\r\n throw new CliError(\r\n `Invalid --conformance value \"${conformance}\". Valid values: 1b, 2b, 3b.`,\r\n 2,\r\n );\r\n }\r\n\r\n const inputBuf = await readFileOrStdin(inputPath);\r\n assertJsonSizeLimit(inputBuf);\r\n\r\n let params: DocumentParams;\r\n try {\r\n params = JSON.parse(inputBuf.toString('utf8')) as DocumentParams;\r\n } catch (e) {\r\n const message = e instanceof Error ? e.message : String(e);\r\n throw new CliError(`Failed to parse JSON input: ${message}`, 1);\r\n }\r\n\r\n if (typeof params !== 'object' || params === null) {\r\n throw new CliError('JSON input must be a DocumentParams object.', 1);\r\n }\r\n\r\n // Inject --conformance flag into params if provided\r\n if (conformance !== undefined) {\r\n (params as unknown as Record<string, unknown>)['pdfaConformance'] = conformance;\r\n }\r\n\r\n if (useStream) {\r\n const generator = buildDocumentPDFStream(params);\r\n await writeStreamingOutput(generator, outputPath);\r\n } else {\r\n const pdfBytes = buildDocumentPDFBytes(params);\r\n await writeOutput(pdfBytes, outputPath);\r\n }\r\n}\r\n","import { readFile } from 'node:fs/promises';\r\nimport {\r\n signPdfBytes,\r\n parseCertificate,\r\n parseRsaPrivateKey,\r\n} from '../core-bridge/index.js';\r\nimport type { PdfSignOptions } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag } from '../utils/args.js';\r\nimport { readFileOrStdin, writeOutput, validatePath } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\n/**\r\n * Decode a PEM-encoded block to DER bytes.\r\n * Strips -----BEGIN ...-----/-----END ...----- headers and base64-decodes.\r\n */\r\nfunction pemToDer(pem: string): Uint8Array {\r\n const body = pem\r\n .replace(/-----BEGIN [^-]+-----/g, '')\r\n .replace(/-----END [^-]+-----/g, '')\r\n .replace(/\\s+/g, '');\r\n const binaryStr = atob(body);\r\n const bytes = new Uint8Array(binaryStr.length);\r\n for (let i = 0; i < binaryStr.length; i++) {\r\n bytes[i] = binaryStr.charCodeAt(i);\r\n }\r\n return bytes;\r\n}\r\n\r\n/**\r\n * Load a PEM string from an environment variable or a file path.\r\n * Environment variable takes precedence over the file flag (security best practice).\r\n *\r\n * @param envVar - Name of the env var containing the PEM string.\r\n * @param filePath - File path from a CLI flag.\r\n * @param label - Human-readable label for error messages (e.g. \"private key\").\r\n */\r\nasync function loadPem(\r\n envVar: string,\r\n filePath: string | undefined,\r\n label: string,\r\n): Promise<string> {\r\n const fromEnv = process.env[envVar];\r\n if (fromEnv !== undefined && fromEnv.trim().length > 0) {\r\n return fromEnv;\r\n }\r\n if (filePath !== undefined) {\r\n validatePath(filePath);\r\n const buf = await readFile(filePath, 'utf8');\r\n return buf;\r\n }\r\n throw new CliError(\r\n `Missing ${label}. Provide $${envVar} (env) or --${label.replace(/ /g, '-')} <path>.`,\r\n 2,\r\n );\r\n}\r\n\r\nexport async function sign(args: ParsedArgs): Promise<void> {\r\n // --input is required for sign (no stdin for PDF to avoid accidental pipe issues)\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const outputPath = getStringFlag(args.flags, 'output', 'o');\r\n const keyPath = getStringFlag(args.flags, 'key');\r\n const certPath = getStringFlag(args.flags, 'cert');\r\n\r\n const pdfBuf = await readFileOrStdin(inputPath);\r\n const pdfBytes = new Uint8Array(pdfBuf);\r\n\r\n // Load secrets — env vars take precedence over file flags (OWASP: avoid key exposure)\r\n const privateKeyPem = await loadPem('PDFNATIVE_SIGN_KEY', keyPath, 'private key');\r\n const certPem = await loadPem('PDFNATIVE_SIGN_CERT', certPath, 'certificate');\r\n\r\n // Parse keys — never include raw key material in error messages\r\n let options: PdfSignOptions;\r\n try {\r\n const keyDer = pemToDer(privateKeyPem);\r\n const certDer = pemToDer(certPem);\r\n const rsaKey = parseRsaPrivateKey(keyDer);\r\n const signerCert = parseCertificate(certDer);\r\n options = { rsaKey, signerCert, algorithm: 'rsa-sha256' };\r\n } catch {\r\n // Do NOT include key material or PEM content in the error message\r\n throw new CliError('Failed to parse signing credentials. Verify key and certificate are valid PEM-encoded files.', 1);\r\n }\r\n\r\n const signedBytes = signPdfBytes(pdfBytes, options);\r\n await writeOutput(signedBytes, outputPath);\r\n}\r\n","import { openPdf } from '../core-bridge/index.js';\r\nimport type { PdfReader } from '../core-bridge/index.js';\r\nimport { type ParsedArgs, getStringFlag } from '../utils/args.js';\r\nimport { readFileOrStdin } from '../utils/io.js';\r\nimport { CliError } from '../utils/error.js';\r\n\r\ninterface InspectResult {\r\n readonly version: string;\r\n readonly pageCount: number;\r\n readonly encrypted: boolean;\r\n readonly pdfaConformance: string | null;\r\n readonly signatures: number;\r\n readonly metadata: {\r\n readonly title: string | null;\r\n readonly author: string | null;\r\n readonly creationDate: string | null;\r\n readonly subject: string | null;\r\n readonly producer: string | null;\r\n };\r\n}\r\n\r\n/**\r\n * Safely extract a string value from a PDF info dictionary entry.\r\n * Returns null if the value is not a plain string (e.g. hex strings, refs, binary blobs).\r\n */\r\nfunction safeInfoString(value: unknown): string | null {\r\n if (typeof value !== 'string') return null;\r\n // Strip surrounding PDF literal string parens if present\r\n const trimmed = value.trim();\r\n if (trimmed.startsWith('(') && trimmed.endsWith(')')) {\r\n return trimmed.slice(1, -1);\r\n }\r\n // Filter out binary/non-printable content (no raw bytes in output — OWASP)\r\n // eslint-disable-next-line no-control-regex\r\n if (/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/.test(trimmed)) {\r\n return null;\r\n }\r\n return trimmed || null;\r\n}\r\n\r\nfunction extractVersion(reader: PdfReader): string {\r\n // Version is in the %PDF-X.Y header (first bytes of the file)\r\n const header = new TextDecoder('ascii', { fatal: false }).decode(\r\n reader.bytes.slice(0, 20),\r\n );\r\n const match = /^%PDF-(\\d+\\.\\d+)/.exec(header);\r\n return match !== null ? match[1] : 'unknown';\r\n}\r\n\r\nfunction extractEncrypted(reader: PdfReader): boolean {\r\n const trailer = reader.trailer;\r\n return trailer.get('Encrypt') !== undefined;\r\n}\r\n\r\nfunction extractPdfaConformance(reader: PdfReader): string | null {\r\n // PDF/A conformance is declared in XMP metadata.\r\n // We check the catalog -> Metadata stream for pdfaid:conformance and pdfaid:part.\r\n try {\r\n const catalog = reader.getCatalog();\r\n const metaRef = catalog.get('Metadata');\r\n if (metaRef === undefined) return null;\r\n const metaObj = reader.resolveValue(metaRef);\r\n if (\r\n typeof metaObj !== 'object' ||\r\n metaObj === null ||\r\n !('streamData' in metaObj)\r\n ) {\r\n return null;\r\n }\r\n const decoded = reader.decodeStream(\r\n metaObj as Parameters<PdfReader['decodeStream']>[0],\r\n );\r\n const xmp = new TextDecoder('utf-8', { fatal: false }).decode(decoded);\r\n const partMatch = /pdfaid:part[^>]*>(\\d+)</.exec(xmp);\r\n const confMatch = /pdfaid:conformance[^>]*>([A-Za-z]+)</.exec(xmp);\r\n if (partMatch !== null && confMatch !== null) {\r\n return `${partMatch[1]}${confMatch[1].toLowerCase()}`;\r\n }\r\n } catch {\r\n // Non-critical — conformance detection is best-effort\r\n }\r\n return null;\r\n}\r\n\r\nfunction countSignatures(reader: PdfReader): number {\r\n // Signatures are AcroForm fields with /FT /Sig\r\n try {\r\n const catalog = reader.getCatalog();\r\n const acroRef = catalog.get('AcroForm');\r\n if (acroRef === undefined) return 0;\r\n const acro = reader.resolveValue(acroRef);\r\n if (!(acro instanceof Map)) return 0;\r\n const fieldsVal = acro.get('Fields');\r\n if (!Array.isArray(fieldsVal)) return 0;\r\n let count = 0;\r\n for (const ref of fieldsVal) {\r\n const field = reader.resolveValue(ref as Parameters<PdfReader['resolveValue']>[0]);\r\n if (field instanceof Map && field.get('FT') === '/Sig') {\r\n count++;\r\n }\r\n }\r\n return count;\r\n } catch {\r\n return 0;\r\n }\r\n}\r\n\r\nexport async function inspect(args: ParsedArgs): Promise<void> {\r\n const inputPath = getStringFlag(args.flags, 'input', 'i');\r\n const format = getStringFlag(args.flags, 'format', 'f') ?? 'json';\r\n\r\n if (format !== 'json' && format !== 'text') {\r\n throw new CliError(`Invalid --format value \"${format}\". Valid values: json, text.`, 2);\r\n }\r\n\r\n const inputBuf = await readFileOrStdin(inputPath);\r\n const pdfBytes = new Uint8Array(inputBuf);\r\n\r\n let reader: PdfReader;\r\n try {\r\n reader = openPdf(pdfBytes);\r\n } catch (e) {\r\n const message = e instanceof Error ? e.message : String(e);\r\n throw new CliError(`Failed to read PDF: ${message}`, 1);\r\n }\r\n\r\n const info = reader.getInfo();\r\n const result: InspectResult = {\r\n version: extractVersion(reader),\r\n pageCount: reader.pageCount,\r\n encrypted: extractEncrypted(reader),\r\n pdfaConformance: extractPdfaConformance(reader),\r\n signatures: countSignatures(reader),\r\n metadata: {\r\n title: info !== null ? safeInfoString(info.get('Title')) : null,\r\n author: info !== null ? safeInfoString(info.get('Author')) : null,\r\n creationDate: info !== null ? safeInfoString(info.get('CreationDate')) : null,\r\n subject: info !== null ? safeInfoString(info.get('Subject')) : null,\r\n producer: info !== null ? safeInfoString(info.get('Producer')) : null,\r\n },\r\n };\r\n\r\n if (format === 'json') {\r\n process.stdout.write(JSON.stringify(result, null, 2) + '\\n');\r\n } else {\r\n // Human-readable text table\r\n const lines = [\r\n `Version: ${result.version}`,\r\n `Pages: ${result.pageCount}`,\r\n `Encrypted: ${result.encrypted ? 'yes' : 'no'}`,\r\n `PDF/A: ${result.pdfaConformance ?? 'none'}`,\r\n `Signatures: ${result.signatures}`,\r\n `Title: ${result.metadata.title ?? '—'}`,\r\n `Author: ${result.metadata.author ?? '—'}`,\r\n `Created: ${result.metadata.creationDate ?? '—'}`,\r\n `Subject: ${result.metadata.subject ?? '—'}`,\r\n `Producer: ${result.metadata.producer ?? '—'}`,\r\n ];\r\n process.stdout.write(lines.join('\\n') + '\\n');\r\n }\r\n}\r\n","import { createRequire } from 'node:module';\r\nimport { parseArgs, hasFlag } from './utils/args.js';\r\nimport { CliError } from './utils/error.js';\r\n\r\n// Lazy-import commands to keep startup fast for --help / --version\r\ntype CommandFn = (args: ReturnType<typeof parseArgs>) => Promise<void>;\r\n\r\nconst USAGE = `\\\r\npdfnative-cli — Official CLI for pdfnative\r\n\r\nUsage:\r\n pdfnative <command> [options]\r\n\r\nCommands:\r\n render Render a JSON document definition to PDF\r\n sign Apply a digital signature to an existing PDF\r\n inspect Analyse a PDF and output metadata / conformance info\r\n\r\nOptions:\r\n --help, -h Show this help message\r\n --version, -V Show version\r\n\r\nRun \\`pdfnative <command> --help\\` for per-command options.\r\n`;\r\n\r\nconst RENDER_USAGE = `\\\r\npdfnative render — Render a JSON DocumentParams to PDF\r\n\r\nUsage:\r\n pdfnative render [--input <file.json>] [--output <out.pdf>] [--stream] [--conformance <level>]\r\n\r\nOptions:\r\n --input, -i Path to JSON input file (default: stdin)\r\n --output, -o Output PDF path (default: stdout)\r\n --stream Use streaming output for large documents\r\n --conformance PDF/A conformance level: 1b, 2b, or 3b\r\n --help, -h Show this help message\r\n`;\r\n\r\nconst SIGN_USAGE = `\\\r\npdfnative sign — Apply a digital signature to an existing PDF\r\n\r\nUsage:\r\n pdfnative sign [--input <file.pdf>] [--output <out.pdf>] [--key <key.pem>] [--cert <cert.pem>]\r\n\r\nOptions:\r\n --input, -i Path to input PDF (default: stdin)\r\n --output, -o Signed PDF output path (default: stdout)\r\n --key Path to PEM private key file\r\n (overridden by $PDFNATIVE_SIGN_KEY env var)\r\n --cert Path to PEM certificate file\r\n (overridden by $PDFNATIVE_SIGN_CERT env var)\r\n --help, -h Show this help message\r\n\r\nSecurity: $PDFNATIVE_SIGN_KEY and $PDFNATIVE_SIGN_CERT env vars take precedence over file flags.\r\n`;\r\n\r\nconst INSPECT_USAGE = `\\\r\npdfnative inspect — Analyse a PDF and output metadata\r\n\r\nUsage:\r\n pdfnative inspect [--input <file.pdf>] [--format <fmt>]\r\n\r\nOptions:\r\n --input, -i Path to input PDF (default: stdin)\r\n --format, -f Output format: json (default) or text\r\n --help, -h Show this help message\r\n`;\r\n\r\nfunction getVersion(): string {\r\n // Use createRequire to load package.json in an ESM-compatible way\r\n const require = createRequire(import.meta.url);\r\n const pkg = require('../package.json') as { version: string };\r\n return pkg.version;\r\n}\r\n\r\nasync function loadCommand(name: string): Promise<CommandFn> {\r\n switch (name) {\r\n case 'render': {\r\n const m = await import('./commands/render.js');\r\n return m.render;\r\n }\r\n case 'sign': {\r\n const m = await import('./commands/sign.js');\r\n return m.sign;\r\n }\r\n case 'inspect': {\r\n const m = await import('./commands/inspect.js');\r\n return m.inspect;\r\n }\r\n default:\r\n return Promise.reject(new CliError(`Unknown command: ${name}. Run pdfnative --help for usage.`, 1));\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const argv = process.argv.slice(2);\r\n const args = parseArgs(argv);\r\n\r\n // Global --help / -h\r\n if (hasFlag(args.flags, 'help', 'h') && args.positionals.length === 0) {\r\n process.stdout.write(USAGE);\r\n process.exit(0);\r\n }\r\n\r\n // Global --version / -V\r\n if (hasFlag(args.flags, 'version', 'V')) {\r\n process.stdout.write(getVersion() + '\\n');\r\n process.exit(0);\r\n }\r\n\r\n const commandName = args.positionals[0];\r\n\r\n if (commandName === undefined) {\r\n process.stdout.write(USAGE);\r\n process.exit(0);\r\n }\r\n\r\n // Per-command --help\r\n if (hasFlag(args.flags, 'help', 'h')) {\r\n switch (commandName) {\r\n case 'render': process.stdout.write(RENDER_USAGE); break;\r\n case 'sign': process.stdout.write(SIGN_USAGE); break;\r\n case 'inspect': process.stdout.write(INSPECT_USAGE); break;\r\n default:\r\n process.stderr.write(`Unknown command: ${commandName}. Run pdfnative --help for usage.\\n`);\r\n process.exit(1);\r\n }\r\n process.exit(0);\r\n }\r\n\r\n // Strip the command name from positionals before passing to the command\r\n const commandArgs = parseArgs(argv.filter((_t, _i) => {\r\n // Remove only the first positional (the command name itself)\r\n if (_t === commandName && args.positionals[0] === commandName) {\r\n return false;\r\n }\r\n return true;\r\n }));\r\n\r\n const command = await loadCommand(commandName);\r\n await command(commandArgs);\r\n}\r\n\r\nmain().catch((e: unknown) => {\r\n if (e instanceof CliError) {\r\n process.stderr.write(e.message + '\\n');\r\n process.exit(e.exitCode);\r\n }\r\n const message = e instanceof Error ? e.message : String(e);\r\n process.stderr.write(`Error: ${message}\\n`);\r\n process.exit(1);\r\n});\r\n"]} |
+21
| MIT License | ||
| Copyright (c) 2026 Nizoka — Plika (https://plika.app) | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
+258
| # pdfnative-cli | ||
| [](https://github.com/Nizoka/pdfnative-cli/actions/workflows/ci.yml) | ||
| [](https://github.com/Nizoka/pdfnative-cli/actions/workflows/codeql.yml) | ||
| [](https://www.npmjs.com/package/pdfnative-cli) | ||
| [](https://www.npmjs.com/package/pdfnative-cli) | ||
| [](https://www.npmjs.com/package/pdfnative-cli) | ||
| [](https://www.typescriptlang.org/) | ||
| [](LICENSE) | ||
| [](https://docs.npmjs.com/generating-provenance-statements) | ||
| [](https://www.npmjs.com/package/pdfnative) | ||
| [](https://pdfnative.dev) | ||
| Official CLI for the [`pdfnative`](https://github.com/Nizoka/pdfnative) library — render JSON to PDF, apply digital signatures, and inspect PDF conformance, directly from the terminal. Zero extra runtime dependencies. | ||
| ## Highlights | ||
| - **`render`** — pipe a JSON document definition into a production-ready PDF (streaming supported) | ||
| - **`sign`** — apply RSA or ECDSA digital signatures (CMS/PKCS#7) using key files or environment variables | ||
| - **`inspect`** — analyse any PDF: version, page count, encryption, PDF/A conformance, signature count, metadata | ||
| - **Zero extra dependencies** — `pdfnative` is the only runtime dependency; all PDF logic lives there | ||
| - **Streaming output** — `--stream` on render emits PDF chunks progressively for large documents | ||
| - **Stdin / stdout** — every command reads from stdin and writes to stdout by default, composable in shell pipelines | ||
| - **Secret-safe** — signing keys are loaded from env vars (`PDFNATIVE_SIGN_KEY`/`PDFNATIVE_SIGN_CERT`) and never logged | ||
| - **ESM-first, TypeScript strict** — built with tsup, typed declarations included | ||
| - **NPM provenance** — signed builds via GitHub Actions OIDC | ||
| ## Supported Features | ||
| | Feature | Status | Notes | | ||
| |---------|--------|-------| | ||
| | **Commands** | | | | ||
| | `render` JSON → PDF | ✅ | Streaming, PDF/A conformance, stdin/stdout | | ||
| | `sign` digital signatures | ✅ | RSA + ECDSA, CMS/PKCS#7, env var secrets | | ||
| | `inspect` PDF metadata | ✅ | Version, pages, encryption, signatures, PDFA | | ||
| | **Document Blocks** | | | | ||
| | Headings, paragraphs, lists | ✅ | Full text styling support | | ||
| | Tables | ✅ | Headers, rows, multi-page | | ||
| | Barcodes | ✅ | QR, Code 128, EAN-13, Data Matrix, PDF417 | | ||
| | Hyperlinks | ✅ | URL validation, blue underlined text | | ||
| | Form fields | ✅ | Text, checkbox, radio, dropdown, listbox | | ||
| | Page breaks, spacers | ✅ | Explicit pagination control | | ||
| | Table of contents | ✅ | Auto-generated with /GoTo links | | ||
| | **Advanced Layouts** | | | | ||
| | PDF/A archival (1b, 2b, 3b) | ✅ | `--conformance` flag | | ||
| | Streaming output | ✅ | `--stream` for large documents | | ||
| | Compression | ✅ | Via pdfnative API (50–90% reduction) | | ||
| | Encryption (AES-128/256) | ⚠️ | Not exposed via CLI; use Node.js API | | ||
| | Watermarks | ⚠️ | Not exposed via CLI; use Node.js API | | ||
| | Custom headers/footers | ⚠️ | Not exposed via CLI; use `footerText` property | | ||
| | Custom page sizes | ⚠️ | Not exposed via CLI; use Node.js API | | ||
| **Note:** Features marked **⚠️** are supported by `pdfnative` but not yet exposed through the CLI JSON interface. Use the `pdfnative` Node.js library directly for these features. | ||
| ## Installation | ||
| ```bash | ||
| npm install --global pdfnative-cli | ||
| ``` | ||
| Or run without installing: | ||
| ```bash | ||
| npx pdfnative-cli render --input doc.json --output report.pdf | ||
| ``` | ||
| **Requirements:** Node.js ≥ 20 | Bun | Deno (`node dist/cli.cjs`) | ||
| ## Documentation | ||
| - 📘 **[Quick Start](#quick-start)** (below) — Get rendering in 5 minutes | ||
| - 🏛️ **[KNOWLEDGE_BASE.md](docs/KNOWLEDGE_BASE.md)** — Full CLI reference, architecture, integration patterns | ||
| - 📚 **[samples/README.md](samples/README.md)** — 22 sample files organized by feature | ||
| - 🔧 **[pdfnative library](https://github.com/Nizoka/pdfnative)** — Underlying PDF engine docs | ||
| - ❓ **[FAQ](docs/KNOWLEDGE_BASE.md#11-frequently-asked-questions)** — Common questions & troubleshooting | ||
| ## Quick Start | ||
| ### Render a PDF from JSON | ||
| ```bash | ||
| # From a file | ||
| pdfnative render --input document.json --output report.pdf | ||
| # From stdin | ||
| cat document.json | pdfnative render --output report.pdf | ||
| # Streaming (large documents) | ||
| pdfnative render --input big-doc.json --output report.pdf --stream | ||
| # PDF/A conformance | ||
| pdfnative render --input document.json --output archived.pdf --conformance 2b | ||
| ``` | ||
| `document.json` is a [`DocumentParams`](https://github.com/Nizoka/pdfnative) object: | ||
| ```json | ||
| { | ||
| "title": "Monthly Report", | ||
| "blocks": [ | ||
| { "type": "heading", "text": "Monthly Report", "level": 1 }, | ||
| { "type": "paragraph", "text": "Summary for April 2026." }, | ||
| { "type": "list", "style": "bullet", "items": ["Revenue: +18%", "NPS: 72"] } | ||
| ], | ||
| "footerText": "Confidential", | ||
| "metadata": { "author": "Finance Team", "subject": "April 2026 Report" } | ||
| } | ||
| ``` | ||
| ### Sign a PDF | ||
| ```bash | ||
| # Keys from environment variables (recommended for CI/CD) | ||
| export PDFNATIVE_SIGN_KEY="$(cat private.pem)" | ||
| export PDFNATIVE_SIGN_CERT="$(cat cert.pem)" | ||
| pdfnative sign --input document.pdf --output signed.pdf | ||
| # Keys from files | ||
| pdfnative sign --input document.pdf --output signed.pdf \ | ||
| --key private.pem --cert cert.pem | ||
| ``` | ||
| ### Inspect a PDF | ||
| ```bash | ||
| # JSON output (default) | ||
| pdfnative inspect --input report.pdf | ||
| # Human-readable | ||
| pdfnative inspect --input report.pdf --format text | ||
| # From stdin | ||
| cat report.pdf | pdfnative inspect | ||
| ``` | ||
| Example output: | ||
| ```json | ||
| { | ||
| "version": "1.7", | ||
| "pageCount": 3, | ||
| "encrypted": false, | ||
| "pdfaConformance": "2b", | ||
| "signatures": 1, | ||
| "metadata": { | ||
| "title": "Monthly Report", | ||
| "author": "Nizoka", | ||
| "creationDate": "2026-04-27T12:00:00+00:00" | ||
| } | ||
| } | ||
| ``` | ||
| ## Examples | ||
| Ready-to-run examples are in [`samples/`](samples/), organized by feature category: | ||
| | Category | Examples | Description | | ||
| |----------|----------|-------------| | ||
| | [`render/document/`](samples/render/document/) | 5 files | Minimal, report, all-blocks reference, invoice, technical spec | | ||
| | [`render/table/`](samples/render/table/) | 2 files | Project status, financial summary | | ||
| | [`render/barcode/`](samples/render/barcode/) | 3 files | QR code, Code 128 shipping label, EAN-13 product | | ||
| | [`render/form/`](samples/render/form/) | 2 files | Contact form, survey | | ||
| | [`render/toc/`](samples/render/toc/) | 1 file | Document with auto-generated table of contents | | ||
| | [`render/link/`](samples/render/link/) | 1 file | Resource directory with hyperlinks | | ||
| | [`render/watermark/`](samples/render/watermark/) | 2 files | Draft watermark, confidential watermark | | ||
| | [`render/layout/`](samples/render/layout/) | 3 files | US Letter, A5 portrait, A4 landscape | | ||
| | [`render/pdfa/`](samples/render/pdfa/) | 3 files | PDF/A-1b, PDF/A-2b, PDF/A-3b archival conformance | | ||
| | [`sign/`](samples/sign/) | 2 scripts | Digital signature (Bash + PowerShell) | | ||
| | [`inspect/`](samples/inspect/) | 4 scripts | JSON & text inspection (Bash + PowerShell) | | ||
| | [`streaming/`](samples/streaming/) | 1 script | 200-section document via streaming render | | ||
| **Render all samples at once:** | ||
| ```bash | ||
| node samples/run-all.js | ||
| ``` | ||
| See [`samples/README.md`](samples/README.md) for full descriptions, block type reference, and integration patterns (GitHub Actions, Docker, TypeScript). | ||
| --- | ||
| ## Command Reference | ||
| ### `pdfnative render` | ||
| | Flag | Default | Description | | ||
| |------|---------|-------------| | ||
| | `--input <file>` | stdin | Path to a JSON file containing `DocumentParams` | | ||
| | `--output <file>` | stdout | Output PDF path | | ||
| | `--stream` | false | Use streaming output (AsyncGenerator) | | ||
| | `--conformance <level>` | — | PDF/A conformance level: `1b`, `2b`, or `3b` | | ||
| ### `pdfnative sign` | ||
| | Flag | Default | Description | | ||
| |------|---------|-------------| | ||
| | `--input <file>` | — **(required)** | Path to the input PDF | | ||
| | `--output <file>` | stdout | Output signed PDF path | | ||
| | `--key <file>` | `PDFNATIVE_SIGN_KEY` env | Path to PEM private key (env var takes precedence) | | ||
| | `--cert <file>` | `PDFNATIVE_SIGN_CERT` env | Path to PEM certificate (env var takes precedence) | | ||
| ### `pdfnative inspect` | ||
| | Flag | Default | Description | | ||
| |------|---------|-------------| | ||
| | `--input <file>` | stdin | Path to the PDF to inspect | | ||
| | `--format <fmt>` | `json` | Output format: `json` or `text` | | ||
| ## Security | ||
| - **Signing keys are never logged** — not in error messages, not in debug output. | ||
| - **Path traversal protection** — all file path arguments are validated against `../` sequences. | ||
| - **JSON size cap** — input is capped at 50 MB before parsing to prevent memory exhaustion. | ||
| - Signed builds with NPM provenance — verify at npmjs.com or with `npm audit signatures`. | ||
| See [SECURITY.md](SECURITY.md) for the full security policy and vulnerability disclosure procedure. | ||
| ## Getting Help | ||
| **Have a question?** | ||
| - 📖 Check the [FAQ](docs/KNOWLEDGE_BASE.md#11-frequently-asked-questions) first | ||
| - 🔍 Search the samples: `grep -r "your-keyword" samples/` | ||
| - 📚 Read [KNOWLEDGE_BASE.md](docs/KNOWLEDGE_BASE.md) for technical details | ||
| - 💬 Open a discussion: [GitHub Discussions](https://github.com/Nizoka/pdfnative-cli/discussions) | ||
| **Found a bug?** | ||
| - 🐛 Open an issue: [GitHub Issues](https://github.com/Nizoka/pdfnative-cli/issues) | ||
| - 🔐 Security issue? See [SECURITY.md](SECURITY.md) for responsible disclosure | ||
| **Want to contribute?** | ||
| - 🤝 See [CONTRIBUTING.md](CONTRIBUTING.md) | ||
| - 📝 All PRs add value — tests, docs, translations, samples | ||
| ## Related Projects | ||
| - [`pdfnative`](https://github.com/Nizoka/pdfnative) — the core PDF generation library | ||
| - [`pdfnative-mcp`](https://github.com/Nizoka/pdfnative-mcp) — Model Context Protocol server for AI clients | ||
| - [pdfnative.dev](https://pdfnative.dev) — documentation, live demos, benchmarks | ||
| ## Citation | ||
| If you use pdfnative-cli in research or academic pipelines, please cite it: | ||
| ```bibtex | ||
| @software{pdfnative_cli_2026, | ||
| title = {pdfnative-cli: Official CLI for the pdfnative PDF generation library}, | ||
| author = {Nizoka}, | ||
| year = {2026}, | ||
| url = {https://github.com/Nizoka/pdfnative-cli}, | ||
| license = {MIT} | ||
| } | ||
| ``` | ||
| See [CITATION.cff](CITATION.cff) for the full metadata (auto-detected by GitHub and Zenodo). | ||
| ## License | ||
| MIT — see [LICENSE](LICENSE). |
+72
-7
| { | ||
| "name": "pdfnative-cli", | ||
| "version": "0.0.1", | ||
| "description": "Temporary placeholder for pdfnative-cli", | ||
| "main": "index.js", | ||
| "version": "0.1.0", | ||
| "description": "Official CLI for pdfnative — render JSON to PDF, sign, and inspect. Zero extra runtime dependencies.", | ||
| "type": "module", | ||
| "bin": { | ||
| "pdfnative": "./dist/cli.cjs" | ||
| }, | ||
| "main": "./dist/cli.cjs", | ||
| "module": "./dist/cli.js", | ||
| "types": "./dist/cli.d.ts", | ||
| "files": [ | ||
| "dist", | ||
| "LICENSE", | ||
| "README.md" | ||
| ], | ||
| "sideEffects": false, | ||
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| "build": "tsup", | ||
| "dev": "tsup --watch", | ||
| "test": "vitest run", | ||
| "test:watch": "vitest", | ||
| "test:coverage": "vitest run --coverage", | ||
| "lint": "eslint src/", | ||
| "typecheck": "tsc --noEmit", | ||
| "typecheck:tests": "tsc --project tsconfig.test.json --noEmit", | ||
| "typecheck:all": "npm run typecheck && npm run typecheck:tests", | ||
| "prepublishOnly": "npm run build" | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC" | ||
| "keywords": [ | ||
| "pdf", | ||
| "pdf-generation", | ||
| "cli", | ||
| "pdfnative", | ||
| "pdf-render", | ||
| "pdf-sign", | ||
| "pdf-inspect", | ||
| "zero-dependency", | ||
| "typescript", | ||
| "pdf-a", | ||
| "digital-signature", | ||
| "command-line" | ||
| ], | ||
| "author": "Nizoka <hello@pdfnative.dev> (https://pdfnative.dev)", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/Nizoka/pdfnative-cli.git" | ||
| }, | ||
| "homepage": "https://github.com/Nizoka/pdfnative-cli#readme", | ||
| "bugs": { | ||
| "url": "https://github.com/Nizoka/pdfnative-cli/issues" | ||
| }, | ||
| "funding": { | ||
| "type": "individual", | ||
| "url": "https://plika.app" | ||
| }, | ||
| "engines": { | ||
| "node": ">=20" | ||
| }, | ||
| "publishConfig": { | ||
| "access": "public", | ||
| "provenance": true | ||
| }, | ||
| "dependencies": { | ||
| "pdfnative": "^1.0.4" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^22.0.0", | ||
| "@vitest/coverage-v8": "^2.1.9", | ||
| "eslint": "^9.0.0", | ||
| "tsup": "^8.0.0", | ||
| "typescript": "^5.4.0", | ||
| "typescript-eslint": "^8.57.2", | ||
| "vitest": "^2.1.9" | ||
| } | ||
| } |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
No website
QualityPackage does not have a website.
136789
51324.44%9
800%1070
Infinity%0
-100%1
-66.67%0
-100%258
Infinity%Yes
NaN1
Infinity%7
Infinity%8
700%+ Added
+ Added