@specific.dev/spectest 0.26.0 → 0.27.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.
Files changed (74) hide show
  1. package/dist/aws-sigv4.d.ts +42 -0
  2. package/dist/aws-sigv4.js +166 -0
  3. package/dist/browser.d.ts +314 -0
  4. package/dist/browser.js +1320 -0
  5. package/dist/components/email.d.ts +135 -0
  6. package/dist/components/email.js +271 -0
  7. package/dist/components/expo.d.ts +69 -0
  8. package/dist/components/expo.js +125 -0
  9. package/dist/components/index.d.ts +8 -0
  10. package/dist/components/index.js +18 -0
  11. package/dist/components/k3s.d.ts +143 -0
  12. package/dist/components/k3s.js +1067 -0
  13. package/dist/components/postgres.d.ts +93 -0
  14. package/dist/components/postgres.js +58 -0
  15. package/dist/components/replayFake.d.ts +169 -0
  16. package/dist/components/replayFake.js +738 -0
  17. package/dist/components/s3.d.ts +99 -0
  18. package/dist/components/s3.js +81 -0
  19. package/dist/components/supabase.d.ts +197 -0
  20. package/dist/components/supabase.js +1003 -0
  21. package/dist/daemon.d.ts +1 -0
  22. package/dist/daemon.js +4223 -0
  23. package/dist/ids.d.ts +2 -0
  24. package/{src/ids.ts → dist/ids.js} +46 -50
  25. package/dist/index.d.ts +1183 -0
  26. package/dist/index.js +769 -0
  27. package/dist/ingress.d.ts +114 -0
  28. package/dist/ingress.js +210 -0
  29. package/dist/inspect.d.ts +228 -0
  30. package/dist/inspect.js +429 -0
  31. package/dist/locator.d.ts +260 -0
  32. package/dist/locator.js +293 -0
  33. package/dist/mobile.d.ts +71 -0
  34. package/dist/mobile.js +65 -0
  35. package/dist/record-secrets.d.ts +9 -0
  36. package/{src/record-secrets.ts → dist/record-secrets.js} +13 -15
  37. package/dist/recorder.d.ts +516 -0
  38. package/dist/recorder.js +219 -0
  39. package/dist/redis.d.ts +54 -0
  40. package/dist/redis.js +126 -0
  41. package/dist/replay-bundle.d.ts +38 -0
  42. package/{src/replay-bundle.ts → dist/replay-bundle.js} +29 -47
  43. package/dist/resolver.d.ts +1 -0
  44. package/dist/resolver.js +309 -0
  45. package/dist/s3.d.ts +89 -0
  46. package/dist/s3.js +198 -0
  47. package/dist/sql.d.ts +74 -0
  48. package/dist/sql.js +151 -0
  49. package/dist/terminal.d.ts +161 -0
  50. package/dist/terminal.js +538 -0
  51. package/package.json +24 -9
  52. package/src/browser.ts +0 -1819
  53. package/src/components/email.ts +0 -398
  54. package/src/components/expo.ts +0 -167
  55. package/src/components/index.ts +0 -63
  56. package/src/components/k3s.ts +0 -1312
  57. package/src/components/postgres.ts +0 -105
  58. package/src/components/replayFake.ts +0 -848
  59. package/src/components/s3.ts +0 -132
  60. package/src/components/supabase.ts +0 -1299
  61. package/src/daemon.ts +0 -4969
  62. package/src/index.ts +0 -2350
  63. package/src/ingress.ts +0 -288
  64. package/src/inspect.ts +0 -673
  65. package/src/locator.ts +0 -594
  66. package/src/mobile.ts +0 -133
  67. package/src/recorder.ts +0 -817
  68. package/src/redis.ts +0 -202
  69. package/src/resolver.ts +0 -351
  70. package/src/s3.ts +0 -333
  71. package/src/sql.ts +0 -243
  72. package/src/terminal.ts +0 -740
  73. package/src/vendor/rrweb-plugin-console-record.umd.js +0 -521
  74. package/src/vendor/rrweb-record.min.js +0 -5061
@@ -0,0 +1,429 @@
1
+ // Provenance wrappers for op return values.
2
+ //
3
+ // When a tracked op (fetch, db query, browser.evaluate) records its
4
+ // event, it wraps its return value via `wrap(value, sourceSeq)`. The
5
+ // wrapper carries an `OP_TAG` describing which op produced it and the
6
+ // access path within that op's payload. When the wrapped value reaches
7
+ // `expect()`, the matcher reads the tag and attaches `sourceSeq` + `path`
8
+ // to its AssertionEvent so the UI can nest the assertion under its
9
+ // originating op.
10
+ //
11
+ // Two shapes of wrapper:
12
+ // - Objects/arrays: lazy `Proxy`. Reading a property returns a fresh
13
+ // wrapped child with the path extended. Structural shape stays raw:
14
+ // an array's `length` is a real number (provenance belongs on the
15
+ // data, not the container's size — `rows.length === 1` must work).
16
+ // - Primitives: a small `Carrier` object holding the value and tag.
17
+ // Coercion sinks (`valueOf`/`toString`/`toJSON`/`Symbol.toPrimitive`)
18
+ // recover the raw primitive, so template interpolation, arithmetic,
19
+ // loose equality, and `JSON.stringify` all behave. The one remaining
20
+ // sharp edge is strict `===` against a primitive (always false — an
21
+ // object can never `===` a number); `.unwrap()` first, and the
22
+ // `Carrier<T>` typing makes that a compile-time error in TS.
23
+ //
24
+ // Cannot tag: `null` / `undefined` (no place to attach a symbol) and
25
+ // functions (we don't currently need to). Those pass through as-is — but a
26
+ // nullish *leaf read* off a proxy is recorded in the pending-nullish register
27
+ // below so `expect()` can still recover its provenance (see `adoptNullishTag`).
28
+ //
29
+ // Escape: every wrapper exposes `.unwrap()` returning the fully raw value (it
30
+ // walks every wrapper layer, so one call always reaches raw). That is the
31
+ // public escape hatch; there is no `unwrap(x)` free function — a spectest op
32
+ // result is always wrapped (see below), so the value always has `.unwrap()`.
33
+ // (`readRaw` is the internal, never-throws primitive `.unwrap()` and `expect()`
34
+ // are built on; it isn't part of the public surface.)
35
+ //
36
+ // Wrapping is UNCONDITIONAL: a spectest op (fetch/exec/terminal/poll/db/
37
+ // browser.evaluate) always returns a wrapped value, in every context — a test,
38
+ // a `ctx.poll` predicate, `setup`, `eval`, a fake handler. Whether the op also
39
+ // recorded a timeline *event* is a separate decision (the recorder may be
40
+ // absent, paused, or have truncated the event): when there is no event the
41
+ // wrapper simply carries no provenance (`sourceSeq: undefined`), so `expect()`
42
+ // can't nest it under an op — but the wrapper SHAPE (`.unwrap()`, coercion
43
+ // sinks, the proxy) is always present. That keeps `Wrapped`/`WrappedResponse`/
44
+ // `Carrier<T>` honest at runtime everywhere, instead of silently collapsing to
45
+ // a raw value wherever recording happened to be off.
46
+ // Registered in the GLOBAL symbol registry (`Symbol.for`), not module-private
47
+ // (`Symbol`), so a value tagged by ONE copy of this module unwraps correctly in
48
+ // ANOTHER. This matters whenever two copies of the SDK load in the same runtime:
49
+ // the in-VM daemon runs the baked SDK at /opt/spectest/sdk and mints the
50
+ // provenance carriers (ctx.fetch / ctx.browser / db results), while a user's
51
+ // test file resolving `@specific.dev/spectest` to a registry-pinned copy gets
52
+ // its `expect`. With module-private symbols the two `UNWRAP`s differed, so
53
+ // `expect`'s auto-unwrap silently no-op'd and wrapped assertions failed
54
+ // nonsensically (`expect("Hello Spec").toContain("Hello")` -> false). The
55
+ // control plane also repoints the dep at the baked copy so normally only one
56
+ // copy loads; this is defense-in-depth for the cases where it can't (and only
57
+ // takes full effect once both copies ship this `Symbol.for`).
58
+ export const OP_TAG = Symbol.for("spectest.opTag");
59
+ export const UNWRAP = Symbol.for("spectest.unwrap");
60
+ /** Read the tag if present; returns undefined for raw values. */
61
+ export function readTag(x) {
62
+ if (x === null || x === undefined)
63
+ return undefined;
64
+ if (typeof x !== "object" && typeof x !== "function")
65
+ return undefined;
66
+ return x[OP_TAG];
67
+ }
68
+ // ───────────────────────────────────────────────────────────────────────────
69
+ // Pending nullish-leaf register
70
+ //
71
+ // Reading a `null`/`undefined` leaf off a wrapped object (`dep.status.readyReplicas`
72
+ // on a failed deployment) hands back a raw nullish value with no tag — a symbol
73
+ // can't ride on `null`/`undefined`, and minting a stand-in object would break
74
+ // every `=== null` / `if (!x)` in real code (see `wrap`). So the natural
75
+ // `expect(dep.status.readyReplicas).toBeFalsy()` used to lose its provenance and
76
+ // render as a disconnected top-level assertion, even though `field(...)` could
77
+ // recover it manually.
78
+ //
79
+ // To make the natural form work too, the proxy *notes* every nullish leaf read
80
+ // here (source op + access path). `expect()` consults the register when it
81
+ // receives an untagged nullish value via `adoptNullishTag`, matching the most
82
+ // recent note. The note is single-consume, freshest-wins, and invalidated by
83
+ // any subsequent recorded op (the recorder calls `clearPendingNullish`), so the
84
+ // window for misattribution is one untagged nullish read immediately followed by
85
+ // an `expect` of an unrelated nullish literal with no op in between — narrow,
86
+ // and the worst case points at a contextually-adjacent field rather than losing
87
+ // the link entirely. The note never reaches program control flow: it is read
88
+ // only inside `expect()`, which `readRaw`s back to the real nullish value.
89
+ // ───────────────────────────────────────────────────────────────────────────
90
+ let pendingNullish;
91
+ function noteNullishLeaf(value, sourceSeq, path) {
92
+ pendingNullish = { value, tag: { sourceSeq, path: [...path] } };
93
+ }
94
+ /** Forget any pending nullish-leaf note. Called by the recorder whenever a new
95
+ * op is recorded, so a note can't outlive the read that produced it. */
96
+ export function clearPendingNullish() {
97
+ pendingNullish = undefined;
98
+ }
99
+ /**
100
+ * If `value` is an untagged `null`/`undefined` that matches the most recent
101
+ * nullish-leaf read, mint a tagged {@link makeCarrier} holder for it (the same
102
+ * stand-in `field`/`retag` use) and clear the note; otherwise return `value`
103
+ * unchanged. The holder is safe because it goes straight to `expect()`, which
104
+ * `readRaw`s it before matching — it never escapes into control flow.
105
+ */
106
+ export function adoptNullishTag(value) {
107
+ if (value !== null && value !== undefined)
108
+ return value;
109
+ const p = pendingNullish;
110
+ if (!p || !Object.is(p.value, value))
111
+ return value;
112
+ pendingNullish = undefined;
113
+ return makeCarrier(value, p.tag.sourceSeq, p.tag.path);
114
+ }
115
+ /** If x is wrapped, return the raw value; otherwise return x. */
116
+ export function readRaw(x) {
117
+ // Walks the chain — a value can carry more than one wrapper at once
118
+ // (e.g. a polled k8s pod is wrapped by the component's `withTagging`
119
+ // with the HTTP seq, then by `ctx.poll`'s `wrap(...)` with the wait
120
+ // seq). A single-step unwrap would still hand callers a proxy.
121
+ let cur = x;
122
+ while (cur !== null && cur !== undefined) {
123
+ const t = typeof cur;
124
+ if (t !== "object" && t !== "function")
125
+ return cur;
126
+ // Presence check, NOT `next === undefined`: a carrier minted by `field`
127
+ // may wrap `undefined` itself (`[UNWRAP] = undefined`). Testing the value
128
+ // would mistake that for "no wrapper" and hand the carrier object back, so
129
+ // a `toBeFalsy()` would see a truthy object.
130
+ if (!(UNWRAP in cur))
131
+ return cur;
132
+ const next = cur[UNWRAP];
133
+ if (next === cur)
134
+ return cur;
135
+ cur = next;
136
+ }
137
+ return cur;
138
+ }
139
+ /**
140
+ * Wrap a value so reads through it carry an `OpTag`. Recursion is lazy:
141
+ * a property read on an object Proxy wraps its child on demand.
142
+ */
143
+ export function wrap(raw, sourceSeq, path = []) {
144
+ if (raw === null || raw === undefined)
145
+ return raw;
146
+ const t = typeof raw;
147
+ if (t === "object") {
148
+ return wrapObject(raw, sourceSeq, path);
149
+ }
150
+ if (t === "function")
151
+ return raw;
152
+ // primitive
153
+ return makeCarrier(raw, sourceSeq, path);
154
+ }
155
+ /**
156
+ * Like {@link wrap}, but for values read *through* a wrapped container, where a
157
+ * `null`/`undefined` leaf must still leave a provenance trail. `wrap` passes
158
+ * nullish through raw (a symbol can't ride on it); here we additionally
159
+ * {@link noteNullishLeaf note} the read so `expect()` can adopt the tag via
160
+ * {@link adoptNullishTag}. The returned value is identical to `wrap`'s — raw
161
+ * nullish or a proxy/carrier — so control flow is unaffected.
162
+ */
163
+ function wrapChild(value, sourceSeq, path) {
164
+ if (value === null || value === undefined) {
165
+ noteNullishLeaf(value, sourceSeq, path);
166
+ return value;
167
+ }
168
+ return wrap(value, sourceSeq, path);
169
+ }
170
+ // Array methods whose return value derives from the array's contents.
171
+ // We re-wrap their results so assertions on `arr.find(...)` etc. still
172
+ // fold under the originating op. Predicates still see raw items (the
173
+ // method runs on the raw target), so `===` comparisons keep working.
174
+ const ARRAY_TAGGED_METHODS = new Set([
175
+ "find",
176
+ "findLast",
177
+ "at",
178
+ "map",
179
+ "filter",
180
+ "slice",
181
+ "flat",
182
+ "flatMap",
183
+ "concat",
184
+ "includes",
185
+ "indexOf",
186
+ "lastIndexOf",
187
+ "findIndex",
188
+ "findLastIndex",
189
+ "some",
190
+ "every",
191
+ ]);
192
+ function wrapObject(raw, sourceSeq, path) {
193
+ const tag = { sourceSeq, path };
194
+ const isArray = Array.isArray(raw);
195
+ const handler = {
196
+ get(target, prop, receiver) {
197
+ if (prop === OP_TAG)
198
+ return tag;
199
+ if (prop === UNWRAP)
200
+ return target;
201
+ if (prop === "unwrap") {
202
+ // Only intercept when the underlying object doesn't already have
203
+ // its own `unwrap` — otherwise we'd shadow a legitimate property.
204
+ // `readRaw` (not a bare `target`) so a value carrying more than one
205
+ // wrapper — e.g. a `ctx.poll` result whose inner value was already
206
+ // tagged by a component's `withTagging` — unwraps all the way to raw
207
+ // in one call, not just one layer.
208
+ if (!(prop in target)) {
209
+ return () => readRaw(target);
210
+ }
211
+ }
212
+ // `.transform(label, fn)` — same shape/guard as `unwrap`: only intercept
213
+ // when the underlying object has no own `transform`, so a real data field
214
+ // named `transform` is never shadowed.
215
+ if (prop === "transform" && !(prop in target)) {
216
+ return makeTransform(readRaw(target), sourceSeq, path);
217
+ }
218
+ // Hide thenable-ness from `await`. We must never accidentally
219
+ // implement `then`, or `await fetch(...)` would resolve to the
220
+ // wrong thing if we ever wrapped a Promise (we don't, but be safe).
221
+ if (prop === "then" && !(prop in target))
222
+ return undefined;
223
+ const value = Reflect.get(target, prop, receiver);
224
+ if (typeof prop === "symbol") {
225
+ // Symbols (iterator, toPrimitive, etc.) — pass through bound.
226
+ return typeof value === "function"
227
+ ? value.bind(target)
228
+ : value;
229
+ }
230
+ // Container shape stays raw: `rows.length === 1` must be a plain
231
+ // number comparison, not carrier-vs-number (silently false).
232
+ // Provenance for "how many" assertions still works — `expect`
233
+ // accepts raw values, it just won't nest under the op.
234
+ if (isArray && prop === "length")
235
+ return value;
236
+ const childPath = path.concat(String(prop));
237
+ if (typeof value === "function") {
238
+ if (isArray && ARRAY_TAGGED_METHODS.has(prop)) {
239
+ const method = value;
240
+ const taggedPath = path.concat(`<${prop}>`);
241
+ return (...args) => {
242
+ const result = method.apply(target, args);
243
+ return wrapChild(result, sourceSeq, taggedPath);
244
+ };
245
+ }
246
+ // Other methods stay bound to the raw target; their return
247
+ // values aren't wrapped here — call sites that need
248
+ // promise-returning methods (e.g. Response.json()) install a
249
+ // bespoke wrapper.
250
+ return value.bind(target);
251
+ }
252
+ return wrapChild(value, sourceSeq, childPath);
253
+ },
254
+ has(target, prop) {
255
+ if (prop === OP_TAG || prop === UNWRAP)
256
+ return true;
257
+ return Reflect.has(target, prop);
258
+ },
259
+ // Block writes; wrappers are read-only views.
260
+ set() {
261
+ return false;
262
+ },
263
+ deleteProperty() {
264
+ return false;
265
+ },
266
+ };
267
+ return new Proxy(raw, handler);
268
+ }
269
+ // The `.transform(label, fn)` method every wrapper (carrier + object/array
270
+ // proxy) carries — the value-level analogue of `.unwrap()`. It runs the raw
271
+ // value through `fn` and re-`wrap`s the result with `path` extended by a
272
+ // `<label>` derived-step marker, so the decoded value keeps the source op's
273
+ // provenance (`sourceSeq`) and stays navigable: a later `expect(...)` on it, or
274
+ // on a subfield, still nests under the originating op (`secret.data.config.<json>.tier`).
275
+ // `label` is a plain word; the `<…>` brackets are added here so they never leak
276
+ // into the call site (the same convention array methods use, `<find>`/`<map>`).
277
+ // A throw in `fn` (a non-string value, malformed base64/JSON) is rethrown
278
+ // prefixed with `<label>:`, failing the test at the bad value.
279
+ function makeTransform(raw, sourceSeq, path) {
280
+ return (label, fn) => {
281
+ const nextPath = [...path, `<${label}>`];
282
+ let result;
283
+ try {
284
+ result = fn(raw);
285
+ }
286
+ catch (err) {
287
+ const detail = err instanceof Error ? err.message : String(err);
288
+ throw new Error(`<${label}>: ${detail}`);
289
+ }
290
+ return wrap(result, sourceSeq, nextPath);
291
+ };
292
+ }
293
+ function makeCarrier(raw, sourceSeq, path) {
294
+ const tag = { sourceSeq, path };
295
+ return {
296
+ [OP_TAG]: tag,
297
+ [UNWRAP]: raw,
298
+ unwrap: () => raw,
299
+ transform: makeTransform(raw, sourceSeq, path),
300
+ // Coercion sinks recover the raw primitive instead of inheriting
301
+ // Object.prototype's defaults ("[object Object]" / NaN / {}). A
302
+ // carrier interpolated into a string, fed to arithmetic, `==`, or
303
+ // `JSON.stringify` now behaves like its value — silent wrongness
304
+ // (a bogus "[object Object]" namespace in a kubectl command) was
305
+ // the most expensive failure mode this wrapper ever produced.
306
+ valueOf: () => raw,
307
+ toString: () => String(raw),
308
+ toJSON: () => raw,
309
+ [Symbol.toPrimitive]: () => raw,
310
+ };
311
+ }
312
+ // ───────────────────────────────────────────────────────────────────────────
313
+ // Provenance-preserving field selection
314
+ //
315
+ // The proxy/carrier only carries the OpTag across *property reads* (and a
316
+ // whitelist of array methods), and it can't carry it across `null`/`undefined`
317
+ // at all — those are primitives with nowhere to hang a symbol, and `wrap`
318
+ // passes them through raw. So `expect(row.deleted_at).toBe(null)` reaches
319
+ // `expect()` as a bare `null` with no tag and renders as a disconnected
320
+ // top-level row. `field` recovers the link by tagging from the *container*.
321
+ // (A decoded value loses its tag the same way, through the decode; the
322
+ // `.transform(label, fn)` method every wrapper carries (see `makeTransform`)
323
+ // recovers it — it runs the still-tagged value through `fn` and re-`wrap`s the
324
+ // result with the source path extended by a `<label>` marker.)
325
+ // ───────────────────────────────────────────────────────────────────────────
326
+ /**
327
+ * Re-tag a value against an existing source op. Uses the normal `wrap` for
328
+ * non-nullish results (object → proxy, primitive → carrier) but a
329
+ * {@link makeCarrier} *holder* for `null`/`undefined` — those can't hold a
330
+ * symbol, and `wrap` deliberately passes them through raw. The holder is an
331
+ * object masquerading as null/undefined, which is ONLY safe because it never
332
+ * escapes into program control flow: it goes straight to `expect()`, which
333
+ * `readRaw`s it back to the real value before matching.
334
+ */
335
+ function retag(raw, sourceSeq, path) {
336
+ return raw === null || raw === undefined
337
+ ? makeCarrier(raw, sourceSeq, path)
338
+ : wrap(raw, sourceSeq, path);
339
+ }
340
+ /**
341
+ * Provenance-preserving, null-safe field selector — for asserting on a leaf
342
+ * that is `null`/`undefined`. Reading such a leaf off a wrapped object hands
343
+ * back a raw `null`/`undefined` (a symbol tag can't ride on those, and minting
344
+ * a stand-in object would break every `=== null` / `if (!x)` in real code), so
345
+ * the raw value alone loses its link to the originating op. `field` reads the
346
+ * tag from the *container* and navigates the raw value, returning a tagged
347
+ * handle even when the leaf is nullish — safe because the handle only ever
348
+ * feeds `expect()`. Pass it straight to `expect(...)`; works with every matcher.
349
+ *
350
+ * Mostly redundant now: the plain `expect(dep.status.readyReplicas).toBeFalsy()`
351
+ * form recovers provenance on its own — the proxy notes each nullish leaf read
352
+ * and `expect` adopts the note (see {@link adoptNullishTag}). Reach for `field`
353
+ * when the nullish read and the `expect` are separated by another recorded op
354
+ * (which clears the note), or to make the navigation explicit.
355
+ *
356
+ * ```ts
357
+ * expect(field(created, "branched_from_environment_id")).toBe(null);
358
+ * expect(field(dep, "status", "readyReplicas")).toBeFalsy();
359
+ * ```
360
+ */
361
+ export function field(value, ...path) {
362
+ const tag = readTag(value);
363
+ let cur = readRaw(value);
364
+ for (const key of path) {
365
+ if (cur === null || cur === undefined) {
366
+ cur = undefined;
367
+ break;
368
+ }
369
+ cur = cur[key];
370
+ }
371
+ if (!tag)
372
+ return cur;
373
+ return retag(cur, tag.sourceSeq, [...tag.path, ...path.map(String)]);
374
+ }
375
+ /**
376
+ * Bespoke wrapper for `fetch` responses. Reads on `status` / `ok` /
377
+ * `statusText` / `url` / `redirected` / `type` return carriers tagged
378
+ * to `sourceSeq`. The body-reading methods (`json`, `text`) return the
379
+ * resolved value wrapped under `path: ["body"]`. Everything else passes
380
+ * through bound to the real Response.
381
+ */
382
+ export function wrapResponse(res, sourceSeq) {
383
+ const tag = { sourceSeq, path: [] };
384
+ const carrierProps = new Set([
385
+ "status",
386
+ "ok",
387
+ "statusText",
388
+ "url",
389
+ "redirected",
390
+ "type",
391
+ ]);
392
+ const bodyMethods = new Set(["json", "text"]);
393
+ const handler = {
394
+ get(target, prop) {
395
+ if (prop === OP_TAG)
396
+ return tag;
397
+ if (prop === UNWRAP)
398
+ return target;
399
+ // Full recursive unwrap (see the note in `wrapObject`).
400
+ if (prop === "unwrap")
401
+ return () => readRaw(target);
402
+ if (prop === "then")
403
+ return undefined;
404
+ if (typeof prop === "string" && carrierProps.has(prop)) {
405
+ const value = target[prop];
406
+ return wrap(value, sourceSeq, [prop]);
407
+ }
408
+ if (typeof prop === "string" && bodyMethods.has(prop)) {
409
+ const fn = target[prop];
410
+ if (typeof fn !== "function")
411
+ return fn;
412
+ return async (...args) => {
413
+ const value = await fn.apply(target, args);
414
+ return wrap(value, sourceSeq, ["body"]);
415
+ };
416
+ }
417
+ const value = target[prop];
418
+ return typeof value === "function"
419
+ ? value.bind(target)
420
+ : value;
421
+ },
422
+ has(target, prop) {
423
+ if (prop === OP_TAG || prop === UNWRAP)
424
+ return true;
425
+ return Reflect.has(target, prop);
426
+ },
427
+ };
428
+ return new Proxy(res, handler);
429
+ }