"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.pemConverter = exports.pem = void 0; exports.encode = encode; exports.encodeMany = encodeMany; exports.decode = decode; exports.find = find; exports.findAll = findAll; exports.decodeFirst = decodeFirst; exports.parse = parse; exports.format = format; const base64_js_1 = require("../encoding/base64.js"); const LABEL_REGEX = /^[A-Z0-9][A-Z0-9 ._-]*[A-Z0-9]$/i; const PEM_BLOCK_REGEX = /-----BEGIN ([^-]+)-----([\s\S]*?)-----END \1-----/g; function assertLabel(label) { if (!LABEL_REGEX.test(label)) { throw new TypeError(`Invalid PEM label '${label}'`); } } function wrap(text, lineLength) { const result = []; for (let i = 0; i < text.length; i += lineLength) { result.push(text.slice(i, i + lineLength)); } return result; } function parseBody(body) { const normalized = body.trim().replace(/\r\n/g, "\n"); const lines = normalized.split("\n").map((line) => line.trim()).filter(Boolean); const headers = {}; let index = 0; for (; index < lines.length; index++) { const line = lines[index]; const separator = line.indexOf(":"); if (separator <= 0) { break; } headers[line.slice(0, separator).trim()] = line.slice(separator + 1).trim(); } return { headers: Object.keys(headers).length ? headers : undefined, base64Lines: lines.slice(index), base64Text: lines.slice(index).join(""), }; } function detectNewline(text) { return /\r\n/.test(text) ? "\r\n" : "\n"; } function collectBlocks(text, options = {}) { const blocks = []; const requestedLabel = options.label; let match; PEM_BLOCK_REGEX.lastIndex = 0; while ((match = PEM_BLOCK_REGEX.exec(text))) { const label = match[1].trim(); if (requestedLabel && label !== requestedLabel) { continue; } assertLabel(label); const parsed = parseBody(match[2]); blocks.push({ label, data: base64_js_1.base64.decode(parsed.base64Text), headers: parsed.headers, lineLength: parsed.base64Lines[0]?.length ?? 64, newline: detectNewline(match[0]), }); } if (options.strict && blocks.length === 0) { throw new TypeError(requestedLabel ? `No PEM block with label '${requestedLabel}' was found` : "No PEM blocks were found"); } return blocks; } function encode(label, data, options = {}) { assertLabel(label); const lineLength = options.lineLength ?? 64; if (!Number.isInteger(lineLength) || lineLength < 1) { throw new RangeError("PEM lineLength must be a positive integer"); } const newline = options.newline ?? "\n"; const lines = [`-----BEGIN ${label}-----`]; if (options.headers) { for (const [name, value] of Object.entries(options.headers)) { lines.push(`${name}: ${value}`); } lines.push(""); } lines.push(...wrap(base64_js_1.base64.encode(data), lineLength)); lines.push(`-----END ${label}-----`); return `${lines.join(newline)}${newline}`; } function encodeMany(blocks, options = {}) { return blocks.map((block) => encode(block.label, block.data, { ...options, headers: block.headers ?? options.headers })).join(""); } function decode(text, options = {}) { return collectBlocks(text, options).map(({ lineLength: _lineLength, newline: _newline, ...block }) => block); } function find(text, label) { return decode(text, { label })[0]; } function findAll(text, label) { return decode(text, { label }); } function decodeFirst(text, label) { const [block] = decode(text, { label, strict: true }); return block.data; } function parse(text, options = {}) { const [block] = collectBlocks(text, { ...options, strict: true }); const format = { label: block.label, headers: block.headers, lineLength: block.lineLength, newline: block.newline, }; return { bytes: block.data, format, normalized: encode(block.label, block.data, format), }; } function format(data, value) { return encode(value.label, data, value); } exports.pem = { decode, decodeFirst, encode, encodeMany, find, findAll, format, parse }; exports.pemConverter = { name: "pem", encode: (data, options) => { if (!options?.label) { throw new TypeError("PEM label is required"); } return encode(options.label, data, options); }, decode: (text, options) => decodeFirst(text, options?.label), format, is: (text) => typeof text === "string" && /-----BEGIN [^-]+-----/.test(text), parse, };