dependabot-hex 0.118.13 → 0.119.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ defmodule Jason.Fragment do
2
+ defstruct [:encode]
3
+
4
+ def new(iodata) when is_list(iodata) or is_binary(iodata) do
5
+ %__MODULE__{encode: fn _ -> iodata end}
6
+ end
7
+
8
+ def new(encode) when is_function(encode, 1) do
9
+ %__MODULE__{encode: encode}
10
+ end
11
+ end
@@ -0,0 +1,98 @@
1
+ defmodule Jason.Helpers do
2
+ @moduledoc """
3
+ Provides macro facilities for partial compile-time encoding of JSON.
4
+ """
5
+
6
+ alias Jason.{Codegen, Fragment}
7
+
8
+ @doc ~S"""
9
+ Encodes a JSON map from a compile-time keyword.
10
+
11
+ Encodes the keys at compile time and strives to create as flat iodata
12
+ structure as possible to achieve maximum efficiency. Does encoding
13
+ right at the call site, but returns an `%Jason.Fragment{}` struct
14
+ that needs to be passed to one of the "main" encoding functions -
15
+ for example `Jason.encode/2` for final encoding into JSON - this
16
+ makes it completely transparent for most uses.
17
+
18
+ Only allows keys that do not require escaping in any of the supported
19
+ encoding modes. This means only ASCII characters from the range
20
+ 0x1F..0x7F excluding '\', '/' and '"' are allowed - this also excludes
21
+ all control characters like newlines.
22
+
23
+ Preserves the order of the keys.
24
+
25
+ ## Example
26
+
27
+ iex> fragment = json_map(foo: 1, bar: 2)
28
+ iex> Jason.encode!(fragment)
29
+ "{\"foo\":1,\"bar\":2}"
30
+
31
+ """
32
+ defmacro json_map(kv) do
33
+ kv_values = Macro.expand(kv, __CALLER__)
34
+ kv_vars = Enum.map(kv_values, fn {key, _} -> {key, generated_var(key, Codegen)} end)
35
+
36
+ values = Enum.map(kv_values, &elem(&1, 1))
37
+ vars = Enum.map(kv_vars, &elem(&1, 1))
38
+
39
+ escape = quote(do: escape)
40
+ encode_map = quote(do: encode_map)
41
+ encode_args = [escape, encode_map]
42
+ kv_iodata = Codegen.build_kv_iodata(kv_vars, encode_args)
43
+
44
+ quote do
45
+ {unquote_splicing(vars)} = {unquote_splicing(values)}
46
+
47
+ %Fragment{
48
+ encode: fn {unquote(escape), unquote(encode_map)} ->
49
+ unquote(kv_iodata)
50
+ end
51
+ }
52
+ end
53
+ end
54
+
55
+ @doc ~S"""
56
+ Encodes a JSON map from a variable containing a map and a compile-time
57
+ list of keys.
58
+
59
+ It is equivalent to calling `Map.take/2` before encoding. Otherwise works
60
+ similar to `json_map/2`.
61
+
62
+ ## Example
63
+
64
+ iex> map = %{a: 1, b: 2, c: 3}
65
+ iex> fragment = json_map_take(map, [:c, :b])
66
+ iex> Jason.encode!(fragment)
67
+ "{\"c\":3,\"b\":2}"
68
+
69
+ """
70
+ defmacro json_map_take(map, take) do
71
+ take = Macro.expand(take, __CALLER__)
72
+ kv = Enum.map(take, &{&1, generated_var(&1, Codegen)})
73
+ escape = quote(do: escape)
74
+ encode_map = quote(do: encode_map)
75
+ encode_args = [escape, encode_map]
76
+ kv_iodata = Codegen.build_kv_iodata(kv, encode_args)
77
+
78
+ quote do
79
+ case unquote(map) do
80
+ %{unquote_splicing(kv)} ->
81
+ %Fragment{
82
+ encode: fn {unquote(escape), unquote(encode_map)} ->
83
+ unquote(kv_iodata)
84
+ end
85
+ }
86
+
87
+ other ->
88
+ raise ArgumentError,
89
+ "expected a map with keys: #{unquote(inspect(take))}, got: #{inspect(other)}"
90
+ end
91
+ end
92
+ end
93
+
94
+ # The same as Macro.var/2 except it sets generated: true
95
+ defp generated_var(name, context) do
96
+ {name, [generated: true], context}
97
+ end
98
+ end
@@ -0,0 +1,228 @@
1
+ defmodule Jason do
2
+ @moduledoc """
3
+ A blazing fast JSON parser and generator in pure Elixir.
4
+ """
5
+
6
+ alias Jason.{Encode, Decoder, DecodeError, EncodeError, Formatter}
7
+
8
+ @type escape :: :json | :unicode_safe | :html_safe | :javascript_safe
9
+ @type maps :: :naive | :strict
10
+
11
+ @type encode_opt :: {:escape, escape} | {:maps, maps} | {:pretty, boolean | Formatter.opts()}
12
+
13
+ @type keys :: :atoms | :atoms! | :strings | :copy | (String.t() -> term)
14
+
15
+ @type strings :: :reference | :copy
16
+
17
+ @type decode_opt :: {:keys, keys} | {:strings, strings}
18
+
19
+ @doc """
20
+ Parses a JSON value from `input` iodata.
21
+
22
+ ## Options
23
+
24
+ * `:keys` - controls how keys in objects are decoded. Possible values are:
25
+
26
+ * `:strings` (default) - decodes keys as binary strings,
27
+ * `:atoms` - keys are converted to atoms using `String.to_atom/1`,
28
+ * `:atoms!` - keys are converted to atoms using `String.to_existing_atom/1`,
29
+ * custom decoder - additionally a function accepting a string and returning a key
30
+ is accepted.
31
+
32
+ * `:strings` - controls how strings (including keys) are decoded. Possible values are:
33
+
34
+ * `:reference` (default) - when possible tries to create a sub-binary into the original
35
+ * `:copy` - always copies the strings. This option is especially useful when parts of the
36
+ decoded data will be stored for a long time (in ets or some process) to avoid keeping
37
+ the reference to the original data.
38
+
39
+ ## Decoding keys to atoms
40
+
41
+ The `:atoms` option uses the `String.to_atom/1` call that can create atoms at runtime.
42
+ Since the atoms are not garbage collected, this can pose a DoS attack vector when used
43
+ on user-controlled data.
44
+
45
+ ## Examples
46
+
47
+ iex> Jason.decode("{}")
48
+ {:ok, %{}}
49
+
50
+ iex> Jason.decode("invalid")
51
+ {:error, %Jason.DecodeError{data: "invalid", position: 0, token: nil}}
52
+ """
53
+ @spec decode(iodata, [decode_opt]) :: {:ok, term} | {:error, DecodeError.t()}
54
+ def decode(input, opts \\ []) do
55
+ input = IO.iodata_to_binary(input)
56
+ Decoder.parse(input, format_decode_opts(opts))
57
+ end
58
+
59
+ @doc """
60
+ Parses a JSON value from `input` iodata.
61
+
62
+ Similar to `decode/2` except it will unwrap the error tuple and raise
63
+ in case of errors.
64
+
65
+ ## Examples
66
+
67
+ iex> Jason.decode!("{}")
68
+ %{}
69
+
70
+ iex> Jason.decode!("invalid")
71
+ ** (Jason.DecodeError) unexpected byte at position 0: 0x69 ('i')
72
+
73
+ """
74
+ @spec decode!(iodata, [decode_opt]) :: term | no_return
75
+ def decode!(input, opts \\ []) do
76
+ case decode(input, opts) do
77
+ {:ok, result} -> result
78
+ {:error, error} -> raise error
79
+ end
80
+ end
81
+
82
+ @doc """
83
+ Generates JSON corresponding to `input`.
84
+
85
+ The generation is controlled by the `Jason.Encoder` protocol,
86
+ please refer to the module to read more on how to define the protocol
87
+ for custom data types.
88
+
89
+ ## Options
90
+
91
+ * `:escape` - controls how strings are encoded. Possible values are:
92
+
93
+ * `:json` (default) - the regular JSON escaping as defined by RFC 7159.
94
+ * `:javascript_safe` - additionally escapes the LINE SEPARATOR (U+2028)
95
+ and PARAGRAPH SEPARATOR (U+2029) characters to make the produced JSON
96
+ valid JavaScript.
97
+ * `:html_safe` - similar to `:javascript_safe`, but also escapes the `/`
98
+ character to prevent XSS.
99
+ * `:unicode_safe` - escapes all non-ascii characters.
100
+
101
+ * `:maps` - controls how maps are encoded. Possible values are:
102
+
103
+ * `:strict` - checks the encoded map for duplicate keys and raises
104
+ if they appear. For example `%{:foo => 1, "foo" => 2}` would be
105
+ rejected, since both keys would be encoded to the string `"foo"`.
106
+ * `:naive` (default) - does not perform the check.
107
+
108
+ * `:pretty` - controls pretty printing of the output. Possible values are:
109
+
110
+ * `true` to pretty print with default configuration
111
+ * a keyword of options as specified by `Jason.Formatter.pretty_print/2`.
112
+
113
+ ## Examples
114
+
115
+ iex> Jason.encode(%{a: 1})
116
+ {:ok, ~S|{"a":1}|}
117
+
118
+ iex> Jason.encode("\\xFF")
119
+ {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}}
120
+
121
+ """
122
+ @spec encode(term, [encode_opt]) ::
123
+ {:ok, String.t()} | {:error, EncodeError.t() | Exception.t()}
124
+ def encode(input, opts \\ []) do
125
+ case do_encode(input, format_encode_opts(opts)) do
126
+ {:ok, result} -> {:ok, IO.iodata_to_binary(result)}
127
+ {:error, error} -> {:error, error}
128
+ end
129
+ end
130
+
131
+ @doc """
132
+ Generates JSON corresponding to `input`.
133
+
134
+ Similar to `encode/1` except it will unwrap the error tuple and raise
135
+ in case of errors.
136
+
137
+ ## Examples
138
+
139
+ iex> Jason.encode!(%{a: 1})
140
+ ~S|{"a":1}|
141
+
142
+ iex> Jason.encode!("\\xFF")
143
+ ** (Jason.EncodeError) invalid byte 0xFF in <<255>>
144
+
145
+ """
146
+ @spec encode!(term, [encode_opt]) :: String.t() | no_return
147
+ def encode!(input, opts \\ []) do
148
+ case do_encode(input, format_encode_opts(opts)) do
149
+ {:ok, result} -> IO.iodata_to_binary(result)
150
+ {:error, error} -> raise error
151
+ end
152
+ end
153
+
154
+ @doc """
155
+ Generates JSON corresponding to `input` and returns iodata.
156
+
157
+ This function should be preferred to `encode/2`, if the generated
158
+ JSON will be handed over to one of the IO functions or sent
159
+ over the socket. The Erlang runtime is able to leverage vectorised
160
+ writes and avoid allocating a continuous buffer for the whole
161
+ resulting string, lowering memory use and increasing performance.
162
+
163
+ ## Examples
164
+
165
+ iex> {:ok, iodata} = Jason.encode_to_iodata(%{a: 1})
166
+ iex> IO.iodata_to_binary(iodata)
167
+ ~S|{"a":1}|
168
+
169
+ iex> Jason.encode_to_iodata("\\xFF")
170
+ {:error, %Jason.EncodeError{message: "invalid byte 0xFF in <<255>>"}}
171
+
172
+ """
173
+ @spec encode_to_iodata(term, [encode_opt]) ::
174
+ {:ok, iodata} | {:error, EncodeError.t() | Exception.t()}
175
+ def encode_to_iodata(input, opts \\ []) do
176
+ do_encode(input, format_encode_opts(opts))
177
+ end
178
+
179
+ @doc """
180
+ Generates JSON corresponding to `input` and returns iodata.
181
+
182
+ Similar to `encode_to_iodata/1` except it will unwrap the error tuple
183
+ and raise in case of errors.
184
+
185
+ ## Examples
186
+
187
+ iex> iodata = Jason.encode_to_iodata!(%{a: 1})
188
+ iex> IO.iodata_to_binary(iodata)
189
+ ~S|{"a":1}|
190
+
191
+ iex> Jason.encode_to_iodata!("\\xFF")
192
+ ** (Jason.EncodeError) invalid byte 0xFF in <<255>>
193
+
194
+ """
195
+ @spec encode_to_iodata!(term, [encode_opt]) :: iodata | no_return
196
+ def encode_to_iodata!(input, opts \\ []) do
197
+ case do_encode(input, format_encode_opts(opts)) do
198
+ {:ok, result} -> result
199
+ {:error, error} -> raise error
200
+ end
201
+ end
202
+
203
+ defp do_encode(input, %{pretty: true} = opts) do
204
+ case Encode.encode(input, opts) do
205
+ {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded)}
206
+ other -> other
207
+ end
208
+ end
209
+
210
+ defp do_encode(input, %{pretty: pretty} = opts) when pretty !== false do
211
+ case Encode.encode(input, opts) do
212
+ {:ok, encoded} -> {:ok, Formatter.pretty_print_to_iodata(encoded, pretty)}
213
+ other -> other
214
+ end
215
+ end
216
+
217
+ defp do_encode(input, opts) do
218
+ Encode.encode(input, opts)
219
+ end
220
+
221
+ defp format_encode_opts(opts) do
222
+ Enum.into(opts, %{escape: :json, maps: :naive})
223
+ end
224
+
225
+ defp format_decode_opts(opts) do
226
+ Enum.into(opts, %{keys: :strings, strings: :reference})
227
+ end
228
+ end
@@ -0,0 +1,76 @@
1
+ defmodule Jason.Mixfile do
2
+ use Mix.Project
3
+
4
+ @version "1.2.1"
5
+
6
+ def project() do
7
+ [
8
+ app: :jason,
9
+ version: @version,
10
+ elixir: "~> 1.4",
11
+ start_permanent: Mix.env() == :prod,
12
+ consolidate_protocols: Mix.env() != :test,
13
+ deps: deps(),
14
+ preferred_cli_env: [docs: :docs],
15
+ dialyzer: dialyzer(),
16
+ description: description(),
17
+ package: package(),
18
+ docs: docs()
19
+ ]
20
+ end
21
+
22
+ def application() do
23
+ [
24
+ extra_applications: []
25
+ ]
26
+ end
27
+
28
+ defp deps() do
29
+ [
30
+ {:decimal, "~> 1.0", optional: true},
31
+ {:dialyxir, "~> 1.0", only: [:dev, :test], runtime: false},
32
+ {:ex_doc, "~> 0.18", only: :docs},
33
+ ] ++ maybe_stream_data()
34
+ end
35
+
36
+ defp maybe_stream_data() do
37
+ if Version.match?(System.version(), "~> 1.5") do
38
+ [{:stream_data, "~> 0.4", only: :test}]
39
+ else
40
+ []
41
+ end
42
+ end
43
+
44
+ defp dialyzer() do
45
+ [
46
+ ignore_warnings: "dialyzer.ignore"
47
+ ]
48
+ end
49
+
50
+ defp description() do
51
+ """
52
+ A blazing fast JSON parser and generator in pure Elixir.
53
+ """
54
+ end
55
+
56
+ defp package() do
57
+ [
58
+ maintainers: ["Michał Muskała"],
59
+ licenses: ["Apache-2.0"],
60
+ links: %{"GitHub" => "https://github.com/michalmuskala/jason"}
61
+ ]
62
+ end
63
+
64
+ defp docs() do
65
+ [
66
+ main: "readme",
67
+ name: "Jason",
68
+ source_ref: "v#{@version}",
69
+ canonical: "http://hexdocs.pm/jason",
70
+ source_url: "https://github.com/michalmuskala/jason",
71
+ extras: [
72
+ "README.md"
73
+ ]
74
+ ]
75
+ end
76
+ end
@@ -0,0 +1,92 @@
1
+ defmodule UpdateChecker do
2
+ def run(dependency_name, credentials) do
3
+ set_credentials(credentials)
4
+
5
+ # Update the lockfile in a session that we can time out
6
+ task = Task.async(fn -> do_resolution(dependency_name) end)
7
+ case Task.yield(task, 30000) || Task.shutdown(task) do
8
+ {:ok, {:ok, :resolution_successful}} ->
9
+ # Read the new lock
10
+ {updated_lock, _updated_rest_lock} =
11
+ Map.split(Mix.Dep.Lock.read(), [String.to_atom(dependency_name)])
12
+
13
+ # Get the new dependency version
14
+ version =
15
+ updated_lock
16
+ |> Map.get(String.to_atom(dependency_name))
17
+ |> elem(2)
18
+ {:ok, version}
19
+
20
+ {:ok, {:error, error}} -> {:error, error}
21
+
22
+ nil -> {:error, :dependency_resolution_timed_out}
23
+
24
+ {:exit, reason} -> {:error, reason}
25
+ end
26
+ end
27
+
28
+ defp set_credentials(credentials) do
29
+ credentials
30
+ |> Enum.reduce([], fn cred, acc ->
31
+ if List.last(acc) == nil || List.last(acc)[:token] do
32
+ List.insert_at(acc, -1, %{organization: cred})
33
+ else
34
+ {item, acc} = List.pop_at(acc, -1)
35
+ item = Map.put(item, :token, cred)
36
+ List.insert_at(acc, -1, item)
37
+ end
38
+ end)
39
+ |> Enum.each(fn cred ->
40
+ hexpm = Hex.Repo.get_repo("hexpm")
41
+
42
+ repo = %{
43
+ url: hexpm.url <> "/repos/#{cred.organization}",
44
+ public_key: nil,
45
+ auth_key: cred.token
46
+ }
47
+
48
+ Hex.Config.read()
49
+ |> Hex.Config.read_repos()
50
+ |> Map.put("hexpm:#{cred.organization}", repo)
51
+ |> Hex.Config.update_repos()
52
+ end)
53
+ end
54
+
55
+ defp do_resolution(dependency_name) do
56
+ # Fetch dependencies that needs updating
57
+ {dependency_lock, rest_lock} =
58
+ Map.split(Mix.Dep.Lock.read(), [String.to_atom(dependency_name)])
59
+
60
+ try do
61
+ Mix.Dep.Fetcher.by_name([dependency_name], dependency_lock, rest_lock, [])
62
+ {:ok, :resolution_successful}
63
+ rescue
64
+ error -> {:error, error}
65
+ end
66
+ end
67
+ end
68
+
69
+ [dependency_name | credentials] = System.argv()
70
+
71
+
72
+ case UpdateChecker.run(dependency_name, credentials) do
73
+ {:ok, version} ->
74
+ version = :erlang.term_to_binary({:ok, version})
75
+ IO.write(:stdio, version)
76
+
77
+ {:error, %Hex.Version.InvalidRequirementError{} = error} ->
78
+ result = :erlang.term_to_binary({:error, "Invalid requirement: #{error.requirement}"})
79
+ IO.write(:stdio, result)
80
+
81
+ {:error, %Mix.Error{} = error} ->
82
+ result = :erlang.term_to_binary({:error, "Dependency resolution failed: #{error.message}"})
83
+ IO.write(:stdio, result)
84
+
85
+ {:error, :dependency_resolution_timed_out} ->
86
+ # We do nothing here because Hex is already printing out a message in stdout
87
+ nil
88
+
89
+ {:error, error} ->
90
+ result = :erlang.term_to_binary({:error, "Unknown error in check_update: #{inspect(error)}"})
91
+ IO.write(:stdio, result)
92
+ end