//#region rolldown:runtime var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
//#endregion const fast_xml_parser = __toESM(require("fast-xml-parser")); const yaml = __toESM(require("yaml"));
//#region src/encode/normalize.ts /** * Normalize unknown value to JsonValue * Handles Date, BigInt, Set, Map, and other special types */ function normalizeValue(value) { if (value === null) return null; if (typeof value === "string" || typeof value === "boolean") return value; if (typeof value === "number") { if (Object.is(value, -0)) return 0; if (!Number.isFinite(value)) return null; return value; } if (typeof value === "bigint") { if (value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER) return Number(value); return value.toString(); } if (value instanceof Date) return value.toISOString(); if (Array.isArray(value)) return value.map(normalizeValue); if (value instanceof Set) return Array.from(value).map(normalizeValue); if (value instanceof Map) return Object.fromEntries(Array.from(value, ([k, v]) => [String(k), normalizeValue(v)])); if (isPlainObject(value)) { const result = {}; for (const key in value) if (Object.prototype.hasOwnProperty.call(value, key)) result[key] = normalizeValue(value[key]); return result; } return null; } function isJsonArray(value) { return Array.isArray(value); } function isJsonObject(value) { return value !== null && typeof value === "object" && !Array.isArray(value); } function isPlainObject(value) { if (value === null || typeof value !== "object") return false; const prototype = Object.getPrototypeOf(value); return prototype === null || prototype === Object.prototype; } function isArrayOfObjects(value) { return value.every((item) => isJsonObject(item)); } /** * Get all keys from array of objects (union) */ function getAllKeys(objects) { const keySet = /* @__PURE__ */ new Set(); for (const obj of objects) for (const key of Object.keys(obj)) keySet.add(key); return Array.from(keySet).sort(); } /** * Analyze fields in array of objects to determine which are required vs optional * A field is optional if it's missing from any object OR empty (array/object with no content) in any object * A field is required only if it appears in ALL objects AND always has content */ function analyzeFields(objects) { const fieldMetadata = /* @__PURE__ */ new Map(); if (objects.length === 0) return fieldMetadata; const fieldCounts = /* @__PURE__ */ new Map(); const fieldEverEmpty = /* @__PURE__ */ new Map(); for (const obj of objects) for (const key of Object.keys(obj)) { fieldCounts.set(key, (fieldCounts.get(key) || 0) + 1); const value = obj[key]; const isEmpty = isJsonArray(value) && value.length === 0 || isJsonObject(value) && !isJsonArray(value) && Object.keys(value).length === 0; if (isEmpty) fieldEverEmpty.set(key, true); } const totalObjects = objects.length; for (const [fieldName, count] of fieldCounts.entries()) { const isMissingSomewhere = count < totalObjects; const isEmptySomewhere = fieldEverEmpty.get(fieldName) || false; fieldMetadata.set(fieldName, { isOptional: isMissingSomewhere || isEmptySomewhere }); } return fieldMetadata; }
//#endregion //#region src/encode/schema.ts /** * Generate PLOON schema from data * Example: [products#2](id,name,price|colors#(name,hex|sizes#(size,sku))) */ function generateSchema(data, config) { const { name, array } = findRootArray$1(data); if (!array) throw new Error("Data must contain at least one array to generate PLOON schema"); const schema = generateArraySchema(name, array, config); return schema; } /** * Find the root array in the data * If data is an object with one array property, use that * If data is an array, name it 'root' * If data is an object with no arrays, wrap it as a single-element array */ function findRootArray$1(data) { if (isJsonArray(data)) return { name: "root", array: data }; if (isJsonObject(data)) { for (const [key, value] of Object.entries(data)) if (isJsonArray(value)) return { name: key, array: value }; return { name: "data", array: [data] }; } return { name: "data", array: [data] }; } /** * Generate schema for an array */ function generateArraySchema(name, array, config) { const { arraySizeMarker, fieldsOpen, fieldsClose, nestedSeparator, schemaOpen, schemaClose } = config; const count = array.length; const fields = analyzeArrayFields(array); const fieldStrings = []; for (const field of fields) if (field.type === "array" && field.nested && isJsonArray(field.nested)) { const nestedSchema = generateNestedArraySchema(field.name, field.nested, field.isOptional, config); fieldStrings.push(nestedSchema); } else if (field.type === "object" && field.nested && isJsonObject(field.nested)) { const nestedSchema = generateNestedObjectSchema(field.name, field.nested, field.isOptional, config); fieldStrings.push(nestedSchema); } else if (field.type === "primitiveArray") { const fieldName = formatFieldName(field.name, field.isOptional, config); fieldStrings.push(`${fieldName}${config.arraySizeMarker}${config.fieldsOpen}${config.fieldsClose}`); } else { const fieldName = formatFieldName(field.name, field.isOptional, config); fieldStrings.push(fieldName); } const fieldsStr = fieldStrings.join(config.schemaFieldSeparator); return `${schemaOpen}${name}${arraySizeMarker}${count}${schemaClose}${fieldsOpen}${fieldsStr}${fieldsClose}`; } /** * Generate schema for nested array (without outer brackets) */ function generateNestedArraySchema(name, array, isOptional, config) { const { arraySizeMarker, fieldsOpen, fieldsClose } = config; const count = array.length; const fields = analyzeArrayFields(array); const fieldStrings = []; for (const field of fields) if (field.type === "array" && field.nested && isJsonArray(field.nested)) { const nestedSchema = generateNestedArraySchema(field.name, field.nested, field.isOptional, config); fieldStrings.push(nestedSchema); } else if (field.type === "object" && field.nested && isJsonObject(field.nested)) { const nestedSchema = generateNestedObjectSchema(field.name, field.nested, field.isOptional, config); fieldStrings.push(nestedSchema); } else if (field.type === "primitiveArray") { const fieldName = formatFieldName(field.name, field.isOptional, config); fieldStrings.push(`${fieldName}${arraySizeMarker}${fieldsOpen}${fieldsClose}`); } else { const fieldName = formatFieldName(field.name, field.isOptional, config); fieldStrings.push(fieldName); } const fieldsStr = fieldStrings.join(config.schemaFieldSeparator); const formattedName = formatFieldName(name, isOptional, config); return `${formattedName}${arraySizeMarker}${count}${fieldsOpen}${fieldsStr}${fieldsClose}`; } /** * Generate schema for nested object * Format: name{field1,field2,nested{field3}} */ function generateNestedObjectSchema(name, obj, isOptional, config) { const keys = Object.keys(obj).sort(); const primitiveKeys = []; const requiredComplexKeys = []; const optionalComplexKeys = []; for (const key of keys) { const value = obj[key]; if (isJsonArray(value)) { const isPrimArray = value.length === 0 || !isArrayOfObjects(value); if (isPrimArray) primitiveKeys.push(key); else requiredComplexKeys.push(key); } else if (isJsonObject(value) && !isJsonArray(value)) if (Object.keys(value).length === 0) optionalComplexKeys.push(key); else requiredComplexKeys.push(key); else primitiveKeys.push(key); } const fieldStrings = []; for (const key of primitiveKeys) { const value = obj[key]; if (isJsonArray(value)) fieldStrings.push(`${key}${config.arraySizeMarker}${config.fieldsOpen}${config.fieldsClose}`); else fieldStrings.push(key); } for (const key of requiredComplexKeys) { const value = obj[key]; if (isJsonArray(value)) { const nestedSchema = generateNestedArraySchema(key, value, false, config); fieldStrings.push(nestedSchema); } else if (isJsonObject(value) && !isJsonArray(value)) { const nestedSchema = generateNestedObjectSchema(key, value, false, config); fieldStrings.push(nestedSchema); } } for (const key of optionalComplexKeys) { const value = obj[key]; if (isJsonArray(value)) { const nestedSchema = generateNestedArraySchema(key, value, true, config); fieldStrings.push(nestedSchema); } else if (isJsonObject(value) && !isJsonArray(value)) { const nestedSchema = generateNestedObjectSchema(key, value, true, config); fieldStrings.push(nestedSchema); } } const fieldsStr = fieldStrings.join(config.schemaFieldSeparator); const formattedName = formatFieldName(name, isOptional, config); return `${formattedName}{${fieldsStr}}`; } /** * Format field name with optional marker suffix if needed */ function formatFieldName(name, isOptional, config) { if (isOptional) return `${name}${config.optionalFieldMarker}`; return name; } /** * Analyze array to determine fields and nested arrays/objects */ function analyzeArrayFields(array) { if (array.length === 0) return []; if (isArrayOfObjects(array)) { const keys = getAllKeys(array); const fieldMetadata = analyzeFields(array); const fields = []; for (const key of keys) { const metadata = fieldMetadata.get(key); const isOptional = metadata?.isOptional ?? false; const nestedArray = findNestedArray(array, key); if (nestedArray) { fields.push({ name: key, type: "array", isOptional, nested: nestedArray }); continue; } const nestedObject = findNestedObject(array, key); if (nestedObject) { fields.push({ name: key, type: "object", isOptional, nested: nestedObject }); continue; } const firstValue = array.find((obj) => key in obj)?.[key]; if (isJsonArray(firstValue)) { const isPrimArray = firstValue.length === 0 || !isArrayOfObjects(firstValue); if (isPrimArray) { fields.push({ name: key, type: "primitiveArray", isOptional }); continue; } } fields.push({ name: key, type: "primitive", isOptional }); } return fields.sort((a, b) => { const aIsPrimitive = a.type === "primitive" || a.type === "primitiveArray"; const bIsPrimitive = b.type === "primitive" || b.type === "primitiveArray"; if (aIsPrimitive !== bIsPrimitive) return aIsPrimitive ? -1 : 1; if (a.isOptional !== b.isOptional) return a.isOptional ? 1 : -1; return 0; }); } return []; } /** * Find nested array for a given key across all objects * Only returns array if MAJORITY of present values are arrays (handles type inconsistencies) */ function findNestedArray(objects, key) { let arrayCount = 0; let primitiveCount = 0; let objectCount = 0; let foundArray; for (const obj of objects) { if (!(key in obj)) continue; const value = obj[key]; if (isJsonArray(value)) { arrayCount++; if (!foundArray || foundArray.length === 0 && value.length > 0) foundArray = value; } else if (isJsonObject(value)) objectCount++; else primitiveCount++; } if (arrayCount > primitiveCount && arrayCount > objectCount) { if (foundArray && foundArray.length > 0 && isArrayOfObjects(foundArray)) return foundArray; } return void 0; } /** * Find nested object for a given key across all objects * Only returns object if MAJORITY of present values are objects (handles type inconsistencies) */ function findNestedObject(objects, key) { let arrayCount = 0; let primitiveCount = 0; let objectCount = 0; let foundObject; for (const obj of objects) { if (!(key in obj)) continue; const value = obj[key]; if (isJsonObject(value) && !isJsonArray(value)) { objectCount++; if (!foundObject || Object.keys(foundObject).length === 0 && Object.keys(value).length > 0) foundObject = value; } else if (isJsonArray(value)) arrayCount++; else primitiveCount++; } if (objectCount > primitiveCount && objectCount > arrayCount) return foundObject; return void 0; }
//#endregion //#region src/encode/path-writer.ts var PathWriter = class PathWriter { pathSegments = []; config; constructor(config) { this.config = config; } /** * Get current path as string * - Arrays: "depth:index" (e.g., "2:1") * - Objects: "depth " (e.g., "2 ") */ getCurrentPath() { if (this.pathSegments.length === 0) return ""; const depth = this.pathSegments.length; const lastSegment = this.pathSegments[this.pathSegments.length - 1]; if (!lastSegment) return ""; if (lastSegment.type === "array") return `${depth}${this.config.pathSeparator}${lastSegment.index}`; else return `${depth} `; } /** * Push an array segment (with index) */ pushArray(index) { this.pathSegments.push({ type: "array", index }); } /** * Push an object segment (no index) */ pushObject() { this.pathSegments.push({ type: "object" }); } /** * Push a new path segment (backwards compatibility) * Defaults to array type */ push(index) { this.pushArray(index); } /** * Pop the last path segment */ pop() { this.pathSegments.pop(); } /** * Get depth (number of path segments) */ getDepth() { return this.pathSegments.length; } /** * Reset to empty path */ reset() { this.pathSegments = []; } /** * Clone the current path writer */ clone() { const clone = new PathWriter(this.config); clone.pathSegments = [...this.pathSegments]; return clone; } };
//#endregion //#region src/shared/string-utils.ts /** * Escape special characters in a value */ function escapeValue(value, config) { const { escapeChar, fieldDelimiter, recordSeparator, pathSeparator } = config; let escaped = value; escaped = escaped.replace(new RegExp(`\\${escapeChar}`, "g"), `${escapeChar}${escapeChar}`); escaped = escaped.replace(new RegExp(`\\${fieldDelimiter}`, "g"), `${escapeChar}${fieldDelimiter}`); if (recordSeparator.length === 1) escaped = escaped.replace(new RegExp(`\\${recordSeparator}`, "g"), `${escapeChar}${recordSeparator}`); else escaped = escaped.replace(new RegExp(escapeRegex(recordSeparator), "g"), `${escapeChar}${recordSeparator}`); escaped = escaped.replace(/,/g, `${escapeChar},`); return escaped; } /** * Unescape special characters in a value */ function unescapeValue(value, config) { const { escapeChar, fieldDelimiter, recordSeparator } = config; let unescaped = value; unescaped = unescaped.replace(new RegExp(`\\${escapeChar}\\${fieldDelimiter}`, "g"), fieldDelimiter); if (recordSeparator.length === 1) unescaped = unescaped.replace(new RegExp(`\\${escapeChar}\\${recordSeparator}`, "g"), recordSeparator); else unescaped = unescaped.replace(new RegExp(`\\${escapeChar}${escapeRegex(recordSeparator)}`, "g"), recordSeparator); unescaped = unescaped.replace(new RegExp(`\\${escapeChar},`, "g"), ","); unescaped = unescaped.replace(new RegExp(`\\${escapeChar}\\${escapeChar}`, "g"), escapeChar); return unescaped; } /** * Format a value for output (converts to string and escapes) */ function formatValue(value, config, isOptional) { if (value === null) return "null"; if (value === void 0) return isOptional ? "" : "null"; const str = String(value); return escapeValue(str, config); } /** * Format a primitive array for inline encoding (comma-separated) * Handles null values and respects preserveEmptyFields config */ function formatPrimitiveArray(array, config, preserveEmpty) { let elements = array.map((item) => { if (item === null) return ""; if (item === void 0) return ""; const str = String(item); return escapeValue(str, config); }); if (!preserveEmpty) elements = elements.filter((el) => el !== ""); return elements.join(","); } /** * Escape regex special characters */ function escapeRegex(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } /** * Split a string by delimiter, respecting escapes * Only unescapes the delimiter and escape char itself, preserves other escape sequences */ function splitEscaped(str, delimiter, escapeChar) { const parts = []; let current = ""; let i = 0; while (i < str.length) if (str[i] === escapeChar && i + 1 < str.length) { const nextChar = str[i + 1]; if (nextChar === delimiter || nextChar === escapeChar) { current += nextChar; i += 2; } else { current += str[i] + nextChar; i += 2; } } else if (str.slice(i, i + delimiter.length) === delimiter) { parts.push(current); current = ""; i += delimiter.length; } else { current += str[i]; i++; } parts.push(current); return parts; }
//#endregion //#region src/encode/encoder.ts /** * Encode data to PLOON format */ function encode(data, config) { const schema = generateSchema(data, config); const rootArray = findRootArray(data); const records = encodeArray(rootArray, new PathWriter(config), config); const { recordSeparator } = config; const result = schema + recordSeparator + recordSeparator + records.join(recordSeparator); return result; } /** * Find root array in data * Auto-wraps single objects/primitives into arrays */ function findRootArray(data) { if (isJsonArray(data)) return data; if (isJsonObject(data)) { for (const value of Object.values(data)) if (isJsonArray(value)) return value; return [data]; } return [data]; } /** * Encode an array to records */ function encodeArray(array, pathWriter, config) { const records = []; if (!isArrayOfObjects(array)) { const { fieldDelimiter } = config; array.forEach((value, index) => { const itemIndex = index + 1; pathWriter.push(itemIndex); const path = pathWriter.getCurrentPath(); const formattedValue = formatValue(value, config); const record = `${path}${fieldDelimiter}${formattedValue}`; records.push(record); pathWriter.pop(); }); return records; } const keys = getAllKeys(array); const fieldMetadata = analyzeFields(array); const fields = keys.map((key) => { const value = array.find((obj) => obj[key] !== void 0)?.[key]; const isOptional = fieldMetadata.get(key)?.isOptional ?? false; if (isJsonArray(value)) if (value.length > 0 && isArrayOfObjects(value)) return { name: key, type: "array", isOptional, nested: value }; else return { name: key, type: "primitiveArray", isOptional }; else if (isJsonObject(value) && !isJsonArray(value)) return { name: key, type: "object", isOptional, nested: value }; else return { name: key, type: "primitive", isOptional }; }); fields.sort((a, b) => { const aIsPrimitive = a.type === "primitive" || a.type === "primitiveArray"; const bIsPrimitive = b.type === "primitive" || b.type === "primitiveArray"; if (aIsPrimitive !== bIsPrimitive) return aIsPrimitive ? -1 : 1; if (a.isOptional !== b.isOptional) return a.isOptional ? 1 : -1; return 0; }); array.forEach((obj, index) => { const itemIndex = index + 1; pathWriter.push(itemIndex); const record = encodeObject(obj, fields, pathWriter, config); records.push(record); for (const field of fields) { if (field.type === "primitive") continue; const value = obj[field.name]; if (field.type === "array" && isJsonArray(value)) { const nestedRecords = encodeArray(value, pathWriter, config); records.push(...nestedRecords); } else if (field.type === "object" && isJsonObject(value) && !isJsonArray(value)) { const nestedRecords = encodeNestedObject(value, pathWriter, config); records.push(...nestedRecords); } } pathWriter.pop(); }); return records; } /** * Encode a single object to a record */ function encodeObject(obj, fields, pathWriter, config) { const { fieldDelimiter } = config; const path = pathWriter.getCurrentPath(); const values = [path]; const primitiveFields = fields.filter((f) => f.type === "primitive" || f.type === "primitiveArray"); const complexFields = fields.filter((f) => f.type === "array" || f.type === "object"); for (const field of primitiveFields) { const value = obj[field.name]; if (field.isOptional && !(field.name in obj)) values.push(""); else if (field.type === "primitiveArray" && Array.isArray(value)) if (value.length === 0) values.push(","); else values.push(formatPrimitiveArray(value, config, config.preserveEmptyFields)); else values.push(formatValue(value, config, false)); } /** * Optional Complex Field Marker Strategy: * * Optional arrays/objects are placed at the END of the schema in alphabetical order. * Their data comes as child records, but we need to tell the decoder which fields * are absent vs present. * * Encoding rules: * 1. Find the LAST optional field that has data (non-empty or non-absent) * 2. For each optional field UP TO that last one: * - If ABSENT or EMPTY: write || marker * - If HAS DATA: write nothing (data comes as child records) * 3. After the last non-empty field: stop (trailing absent fields omitted) * * Example with fields [_collections?, collections?, is_yalla?, variants?]: * Hit has collections=38, is_yalla=2 (no _collections, no variants) * → Last non-empty is is_yalla at index 2 * → Write: || (for _collections) then stop * → Child records: 38 for collections, 2 for is_yalla */ let lastPresentOptionalIndex = -1; for (let i = 0; i < complexFields.length; i++) { const field = complexFields[i]; if (!field.isOptional) continue; if (field.name in obj) lastPresentOptionalIndex = i; } for (let i = 0; i < complexFields.length; i++) { const field = complexFields[i]; if (!field.isOptional) continue; if (i > lastPresentOptionalIndex) break; if (field.name in obj) { const value = obj[field.name]; const isEmpty = field.type === "array" && isJsonArray(value) && value.length === 0 || field.type === "object" && isJsonObject(value) && !isJsonArray(value) && Object.keys(value).length === 0; if (isEmpty) values.push(""); } } return values.join(fieldDelimiter); } /** * Encode a nested object to records * Objects use "depth " path notation (no index) */ function encodeNestedObject(obj, pathWriter, config) { const records = []; pathWriter.pushObject(); const keys = Object.keys(obj).sort(); const { fieldDelimiter } = config; const path = pathWriter.getCurrentPath(); const values = [path]; const primitiveKeys = []; const complexKeys = []; for (const key of keys) { const value = obj[key]; if (isJsonArray(value)) { const isPrimArray = value.length === 0 || !isArrayOfObjects(value); if (isPrimArray) primitiveKeys.push(key); else complexKeys.push(key); } else if (isJsonObject(value) && !isJsonArray(value)) complexKeys.push(key); else primitiveKeys.push(key); } for (const key of primitiveKeys) { const value = obj[key]; if (isJsonArray(value)) if (value.length === 0) values.push(","); else values.push(formatPrimitiveArray(value, config, config.preserveEmptyFields)); else values.push(formatValue(value, config)); } let lastNonEmptyIndex = -1; for (let i = 0; i < complexKeys.length; i++) { const key = complexKeys[i]; const value = obj[key]; const isEmpty = isJsonArray(value) && value.length === 0 || isJsonObject(value) && !isJsonArray(value) && Object.keys(value).length === 0; if (!isEmpty) lastNonEmptyIndex = i; } for (let i = 0; i <= lastNonEmptyIndex; i++) { const key = complexKeys[i]; const value = obj[key]; const isEmpty = isJsonArray(value) && value.length === 0 || isJsonObject(value) && !isJsonArray(value) && Object.keys(value).length === 0; if (isEmpty) values.push(""); } records.push(values.join(fieldDelimiter)); for (const key of keys) { const value = obj[key]; if (isJsonArray(value)) { const isPrimArray = value.length === 0 || !isArrayOfObjects(value); if (!isPrimArray) { const nestedRecords = encodeArray(value, pathWriter, config); records.push(...nestedRecords); } } else if (isJsonObject(value) && !isJsonArray(value)) { const nestedRecords = encodeNestedObject(value, pathWriter, config); records.push(...nestedRecords); } } pathWriter.pop(); return records; }
//#endregion //#region src/constants.ts /** * Standard PLOON configuration (human-readable, newline-separated) */ const PLOON_STANDARD = { fieldDelimiter: "|", pathSeparator: ":", arraySizeMarker: "#", recordSeparator: "\n", escapeChar: "\\", schemaOpen: "[", schemaClose: "]", fieldsOpen: "(", fieldsClose: ")", nestedSeparator: "|", schemaFieldSeparator: "|", schemaWhitespace: " ", optionalFieldMarker: "?", preserveEmptyFields: false }; /** * Compact PLOON configuration (token-optimized, semicolon-separated) */ const PLOON_COMPACT = { ...PLOON_STANDARD, recordSeparator: ";" }; /** * Default configuration (uses standard format) */ const DEFAULT_CONFIG = PLOON_STANDARD;
//#endregion //#region src/config.ts /** * Merge user-provided config with defaults */ function resolveConfig(config) { if (!config) return DEFAULT_CONFIG; return { ...DEFAULT_CONFIG, ...config }; } /** * Resolve stringify options with defaults */ function resolveStringifyOptions(options) { const format = options?.format ?? "standard"; const baseConfig = format === "compact" ? PLOON_COMPACT : PLOON_STANDARD; const config = options?.config ? { ...baseConfig, ...options.config } : baseConfig; return { format, config }; } /** * Resolve parse options with defaults */ function resolveParseOptions(options) { return { strict: options?.strict ?? true, config: resolveConfig(options?.config) }; } /** * Validate configuration * Ensures all config values are single characters (except recordSeparator) */ function validateConfig(config) { const errors = []; const singleCharFields = [ "fieldDelimiter", "pathSeparator", "arraySizeMarker", "escapeChar", "schemaOpen", "schemaClose", "fieldsOpen", "fieldsClose", "nestedSeparator", "schemaFieldSeparator", "schemaWhitespace", "optionalFieldMarker" ]; for (const field of singleCharFields) { const value = config[field]; if (typeof value === "string" && value.length !== 1) errors.push(`${field} must be exactly 1 character, got: "${value}"`); } if (config.recordSeparator.length === 0) errors.push("recordSeparator cannot be empty"); const chars = /* @__PURE__ */ new Map(); const addChar = (char, field) => { if (!chars.has(char)) chars.set(char, []); chars.get(char).push(field); }; addChar(config.fieldDelimiter, "fieldDelimiter"); addChar(config.pathSeparator, "pathSeparator"); addChar(config.arraySizeMarker, "arraySizeMarker"); addChar(config.escapeChar, "escapeChar"); addChar(config.schemaOpen, "schemaOpen"); addChar(config.schemaClose, "schemaClose"); addChar(config.fieldsOpen, "fieldsOpen"); addChar(config.fieldsClose, "fieldsClose"); addChar(config.nestedSeparator, "nestedSeparator"); addChar(config.schemaFieldSeparator, "schemaFieldSeparator"); addChar(config.schemaWhitespace, "schemaWhitespace"); addChar(config.optionalFieldMarker, "optionalFieldMarker"); for (const [char, fields] of chars.entries()) if (fields.length > 1) errors.push(`Character "${char}" is used for multiple purposes: ${fields.join(", ")}`); if (errors.length > 0) throw new Error(`Invalid PLOON configuration:\n${errors.join("\n")}`); } /** * Detect format from PLOON string content * Returns 'standard' if uses newlines, 'compact' if uses semicolons */ function detectFormat(ploonString) { const schemaEnd = ploonString.indexOf(")"); if (schemaEnd === -1) return "standard"; const afterSchema = ploonString.slice(schemaEnd + 1); if (afterSchema.includes(";") && !afterSchema.includes("\n")) return "compact"; if (afterSchema.includes("\n")) return "standard"; return "standard"; } /** * Auto-detect configuration from PLOON string * Attempts to detect delimiters used */ function detectConfig(ploonString) { const format = detectFormat(ploonString); const baseConfig = format === "compact" ? PLOON_COMPACT : PLOON_STANDARD; return baseConfig; }
//#endregion //#region src/core/stringify.ts /** * Convert JavaScript object to PLOON string * * @param input - JavaScript object, array, or primitive * @param options - Stringify options (format, config) * @returns PLOON formatted string * * @example * ```typescript * const data = { products: [{ id: 1, name: "Shirt" }] } * const ploon = stringify(data) * // [products#1](id,name) * // * // 1|1|Shirt * ``` */ function stringify(input, options) { const resolved = resolveStringifyOptions(options); const normalized = normalizeValue(input); const ploon = encode(normalized, resolved.config); return ploon; }
//#endregion //#region src/decode/scanner.ts /** * Scan PLOON string and extract schema + records */ function scan(ploonString, config) { const { recordSeparator, fieldDelimiter, escapeChar } = config; const parts = splitEscaped(ploonString, recordSeparator, escapeChar); if (parts.length < 1) throw new Error("Invalid PLOON: No schema found"); let schemaIndex = 0; let schema = parts[0].trim(); while (schemaIndex + 1 < parts.length && parts[schemaIndex + 1].trim() === "") schemaIndex++; const recordParts = parts.slice(schemaIndex + 1); const records = []; for (const part of recordParts) { const trimmed = part.trim(); if (trimmed === "") continue; const fields = splitEscaped(trimmed, fieldDelimiter, escapeChar); if (fields.length < 1) continue; const [path, ...values] = fields; records.push({ path, values }); } return { schema, records }; }
//#endregion //#region src/decode/parser.ts /** * Parse PLOON schema * Example: [products#2](id,name,price|colors#(name,hex)) */ function parseSchema(schema, config) { const { schemaOpen, schemaClose, fieldsOpen, fieldsClose, arraySizeMarker } = config; const headerStart = schema.indexOf(schemaOpen); const headerEnd = schema.indexOf(schemaClose, headerStart); if (headerStart === -1 || headerEnd === -1) throw new Error(`Invalid PLOON schema: ${schema}`); const header = schema.slice(headerStart + 1, headerEnd); const [rootName, countStr] = header.split(arraySizeMarker); const count = countStr ? parseInt(countStr, 10) : 0; const fieldsStart = schema.indexOf(fieldsOpen, headerEnd); if (fieldsStart === -1) throw new Error(`Invalid PLOON schema: No fields found`); const fieldsEnd = findMatchingCloseParen(schema, fieldsStart, fieldsOpen, fieldsClose); if (fieldsEnd === -1) throw new Error(`Invalid PLOON schema: Unmatched parentheses`); const fieldsStr = schema.slice(fieldsStart + 1, fieldsEnd); const fields = parseFields(fieldsStr, config); return { rootName, count, fields }; } /** * Parse fields string with proper parentheses matching */ function parseFields(fieldsStr, config) { const fields = []; let pos = 0; while (pos < fieldsStr.length) { while (pos < fieldsStr.length && fieldsStr[pos] === config.schemaWhitespace) pos++; if (pos >= fieldsStr.length) break; const { field, nextPos } = parseNextField(fieldsStr, pos, config); fields.push(field); pos = nextPos; while (pos < fieldsStr.length && (fieldsStr[pos] === config.schemaFieldSeparator || fieldsStr[pos] === config.schemaWhitespace)) pos++; } return fields; } /** * Parse a single field definition * Handles nested arrays like "colors#(name,hex)" and objects like "customer{id,name}" */ function parseNextField(str, start, config) { const { arraySizeMarker, fieldsOpen, fieldsClose, schemaFieldSeparator } = config; let depth = 0; let braceDepth = 0; let pos = start; while (pos < str.length) { if (str[pos] === fieldsOpen) depth++; else if (str[pos] === fieldsClose) depth--; else if (str[pos] === "{") braceDepth++; else if (str[pos] === "}") braceDepth--; else if (str[pos] === schemaFieldSeparator && depth === 0 && braceDepth === 0) break; pos++; } const fieldStr = str.slice(start, pos).trim(); const field = parseFieldDefinition(fieldStr, config); return { field, nextPos: pos }; } /** * Parse field definition string * Returns ParsedField with nested schema if array or object * Examples: * - Array: "colors#(name,hex)" * - Array (optional): "colors?#(name,hex)" * - Object: "customer{id,name,address{street,city}}" * - Object (optional): "customer?{id,name}" * - Primitive (optional): "email?" */ function parseFieldDefinition(fieldStr, config) { const { arraySizeMarker, fieldsOpen, fieldsClose, optionalFieldMarker } = config; let arrayMarkerIndex = fieldStr.indexOf(arraySizeMarker); let objectBraceIndex = fieldStr.indexOf("{"); let hasArray = arrayMarkerIndex !== -1; let hasObject = objectBraceIndex !== -1; if (hasArray && hasObject) if (arrayMarkerIndex < objectBraceIndex) hasObject = false; else hasArray = false; let isOptional = false; let fieldStrWithoutMarker = fieldStr; if (hasObject) { const beforeBrace = fieldStr.slice(0, objectBraceIndex); if (beforeBrace.endsWith(optionalFieldMarker)) { isOptional = true; fieldStrWithoutMarker = beforeBrace.slice(0, -optionalFieldMarker.length) + fieldStr.slice(objectBraceIndex); } } else if (hasArray) { const beforeMarker = fieldStr.slice(0, arrayMarkerIndex); if (beforeMarker.endsWith(optionalFieldMarker)) { isOptional = true; fieldStrWithoutMarker = beforeMarker.slice(0, -optionalFieldMarker.length) + fieldStr.slice(arrayMarkerIndex); } } else if (fieldStr.endsWith(optionalFieldMarker)) { isOptional = true; fieldStrWithoutMarker = fieldStr.slice(0, -optionalFieldMarker.length); } fieldStr = fieldStrWithoutMarker; if (hasArray) arrayMarkerIndex = fieldStr.indexOf(arraySizeMarker); if (hasObject) objectBraceIndex = fieldStr.indexOf("{"); if (hasObject) { const name = fieldStr.slice(0, objectBraceIndex).trim(); const closePos = findMatchingCloseBrace(fieldStr, objectBraceIndex); if (closePos === -1) throw new Error(`Unmatched braces in field: ${fieldStr}`); const nestedFieldsStr = fieldStr.slice(objectBraceIndex + 1, closePos); const nestedFields = parseFields(nestedFieldsStr, config); return { name, isArray: false, isObject: true, isOptional, nested: { rootName: name, count: 0, fields: nestedFields } }; } if (hasArray) { const name = fieldStr.slice(0, arrayMarkerIndex).trim(); let openPos = arrayMarkerIndex + arraySizeMarker.length; let count = 0; const openParenPos = fieldStr.indexOf(fieldsOpen, openPos); if (openParenPos !== -1 && openParenPos > openPos) { const countStr = fieldStr.slice(openPos, openParenPos); count = parseInt(countStr, 10) || 0; openPos = openParenPos; } const closePos = findMatchingCloseParen(fieldStr, openPos, fieldsOpen, fieldsClose); if (closePos === -1) throw new Error(`Unmatched parentheses in field: ${fieldStr}`); const nestedFieldsStr = fieldStr.slice(openPos + 1, closePos).trim(); const nestedFields = parseFields(nestedFieldsStr, config); const isPrimitiveArray = nestedFields.length === 0; return { name, isArray: true, isObject: false, isPrimitiveArray, isOptional, nested: { rootName: name, count, fields: nestedFields } }; } return { name: fieldStr.trim(), isArray: false, isObject: false, isOptional }; } /** * Find matching closing parenthesis */ function findMatchingCloseParen(str, openPos, openChar, closeChar) { let depth = 1; let pos = openPos + 1; while (pos < str.length && depth > 0) { if (str[pos] === openChar) depth++; else if (str[pos] === closeChar) depth--; pos++; } return depth === 0 ? pos - 1 : -1; } /** * Find matching closing brace for objects */ function findMatchingCloseBrace(str, openPos) { let depth = 1; let pos = openPos + 1; while (pos < str.length && depth > 0) { if (str[pos] === "{") depth++; else if (str[pos] === "}") depth--; pos++; } return depth === 0 ? pos - 1 : -1; }
//#endregion //#region src/decode/decoder.ts /** * Parse path format * - Array: "depth:index" (e.g., "2:1") → { depth: 2, index: 1, isObject: false } * - Object: "depth " (e.g., "2 ") → { depth: 2, isObject: true } */ function parsePath(path, separator) { const trimmed = path.trim(); if (!trimmed.includes(separator)) return { depth: parseInt(trimmed, 10), isObject: true }; const [depthStr, indexStr] = trimmed.split(separator); return { depth: parseInt(depthStr, 10), index: parseInt(indexStr, 10), isObject: false }; } /** * Decode PLOON string to JavaScript object */ function decode(ploonString, config) { const { schema, records } = scan(ploonString, config); const parsedSchema = parseSchema(schema, config); const result = reconstruct(records, parsedSchema, config); return result; } /** * Reconstruct object tree from records using BFS (level-by-level) */ function reconstruct(records, schema, config) { const tree = buildTree(records, schema, config); return { [schema.rootName]: tree }; } function buildRoutingMap(childRecords, allFields, presenceMarkers, pathSeparator) { const groups = []; let currentBatch = []; let lastIndex = -1; let lastWasObject = false; let fieldIdx = 0; for (const item of childRecords) { const { record } = item; const parsed = parsePath(record.path, pathSeparator); const currentIndex = parsed.index !== void 0 ? parsed.index : 0; const isObject = parsed.isObject; const fieldBoundary = currentBatch.length > 0 && currentIndex <= lastIndex || currentBatch.length > 0 && isObject !== lastWasObject || lastWasObject && isObject; if (fieldBoundary) if (fieldIdx < allFields.length) { const field = allFields[fieldIdx]; groups.push({ fieldName: field.name, fieldIndex: fieldIdx, isArray: field.isArray, isObject: field.isObject, records: currentBatch.map((it) => it.record), recordIndices: currentBatch.map((it) => it.index) }); fieldIdx++; currentBatch = []; if (fieldIdx >= allFields.length) break; } else break; currentBatch.push(item); lastIndex = currentIndex; lastWasObject = isObject; } if (currentBatch.length > 0 && fieldIdx < allFields.length) { const field = allFields[fieldIdx]; groups.push({ fieldName: field.name, fieldIndex: fieldIdx, isArray: field.isArray, isObject: field.isObject, records: currentBatch.map((it) => it.record), recordIndices: currentBatch.map((it) => it.index) }); } return groups; } /** * Pre-compute parent-child relationships for all records * Returns a map from record index to array of child record indices */ function buildParentChildMap(records, pathSeparator) { const parentChildMap = /* @__PURE__ */ new Map(); for (let i = 0; i < records.length; i++) { const record = records[i]; const parsed = parsePath(record.path, pathSeparator); const myDepth = parsed.depth; const children = []; for (let j = i + 1; j < records.length; j++) { const childRecord = records[j]; const childParsed = parsePath(childRecord.path, pathSeparator); const childDepth = childParsed.depth; if (childDepth === myDepth + 1) children.push(j); else if (childDepth <= myDepth) break; } parentChildMap.set(i, children); } return parentChildMap; } /** * Build tree structure from flat records using BFS (Breadth-First Search) * Process level by level with sequential consumption of children */ function buildTree(records, schema, config) { const { pathSeparator } = config; if (records.length === 0) return []; const parentChildMap = buildParentChildMap(records, pathSeparator); const recordsByDepth = /* @__PURE__ */ new Map(); for (const record of records) { const parsed = parsePath(record.path, pathSeparator); if (!recordsByDepth.has(parsed.depth)) recordsByDepth.set(parsed.depth, []); recordsByDepth.get(parsed.depth).push(record); } const rootItems = []; const depth1Records = recordsByDepth.get(1) || []; const currentLevelParents = []; for (let i = 0; i < records.length; i++) { const record = records[i]; const parsed = parsePath(record.path, pathSeparator); if (parsed.depth === 1 && !parsed.isObject) { const { obj } = createObjectFromRecord(record, schema.fields, config); rootItems.push(obj); currentLevelParents.push({ obj, fields: schema.fields, recordIndex: i }); } } let currentDepth = 1; let parentsAtCurrentDepth = currentLevelParents; while (parentsAtCurrentDepth.length > 0) { const childDepth = currentDepth + 1; const nextLevelParents = []; for (const parent of parentsAtCurrentDepth) { const { obj: parentObj, fields: parentFields, recordIndex: parentIdx } = parent; const presenceMarkers = parentObj.__presenceMarkers__ || /* @__PURE__ */ new Set(); const childIndices = parentChildMap.get(parentIdx) || []; const myChildrenWithIndices = childIndices.map((idx) => ({ record: records[idx], index: idx })).filter((item) => { const parsed = parsePath(item.record.path, pathSeparator); return parsed.depth === childDepth; }); if (myChildrenWithIndices.length === 0) continue; const complexFields = parentFields.filter((f) => !f.isPrimitiveArray && (f.isArray || f.isObject)).filter((f) => !presenceMarkers.has(f.name)); const routingMap = buildRoutingMap(myChildrenWithIndices, complexFields, presenceMarkers, pathSeparator); if (routingMap.length === 0) continue; for (const group of routingMap) { const field = complexFields[group.fieldIndex]; if (group.isArray) { if (!(field.name in parentObj)) parentObj[field.name] = []; const targetArray = parentObj[field.name]; for (let i = 0; i < group.records.length; i++) { const rec = group.records[i]; const recIdx = group.recordIndices[i]; const fields = field.nested?.fields || []; if (fields.length === 0) { const value = rec.values.length > 0 ? parseValue(rec.values[0], config) : null; targetArray.push(value); } else { const { obj: arrayItem } = createObjectFromRecord(rec, fields, config); targetArray.push(arrayItem); nextLevelParents.push({ obj: arrayItem, fields, recordIndex: recIdx }); } } } else if (group.isObject) { if (field.nested && group.records.length > 0) { const fields = field.nested.fields || []; const rec = group.records[0]; const recIdx = group.recordIndices[0]; const { obj: nestedObj } = createObjectFromRecord(rec, fields, config); parentObj[field.name] = nestedObj; nextLevelParents.push({ obj: nestedObj, fields, recordIndex: recIdx }); } } } } parentsAtCurrentDepth = nextLevelParents; currentDepth++; } for (const item of rootItems) cleanupOptionalFields(item, schema.fields, config); return rootItems; } /** * Create object from record based on schema fields * Returns both the object and presence markers for optional empty fields */ function createObjectFromRecord(record, fields, config) { const obj = {}; const presenceMarkers = /* @__PURE__ */ new Set(); const primitiveFields = fields.filter((f) => f.isPrimitiveArray || !f.isArray && !f.isObject); const complexFields = fields.filter((f) => !f.isPrimitiveArray && (f.isArray || f.isObject)); const optionalComplexFields = complexFields.filter((f) => f.isOptional); let valueIndex = 0; for (const field of primitiveFields) { if (valueIndex >= record.values.length) break; const rawValue = record.values[valueIndex]; if (field.isOptional && rawValue === "") { valueIndex++; continue; } if (field.isPrimitiveArray) if (rawValue === ",") obj[field.name] = []; else { const elements = splitEscaped(rawValue, ",", config.escapeChar); obj[field.name] = elements.map(parseValueFromUnescaped); } else obj[field.name] = parseValue(rawValue, config); valueIndex++; } /** * Optional Complex Field Marker Decoding: * * When creating an object, we read || markers to determine which optional * fields are absent/empty vs present. * * Decoding rules: * 1. Read markers sequentially for optional complex fields * 2. || marker means: field is absent or empty (create empty array/object) * 3. No marker but more data exists: field has data (will be populated from child records) * 4. No more markers/values: all remaining optional fields are absent (don't create them) * * Example: fields [_collections?, collections?, is_yalla?, variants?] * Marker: || * Child records: 38 records, then 2 records * → _collections: empty (from marker) * → collections: populate from 38 child records * → is_yalla: populate from 2 child records * → variants: absent (no marker, no data after cutoff) */ let markersRead = 0; let hitDataField = false; for (let i = 0; i < optionalComplexFields.length; i++) { const field = optionalComplexFields[i]; if (hitDataField) { obj[field.name] = field.isArray ? [] : {}; continue; } if (valueIndex < record.values.length) { const markerValue = record.values[valueIndex]; if (markerValue === "") { presenceMarkers.add(field.name); obj[field.name] = field.isArray ? [] : {}; valueIndex++; markersRead++; continue; } else { hitDataField = true; obj[field.name] = field.isArray ? [] : {}; continue; } } else if (markersRead > 0) { hitDataField = true; obj[field.name] = field.isArray ? [] : {}; } else break; } for (const field of complexFields) if (!field.isOptional && !(field.name in obj)) obj[field.name] = field.isArray ? [] : {}; if (presenceMarkers.size > 0) obj.__presenceMarkers__ = presenceMarkers; return { obj, presenceMarkers }; } /** * Recursively clean up empty optional fields that have no presence marker * This runs AFTER all child records have been processed */ function cleanupOptionalFields(obj, fields, config) { const presenceMarkers = obj.__presenceMarkers__; for (const field of fields) { const value = obj[field.name]; if (field.isArray && !field.isPrimitiveArray && Array.isArray(value)) { if (field.nested?.fields) { for (const item of value) if (item && typeof item === "object" && !Array.isArray(item)) cleanupOptionalFields(item, field.nested.fields, config); } if (field.isOptional && value.length === 0 && !presenceMarkers?.has(field.name)) delete obj[field.name]; } else if (field.isObject && value && typeof value === "object" && !Array.isArray(value)) { if (field.nested?.fields) cleanupOptionalFields(value, field.nested.fields, config); if (field.isOptional && Object.keys(value).filter((k) => k !== "__presenceMarkers__").length === 0 && !presenceMarkers?.has(field.name)) delete obj[field.name]; } } delete obj.__presenceMarkers__; } /** * Parse a value string to its JSON type */ function parseValue(value, config) { const unescaped = unescapeValue(value, config); return parseValueFromUnescaped(unescaped); } /** * Parse an already-unescaped value to its JSON type * Used for primitive array elements that were unescaped by splitEscaped */ function parseValueFromUnescaped(value) { if (/^-?\d+(\.\d+)?$/.test(value)) return parseFloat(value); if (value === "null") return null; if (value === "true") return true; if (value === "false") return false; return value; }
//#endregion //#region src/core/parse.ts /** * Parse PLOON string to JavaScript object * * @param ploonString - PLOON formatted string * @param options - Parse options (strict, config) * @returns JavaScript object * * @example * ```typescript * const ploon = `[products#1](id,name) * * 1|1|Shirt` * const data = parse(ploon) * // { products: [{ id: 1, name: "Shirt" }] } * ``` */ function parse(ploonString, options) { const resolved = resolveParseOptions(options); const result = decode(ploonString, resolved.config); return result; }
//#endregion //#region src/core/minify.ts /** * Minify PLOON string (Standard → Compact) * Replaces newlines with semicolons and removes unnecessary whitespace */ function minify(ploonString) { const lines = ploonString.split("\n"); const nonEmptyLines = lines.filter((line) => line.trim().length > 0); const minified = nonEmptyLines.join(";"); return minified.endsWith(";") ? minified.slice(0, -1) : minified; }
//#endregion //#region src/core/prettify.ts /** * PLOON Prettify * Convert Compact format (semicolon-separated) to Standard format (newline-separated) */ /** * Prettify PLOON string (Compact → Standard) * Replaces semicolons with newlines for readability */ function prettify(ploonString) { const parts = ploonString.split(";"); if (parts.length < 2) return ploonString; const [schema, ...records] = parts; return `${schema}\n\n${records.join("\n")}`; }
//#endregion //#region src/core/validate.ts /** * Check if a string is valid PLOON */ function isValid(ploonString, config = DEFAULT_CONFIG) { try { const result = validate(ploonString, config); return result.valid; } catch { return false; } } /** * Validate PLOON string and return detailed result */ function validate(ploonString, config = DEFAULT_CONFIG) { const errors = []; const warnings = []; try { const { schema, records } = scan(ploonString, config); if (!schema) errors.push("No schema found"); if (records.length === 0) warnings.push("No records found"); try { parseSchema(schema, config); } catch (error) { const message = error instanceof Error ? error.message : String(error); errors.push(`Schema parsing failed: ${message}`); } const paths = records.map((r) => r.path); const uniquePaths = new Set(paths); if (paths.length !== uniquePaths.size) warnings.push("Duplicate paths found"); } catch (error) { const message = error instanceof Error ? error.message : String(error); errors.push(message); } return { valid: errors.length === 0, errors: errors.length > 0 ? errors : void 0, warnings: warnings.length > 0 ? warnings : void 0 }; }
//#endregion //#region src/parsers/json.ts /** * Parse JSON string to JavaScript object * Uses native JSON.parse() - zero dependencies */ function fromJSON(input) { try { return JSON.parse(input); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`Invalid JSON: ${message}`); } } /** * Check if a string is valid JSON */ function isJSON(input) { try { JSON.parse(input); return true; } catch { return false; } } /** * Convert JavaScript object to JSON string * Convenience wrapper around JSON.stringify */ function toJSON(value, pretty = false) { return JSON.stringify(value, null, pretty ? 2 : 0); }
//#endregion //#region src/parsers/xml.ts /** * Parse XML string to JavaScript object */ function fromXML(input) { const validation = fast_xml_parser.XMLValidator.validate(input); if (validation !== true) { const error = validation.err; throw new Error(`Invalid XML at line ${error.line}: ${error.msg}`); } const parser = new fast_xml_parser.XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_", textNodeName: "#text", parseAttributeValue: true, parseTagValue: true, trimValues: true }); try { return parser.parse(input); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`XML parsing failed: ${message}`); } } /** * Check if a string is valid XML */ function isXML(input) { const validation = fast_xml_parser.XMLValidator.validate(input); return validation === true; } /** * Convert JavaScript object to XML string */ function toXML(value, pretty = false) { const builder = new fast_xml_parser.XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: "@_", textNodeName: "#text", format: pretty, indentBy: pretty ? " " : "", suppressEmptyNode: true }); try { return builder.build(value); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`XML building failed: ${message}`); } }
//#endregion //#region src/parsers/yaml.ts /** * Parse YAML string to JavaScript object */ function fromYAML(input) { try { return yaml.default.parse(input); } catch (error) { const message = error instanceof Error ? error.message : String(error); if (error && typeof error === "object" && "linePos" in error) { const linePos = error.linePos; throw new Error(`Invalid YAML at line ${linePos.line}, col ${linePos.col}: ${message}`); } throw new Error(`Invalid YAML: ${message}`); } } /** * Check if a string is valid YAML */ function isYAML(input) { try { yaml.default.parse(input); return true; } catch { return false; } } /** * Convert JavaScript object to YAML string */ function toYAML(value, pretty = false) { try { return yaml.default.stringify(value, { indent: pretty ? 2 : 0, lineWidth: 0, minContentWidth: 0 }); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`YAML serialization failed: ${message}`); } }
//#endregion exports.PLOON_COMPACT = PLOON_COMPACT; exports.PLOON_STANDARD = PLOON_STANDARD; exports.detectConfig = detectConfig; exports.detectFormat = detectFormat; exports.fromJSON = fromJSON; exports.fromXML = fromXML; exports.fromYAML = fromYAML; exports.isJSON = isJSON; exports.isValid = isValid; exports.isXML = isXML; exports.isYAML = isYAML; exports.minify = minify; exports.parse = parse; exports.prettify = prettify; exports.resolveConfig = resolveConfig; exports.stringify = stringify; exports.toJSON = toJSON; exports.toXML = toXML; exports.toYAML = toYAML; exports.validate = validate; exports.validateConfig = validateConfig; //# sourceMappingURL=index.cjs.map