-module(magic_string@codec). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/magic_string/codec.gleam"). -export([encode_vlq/1, decode_vlq/1, decode_mappings/1, generate_mappings/1, to_json/1, url_comment/2]). -export_type([source_map/0, map_mode/0, segment/0, delta_state/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC( " Source Map v3 model and base64 VLQ codec.\n" "\n" " `generate_mappings` turns a flat `Segment` list into the v3 `mappings`\n" " string; `decode_mappings` is its inverse. `to_json` serializes the whole\n" " `SourceMap`. See https://tc39.es/ecma426/ for the format.\n" ). -type source_map() :: {source_map, integer(), gleam@option:option(binary()), gleam@option:option(binary()), list(binary()), list(gleam@option:option(binary())), list(binary()), binary()}. -type map_mode() :: {external, binary()} | inline | hidden. -type segment() :: {segment, integer(), integer(), integer(), integer(), integer()}. -type delta_state() :: {delta_state, integer(), integer(), integer()}. -file("src/magic_string/codec.gleam", 84). -spec base64_char(integer()) -> binary(). base64_char(N) -> gleam@string:slice( <<"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"/utf8>>, N, 1 ). -file("src/magic_string/codec.gleam", 74). -spec encode_vlq_loop(integer(), binary()) -> binary(). encode_vlq_loop(Vlq, Acc) -> Digit = erlang:'band'(Vlq, 31), Rest = erlang:'bsr'(Vlq, 5), case Rest > 0 of true -> encode_vlq_loop( Rest, <> ); false -> <> end. -file("src/magic_string/codec.gleam", 66). ?DOC( " Encode a single signed integer as a base64 VLQ string.\n" "\n" " Sign is folded into the low bit (`value << 1`, or `(-value << 1) | 1` for\n" " negatives), then the magnitude is split into 5-bit groups emitted\n" " low-group-first. Every group except the last carries the continuation bit\n" " (32); each resulting 6-bit value indexes into `base64_alphabet`.\n" ). -spec encode_vlq(integer()) -> binary(). encode_vlq(Value) -> Vlq = case Value < 0 of true -> erlang:'bor'(erlang:'bsl'(- Value, 1), 1); false -> erlang:'bsl'(Value, 1) end, encode_vlq_loop(Vlq, <<""/utf8>>). -file("src/magic_string/codec.gleam", 132). ?DOC(" Undo the sign-in-low-bit folding `encode_vlq` applies.\n"). -spec from_vlq(integer()) -> integer(). from_vlq(Value) -> Magnitude = erlang:'bsr'(Value, 1), case erlang:'band'(Value, 1) of 0 -> Magnitude; _ -> - Magnitude end. -file("src/magic_string/codec.gleam", 159). -spec char_code(binary()) -> integer(). char_code(Char) -> _pipe = Char, _pipe@1 = gleam@string:to_utf_codepoints(_pipe), _pipe@2 = gleam@list:first(_pipe@1), _pipe@3 = gleam@result:map(_pipe@2, fun gleam_stdlib:identity/1), gleam@result:unwrap(_pipe@3, 0). -file("src/magic_string/codec.gleam", 141). ?DOC(" Position of a base64 character in the Source Map v3 alphabet.\n"). -spec base64_value(binary()) -> {ok, integer()} | {error, binary()}. base64_value(Char) -> case char_code(Char) of Code when (Code >= 65) andalso (Code =< 90) -> {ok, Code - 65}; Code@1 when (Code@1 >= 97) andalso (Code@1 =< 122) -> {ok, (Code@1 - 97) + 26}; Code@2 when (Code@2 >= 48) andalso (Code@2 =< 57) -> {ok, (Code@2 - 48) + 52}; 43 -> {ok, 62}; 47 -> {ok, 63}; Code@3 -> {error, <<<<<<<<"invalid base64 VLQ character '"/utf8, Char/binary>>/binary, "' (codepoint "/utf8>>/binary, (erlang:integer_to_binary(Code@3))/binary>>/binary, ")"/utf8>>} end. -file("src/magic_string/codec.gleam", 98). -spec decode_vlq_loop(list(binary()), integer(), integer(), list(integer())) -> {ok, list(integer())} | {error, binary()}. decode_vlq_loop(Chars, Partial, Shift, Acc) -> case Chars of [] -> case Shift of 0 -> {ok, lists:reverse(Acc)}; _ -> {error, <<<<"truncated VLQ: continuation bit set on final digit (shift "/utf8, (erlang:integer_to_binary(Shift))/binary>>/binary, ")"/utf8>>} end; [C | Rest] -> case base64_value(C) of {error, Msg} -> {error, Msg}; {ok, Digit} -> Chunk = erlang:'band'(Digit, 31), Partial@1 = Partial + erlang:'bsl'(Chunk, Shift), case erlang:'band'(Digit, 32) of 0 -> decode_vlq_loop( Rest, 0, 0, [from_vlq(Partial@1) | Acc] ); _ -> decode_vlq_loop(Rest, Partial@1, Shift + 5, Acc) end end end. -file("src/magic_string/codec.gleam", 94). ?DOC( " Decode a single base64-VLQ field string back to its list of signed\n" " integers. This is the exact inverse of concatenating `encode_vlq` calls:\n" " `decode_vlq(encode_vlq(a) <> encode_vlq(b)) == [a, b]`.\n" "\n" " Returns `Error` when `field` contains a character outside the base64\n" " alphabet, or ends mid-value (a continuation bit with no following digit).\n" ). -spec decode_vlq(binary()) -> {ok, list(integer())} | {error, binary()}. decode_vlq(Field) -> decode_vlq_loop(gleam@string:to_graphemes(Field), 0, 0, []). -file("src/magic_string/codec.gleam", 192). -spec decode_line(binary(), integer(), {list(segment()), delta_state()}) -> {ok, {list(segment()), delta_state()}} | {error, binary()}. decode_line(Line, Gen_line, Acc) -> case Line of <<""/utf8>> -> {ok, Acc}; _ -> _pipe = gleam@string:split(Line, <<","/utf8>>), _pipe@2 = gleam@list:try_fold( _pipe, {erlang:element(1, Acc), erlang:element(2, Acc), 0}, fun(State, Field) -> {Segs, Delta, Prev_gen_col} = State, gleam@result:'try'( begin _pipe@1 = decode_vlq(Field), gleam@result:map_error( _pipe@1, fun(Err) -> <<<<<<"line "/utf8, (erlang:integer_to_binary( Gen_line ))/binary>>/binary, ": "/utf8>>/binary, Err/binary>> end ) end, fun(Values) -> case Values of [Gc, Si, Ol, Oc | _] -> Gen_col = Prev_gen_col + Gc, Source_idx = erlang:element(2, Delta) + Si, Orig_line = erlang:element(3, Delta) + Ol, Orig_col = erlang:element(4, Delta) + Oc, Seg = {segment, Gen_line, Gen_col, Source_idx, Orig_line, Orig_col}, {ok, {[Seg | Segs], {delta_state, Source_idx, Orig_line, Orig_col}, Gen_col}}; [Gc@1] -> {ok, {Segs, Delta, Prev_gen_col + Gc@1}}; Other -> {error, <<<<<<<<"line "/utf8, (erlang:integer_to_binary( Gen_line ))/binary>>/binary, ": segment has "/utf8>>/binary, (erlang:integer_to_binary( erlang:length(Other) ))/binary>>/binary, " fields; expected 1, 4, or 5"/utf8>>} end end ) end ), gleam@result:map( _pipe@2, fun(State@1) -> {erlang:element(1, State@1), erlang:element(2, State@1)} end ) end. -file("src/magic_string/codec.gleam", 177). ?DOC( " Decode a Source Map v3 `mappings` string back to a flat `Segment` list.\n" " This is the inverse of `generate_mappings`:\n" " `decode_mappings(generate_mappings(segs))` returns the same segments\n" " (in `(gen_line, gen_col)` order).\n" "\n" " Only 4- and 5-field segments (those carrying a source pointer) are\n" " returned; 1-field segments (generated column only, no source) are skipped\n" " since `Segment` requires a source position. The optional 5th `name` field\n" " is decoded for delta-tracking but discarded (this codec does not model\n" " names).\n" ). -spec decode_mappings(binary()) -> {ok, list(segment())} | {error, binary()}. decode_mappings(Mappings) -> Lines = gleam@string:split(Mappings, <<";"/utf8>>), gleam@result:map( gleam@list:try_fold( gleam@list:index_map(Lines, fun(Line, Idx) -> {Idx, Line} end), {[], {delta_state, 0, 0, 0}}, fun(Acc, Entry) -> {Gen_line, Line@1} = Entry, decode_line(Line@1, Gen_line, Acc) end ), fun(_use0) -> {Segs_rev, _} = _use0, lists:reverse(Segs_rev) end ). -file("src/magic_string/codec.gleam", 288). ?DOC( " Encode one output line's segments, threading the cross-map delta state in\n" " and out. `gen_col` resets to 0 at the start of every line.\n" ). -spec emit_line(list(segment()), delta_state()) -> {binary(), delta_state()}. emit_line(Segs, Delta) -> {Parts_rev, _, Next_delta} = gleam@list:fold( Segs, {[], 0, Delta}, fun(Acc, Seg) -> {Parts, Prev_gen_col, D} = Acc, Field = erlang:list_to_binary( [encode_vlq(erlang:element(3, Seg) - Prev_gen_col), encode_vlq(erlang:element(4, Seg) - erlang:element(2, D)), encode_vlq(erlang:element(5, Seg) - erlang:element(3, D)), encode_vlq(erlang:element(6, Seg) - erlang:element(4, D))] ), Next = {delta_state, erlang:element(4, Seg), erlang:element(5, Seg), erlang:element(6, Seg)}, {[Field | Parts], erlang:element(3, Seg), Next} end ), {begin _pipe = Parts_rev, _pipe@1 = lists:reverse(_pipe), gleam@string:join(_pipe@1, <<","/utf8>>) end, Next_delta}. -file("src/magic_string/codec.gleam", 305). -spec compare_segment(segment(), segment()) -> gleam@order:order(). compare_segment(A, B) -> case gleam@int:compare(erlang:element(2, A), erlang:element(2, B)) of eq -> gleam@int:compare(erlang:element(3, A), erlang:element(3, B)); Other -> Other end. -file("src/magic_string/codec.gleam", 254). ?DOC( " Build the Source Map v3 `mappings` string from a flat segment list.\n" "\n" " Output lines are `;`-separated and segments within a line are\n" " `,`-separated. Each segment is the VLQ of four deltas:\n" " `[gen_col, source_idx, orig_line, orig_col]`. `gen_col` is delta'd within\n" " the line (reset to 0 per line); the other three are delta'd across the\n" " whole map. Inserted text contributes no segment (so a line with only\n" " inserted output is empty). `hires` is false: one segment per chunk.\n" ). -spec generate_mappings(list(segment())) -> binary(). generate_mappings(Segments) -> case Segments of [] -> <<""/utf8>>; _ -> Sorted = gleam@list:sort(Segments, fun compare_segment/2), Max_line = gleam@list:fold( Sorted, 0, fun(Acc, S) -> case erlang:element(2, S) > Acc of true -> erlang:element(2, S); false -> Acc end end ), By_line = gleam@list:group( Sorted, fun(S@1) -> erlang:element(2, S@1) end ), {Lines_rev, _} = gleam@int:range( 0, Max_line + 1, {[], {delta_state, 0, 0, 0}}, fun(Acc@1, Line) -> {Lines, Delta} = Acc@1, Segs = begin _pipe = gleam_stdlib:map_get(By_line, Line), _pipe@1 = gleam@result:unwrap(_pipe, []), gleam@list:sort( _pipe@1, fun(A, B) -> gleam@int:compare( erlang:element(3, A), erlang:element(3, B) ) end ) end, {Line_str, Next_delta} = emit_line(Segs, Delta), {[Line_str | Lines], Next_delta} end ), _pipe@2 = Lines_rev, _pipe@3 = lists:reverse(_pipe@2), gleam@string:join(_pipe@3, <<";"/utf8>>) end. -file("src/magic_string/codec.gleam", 343). -spec json_string(binary()) -> binary(). json_string(Value) -> Escaped = begin _pipe = Value, _pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>), _pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>), _pipe@3 = gleam@string:replace(_pipe@2, <<"\n"/utf8>>, <<"\\n"/utf8>>), _pipe@4 = gleam@string:replace(_pipe@3, <<"\r"/utf8>>, <<"\\r"/utf8>>), gleam@string:replace(_pipe@4, <<"\t"/utf8>>, <<"\\t"/utf8>>) end, <<<<"\""/utf8, Escaped/binary>>/binary, "\""/utf8>>. -file("src/magic_string/codec.gleam", 332). -spec json_array(list(binary())) -> binary(). json_array(Items) -> <<<<"["/utf8, (gleam@string:join(Items, <<","/utf8>>))/binary>>/binary, "]"/utf8>>. -file("src/magic_string/codec.gleam", 336). -spec json_nullable(gleam@option:option(binary())) -> binary(). json_nullable(Value) -> case Value of {some, S} -> json_string(S); none -> <<"null"/utf8>> end. -file("src/magic_string/codec.gleam", 314). ?DOC( " Serialize a `SourceMap` to a JSON string. `file` and `sourceRoot` are\n" " omitted when `None`; `sourcesContent` entries are `null` when `None`.\n" ). -spec to_json(source_map()) -> binary(). to_json(Map) -> Fields = begin _pipe = [{some, <<"\"version\":"/utf8, (erlang:integer_to_binary(erlang:element(2, Map)))/binary>>}, gleam@option:map( erlang:element(3, Map), fun(F) -> <<"\"file\":"/utf8, (json_string(F))/binary>> end ), gleam@option:map( erlang:element(4, Map), fun(R) -> <<"\"sourceRoot\":"/utf8, (json_string(R))/binary>> end ), {some, <<"\"sources\":"/utf8, (json_array( gleam@list:map( erlang:element(5, Map), fun json_string/1 ) ))/binary>>}, {some, <<"\"sourcesContent\":"/utf8, (json_array( gleam@list:map( erlang:element(6, Map), fun json_nullable/1 ) ))/binary>>}, {some, <<"\"names\":"/utf8, (json_array( gleam@list:map( erlang:element(7, Map), fun json_string/1 ) ))/binary>>}, {some, <<"\"mappings\":"/utf8, (json_string(erlang:element(8, Map)))/binary>>}], gleam@option:values(_pipe) end, <<<<"{"/utf8, (gleam@string:join(Fields, <<","/utf8>>))/binary>>/binary, "}"/utf8>>. -file("src/magic_string/codec.gleam", 358). ?DOC( " Produce the `sourceMappingURL` comment for the given map and mode.\n" "\n" " `External` points at a sibling `.map` URL, `Inline` embeds the whole map\n" " as a base64 `data:` URI, and `Hidden` emits nothing.\n" ). -spec url_comment(source_map(), map_mode()) -> binary(). url_comment(Map, Mode) -> case Mode of {external, Url} -> <<"//# sourceMappingURL="/utf8, Url/binary>>; hidden -> <<""/utf8>>; inline -> B64 = gleam_stdlib:base64_encode( gleam_stdlib:identity(to_json(Map)), true ), <<"//# sourceMappingURL=data:application/json;charset=utf-8;base64,"/utf8, B64/binary>> end.