Skip to content

Commit b531304

Browse files
Naorayclaude
andcommitted
feat(livewire): capture method call parameters and property update values
Closes #33 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7b4f091 commit b531304

File tree

2 files changed

+152
-11
lines changed

2 files changed

+152
-11
lines changed

src/Tracing/LivewireDataCollector.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,23 @@ protected function extractComponents(array $payload): array
101101
'path' => $memo['path'] ?? null,
102102
];
103103

104-
// Extract method calls
104+
// Extract method calls with parameters
105105
$calls = $component['calls'] ?? [];
106106
if (! empty($calls)) {
107107
$componentData['methods'] = collect($calls)
108-
->pluck('method')
109-
->filter()
108+
->filter(fn ($call) => isset($call['method']))
109+
->map(fn ($call) => [
110+
'method' => $call['method'],
111+
'params' => $call['params'] ?? [],
112+
])
110113
->values()
111114
->toArray();
112115
}
113116

114-
// Extract updated properties (wire:model updates)
117+
// Extract updated properties with values (wire:model updates)
115118
$updates = $component['updates'] ?? [];
116119
if (! empty($updates)) {
117-
$componentData['updates'] = collect($updates)
118-
->keys()
119-
->toArray();
120+
$componentData['updates'] = $updates;
120121
}
121122

122123
$components[] = array_filter($componentData);

tests/Tracing/LivewireDataCollectorTest.php

Lines changed: 144 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@
5252
expect($livewireData)->toHaveKey('components');
5353
expect($livewireData['components'][0])->toHaveKey('name');
5454
expect($livewireData['components'][0]['name'])->toBe('user-profile');
55-
expect($livewireData['components'][0]['methods'])->toBe(['save']);
56-
expect($livewireData['components'][0]['updates'])->toBe(['name']);
55+
expect($livewireData['components'][0]['methods'])->toBe([
56+
['method' => 'save', 'params' => []],
57+
]);
58+
expect($livewireData['components'][0]['updates'])->toBe(['name' => 'John Doe']);
5759
});
5860

5961
it('detects livewire request via path containing livewire/update', function () {
@@ -208,6 +210,144 @@
208210
$this->collector->__invoke($event);
209211

210212
$livewireData = Context::get('livewire');
211-
// Updates should only contain keys, not values (by design)
212-
expect($livewireData['components'][0]['updates'])->toBe(['email', 'password']);
213+
// Updates now include values, with sensitive fields redacted
214+
expect($livewireData['components'][0]['updates']['email'])->toBe('test@example.com');
215+
expect($livewireData['components'][0]['updates']['password'])->toBe('[9 bytes redacted]');
216+
});
217+
218+
it('captures method call parameters', function () {
219+
$request = Request::create('/livewire/update', 'POST');
220+
$request->headers->set('X-Livewire', 'true');
221+
$request->headers->set('Content-Type', 'application/json');
222+
223+
$payload = [
224+
'components' => [
225+
[
226+
'snapshot' => json_encode([
227+
'memo' => [
228+
'name' => 'user-profile',
229+
'id' => 'abc123',
230+
],
231+
]),
232+
'calls' => [
233+
['method' => 'save', 'params' => ['draft' => true]],
234+
['method' => 'validate', 'params' => ['name', 'email']],
235+
],
236+
],
237+
],
238+
];
239+
240+
$request->merge($payload);
241+
app()->instance('request', $request);
242+
243+
$event = new RequestHandled($request, new Response);
244+
$this->collector->__invoke($event);
245+
246+
$livewireData = Context::get('livewire');
247+
expect($livewireData['components'][0]['methods'])->toBe([
248+
['method' => 'save', 'params' => ['draft' => true]],
249+
['method' => 'validate', 'params' => ['name', 'email']],
250+
]);
251+
});
252+
253+
it('captures update values alongside keys', function () {
254+
$request = Request::create('/livewire/update', 'POST');
255+
$request->headers->set('X-Livewire', 'true');
256+
$request->headers->set('Content-Type', 'application/json');
257+
258+
$payload = [
259+
'components' => [
260+
[
261+
'snapshot' => json_encode([
262+
'memo' => [
263+
'name' => 'contact-form',
264+
'id' => 'def456',
265+
],
266+
]),
267+
'updates' => [
268+
'name' => 'Jane Smith',
269+
'email' => 'jane@example.com',
270+
'message' => 'Hello there',
271+
],
272+
],
273+
],
274+
];
275+
276+
$request->merge($payload);
277+
app()->instance('request', $request);
278+
279+
$event = new RequestHandled($request, new Response);
280+
$this->collector->__invoke($event);
281+
282+
$livewireData = Context::get('livewire');
283+
expect($livewireData['components'][0]['updates'])->toBe([
284+
'name' => 'Jane Smith',
285+
'email' => 'jane@example.com',
286+
'message' => 'Hello there',
287+
]);
288+
});
289+
290+
it('redacts sensitive data in method params', function () {
291+
$request = Request::create('/livewire/update', 'POST');
292+
$request->headers->set('X-Livewire', 'true');
293+
$request->headers->set('Content-Type', 'application/json');
294+
295+
$payload = [
296+
'components' => [
297+
[
298+
'snapshot' => json_encode([
299+
'memo' => [
300+
'name' => 'auth-form',
301+
'id' => 'auth123',
302+
],
303+
]),
304+
'calls' => [
305+
['method' => 'login', 'params' => ['password' => 'my-secret-pw']],
306+
],
307+
],
308+
],
309+
];
310+
311+
$request->merge($payload);
312+
app()->instance('request', $request);
313+
314+
$event = new RequestHandled($request, new Response);
315+
$this->collector->__invoke($event);
316+
317+
$livewireData = Context::get('livewire');
318+
expect($livewireData['components'][0]['methods'][0]['method'])->toBe('login');
319+
expect($livewireData['components'][0]['methods'][0]['params']['password'])->toBe('[12 bytes redacted]');
320+
});
321+
322+
it('defaults to empty params array when calls have no params key', function () {
323+
$request = Request::create('/livewire/update', 'POST');
324+
$request->headers->set('X-Livewire', 'true');
325+
$request->headers->set('Content-Type', 'application/json');
326+
327+
$payload = [
328+
'components' => [
329+
[
330+
'snapshot' => json_encode([
331+
'memo' => [
332+
'name' => 'counter',
333+
'id' => 'cnt123',
334+
],
335+
]),
336+
'calls' => [
337+
['method' => 'increment'],
338+
],
339+
],
340+
],
341+
];
342+
343+
$request->merge($payload);
344+
app()->instance('request', $request);
345+
346+
$event = new RequestHandled($request, new Response);
347+
$this->collector->__invoke($event);
348+
349+
$livewireData = Context::get('livewire');
350+
expect($livewireData['components'][0]['methods'])->toBe([
351+
['method' => 'increment', 'params' => []],
352+
]);
213353
});

0 commit comments

Comments
 (0)