LarpixClient/electron/node_modules/@peculiar/utils/build/cjs/pem/pem.js
2026-07-09 22:38:33 +02:00

140 lines
4.7 KiB
JavaScript

"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,
};