Current section

20 Versions

Jump to

Compare versions

24 files changed
+425 additions
-458 deletions
  @@ -1,5 +1,5 @@
1 1 name = "spectator"
2 - version = "2.0.1"
2 + version = "2.1.0"
3 3 description = "BEAM observer tool"
4 4 licences = ["MIT"]
5 5 repository = { type = "github", user = "JonasGruenwald", repo = "spectator", path = "spectator" }
  @@ -1,6 +1,6 @@
1 1 {<<"name">>, <<"spectator">>}.
2 2 {<<"app">>, <<"spectator">>}.
3 - {<<"version">>, <<"2.0.1">>}.
3 + {<<"version">>, <<"2.1.0">>}.
4 4 {<<"description">>, <<"BEAM observer tool"/utf8>>}.
5 5 {<<"licenses">>, [<<"MIT">>]}.
6 6 {<<"build_tools">>, [<<"gleam">>]}.
  @@ -8,55 +8,55 @@
8 8 {<<"Repository">>, <<"https://github.com/JonasGruenwald/spectator">>}
9 9 ]}.
10 10 {<<"requirements">>, [
11 + {<<"logging">>, [
12 + {<<"app">>, <<"logging">>},
13 + {<<"optional">>, false},
14 + {<<"requirement">>, <<">= 1.3.0 and < 2.0.0">>}
15 + ]},
11 16 {<<"lustre">>, [
12 17 {<<"app">>, <<"lustre">>},
13 18 {<<"optional">>, false},
14 19 {<<"requirement">>, <<">= 5.0.0 and < 6.0.0">>}
15 20 ]},
16 - {<<"pprint">>, [
17 - {<<"app">>, <<"pprint">>},
21 + {<<"simplifile">>, [
22 + {<<"app">>, <<"simplifile">>},
18 23 {<<"optional">>, false},
19 - {<<"requirement">>, <<">= 1.0.3 and < 2.0.0">>}
20 - ]},
21 - {<<"gleam_otp">>, [
22 - {<<"app">>, <<"gleam_otp">>},
23 - {<<"optional">>, false},
24 - {<<"requirement">>, <<">= 1.0.0 and < 2.0.0">>}
25 - ]},
26 - {<<"gleam_http">>, [
27 - {<<"app">>, <<"gleam_http">>},
28 - {<<"optional">>, false},
29 - {<<"requirement">>, <<">= 4.0.0 and < 5.0.0">>}
24 + {<<"requirement">>, <<">= 2.2.0 and < 3.0.0">>}
30 25 ]},
31 26 {<<"mist">>, [
32 27 {<<"app">>, <<"mist">>},
33 28 {<<"optional">>, false},
34 29 {<<"requirement">>, <<">= 5.0.0 and < 6.0.0">>}
35 30 ]},
31 + {<<"gleam_http">>, [
32 + {<<"app">>, <<"gleam_http">>},
33 + {<<"optional">>, false},
34 + {<<"requirement">>, <<">= 4.0.0 and < 5.0.0">>}
35 + ]},
36 + {<<"gleam_otp">>, [
37 + {<<"app">>, <<"gleam_otp">>},
38 + {<<"optional">>, false},
39 + {<<"requirement">>, <<">= 1.0.0 and < 2.0.0">>}
40 + ]},
41 + {<<"pprint">>, [
42 + {<<"app">>, <<"pprint">>},
43 + {<<"optional">>, false},
44 + {<<"requirement">>, <<">= 1.0.3 and < 2.0.0">>}
45 + ]},
36 46 {<<"gleam_json">>, [
37 47 {<<"app">>, <<"gleam_json">>},
38 48 {<<"optional">>, false},
39 49 {<<"requirement">>, <<">= 3.0.0 and < 4.0.0">>}
40 50 ]},
41 - {<<"logging">>, [
42 - {<<"app">>, <<"logging">>},
43 - {<<"optional">>, false},
44 - {<<"requirement">>, <<">= 1.3.0 and < 2.0.0">>}
45 - ]},
46 - {<<"gleam_stdlib">>, [
47 - {<<"app">>, <<"gleam_stdlib">>},
48 - {<<"optional">>, false},
49 - {<<"requirement">>, <<">= 0.65.0 and < 1.0.0">>}
50 - ]},
51 51 {<<"gleam_erlang">>, [
52 52 {<<"app">>, <<"gleam_erlang">>},
53 53 {<<"optional">>, false},
54 54 {<<"requirement">>, <<">= 1.0.0 and < 2.0.0">>}
55 55 ]},
56 - {<<"simplifile">>, [
57 - {<<"app">>, <<"simplifile">>},
56 + {<<"gleam_stdlib">>, [
57 + {<<"app">>, <<"gleam_stdlib">>},
58 58 {<<"optional">>, false},
59 - {<<"requirement">>, <<">= 2.2.0 and < 3.0.0">>}
59 + {<<"requirement">>, <<">= 0.65.0 and < 1.0.0">>}
60 60 ]}
61 61 ]}.
62 62 {<<"files">>, [
  @@ -1,5 +1,5 @@
1 1 {application, spectator, [
2 - {vsn, "2.0.1"},
2 + {vsn, "2.1.0"},
3 3 {applications, [gleam_erlang,
4 4 gleam_http,
5 5 gleam_json,
  @@ -12,13 +12,11 @@
12 12 -define(DOC(Str), -compile([])).
13 13 -endif.
14 14
15 - -type node_connection_error() :: not_distributed_error |
16 - failed_to_set_cookie_error |
17 - failed_to_connect_error.
15 + -type node_connection_error() :: not_distributed_error | failed_to_connect_error.
18 16
19 - -type socket(AFMB) :: {socket,
20 - lustre:runtime(AFMB),
21 - gleam@erlang@process:subject(lustre@runtime@transport:client_message(AFMB))}.
17 + -type socket(AGCV) :: {socket,
18 + lustre:runtime(AGCV),
19 + gleam@erlang@process:subject(lustre@runtime@transport:client_message(AGCV))}.
22 20
23 21 -file("src/spectator.gleam", 142).
24 22 ?DOC(
  @@ -35,7 +33,7 @@ tag(Pid, Name) ->
35 33 " Tag a process given by subject with a name for easier identification in the spectator UI.\n"
36 34 " You must call `start` before calling this function.\n"
37 35 ).
38 - -spec tag_subject(gleam@erlang@process:subject(AFMI), binary()) -> gleam@erlang@process:subject(AFMI).
36 + -spec tag_subject(gleam@erlang@process:subject(AGDC), binary()) -> gleam@erlang@process:subject(AGDC).
39 37 tag_subject(Sub, Name) ->
40 38 Pid@1 = case gleam@erlang@process:subject_owner(Sub) of
41 39 {ok, Pid} -> Pid;
  @@ -61,9 +59,9 @@ tag_subject(Sub, Name) ->
61 59 " You must call `start` before calling this function.\n"
62 60 ).
63 61 -spec tag_result(
64 - {ok, gleam@erlang@process:subject(AFML)} | {error, AFMN},
62 + {ok, gleam@erlang@process:subject(AGDF)} | {error, AGDH},
65 63 binary()
66 - ) -> {ok, gleam@erlang@process:subject(AFML)} | {error, AFMN}.
64 + ) -> {ok, gleam@erlang@process:subject(AGDF)} | {error, AGDH}.
67 65 tag_result(Result, Name) ->
68 66 case Result of
69 67 {ok, Sub} ->
  @@ -73,7 +71,7 @@ tag_result(Result, Name) ->
73 71 Other
74 72 end.
75 73
76 - -file("src/spectator.gleam", 176).
74 + -file("src/spectator.gleam", 175).
77 75 -spec validate_node_connection(list({binary(), binary()})) -> {ok, binary()} |
78 76 {error, node_connection_error()}.
79 77 validate_node_connection(Params) ->
  @@ -97,41 +95,19 @@ validate_node_connection(Params) ->
97 95 {error, not_distributed_error},
98 96 fun() ->
99 97 Node_atom = erlang:binary_to_atom(Node),
100 - Cookie_validation_passed = case spectator@internal@common:get_param(
101 - Params,
102 - <<"cookie"/utf8>>
103 - ) of
104 - {error, _} ->
105 - true;
106 -
107 - {ok, Cookie} ->
108 - Cookie_atom = erlang:binary_to_atom(Cookie),
109 - gleam@result:unwrap(
110 - spectator_ffi:set_cookie(Node_atom, Cookie_atom),
111 - false
112 - )
113 - end,
114 98 gleam@bool:guard(
115 - not Cookie_validation_passed,
116 - {error, failed_to_set_cookie_error},
117 - fun() ->
118 - gleam@bool:guard(
119 - not gleam@result:unwrap(
120 - spectator_ffi:hidden_connect_node(Node_atom),
121 - false
122 - ),
123 - {error, failed_to_connect_error},
124 - fun() ->
125 - {ok, erlang:atom_to_binary(Node_atom)}
126 - end
127 - )
128 - end
99 + not gleam@result:unwrap(
100 + spectator_ffi:hidden_connect_node(Node_atom),
101 + false
102 + ),
103 + {error, failed_to_connect_error},
104 + fun() -> {ok, erlang:atom_to_binary(Node_atom)} end
129 105 )
130 106 end
131 107 )
132 108 end.
133 109
134 - -file("src/spectator.gleam", 219).
110 + -file("src/spectator.gleam", 205).
135 111 -spec serve_string(binary()) -> gleam@http@response:response(mist:response_data()).
136 112 serve_string(Content) ->
137 113 _pipe = gleam@http@response:new(200),
  @@ -140,7 +116,7 @@ serve_string(Content) ->
140 116 {bytes, gleam_stdlib:wrap_list(Content)}
141 117 ).
142 118
143 - -file("src/spectator.gleam", 224).
119 + -file("src/spectator.gleam", 210).
144 120 -spec connect_widget_script() -> lustre@vdom@vnode:element(any()).
145 121 connect_widget_script() ->
146 122 lustre@element@html:script(
  @@ -148,7 +124,7 @@ connect_widget_script() ->
148 124 <<""/utf8>>
149 125 ).
150 126
151 - -file("src/spectator.gleam", 228).
127 + -file("src/spectator.gleam", 214).
152 128 -spec render_server_component(binary(), binary(), list({binary(), binary()})) -> gleam@http@response:response(mist:response_data()).
153 129 render_server_component(Title, Path, Params) ->
154 130 Res = gleam@http@response:new(200),
  @@ -263,11 +239,8 @@ render_server_component(Title, Path, Params) ->
263 239 not_distributed_error ->
264 240 <<"Node is not distributed, cannot connect to other nodes. Please start the spectator instance in distributed mode by setting a node name."/utf8>>;
265 241
266 - failed_to_set_cookie_error ->
267 - <<"Failed to set cookie, could not apply the cookie to the node"/utf8>>;
268 -
269 242 failed_to_connect_error ->
270 - <<"Failed to connect to node, please check the node name and cookie"/utf8>>
243 + <<"Failed to connect to node, please check the node name and ensure the target node has the same Erlang cookie set as spectator"/utf8>>
271 244 end
272 245 )]
273 246 ),
  @@ -297,10 +270,10 @@ render_server_component(Title, Path, Params) ->
297 270 end
298 271 ).
299 272
300 - -file("src/spectator.gleam", 329).
273 + -file("src/spectator.gleam", 313).
301 274 -spec connect_server_component(
302 275 gleam@http@request:request(mist@internal@http:connection()),
303 - fun(() -> lustre:app(list({binary(), binary()}), any(), any())),
276 + fun(() -> lustre@runtime@app:app(list({binary(), binary()}), any(), any())),
304 277 list({binary(), binary()})
305 278 ) -> gleam@http@response:response(mist:response_data()).
306 279 connect_server_component(Req, Lustre_application, Params) ->
  @@ -315,12 +288,12 @@ connect_server_component(Req, Lustre_application, Params) ->
315 288 file => <<?FILEPATH/utf8>>,
316 289 module => <<"spectator"/utf8>>,
317 290 function => <<"connect_server_component"/utf8>>,
318 - line => 338,
291 + line => 322,
319 292 value => _assert_fail,
320 - start => 10077,
321 - 'end' => 10146,
322 - pattern_start => 10088,
323 - pattern_end => 10101})
293 + start => 9576,
294 + 'end' => 9645,
295 + pattern_start => 9587,
296 + pattern_end => 9600})
324 297 end,
325 298 tag_subject(
326 299 lustre@server_component:subject(Component@1),
  @@ -371,12 +344,12 @@ connect_server_component(Req, Lustre_application, Params) ->
371 344 file => <<?FILEPATH/utf8>>,
372 345 module => <<"spectator"/utf8>>,
373 346 function => <<"connect_server_component"/utf8>>,
374 - line => 370,
347 + line => 354,
375 348 value => _assert_fail@1,
376 - start => 11015,
377 - 'end' => 11175,
378 - pattern_start => 11026,
379 - pattern_end => 11031})
349 + start => 10514,
350 + 'end' => 10674,
351 + pattern_start => 10525,
352 + pattern_end => 10530})
380 353 end,
381 354 mist:continue(State);
382 355
  @@ -492,7 +465,7 @@ mist_supervised(Host, Port) ->
492 465
493 466 [<<"connect-widget.js"/utf8>>] ->
494 467 serve_string(
495 - <<"console.log(\"Connect widget loaded!\");\n\nconst dialogHtml = `\n <dialog class=\"connect-widget-dialog\">\n <div class=\"connect-widget-content\">\n <h2>Inspect BEAM Node</h2>\n <div class=\"connect-widget-form\">\n <label for=\"node-name\">Node Name</label>\n <input required=\"true\" type=\"text\" id=\"node-name\" name=\"node-address\" placeholder=\"wibble@127.0.0.1\" />\n <label for=\"node-cookie\">Cookie</label>\n <input type=\"text\" id=\"node-cookie\" name=\"node-cookie\" placeholder=\"wobble\" />\n <label for=\"refresh-interval\">Refresh Interval</label>\n <select id=\"refresh-interval\" name=\"refresh-interval\">\n <option value=\"\">Default</option>\n <option value=\"500\">500 milliseconds</option>\n <option value=\"1000\">1 second</option>\n <option value=\"2000\">2 seconds</option>\n <option value=\"5000\">5 seconds</option>\n <option value=\"10000\">10 seconds</option>\n <option value=\"30000\">30 seconds</option>\n <option value=\"60000\">1 minute</option>\n </select>\n </div>\n <div class=\"connect-widget-actions\">\n <button title=\"Close this dialog\" id=\"connect-cancel\">Cancel</button>\n <button title=\"Inspect the node that spectator itself is running on\" id=\"connect-resetl\">Inspect Spectator Node</button>\n <button title=\"Connect to specified node\" id=\"connect-submit\">Connect</button>\n </div>\n </div>\n </dialog>\n `;\n\nconst template = document.createElement(\"template\");\ntemplate.innerHTML = dialogHtml.trim();\nconst dialogElement = template.content.firstChild;\ndocument.body.appendChild(dialogElement);\n\nconst cancelButton = dialogElement.querySelector(\"#connect-cancel\");\nconst resetButton = dialogElement.querySelector(\"#connect-resetl\");\nconst submitButton = dialogElement.querySelector(\"#connect-submit\");\nconst nodeNameInput = dialogElement.querySelector(\"#node-name\");\nconst nodeCookieInput = dialogElement.querySelector(\"#node-cookie\");\nconst refreshIntervalSelect = dialogElement.querySelector(\"#refresh-interval\");\n\n// Prefill with current params\nconst urlParams = new URLSearchParams(window.location.search);\nconst currentNode = urlParams.get(\"node\") || \"\";\nconst currentCookie = urlParams.get(\"cookie\") || \"\";\nconst currentInterval = urlParams.get(\"refresh\") || \"\";\nnodeNameInput.value = currentNode;\nnodeCookieInput.value = currentCookie;\nrefreshIntervalSelect.value = currentInterval;\n\nsubmitButton.addEventListener(\"click\", () => {\n const nodeName = nodeNameInput.value;\n const nodeCookie = nodeCookieInput.value;\n const refreshInterval = refreshIntervalSelect.value;\n\n if (nodeNameInput.reportValidity()) {\n const newParams = new URLSearchParams();\n newParams.set(\"node\", nodeName);\n if (nodeCookie) {\n newParams.set(\"cookie\", nodeCookie);\n }\n if (refreshInterval !== \"\") {\n newParams.set(\"refresh\", refreshInterval);\n }\n window.location.search = newParams.toString();\n }\n});\n\ncancelButton.addEventListener(\"click\", () => {\n dialogElement.close();\n});\n\nresetButton.addEventListener(\"click\", () => {\n window.location.search = \"\";\n dialogElement.close();\n});\n\ndocument.querySelectorAll(\".change-target-button\").forEach((button) => {\n button.style.display = \"inline-block\";\n button.addEventListener(\"click\", dialogElement.showModal.bind(dialogElement));\n});"/utf8>>
468 + <<"const dialogHtml = `\n <dialog class=\"connect-widget-dialog\">\n <div class=\"connect-widget-content\">\n <h2>Inspect BEAM Node</h2>\n <div class=\"connect-widget-form\">\n <label for=\"node-name\">Node Name</label>\n <input required=\"true\" type=\"text\" id=\"node-name\" name=\"node-address\" placeholder=\"wibble@127.0.0.1\" />\n <label for=\"refresh-interval\">Refresh Interval</label>\n <select id=\"refresh-interval\" name=\"refresh-interval\">\n <option value=\"\">Default</option>\n <option value=\"500\">500 milliseconds</option>\n <option value=\"1000\">1 second</option>\n <option value=\"2000\">2 seconds</option>\n <option value=\"5000\">5 seconds</option>\n <option value=\"10000\">10 seconds</option>\n <option value=\"30000\">30 seconds</option>\n <option value=\"60000\">1 minute</option>\n </select>\n </div>\n <div class=\"connect-widget-actions\">\n <button title=\"Close this dialog\" id=\"connect-cancel\">Cancel</button>\n <button title=\"Inspect the node that spectator itself is running on\" id=\"connect-resetl\">Inspect Spectator Node</button>\n <button title=\"Connect to specified node\" id=\"connect-submit\">Connect</button>\n </div>\n </div>\n </dialog>\n `;\n\nconst template = document.createElement(\"template\");\ntemplate.innerHTML = dialogHtml.trim();\nconst dialogElement = template.content.firstChild;\ndocument.body.appendChild(dialogElement);\n\nconst cancelButton = dialogElement.querySelector(\"#connect-cancel\");\nconst resetButton = dialogElement.querySelector(\"#connect-resetl\");\nconst submitButton = dialogElement.querySelector(\"#connect-submit\");\nconst nodeNameInput = dialogElement.querySelector(\"#node-name\");\nconst refreshIntervalSelect = dialogElement.querySelector(\"#refresh-interval\");\n\n// Prefill with current params\nconst urlParams = new URLSearchParams(window.location.search);\nconst currentNode = urlParams.get(\"node\") || \"\";\nconst currentInterval = urlParams.get(\"refresh\") || \"\";\nnodeNameInput.value = currentNode;\nrefreshIntervalSelect.value = currentInterval;\n\nsubmitButton.addEventListener(\"click\", () => {\n const nodeName = nodeNameInput.value;\n const refreshInterval = refreshIntervalSelect.value;\n\n if (nodeNameInput.reportValidity()) {\n const newParams = new URLSearchParams();\n newParams.set(\"node\", nodeName);\n if (refreshInterval !== \"\") {\n newParams.set(\"refresh\", refreshInterval);\n }\n window.location.search = newParams.toString();\n }\n});\n\ncancelButton.addEventListener(\"click\", () => {\n dialogElement.close();\n});\n\nresetButton.addEventListener(\"click\", () => {\n window.location.search = \"\";\n dialogElement.close();\n});\n\ndocument.querySelectorAll(\".change-target-button\").forEach((button) => {\n button.style.display = \"inline-block\";\n button.addEventListener(\"click\", dialogElement.showModal.bind(dialogElement));\n});"/utf8>>
496 469 );
497 470
498 471 [] ->
  @@ -169,7 +169,6 @@ pub fn tag_result(
169 169
170 170 type NodeConnectionError {
171 171 NotDistributedError
172 - FailedToSetCookieError
173 172 FailedToConnectError
174 173 }
175 174
  @@ -192,19 +191,6 @@ fn validate_node_connection(
192 191 )
193 192
194 193 let node_atom = atom.create(node)
195 - let cookie_validation_passed = case common.get_param(params, "cookie") {
196 - // No cookie, validation passes
197 - Error(_) -> True
198 - Ok(cookie) -> {
199 - let cookie_atom = atom.create(cookie)
200 - result.unwrap(api.set_cookie(node_atom, cookie_atom), False)
201 - }
202 - }
203 -
204 - use <- bool.guard(
205 - !cookie_validation_passed,
206 - Error(FailedToSetCookieError),
207 - )
208 194
209 195 use <- bool.guard(
210 196 !result.unwrap(api.hidden_connect_node(node_atom), False),
  @@ -291,10 +277,8 @@ fn render_server_component(
291 277 html.text(case connection_error {
292 278 NotDistributedError ->
293 279 "Node is not distributed, cannot connect to other nodes. Please start the spectator instance in distributed mode by setting a node name."
294 - FailedToSetCookieError ->
295 - "Failed to set cookie, could not apply the cookie to the node"
296 280 FailedToConnectError ->
297 - "Failed to connect to node, please check the node name and cookie"
281 + "Failed to connect to node, please check the node name and ensure the target node has the same Erlang cookie set as spectator"
298 282 }),
299 283 ]),
300 284 html.div([], [
Loading more files…