flugrekorder 1.0.0-beta.5 → 1.0.0-beta.6

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.
Files changed (2) hide show
  1. package/README.md +65 -0
  2. 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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flugrekorder",
3
- "version": "1.0.0-beta.5",
3
+ "version": "1.0.0-beta.6",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },