flugrekorder 1.0.0-beta.5 → 1.0.0-beta.7
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.
- package/README.md +65 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,6 +194,71 @@ type Rekording = {
|
|
|
194
194
|
|
|
195
195
|
---
|
|
196
196
|
|
|
197
|
+
## Resolving paths inside a callback
|
|
198
|
+
|
|
199
|
+
`getProxyById` and `getPath` are most useful when called *inside* the callback while the trap is still in context — not as a post-processing step on the raw recordings.
|
|
200
|
+
|
|
201
|
+
When an `apply` trap fires, `origin.source` is the ID of the function proxy that was called. Resolving it immediately gives you a human-readable path:
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
let p!: typeof myService;
|
|
205
|
+
|
|
206
|
+
p = create(myService, {
|
|
207
|
+
only: ['get', 'apply'],
|
|
208
|
+
callback(r) {
|
|
209
|
+
if (r.trap !== 'apply' || !r.origin || !('source' in r.origin)) return;
|
|
210
|
+
const fn = getProxyById(r.origin.source, p);
|
|
211
|
+
if (fn) console.log('called:', getPath(fn)); // e.g. "users.find"
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The same pattern works for `set` traps — `origin.parent` is the ID of the proxy being written to:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
let p!: typeof config;
|
|
220
|
+
|
|
221
|
+
p = create(config, {
|
|
222
|
+
callback(r) {
|
|
223
|
+
if (r.trap !== 'set' || !r.origin || !('parent' in r.origin)) return;
|
|
224
|
+
const parent = getProxyById(r.origin.parent, p);
|
|
225
|
+
const prefix = parent ? getPath(parent) : '';
|
|
226
|
+
console.log(`${prefix ? `${prefix}.` : ''}${r.origin.key} =`, r.args[2]);
|
|
227
|
+
// e.g. "db.port = 5433"
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Note the `let p!: typeof …` pattern: the callback closes over `p` before it is assigned, but `p` is fully set by the time any trap fires, so the reference is always valid.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## How it works
|
|
237
|
+
|
|
238
|
+
### One graph per session
|
|
239
|
+
|
|
240
|
+
Every `create()` call produces an isolated `Graph` — a session-scoped registry that maps proxies, targets, and IDs to each other. Once all references to a proxied tree are dropped, the graph is eligible for garbage collection. There are no module-level leaks between independent recordings.
|
|
241
|
+
|
|
242
|
+
### Structured origin
|
|
243
|
+
|
|
244
|
+
Each proxy carries an `Origin` that describes exactly how it was created: which trap fired, on which parent proxy, and under which key (for property traps) or from which function proxy (for call traps). This makes the recording self-describing — you can reconstruct a full call graph from the records alone, without keeping any external state.
|
|
245
|
+
|
|
246
|
+
Earlier designs tracked paths as arrays of keys. That approach broke down when the same object was accessed via different routes, or when proxies were collected before the path could be read. Storing a parent ID and a key instead of a full path makes the origin both stable and compact.
|
|
247
|
+
|
|
248
|
+
### `{ $proxy: id }` serialization
|
|
249
|
+
|
|
250
|
+
Proxiable values (objects and functions) in args and results are replaced with `{ $proxy: '<id>' }` tags rather than inlined. This keeps records JSON-safe, avoids circular reference problems, and lets you resolve references back to live proxies via `getProxyById` when needed.
|
|
251
|
+
|
|
252
|
+
### Promises
|
|
253
|
+
|
|
254
|
+
Promises cannot be proxied directly — native `.then()` checks for the `[[PromiseState]]` internal slot and throws if `this` is a Proxy. Instead, flugrekorder returns a new Promise that resolves to a proxy of the settled value, maintaining the stability guarantee across async boundaries.
|
|
255
|
+
|
|
256
|
+
### `wrap` vs `wrapKnown`
|
|
257
|
+
|
|
258
|
+
Trap specs use two wrapping modes. `wrap` creates a new proxy for any proxiable value not already in the graph — used for results, where a newly returned object should be recorded. `wrapKnown` only wraps values that are already in the graph — used for call arguments, where passing a plain object to a proxied function should not silently create a new proxy out of it.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
197
262
|
## Examples
|
|
198
263
|
|
|
199
264
|
### Stream interactions to a file (NDJSON)
|