UNPKG

@remotex-labs/xmap

Version:

A library with a sourcemap parser and TypeScript code formatter for the CLI

1,204 lines (1,197 loc) 34.8 kB
// src/components/path.component.ts import * as p from "path"; import * as h from "path/posix"; var P = /\\/g, k = /\/+/g; function g(n) { if (!n) return ""; let e = String(n); return e = e.replace(P, "/"), e = e.replace(k, "/"), e; } function F(n) { return h.normalize(g(n)); } function Q(...n) { return h.join(...n.map(g)); } function y(...n) { return g(p.resolve(...n)); } function b(n) { return g(p.dirname(n)); } function L(n, e) { return h.relative(g(n), g(e)) || "."; } function T(n, e) { return g(p.basename(n, e)); } // src/components/base64.component.ts var B = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", $ = /* @__PURE__ */ new Uint8Array([ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, // + / 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, // 0-9 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // A-O 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, // P-Z 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // a-o 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 // p-z ]); function U(n) { let e = n < 0 ? -n * 2 | 1 : n * 2, r = ""; do { let t = e & 31; e >>>= 5, r += B[e > 0 ? t | 32 : t]; } while (e > 0); return r; } function A(n) { return n.map(U).join(""); } function I(n) { let e = [], r = 0, t = 0; for (let o = 0; o < n.length; o++) { let s = n.charCodeAt(o), i = s < 128 ? $[s] : 255; if (i === 255) throw new Error(`Invalid Base64 character: '${n[o]}' at index ${o}`); t += (i & 31) << r, i & 32 ? r += 5 : (e.push(t & 1 ? -(t >> 1) : t >> 1), t = r = 0); } if (r > 0) throw new Error("Unexpected end of VLQ input: incomplete sequence"); return e; } // src/components/segment.component.ts var w = /* @__PURE__ */ ((t) => (t[t.BOUND = 0] = "BOUND", t[t.LOWER_BOUND = 1] = "LOWER_BOUND", t[t.UPPER_BOUND = 2] = "UPPER_BOUND", t))(w || {}); function v(n = 0, e = 0) { return { line: 0, column: 0, nameIndex: n, sourceIndex: e, generatedLine: 0, generatedColumn: 0 }; } function O(n, e) { let r = e[0], t = e[1], o = e[2], s = e[3], i = e[4]; return n.line += o ?? 0, n.column += s ?? 0, n.sourceIndex += t ?? 0, n.generatedColumn += r, i !== void 0 && (n.nameIndex += i), { // +1 converts 0-based VLQ accumulator → 1-based public API line: n.line + 1, column: n.column + 1, generatedLine: n.generatedLine + 1, generatedColumn: n.generatedColumn + 1, sourceIndex: n.sourceIndex, nameIndex: i !== void 0 ? n.nameIndex : null }; } function _(n, e) { return O(n, I(e)); } function C(n, e) { let r = e.line - 1, t = e.column - 1, o = e.generatedColumn - 1, s = [ o - n.generatedColumn, // Δ generatedColumn e.sourceIndex - n.sourceIndex, // Δ sourceIndex r - n.line, // Δ sourceLine t - n.column // Δ sourceColumn ]; return e.nameIndex !== null && e.nameIndex !== void 0 && (s[4] = e.nameIndex - n.nameIndex, n.nameIndex = e.nameIndex), n.generatedColumn = o, n.sourceIndex = e.sourceIndex, n.line = r, n.column = t, A(s); } function M(n) { let e = ["line", "column", "generatedLine", "generatedColumn"]; for (let r of e) { let t = n[r]; if (!Number.isFinite(t) || t < 1) throw new Error(`Invalid segment: '${r}' must be a finite number \u2265 1, received ${t}`); } if (!Number.isFinite(n.sourceIndex)) throw new Error(`Invalid segment: 'sourceIndex' must be a finite number, received ${n.sourceIndex}`); if (n.nameIndex !== null && !Number.isFinite(n.nameIndex)) throw new Error(`Invalid segment: 'nameIndex' must be a finite number or null, received ${n.nameIndex}`); } // src/services/source.service.ts import { cwd as D } from "process"; // src/services/mapping.service.ts var S = class n { /** * The generated file this source map is associated with. * * @remarks * Optional field specifying the filename of the generated code that this * source map describes. This is typically a relative path. * * Can be `null` or omitted if the filename is not relevant or is provided * through other means (e.g., HTTP headers, file naming conventions). * * @example * ```ts * file: 'bundle.min.js' * ``` * * @since 5.0.0 */ lines = []; /** * List of identifier names referenced in the mappings. * * @remarks * Optional array containing all identifier names (variables, functions, etc.) * that are referenced by segments with a `nameIndex`. These names correspond * to their positions in the original source code. * * Segments can reference these names by index to indicate which identifier * in the original code corresponds to a position in the generated code. * * Omit this field if no names need to be tracked, or provide an empty array. * * @example * ```ts * names: ['myFunction', 'result', 'value'] * ``` * * @since 5.0.0 */ offsets = { line: 0, name: 0, sources: 0 }; /** * Gets the current line offset used for coordinate adjustments. * * @returns The line offset value * * @remarks * This offset is applied when decoding mappings to shift generated line numbers. * Useful when merging multiple source maps where line numbers need adjustment. * * @since 5.0.0 */ get lineOffset() { return this.offsets.line; } /** * Gets the current name index offset used for coordinate adjustments. * * @returns The name index offset value * * @remarks * This offset is applied when decoding mappings to shift name indices. * Necessary when concatenating source maps with different name arrays. * * @since 5.0.0 */ get namesOffset() { return this.offsets.name; } /** * Gets the current source file index offset used for coordinate adjustments. * * @returns The source index offset value * * @remarks * This offset is applied when decoding mappings to shift source file indices. * Required when merging source maps with different source file arrays. * * @since 5.0.0 */ get sourcesOffset() { return this.offsets.sources; } /** * Encodes all stored mappings to a Source Map v3 VLQ string. * * @returns A Base64 VLQ-encoded mappings string * * @remarks * Converts the internal 1-based segment representation to 0-based VLQ deltas * and encodes them as a semicolon-separated string where: * - Semicolons (`;`) separate lines * - Commas (`,`) separate segments within a line * - Empty frames are represented as empty strings between semicolons * * The encoding process: * 1. Resets column offset at each line boundary * 2. Computes deltas relative to a previous segment * 3. Encodes deltas using Base64 VLQ * * @example Encoding mappings * ```ts * const service = new MappingService(); * service.decode(mappingArray); * const vlqString = service.encode(); * // Returns: "AAAA;AACA,AADA;AAGA;" * ``` * * @since 5.0.0 */ encode() { return this.encodeSourceMapping(this.lines); } /** * Decodes and stores source map data from various input formats. * * @param mapping - Source map data (VLQ string, array, or MappingService instance) * @param namesOffset - Offset to apply to name indices (default: 0) * @param sourcesOffset - Offset to apply to source file indices (default: 0) * @param lineOffset - Offset to apply to generated line numbers (default: 0) * * @throws Error - If the mapping format is invalid or contains malformed data * * @remarks * Accepts three input formats: * - **VLQ string**: Standard Source Map v3 encoded mappings * - **Array**: Structured {@link SourceMappingType} with segments * - **MappingService**: Another service instance (copies its lines) * * Offsets are useful when merging multiple source maps, allowing index * adjustment to prevent conflicts in name and source arrays. * * The method appends to existing mappings, so calling it multiple times * will accumulate all decoded segments. * * @example Decoding from string * ```ts * const service = new MappingService(); * service.decode('AAAA;AACA;AADA;'); * ``` * * @example Decoding with offsets * ```ts * const service = new MappingService(); * service.decode(mappingArray, 5, 3, 10); * // All name indices += 5, source indices += 3, generated lines += 10 * ``` * * @example Decoding from another service * ```ts * const source = new MappingService(); * source.decode('AAAA;'); * const target = new MappingService(); * target.decode(source); * ``` * * @since 5.0.0 */ decode(e, r = 0, t = 0, o = 0) { this.offsets.line = o, this.offsets.name = r, this.offsets.sources = t; let s = e instanceof n ? e.lines : e; Array.isArray(s) ? this.decodeSourceMappingArray(s) : this.decodeSourceMappingString(s); } /** * Finds a segment at the specified generated position using binary search. * * @param generatedLine - 1-based generated line number * @param generatedColumn - 1-based generated column number * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * * @returns The matching segment, or `null` if no suitable segment exists * * @remarks * Uses binary search for O(log n) performance on sorted segment arrays. * * Bias behavior: * - **`BOUND`**: Returns exact matches only * - **`LOWER_BOUND`**: Returns the largest segment ≤ target column * - **`UPPER_BOUND`**: Returns the smallest segment ≥ target column * * The line index is adjusted by the current {@link lineOffset} to support * merged source maps with shifted line numbers. * * @example Finding exact segment * ```ts * const service = new MappingService(); * service.decode(mappings); * const segment = service.getSegment(1, 5); * // Returns a segment at line 1, column 5, or null * ``` * * @example Finding nearest lower bound * ```ts * const segment = service.getSegment(1, 7, Bias.LOWER_BOUND); * // Returns the closest segment at or before column 7 * ``` * * @see {@link Bias} for bias options * @see {@link getOriginalSegment} for reverse lookups * * @since 5.0.0 */ getSegment(e, r, t = 0) { let o = e - this.offsets.line - 1, s = this.lines[o]; if (!s || s.length === 0) return null; let i = 0, c = s.length - 1, l = null; for (; i <= c; ) { let a = i + c >>> 1, u = s[a]; if (u.generatedColumn < r) i = a + 1, t === 1 && (l = u); else if (u.generatedColumn > r) c = a - 1, t === 2 && (l = u); else return u; } return l; } /** * Finds a segment at the specified original source position using linear search. * * @param sourceLine - 1-based original source line number * @param sourceColumn - 1-based original source column number * @param sourceIndex - Source file index * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * * @returns The matching segment, or `null` if no suitable segment exists * * @remarks * Performs exhaustive linear search across all segments to find matches * by original position. For frequent lookups, consider building an index * first using {@link buildOriginalPositionIndex}. * * Returns immediately on the exact column match. For bias modes, tracks the * closest segment by computing column distance. * * Bias behavior: * - **`BOUND`**: Returns exact matches only * - **`LOWER_BOUND`**: Returns the closest segment with column ≤ target * - **`UPPER_BOUND`**: Returns the closest segment with column ≥ target * * @example Finding original segment * ```ts * const service = new MappingService(); * service.decode(mappings); * const segment = service.getOriginalSegment(10, 25, 0); * // Returns segment mapping to source file 0, line 10, column 25 * ``` * * @example Finding the nearest original position * ```ts * const segment = service.getOriginalSegment(10, 25, 0, Bias.LOWER_BOUND); * // Returns the closest segment at or before column 25 * ``` * * @see {@link Bias} for bias options * @see {@link getSegment} for generated position lookups * @see {@link buildOriginalPositionIndex} for optimized repeated lookups * * @since 5.0.0 */ getOriginalSegment(e, r, t, o = 0) { let s = null, i = 1 / 0; for (let c = 0; c < this.lines.length; c++) { let l = this.lines[c]; if (l) for (let a = 0; a < l.length; a++) { let u = l[a]; if (u.sourceIndex !== t || u.line !== e) continue; let f = u.column; if (f === r) return u; if (o === 1) { if (f < r) { let m = r - f; m < i && (i = m, s = u); } } else if (o === 2 && f > r) { let m = f - r; m < i && (i = m, s = u); } } } return s; } /** * Builds an indexed map of segments grouped by original source position. * * @returns A map keyed by `"sourceIndex:line"` with sorted segment arrays * * @remarks * Creates a lookup structure for efficient reverse mapping from original * to generated positions. Each bucket contains all segments that map to * the same source file and line, sorted by column in ascending order. * * The index structure: * - **Keys**: Formatted as `"sourceIndex:line"` (e.g., `"0:1"`, `"2:15"`) * - **Values**: Arrays of {@link SegmentInterface} sorted by `column` * * This is useful when performing many original position lookups, as it * avoids the O(n) linear search of {@link getOriginalSegment}. * * Null/empty lines are safely skipped during index construction. * * @example Building and using index * ```ts * const service = new MappingService(); * service.decode(mappings); * const index = service.buildOriginalPositionIndex(); * * const segments = index.get('0:10'); * // Returns all segments mapping to source file 0, line 10 * ``` * * @example Binary search on indexed bucket * ```ts * const index = service.buildOriginalPositionIndex(); * const bucket = index.get('0:10') || []; * // Perform binary search on bucket for specific column * ``` * * @see {@link getOriginalSegment} for single lookups * * @since 5.0.0 */ buildOriginalPositionIndex() { let e = /* @__PURE__ */ new Map(); for (let r = 0; r < this.lines.length; r++) { let t = this.lines[r]; if (t) for (let o = 0; o < t.length; o++) { let s = t[o], i = `${s.sourceIndex}:${s.line}`, c = e.get(i); c || (c = [], e.set(i, c)), c.push(s); } } for (let r of e.values()) r.sort((t, o) => t.column - o.column); return e; } /** * Encodes a structured source mapping array to a VLQ string. * * @param sourceMapping - Array of segment lines to encode * * @returns A Base64 VLQ-encoded mappings string * * @remarks * Converts 1-based segment positions to 0-based VLQ deltas and encodes * them according to Source Map v3 specification. * * The encoding process: * 1. Pre-allocates arrays for optimal performance * 2. Resets `generatedColumn` offset to 0 at each line boundary * 3. Encodes each segment as deltas from the previous segment * 4. Joins segments with commas (`,`) and lines with semicolons (`;`) * * Null lines are encoded as empty strings (represented by consecutive * semicolons in the output). * * This method never reads the `generatedLine` field from segments, as * line boundaries are encoded structurally via semicolons. * * @example Internal encoding * ```ts * const encoded = this.encodeSourceMapping([ * [{ generatedColumn: 1, sourceIndex: 0, line: 1, column: 1, nameIndex: null, generatedLine: 1 }], * null, * [{ generatedColumn: 5, sourceIndex: 0, line: 2, column: 5, nameIndex: null, generatedLine: 3 }] * ]); * // Returns: "AAAA;;IAIA" * ``` * * @see {@link encode} for public API * @see {@link encodeSegment} for single segment encoding * * @since 5.0.0 */ encodeSourceMapping(e) { let r = v(), t = new Array(e.length); for (let o = 0; o < e.length; o++) { let s = e[o]; if (r.generatedColumn = 0, !s) { t[o] = ""; continue; } let i = new Array(s.length); for (let c = 0; c < s.length; c++) i[c] = C(r, s[c]); t[o] = i.join(","); } return t.join(";"); } /** * Validates that a string contains only valid VLQ characters. * * @param raw - String to validate * * @returns `true` if the string is valid, `false` otherwise * * @remarks * Checks that the input contains only characters from the Source Map v3 * VLQ alphabet plus structural separators: * - Base64 VLQ characters: `A-Z`, `a-z`, `0-9`, `+`, `/` * - Separators: `,` (segments), `;` (lines) * * Empty strings are considered invalid. * * @example Valid mapping string * ```ts * this.validateSourceMappingString('AAAA;AACA,AADA;') * // Returns: true * ``` * * @example Invalid mapping string * ```ts * this.validateSourceMappingString('AAAA;A#A;') * // Returns: false (contains '#') * ``` * * @since 5.0.0 */ validateSourceMappingString(e) { return /^[a-zA-Z0-9+/,;]+$/.test(e); } /** * Decodes a structured source mapping array and appends segments to internal state. * * @param sourceMapping - Array of segment lines or null * * @throws Error - If the array structure is invalid or segments fail validation * * @remarks * Processes a pre-parsed {@link SourceMappingType} array, applying configured * offsets to all indices and positions. * * The decoding process: * 1. Validates that input is an array * 2. Pre-computes combined line shift for performance * 3. Validates each segment using {@link validateSegment} * 4. Applies name, source, and line offsets to all coordinates * 5. Appends decoded segments to an internal lines array * * Null lines are preserved as-is. Each segment is validated before the offset * application to ensure data integrity. * * Error messages include 1-based line numbers for easier debugging. * * @example Internal array decoding * ```ts * this.decodeSourceMappingArray([ * [{ line: 1, column: 1, nameIndex: null, sourceIndex: 0, generatedLine: 1, generatedColumn: 1 }], * null, * [{ line: 2, column: 5, nameIndex: 0, sourceIndex: 0, generatedLine: 3, generatedColumn: 3 }] * ]); * ``` * * @see {@link decode} for public API * @see {@link validateSegment} for validation rules * * @since 5.0.0 */ decodeSourceMappingArray(e) { if (!Array.isArray(e)) throw new Error("Invalid source mapping: expected an array of lines."); let r = this.lines.length, { name: t, sources: o, line: s } = this.offsets, i = r + s; for (let c = 0; c < e.length; c++) { let l = e[c]; if (!l) { this.lines.push(l); continue; } if (!Array.isArray(l)) throw new Error(`Invalid source mapping at line ${c + 1}: expected an array of segments, got ${typeof l}.`); try { let a = new Array(l.length); for (let u = 0; u < l.length; u++) { let f = l[u]; M(f), a[u] = { ...f, nameIndex: typeof f.nameIndex == "number" ? f.nameIndex + t : null, sourceIndex: f.sourceIndex + o, generatedLine: f.generatedLine + i }; } this.lines.push(a); } catch (a) { throw new Error(`Error decoding mappings at line ${c + 1}: ${a instanceof Error ? a.message : String(a)}`); } } } /** * Decodes a VLQ-encoded mapping string and appends segments to internal state. * * @param encodedMapping - Base64 VLQ-encoded mappings string * * @throws Error - If the string format is invalid or decoding fails * * @remarks * Parses a Source Map v3 VLQ string into structured segments with 1-based * positions, applying configured offsets. * * The decoding process: * 1. Validates a character set using {@link validateSourceMappingString} * 2. Splits string by semicolons (`;`) to separate lines * 3. Pre-computes offset flags to minimize branching in hot loop * 4. For each line: * - Resets `generatedColumn` offset to 0 * - Splits by commas (`,`) to separate segments * - Decodes VLQ deltas using {@link decodeVLQ} * - Applies offsets (only allocating new objects when needed) * 5. Appends decoded segments to an internal lines array * * Empty lines (consecutive semicolons) are stored as `null`. * * Line offset is only applied when non-zero, avoiding unnecessary object * allocations for the common case. * * Error messages include 1-based line numbers for debugging. * * @example Internal string decoding * ```ts * this.decodeSourceMappingString('AAAA;AACA,AADA;AAGA;'); * ``` * * @see {@link decode} for public API * @see {@link decodeVLQ} for VLQ decoding * @see {@link decodeSegment} for delta application * * @since 5.0.0 */ decodeSourceMappingString(e) { if (!this.validateSourceMappingString(e)) throw new Error("Invalid mappings string: contains characters outside the VLQ alphabet."); let r = e.split(";"), t = this.lines.length, { name: o, sources: s, line: i } = this.offsets, c = v(o, s), l = i !== 0; for (let a = 0; a < r.length; a++) { let u = r[a]; if (!u) { this.lines.push(null); continue; } c.generatedColumn = 0, c.generatedLine = t + a; let f = u.split(","), m = new Array(f.length); try { for (let d = 0; d < f.length; d++) { let x = O(c, I(f[d])); m[d] = l ? { ...x, generatedLine: x.generatedLine + i } : x; } } catch (d) { throw new Error(`Error decoding mappings at line ${a + 1}: ${d.message}`); } this.lines.push(m); } } }; // src/services/source.service.ts var E = class n { /** * Normalized generated file path associated with this source map. * @since 5.0.0 */ file = ""; /** * Identifier names referenced by mapped segments. * @since 5.0.0 */ names = []; /** * Resolved source file paths used by the map. * @since 5.0.0 */ sources = []; /** * Optional source root from the source map payload. * @since 5.0.0 */ sourceRoot = ""; /** * Inline source contents aligned with {@link sources} by index. * @since 5.0.0 */ sourcesContent = []; /** * Internal mappings handler for encode/decode and lookups. * @since 5.0.0 */ mappings = new S(); /** * Creates a new SourceService from a source map object or JSON string. * * @param source - Source map payload as object or JSON string * @param fileOrOffset - Generated file path override, or generated line offset when numeric * @param offset - Generated line offset when `fileOrOffset` is a file path * * @throws Error - If file path is missing after normalization * @throws Error - If source map input is invalid or unsupported * * @since 5.0.0 */ constructor(e, r, t) { if (!e) return; let o = n.parseSourceMap(e), s = typeof r == "string" ? r : o.file; if (!s) throw new Error("File Path not set for the sourcemap"); this.file = y(s); let i = typeof r == "number" ? r : t ?? 0; this.mappings.decode(o.mappings, 0, 0, i), this.sources.push(...this.resolveSources(o.sources)), o.names && this.names.push(...o.names), o.sourceRoot && (this.sourceRoot = o.sourceRoot), o.sourcesContent && this.sourcesContent.push(...o.sourcesContent); } /** * Concatenates multiple source maps into one service instance. * * @param sources - Source services to merge in order * * @returns A new service containing merged mappings, names, sources, and contents * * @throws Error - If no source services are provided * * @remarks * Name and source indices are offset during decode to preserve index * correctness across all merged source maps. * * @example * ```ts * const merged = SourceService.assign(sourceA, sourceB, ...); * ``` * * @since 5.0.0 */ static assign(...e) { if (e.length < 1) throw new Error("At least one source-map must be provided for assign."); let r = new n(); for (let t of e) r.mappings.decode(t.mappings, r.names.length, r.sources.length), r.names.push(...t.names), r.sources.push(...t.sources.map((o) => this.isURL(o) ? o : t.sourceRoot + o)), r.sourcesContent.push(...t.sourcesContent); return r; } /** * Serializes current data to a Source Map v3 object. * * @returns A source map object with encoded mappings * * @remarks * Includes optional `file` and `sourceRoot` only when they are set. * * @example * ```ts * const map = source.getSourceObject(); * ``` * * @since 5.0.0 */ getSourceObject() { let e = { version: 3, file: this.file, names: this.names, sources: this.sources, mappings: this.mappings.encode(), sourcesContent: this.sourcesContent }; return this.sourceRoot && (e.sourceRoot = this.sourceRoot), e; } /** * Resolves an original position from a generated position. * * @param line - 1-based generated line number * @param column - 1-based generated column number * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * * @returns Position details, or `null` when no segment matches * * @remarks * The returned `name` can be `null` when the mapped segment has no * associated name index. * * @example * ```ts * const pos = source.getPosition(12, 8); * ``` * * @since 5.0.0 */ getPosition(e, r, t = 0) { let o = this.mappings.getSegment(e, r, t); return o ? { name: this.names[o.nameIndex ?? -1] ?? null, line: o.line, column: o.column, source: this.sources[o.sourceIndex], sourceRoot: this.sourceRoot, sourceIndex: o.sourceIndex, generatedLine: o.generatedLine, generatedColumn: o.generatedColumn } : null; } /** * Resolves an original position and includes full source content for that file. * * @param line - 1-based generated line number * @param column - 1-based generated column number * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * * @returns Position details with source content, or `null` when unavailable * * @remarks * Returns `null` if no mapped segment is found for the generated position. * * @example * ```ts * const pos = source.getPositionWithContent(12, 8); * ``` * * @since 5.0.0 */ getPositionWithContent(e, r, t = 0) { let o = this.getPosition(e, r, t); return o ? { ...o, sourcesContent: this.sourcesContent[o.sourceIndex] } : null; } /** * Resolves an original position and includes surrounding code context. * * @param line - 1-based generated line number * @param column - 1-based generated column number * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * @param options - Optional line window configuration * * @returns Position details with extracted code context, or `null` when unavailable * * @remarks * Default window uses `3` lines before and `4` lines after the target line. * * @example * ```ts * const pos = source.getPositionWithCode(12, 8, Bias.BOUND, { * linesBefore: 2, * linesAfter: 2 * }); * ``` * * @since 5.0.0 */ getPositionWithCode(e, r, t = 0, o) { let s = this.getPosition(e, r, t); if (!s || !this.sourcesContent[s.sourceIndex]) return null; let i = Object.assign({ linesAfter: 4, linesBefore: 3 }, o), c = this.sourcesContent[s.sourceIndex].split(` `), l = (s.line ?? 1) - 1, a = Math.max(l - i.linesBefore, 0), u = Math.min(l + i.linesAfter, c.length - 1), f = c.slice(a, u + 1).join(` `); return { ...s, code: f, endLine: u, startLine: a }; } /** * Resolves a generated position from an original source position. * * @param line - 1-based original source line number * @param column - 1-based original source column number * @param sourceIndex - Source index or a partial source path to locate the index * @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND}) * * @returns Position details, or `null` when no segment/source matches * * @remarks * String `sourceIndex` is matched using substring search against resolved * source paths in {@link sources}. * * @example * ```ts * const pos = source.getPositionByOriginal(40, 3, 'src/main.ts'); * ``` * * @since 5.0.0 */ getPositionByOriginal(e, r, t, o = 0) { let s = t; if (typeof t == "string" && (s = this.sources.findIndex((c) => c.includes(t))), s < 0) return null; let i = this.mappings.getOriginalSegment(e, r, s, o); return i ? { name: this.names[i.nameIndex ?? -1] ?? null, line: i.line, column: i.column, source: this.sources[i.sourceIndex], sourceRoot: this.sourceRoot, sourceIndex: i.sourceIndex, generatedLine: i.generatedLine, generatedColumn: i.generatedColumn } : null; } /** * Serializes the current source map object as JSON. * * @returns JSON string representation of {@link getSourceObject} * * @remarks * Output is suitable for writing a `.map` file or transport over APIs. * * @example * ```ts * const json = source.toString(); * ``` * * @since 5.0.0 */ toString() { return JSON.stringify(this.getSourceObject()); } /** * Parses and validates Source Map v3 input. * * @param input - Source map object or JSON string * * @returns Parsed and validated source map object * * @throws Error - If source map version is not `3` * @throws Error - If required keys are missing * * @remarks * Accepts JSON input for convenience and validates required structural keys * before decode operations are attempted. * * @example * ```ts * const map = SourceService['parseSourceMap'](raw); * ``` * * @since 5.0.0 */ static parseSourceMap(e) { if (typeof e == "string" && (e = JSON.parse(e)), e.version !== 3) throw new Error(`Unsupported SourceMap version: ${e.version}. Expected version 3.`); let t = ["sources", "mappings"].filter((o) => !(o in e)); if (t.length > 0) throw new Error( `Invalid SourceMap: missing required ${t.length === 1 ? "key" : "keys"}: ${t.join(", ")}.` ); return e; } /** * Checks whether a source path is an HTTP(S) URL. * * @param path - Path or URL candidate to evaluate * * @returns `true` when the value starts with `http` or `https`, otherwise `false` * * @remarks * This helper is used to preserve absolute URL sources and avoid filesystem * path normalization for remote resources. * * @example * ```ts * SourceService['isURL']('https://cdn.example.com/app.ts'); // true * SourceService['isURL']('src/app.ts'); // false * ``` * * @since 5.0.0 */ static isURL(e) { return e.startsWith("http") || e.startsWith("https"); } /** * Resolves source paths relative to the current working directory. * * @param sources - Raw source entries from source map payload * * @returns Normalized relative source file paths * * @remarks * Each path is resolved from the generated file location, then converted to * a path relative to `process.cwd()`. * * @example * ```ts * // internal utility used during construction * ``` * * @since 5.0.0 */ resolveSources(e) { return e?.map((r) => n.isURL(r) ? r : r.startsWith("..") ? L(D(), y(b(this.file), r)) : r) ?? []; } }; // src/services/resolve.service.ts import { xterm as R } from "@remotex-labs/xansi/xterm.component"; function N(n) { let e = n.fileName ?? n.source ?? "", r = n.functionName ? `${n.functionName} ` : ""; e.startsWith("http") && (e += `#L${n.line}`); let t = n.line && n.column ? R.gray(`[${n.line}:${n.column}]`) : ""; return `at ${r}${R.darkGray(e)} ${t}`.trim(); } function j(n, e) { return e.line = n.line, e.column = n.column, n.name && (e.functionName = n.name), n.sourceRoot && !n.source.startsWith("http") && (e.fileName = `${n.sourceRoot}${n.source}`), { ...e, code: n.code, format: N(e), stratLine: n.startLine, sourceRoot: n.sourceRoot }; } function W(n, e) { if (!e?.withNativeFrames && n.native || !n.line && !n.column && !n.fileName && !n.functionName) return; let r = e?.getSource?.(n.fileName ?? ""); if (r && n.line && n.column) { let t = r.getPositionWithCode(n.line, n.column, e?.bias ?? 0, { linesAfter: e?.linesAfter ?? 4, linesBefore: e?.linesBefore ?? 3 }); return t ? (t.line += e?.lineOffset ?? 0, t.endLine += e?.lineOffset ?? 0, t.startLine += e?.lineOffset ?? 0, j(t, n)) : void 0; } return { ...n, format: N(n) }; } function te(n, e = {}) { let r = n.stack.map((t) => W(t, e)).filter(Boolean); return { name: n.name, stack: r, message: n.message }; } export { w as Bias, E as SourceService, T as basename, v as createOffset, O as decodeSegment, _ as decodeSegmentRaw, I as decodeVLQ, b as dirname, A as encodeArrayVLQ, C as encodeSegment, U as encodeVLQ, N as formatStackLine, Q as join, F as normalize, L as relative, y as resolve, te as resolveError, W as stackEntry, j as stackSourceEntry, g as toPosix, M as validateSegment }; //# sourceMappingURL=index.js.map