dommy-rack 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/dommy/rack/http_exchange.rb +26 -2
- data/lib/dommy/rack/session.rb +64 -1
- data/lib/dommy/rack/trace/formatter.rb +1 -0
- data/lib/dommy/rack/trace.rb +131 -11
- data/lib/dommy/rack/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 242882a8db41a841906a2292592dcf047941a3578e043cf9dc872d03b6649c77
|
|
4
|
+
data.tar.gz: 62adc178e7d8bffd1e4d923a13f75d130de05d12b953c43377c0d7dc48ee72c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41a58a2614a4608d75957cd777bf9e30d657cba682a90509c473627b50c5f88ad929c438afa10a5b54f641f26b7b4a8061280b567b84325ba23445543c895a32
|
|
7
|
+
data.tar.gz: 9e68474ae55c4051d0844da0ffea2c2df9e043a5e31cf86a6d52b805df78dd410110f3aab8bd006bc6690fc483474217154f1be3af037eab87c4e06c7e31dbf9
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.0 — 2026-09-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Session#dialog_handler=` — answer the page's `alert` / `confirm` / `prompt`. The handler stays installed across navigations, so it also answers dialogs on pages loaded later.
|
|
7
|
+
- Traced requests now carry spans from inside the app (controller, SQL, render, and the jobs and mails a request triggers), flushed with the request they belong to. See dommy-rails for the Rails-side instrumentation.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- A request whose app raises still closes its trace bracket, so the trace ends with the failure instead of an open request.
|
|
11
|
+
- A subresource fetch made while a request is in flight keeps its own spans rather than attaching them to the outer request.
|
|
12
|
+
- SQL bind values, when enabled, are masked through the trace's own sensitive-key filter — the same one that masks form params.
|
|
13
|
+
- A snapshot's content is stored before its artifact event is written, so a trace read back straight away is complete.
|
|
14
|
+
|
|
3
15
|
## 0.10.0 — 2026-07-13
|
|
4
16
|
|
|
5
17
|
### Added
|
|
@@ -17,6 +17,13 @@ module Dommy
|
|
|
17
17
|
# it as `last_request` and fires request listeners; an async path routes the
|
|
18
18
|
# same notification through the scheduler inbox).
|
|
19
19
|
# * `on_response` — observe the Response (response listeners).
|
|
20
|
+
# * `on_abort` — observe an env whose `@app.call` raised (no Response).
|
|
21
|
+
# * `on_app_start` / `on_app_finish` — bracket the app call ITSELF. Unlike
|
|
22
|
+
# the observation hooks above (which an async path routes through the
|
|
23
|
+
# scheduler inbox, i.e. LATER and on the page thread), these run inline,
|
|
24
|
+
# on whatever thread calls the app, so per-request state a hook installs
|
|
25
|
+
# for the app to find — the Trace's thread-local — is live exactly while
|
|
26
|
+
# the app runs. Anything they need to hand back travels on the env.
|
|
20
27
|
#
|
|
21
28
|
# Reload bookkeeping (`last_request_args`) and history/document application are
|
|
22
29
|
# NOT here: they belong to the page thread and stay in Session / Navigation.
|
|
@@ -24,13 +31,17 @@ module Dommy
|
|
|
24
31
|
# @param headers [HeaderStore, Hash] anything responding to `merge(overrides)
|
|
25
32
|
# -> Hash`; the Session passes its live HeaderStore for the page path, a
|
|
26
33
|
# plain snapshot Hash for a worker path.
|
|
27
|
-
def initialize(app:, config:, cookie_jar:, headers:, on_request: nil, on_response: nil
|
|
34
|
+
def initialize(app:, config:, cookie_jar:, headers:, on_request: nil, on_response: nil, on_abort: nil,
|
|
35
|
+
on_app_start: nil, on_app_finish: nil)
|
|
28
36
|
@app = app
|
|
29
37
|
@config = config
|
|
30
38
|
@cookie_jar = cookie_jar
|
|
31
39
|
@headers = headers
|
|
32
40
|
@on_request = on_request
|
|
33
41
|
@on_response = on_response
|
|
42
|
+
@on_abort = on_abort
|
|
43
|
+
@on_app_start = on_app_start
|
|
44
|
+
@on_app_finish = on_app_finish
|
|
34
45
|
end
|
|
35
46
|
|
|
36
47
|
# Perform one request and return its Response. `headers` are per-request
|
|
@@ -45,7 +56,20 @@ module Dommy
|
|
|
45
56
|
cookie_string: @cookie_jar.cookies_for(absolute_url)
|
|
46
57
|
)
|
|
47
58
|
@on_request&.call(env)
|
|
48
|
-
|
|
59
|
+
# on_app_start opened a request bracket (the Trace exposes itself
|
|
60
|
+
# thread-locally inside it); an app exception must still close it, or
|
|
61
|
+
# per-request state leaks past the failed request. Close it BEFORE
|
|
62
|
+
# on_abort, which reads what the bracket left on the env. The exception
|
|
63
|
+
# itself propagates unchanged.
|
|
64
|
+
@on_app_start&.call(env)
|
|
65
|
+
begin
|
|
66
|
+
status, response_headers, response_body = @app.call(env)
|
|
67
|
+
rescue ::Exception # rubocop:disable Lint/RescueException -- close the bracket for ANY abort
|
|
68
|
+
@on_app_finish&.call(env)
|
|
69
|
+
@on_abort&.call(env)
|
|
70
|
+
raise
|
|
71
|
+
end
|
|
72
|
+
@on_app_finish&.call(env)
|
|
49
73
|
response = Response.new(status, response_headers, response_body, url: absolute_url)
|
|
50
74
|
response.set_cookie_strings.each do |sc|
|
|
51
75
|
@cookie_jar.store_from_header(sc, absolute_url)
|
data/lib/dommy/rack/session.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Dommy
|
|
|
49
49
|
keyword_init: true
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
attr_reader :last_request, :last_response, :history, :trace
|
|
52
|
+
attr_reader :last_request, :last_response, :history, :trace, :dialog_handler
|
|
53
53
|
|
|
54
54
|
# A factory `->(session) { js_runtime_host }` that binds a JS runtime to a
|
|
55
55
|
# session for `javascript: true`. dommy-js-quickjs installs one when its
|
|
@@ -129,6 +129,9 @@ module Dommy
|
|
|
129
129
|
@scope_stack = []
|
|
130
130
|
@request_listeners = []
|
|
131
131
|
@response_listeners = []
|
|
132
|
+
@abort_listeners = []
|
|
133
|
+
@app_start_listeners = []
|
|
134
|
+
@app_finish_listeners = []
|
|
132
135
|
@document_loaded_listeners = []
|
|
133
136
|
@subresource_allowlist = [] # hosts allowed for cross-origin <script>/fetch/XHR
|
|
134
137
|
@blocked_subresource_hosts = [] # cross-origin hosts declined since the last reset (awaiting a decision)
|
|
@@ -145,6 +148,14 @@ module Dommy
|
|
|
145
148
|
# verbs drive JS handlers.
|
|
146
149
|
def javascript? = !@js_runtime.nil?
|
|
147
150
|
|
|
151
|
+
# Supply native dialog answers to the current page and to every page
|
|
152
|
+
# subsequently installed while the handler is active. The Capybara driver
|
|
153
|
+
# uses this around accept_confirm / dismiss_confirm (and alert/prompt).
|
|
154
|
+
def dialog_handler=(handler)
|
|
155
|
+
@dialog_handler = handler
|
|
156
|
+
@current_window.dialog_handler = handler if @current_window
|
|
157
|
+
end
|
|
158
|
+
|
|
148
159
|
# The bound JS runtime (a SessionRuntime), or nil when JS is disabled.
|
|
149
160
|
# Exposed for the Trace to subscribe to the runtime's console / js_error /
|
|
150
161
|
# script seams; not part of the everyday browsing API.
|
|
@@ -511,6 +522,35 @@ module Dommy
|
|
|
511
522
|
self
|
|
512
523
|
end
|
|
513
524
|
|
|
525
|
+
# Register a callback invoked with the Rack env when a request ABORTS
|
|
526
|
+
# (the app raised, so no Response exists). Every on_request has a
|
|
527
|
+
# matching on_response OR on_abort — per-request state can bracket on
|
|
528
|
+
# the pair.
|
|
529
|
+
def on_abort(&block)
|
|
530
|
+
@abort_listeners << block
|
|
531
|
+
self
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
# Register callbacks that BRACKET the Rack app call itself, with the env.
|
|
535
|
+
# Unlike on_request / on_response (which the async-network path routes
|
|
536
|
+
# through the scheduler inbox, so they run after the fact on the page
|
|
537
|
+
# thread), these fire inline on whatever thread calls the app — which is
|
|
538
|
+
# the only place a hook can install per-request state the app itself will
|
|
539
|
+
# find, such as the Trace's thread-local. State they need to hand to the
|
|
540
|
+
# later on_response travels on the env, not on the listener's owner.
|
|
541
|
+
def on_app_start(&block)
|
|
542
|
+
@app_start_listeners << block
|
|
543
|
+
self
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# The matching close. Runs for a successful AND a raising app call, and
|
|
547
|
+
# before on_abort so the aborted request's observer sees what the bracket
|
|
548
|
+
# left on the env.
|
|
549
|
+
def on_app_finish(&block)
|
|
550
|
+
@app_finish_listeners << block
|
|
551
|
+
self
|
|
552
|
+
end
|
|
553
|
+
|
|
514
554
|
# Register a callback invoked with the new Window each time a navigation
|
|
515
555
|
# installs an HTML document (visit, redirects, link clicks, form submits,
|
|
516
556
|
# back/forward, reload, meta refresh). This is the page-load lifecycle
|
|
@@ -680,6 +720,15 @@ module Dommy
|
|
|
680
720
|
},
|
|
681
721
|
on_response: lambda { |response|
|
|
682
722
|
@response_listeners.each { |cb| cb.call(response) }
|
|
723
|
+
},
|
|
724
|
+
on_abort: lambda { |env|
|
|
725
|
+
@abort_listeners.each { |cb| cb.call(env) }
|
|
726
|
+
},
|
|
727
|
+
on_app_start: lambda { |env|
|
|
728
|
+
@app_start_listeners.each { |cb| cb.call(env) }
|
|
729
|
+
},
|
|
730
|
+
on_app_finish: lambda { |env|
|
|
731
|
+
@app_finish_listeners.each { |cb| cb.call(env) }
|
|
683
732
|
}
|
|
684
733
|
)
|
|
685
734
|
end
|
|
@@ -728,6 +777,7 @@ module Dommy
|
|
|
728
777
|
@current_url = final_url
|
|
729
778
|
if response.html?
|
|
730
779
|
@current_window = response.window
|
|
780
|
+
@current_window.dialog_handler = @dialog_handler
|
|
731
781
|
# Set the geometry mode before scripts boot so the very first
|
|
732
782
|
# getBoundingClientRect a framework calls already sees it.
|
|
733
783
|
@current_window.approximate_layout = @approximate_layout if @approximate_layout
|
|
@@ -848,6 +898,19 @@ module Dommy
|
|
|
848
898
|
},
|
|
849
899
|
on_response: sched && lambda { |response|
|
|
850
900
|
sched.post_external { @response_listeners.each { |cb| cb.call(response) } }
|
|
901
|
+
},
|
|
902
|
+
on_abort: sched && lambda { |env|
|
|
903
|
+
sched.post_external { @abort_listeners.each { |cb| cb.call(env) } }
|
|
904
|
+
},
|
|
905
|
+
# The app-call bracket is NOT posted to the inbox: it has to be open
|
|
906
|
+
# while the worker thread is inside the app, which a page-thread
|
|
907
|
+
# callback scheduled for later can never be. Its listeners touch only
|
|
908
|
+
# thread-locals and the env, so running them off-thread is safe.
|
|
909
|
+
on_app_start: lambda { |env|
|
|
910
|
+
@app_start_listeners.each { |cb| cb.call(env) }
|
|
911
|
+
},
|
|
912
|
+
on_app_finish: lambda { |env|
|
|
913
|
+
@app_finish_listeners.each { |cb| cb.call(env) }
|
|
851
914
|
}
|
|
852
915
|
)
|
|
853
916
|
end
|
|
@@ -35,6 +35,7 @@ module Dommy
|
|
|
35
35
|
when :console then "CONSOLE [#{data[:level]}] #{data[:text]}"
|
|
36
36
|
when :js_error then "JS_ERROR #{data[:message]}"
|
|
37
37
|
when :dom then dom(data)
|
|
38
|
+
when :span then "SPAN [#{data[:kind]}] #{data[:label]} (#{data[:duration_ms]}ms)"
|
|
38
39
|
else "#{event.type.to_s.upcase} #{compact(data)}"
|
|
39
40
|
end
|
|
40
41
|
end
|
data/lib/dommy/rack/trace.rb
CHANGED
|
@@ -36,6 +36,16 @@ module Dommy
|
|
|
36
36
|
LEVELS = %i[off actions verbose].freeze
|
|
37
37
|
REALM_TYPES = %i[script console js_error].freeze
|
|
38
38
|
|
|
39
|
+
# The app-call bracket's channels. The thread-locals are how in-app
|
|
40
|
+
# instrumentation (dommy-rails' TraceInstrumentation) finds the trace
|
|
41
|
+
# whose request is running on THIS thread; the env keys carry that
|
|
42
|
+
# request's spans — and the bracket's saved outer values — back to the
|
|
43
|
+
# response listener, which may run later and on another thread.
|
|
44
|
+
TRACE_THREAD_KEY = :__dommy_active_trace__
|
|
45
|
+
SPANS_THREAD_KEY = :__dommy_trace_spans__
|
|
46
|
+
SPANS_ENV_KEY = "dommy.trace.spans"
|
|
47
|
+
BRACKET_ENV_KEY = "dommy.trace.outer_bracket"
|
|
48
|
+
|
|
39
49
|
# Build a Trace, wire it to the session's (and its runtime's) seams, and
|
|
40
50
|
# return it. A `:off` trace wires nothing.
|
|
41
51
|
def self.attach(session, level: :verbose, dom: false, filter: ParamFilter::DEFAULT, snapshots: false)
|
|
@@ -68,6 +78,11 @@ module Dommy
|
|
|
68
78
|
|
|
69
79
|
@session.on_request { |env| __internal_on_request(env) }
|
|
70
80
|
@session.on_response { |response| __internal_on_response(response) }
|
|
81
|
+
@session.on_abort { |env| __internal_on_abort(env) } if @session.respond_to?(:on_abort)
|
|
82
|
+
if @session.respond_to?(:on_app_start)
|
|
83
|
+
@session.on_app_start { |env| __internal_open_app_bracket(env) }
|
|
84
|
+
@session.on_app_finish { |env| __internal_close_app_bracket(env) }
|
|
85
|
+
end
|
|
71
86
|
|
|
72
87
|
runtime = @session.respond_to?(:__internal_js_runtime) ? @session.__internal_js_runtime : nil
|
|
73
88
|
if runtime
|
|
@@ -90,8 +105,9 @@ module Dommy
|
|
|
90
105
|
|
|
91
106
|
@seq += 1
|
|
92
107
|
@action_seq = @seq
|
|
93
|
-
|
|
108
|
+
event = Event.new(seq: @seq, t: now_ms, wall_ms: monotonic_ms, type: :action, name: verb,
|
|
94
109
|
action_seq: nil, data: {verb: verb, label: label, source: __internal_caller_source})
|
|
110
|
+
@events << event
|
|
95
111
|
nil
|
|
96
112
|
end
|
|
97
113
|
|
|
@@ -117,6 +133,10 @@ module Dommy
|
|
|
117
133
|
nil
|
|
118
134
|
end
|
|
119
135
|
|
|
136
|
+
# The configured sensitive-key filter — instrumentation layers mask
|
|
137
|
+
# values they record (SQL binds) with the SAME rules as form params.
|
|
138
|
+
def __internal_param_filter__ = @param_filter
|
|
139
|
+
|
|
120
140
|
# --- Queries (read-only views over the event stream) ---
|
|
121
141
|
|
|
122
142
|
def http = events_of(:http)
|
|
@@ -139,7 +159,7 @@ module Dommy
|
|
|
139
159
|
def to_s = to_text
|
|
140
160
|
|
|
141
161
|
def to_ndjson(status: "ok", wall_time: nil, metadata: nil)
|
|
142
|
-
inline = @artifacts
|
|
162
|
+
inline = InlineArtifacts.new(@artifacts)
|
|
143
163
|
Ndjson.new(@events, level: @level, wall_time: wall_time, metadata: metadata,
|
|
144
164
|
artifacts: inline, end_wall_ms: monotonic_ms).document(status: status)
|
|
145
165
|
end
|
|
@@ -166,38 +186,109 @@ module Dommy
|
|
|
166
186
|
dir
|
|
167
187
|
end
|
|
168
188
|
|
|
189
|
+
# Lazily shapes an artifact's inline emission fields ({content:,
|
|
190
|
+
# encoding:}) by seq — the single owner of the inline wire shape that
|
|
191
|
+
# to_ndjson hands the serializer.
|
|
192
|
+
class InlineArtifacts
|
|
193
|
+
def initialize(artifacts)
|
|
194
|
+
@artifacts = artifacts
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def [](seq)
|
|
198
|
+
content = @artifacts[seq]
|
|
199
|
+
content ? {content: content, encoding: "utf-8"} : nil
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Instrumentation entry point (reached via the thread-local): buffer an
|
|
204
|
+
# inside-the-request span (controller/db/render — anything an
|
|
205
|
+
# instrumentation layer measured). Emitted as completed span events,
|
|
206
|
+
# parented to the enclosing :http event, when the response arrives —
|
|
207
|
+
# spans finish before the response exists, so they can't reference it
|
|
208
|
+
# any earlier. Public; the surrounding request seams stay private.
|
|
209
|
+
#
|
|
210
|
+
# The buffer is the one the app-call bracket published thread-locally,
|
|
211
|
+
# not an ivar: a subresource fetch (JS `fetch`/XHR) runs the app on a
|
|
212
|
+
# network worker thread, so a per-Trace buffer would be a race AND would
|
|
213
|
+
# mis-attribute those spans to whatever the page thread was doing.
|
|
214
|
+
def __internal_record_span__(kind:, label:, duration_ms:, data: nil)
|
|
215
|
+
buffer = Thread.current[SPANS_THREAD_KEY]
|
|
216
|
+
return if @level == :off || buffer.nil?
|
|
217
|
+
|
|
218
|
+
buffer << {kind: kind.to_s, label: label.to_s,
|
|
219
|
+
duration_ms: duration_ms.to_f.round(2), data: data}
|
|
220
|
+
nil
|
|
221
|
+
end
|
|
222
|
+
|
|
169
223
|
private
|
|
170
224
|
|
|
171
225
|
# Emit one event, gated by the recording level, and return it (or nil if
|
|
172
226
|
# gated out). `seq` is the canonical order; `t` is the virtual clock if a
|
|
173
227
|
# window exists.
|
|
174
|
-
|
|
228
|
+
# `artifact:` carries the event's captured content (a DOM snapshot),
|
|
229
|
+
# stored under the event's seq alongside it.
|
|
230
|
+
def __internal_emit(type, data, name: nil, window: nil, artifact: nil)
|
|
175
231
|
return if @level == :off
|
|
176
232
|
return if REALM_TYPES.include?(type) && @level != :verbose
|
|
177
233
|
|
|
178
234
|
@seq += 1
|
|
179
235
|
event = Event.new(seq: @seq, t: window&.scheduler&.now_ms, wall_ms: monotonic_ms, type: type,
|
|
180
236
|
name: name, action_seq: @action_seq, data: data)
|
|
237
|
+
@artifacts[event.seq] = artifact if artifact
|
|
181
238
|
@events << event
|
|
182
239
|
event
|
|
183
240
|
end
|
|
184
241
|
|
|
185
|
-
# on_request fires before its on_response (
|
|
186
|
-
#
|
|
187
|
-
# response arrives with its status.
|
|
242
|
+
# on_request fires before its on_response (per redirect hop, in order),
|
|
243
|
+
# so stash the method/path here and emit the `:http` event when the
|
|
244
|
+
# response arrives with its status. The env is stashed too: the app-call
|
|
245
|
+
# bracket leaves this request's spans on it, and on the async-network
|
|
246
|
+
# path this listener runs after the app already finished.
|
|
188
247
|
def __internal_on_request(env)
|
|
189
248
|
@pending_request = {
|
|
190
249
|
method: env["REQUEST_METHOD"],
|
|
191
250
|
path: env["PATH_INFO"],
|
|
192
|
-
query: presence(env["QUERY_STRING"])
|
|
251
|
+
query: presence(env["QUERY_STRING"]),
|
|
252
|
+
env: env
|
|
193
253
|
}
|
|
194
254
|
nil
|
|
195
255
|
end
|
|
196
256
|
|
|
257
|
+
# --- the app-call bracket (runs on the thread that calls the app) ---
|
|
258
|
+
|
|
259
|
+
# Expose this trace to in-app instrumentation (dommy-rails subscribes to
|
|
260
|
+
# ActiveSupport::Notifications and records spans through the thread-local)
|
|
261
|
+
# for exactly the duration of the app call, and give it a buffer to fill.
|
|
262
|
+
# The buffer rides on the env, so the response listener finds this
|
|
263
|
+
# request's spans even when the app ran on another thread.
|
|
264
|
+
#
|
|
265
|
+
# The previous values are saved rather than assumed nil: a nested request
|
|
266
|
+
# (an app that drives a Dommy session of its own) must restore its
|
|
267
|
+
# caller's bracket instead of tearing it down.
|
|
268
|
+
def __internal_open_app_bracket(env)
|
|
269
|
+
return if @level == :off
|
|
270
|
+
|
|
271
|
+
spans = []
|
|
272
|
+
env[SPANS_ENV_KEY] = spans
|
|
273
|
+
env[BRACKET_ENV_KEY] = [Thread.current[TRACE_THREAD_KEY], Thread.current[SPANS_THREAD_KEY]]
|
|
274
|
+
Thread.current[TRACE_THREAD_KEY] = self
|
|
275
|
+
Thread.current[SPANS_THREAD_KEY] = spans
|
|
276
|
+
nil
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def __internal_close_app_bracket(env)
|
|
280
|
+
saved = env.delete(BRACKET_ENV_KEY)
|
|
281
|
+
return if saved.nil?
|
|
282
|
+
|
|
283
|
+
Thread.current[TRACE_THREAD_KEY] = saved[0]
|
|
284
|
+
Thread.current[SPANS_THREAD_KEY] = saved[1]
|
|
285
|
+
nil
|
|
286
|
+
end
|
|
287
|
+
|
|
197
288
|
def __internal_on_response(response)
|
|
198
289
|
request = @pending_request || {}
|
|
199
290
|
@pending_request = nil
|
|
200
|
-
__internal_emit(:http, {
|
|
291
|
+
http = __internal_emit(:http, {
|
|
201
292
|
method: request[:method],
|
|
202
293
|
path: request[:path] || path_of(response.url),
|
|
203
294
|
query: request[:query],
|
|
@@ -206,6 +297,36 @@ module Dommy
|
|
|
206
297
|
location: response.location_header,
|
|
207
298
|
set_cookie: response.set_cookie_strings.map { |raw| cookie_name(raw) }
|
|
208
299
|
})
|
|
300
|
+
__internal_flush_spans(http, request[:env])
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# The request never produced a Response (the app raised): record the
|
|
304
|
+
# aborted request itself, with the spans the closed bracket left on the
|
|
305
|
+
# env, so the trace shows what ran before the exception.
|
|
306
|
+
def __internal_on_abort(env)
|
|
307
|
+
request = @pending_request || {}
|
|
308
|
+
@pending_request = nil
|
|
309
|
+
http = __internal_emit(:http, {
|
|
310
|
+
method: request[:method] || env["REQUEST_METHOD"],
|
|
311
|
+
path: request[:path] || env["PATH_INFO"],
|
|
312
|
+
query: request[:query],
|
|
313
|
+
status: nil,
|
|
314
|
+
aborted: true
|
|
315
|
+
})
|
|
316
|
+
__internal_flush_spans(http, request[:env] || env)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def __internal_flush_spans(http_event, env)
|
|
320
|
+
spans = env && env.delete(SPANS_ENV_KEY)
|
|
321
|
+
return if spans.nil? || spans.empty? || http_event.nil?
|
|
322
|
+
|
|
323
|
+
spans.each do |span|
|
|
324
|
+
payload = {kind: span[:kind], label: span[:label],
|
|
325
|
+
duration_ms: span[:duration_ms], parent: http_event.seq}
|
|
326
|
+
payload.merge!(span[:data]) if span[:data]
|
|
327
|
+
__internal_emit(:span, payload)
|
|
328
|
+
end
|
|
329
|
+
nil
|
|
209
330
|
end
|
|
210
331
|
|
|
211
332
|
def __internal_on_document(window)
|
|
@@ -219,10 +340,9 @@ module Dommy
|
|
|
219
340
|
html = @session.document&.to_html
|
|
220
341
|
return unless html
|
|
221
342
|
|
|
222
|
-
|
|
343
|
+
__internal_emit(:artifact,
|
|
223
344
|
{kind: "dom_snapshot", label: "DOM #{@session.current_url}", content_type: "text/html"},
|
|
224
|
-
window: window)
|
|
225
|
-
@artifacts[event.seq] = html if event
|
|
345
|
+
window: window, artifact: html)
|
|
226
346
|
end
|
|
227
347
|
|
|
228
348
|
# --- DOM observation ---
|
data/lib/dommy/rack/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dommy-rack
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- takahashim
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.11.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.11.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: rack
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
102
|
version: '0'
|
|
103
103
|
requirements: []
|
|
104
|
-
rubygems_version:
|
|
104
|
+
rubygems_version: 3.6.9
|
|
105
105
|
specification_version: 4
|
|
106
106
|
summary: Rack-backed browser session layer for Dommy
|
|
107
107
|
test_files: []
|