quickjs-emscripten-sync 1.10.0 → 1.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.
@@ -1,7 +1,7 @@
1
- import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
1
+ import type { QuickJSAsyncContext, QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
2
 
3
3
  import { isES2015Class, isObject } from "../util";
4
- import { call } from "../vmutil";
4
+ import { call, consume } from "../vmutil";
5
5
 
6
6
  import marshalProperties from "./properties";
7
7
 
@@ -14,61 +14,110 @@ export default function marshalFunction(
14
14
  preApply?: (target: (...args: any[]) => any, thisArg: unknown, args: unknown[]) => any,
15
15
  disposeTransient: (handle: QuickJSHandle) => void = () => {},
16
16
  prepareReturn: (handle: QuickJSHandle) => QuickJSHandle = h => h,
17
+ unwrap: (target: unknown) => unknown = t => t,
18
+ asyncify?: (target: unknown) => boolean,
17
19
  ): QuickJSHandle | undefined {
18
20
  if (typeof target !== "function") return;
19
21
 
20
- const raw = ctx
21
- .newFunction(target.name, function (...argHandles) {
22
- // A plain call (`fn()`) passes the VM global object as `this`. Unmarshalling
23
- // it would eagerly deep-copy the entire global graph (hundreds of handles)
24
- // on the first call, for a `this` host functions almost never use — and
25
- // leaking globalThis to the host is undesirable. So global `this` is passed
26
- // to the host function as `undefined`, which differs from plain JS where a
27
- // non-strict function sees `this === globalThis` (see README Limitations).
28
- // Real method calls still get their receiver unmarshalled.
29
- const that = ctx.sameValue(this, ctx.global) ? undefined : unmarshal(this);
30
- const args = argHandles.map(a => unmarshal(a));
22
+ // `target` may be a host-side proxy wrapper; unwrap it before the class check,
23
+ // because Function.prototype.toString on a callable proxy never matches /^class/.
24
+ // Computed once here rather than per call to avoid the toString + regex on every
25
+ // VM→host invocation.
26
+ const unwrapped = unwrap(target);
27
+ const isClass = isES2015Class(unwrapped);
31
28
 
32
- if (isES2015Class(target) && isObject(that)) {
33
- // Class constructors cannot be invoked without new expression, and new.target is not changed
34
- const result = new target(...args);
35
- Object.entries(result).forEach(([key, value]) => {
36
- const valueHandle = marshal(value);
37
- ctx.setProp(this, key, valueHandle);
38
- // setProp dup'd the value into `this`; drop ours if it was transient.
39
- disposeTransient(valueHandle);
40
- });
41
- return this;
42
- }
29
+ // Marshal as an Asyncified function only when the caller opts in for this
30
+ // target AND the context actually supports it (a plain sync context has no
31
+ // `newAsyncifiedFunction`, so fall back to the normal function marshalling,
32
+ // which hands the guest a marshalled Promise as before).
33
+ const useAsyncify =
34
+ !!asyncify && asyncify(unwrapped) && "newAsyncifiedFunction" in ctx;
43
35
 
44
- // The VM disposes whatever we return here. `prepareReturn` dups the
45
- // handle when the VMMap retains it, so the map keeps a live copy and
46
- // identity (`x === fn()` across calls) survives instead of going stale.
47
- return prepareReturn(
48
- marshal(
49
- preApply
36
+ const inner = (
37
+ useAsyncify
38
+ ? // Asyncify: the VM stack is suspended until the host promise settles, so
39
+ // the guest receives the resolved value synchronously. Async functions
40
+ // can never be class constructors, so the class-constructor path is
41
+ // skipped here.
42
+ (ctx as QuickJSAsyncContext).newAsyncifiedFunction(target.name, async function (
43
+ ...argHandles
44
+ ) {
45
+ const that = ctx.sameValue(this, ctx.global) ? undefined : unmarshal(this);
46
+ const args = argHandles.map(a => unmarshal(a));
47
+
48
+ // `preApply` wraps the (synchronous) invocation to toggle temporal sync
49
+ // around it; it returns the host promise, which we await here. Note that
50
+ // its finally runs as soon as `apply` returns the promise, so temporal
51
+ // sync is only active for the synchronous portion of the async function,
52
+ // not across its awaits.
53
+ const result = await (preApply
50
54
  ? preApply(target as (...args: any[]) => any, that, args)
51
- : (target as (...args: any[]) => any).apply(that, args),
52
- ),
53
- );
54
- })
55
- .consume(handle2 =>
56
- // fucntions created by vm.newFunction are not callable as a class constrcutor
57
- call(
58
- ctx,
59
- `Cls => {
60
- const fn = function(...args) { return Cls.apply(this, args); };
61
- fn.name = Cls.name;
62
- fn.length = Cls.length;
63
- return fn;
64
- }`,
65
- undefined,
66
- handle2,
67
- ),
68
- );
55
+ : (target as (...args: any[]) => any).apply(that, args));
56
+
57
+ return prepareReturn(marshal(result));
58
+ })
59
+ : ctx.newFunction(target.name, function (...argHandles) {
60
+ // A plain call (`fn()`) passes the VM global object as `this`. Unmarshalling
61
+ // it would eagerly deep-copy the entire global graph (hundreds of handles)
62
+ // on the first call, for a `this` host functions almost never use — and
63
+ // leaking globalThis to the host is undesirable. So global `this` is passed
64
+ // to the host function as `undefined`, which differs from plain JS where a
65
+ // non-strict function sees `this === globalThis` (see README Limitations).
66
+ // Real method calls still get their receiver unmarshalled.
67
+ const that = ctx.sameValue(this, ctx.global) ? undefined : unmarshal(this);
68
+ const args = argHandles.map(a => unmarshal(a));
69
+
70
+ if (isClass && isObject(that)) {
71
+ // Class constructors cannot be invoked without new expression, and new.target is not changed
72
+ const result = new (target as new (...args: any[]) => any)(...args);
73
+ Object.entries(result).forEach(([key, value]) => {
74
+ const valueHandle = marshal(value);
75
+ ctx.setProp(this, key, valueHandle);
76
+ // setProp dup'd the value into `this`; drop ours if it was transient.
77
+ disposeTransient(valueHandle);
78
+ });
79
+ return this;
80
+ }
81
+
82
+ // The VM disposes whatever we return here. `prepareReturn` dups the
83
+ // handle when the VMMap retains it, so the map keeps a live copy and
84
+ // identity (`x === fn()` across calls) survives instead of going stale.
85
+ return prepareReturn(
86
+ marshal(
87
+ preApply
88
+ ? preApply(target as (...args: any[]) => any, that, args)
89
+ : (target as (...args: any[]) => any).apply(that, args),
90
+ ),
91
+ );
92
+ })
93
+ );
69
94
 
70
- const handle = preMarshal(target, raw) ?? raw;
71
- marshalProperties(ctx, target, raw, marshal, disposeTransient);
95
+ // `consume` disposes the raw newFunction handle even if `call` throws (the raw
96
+ // `Lifetime.consume` would skip disposal on throw, leaking the function handle).
97
+ const raw = consume(inner, handle2 =>
98
+ // functions created by vm.newFunction are not callable as a class constructor
99
+ call(
100
+ ctx,
101
+ `Cls => {
102
+ const fn = function(...args) { return Cls.apply(this, args); };
103
+ fn.name = Cls.name;
104
+ fn.length = Cls.length;
105
+ return fn;
106
+ }`,
107
+ undefined,
108
+ handle2,
109
+ ),
110
+ );
72
111
 
73
- return handle;
112
+ // Own `raw` until `preMarshal` registers it; dispose it if `preMarshal` throws
113
+ // mid-flight so the wrapped function handle is not orphaned.
114
+ let ownRaw = true;
115
+ try {
116
+ const handle = preMarshal(target, raw) ?? raw;
117
+ ownRaw = false;
118
+ marshalProperties(ctx, target, raw, marshal, disposeTransient);
119
+ return handle;
120
+ } finally {
121
+ if (ownRaw && raw.alive) raw.dispose();
122
+ }
74
123
  }
@@ -33,6 +33,14 @@ export type Options = {
33
33
  // the VMMap retains (for identity, while sync is on) must be dup'd here or its
34
34
  // map entry goes stale. Defaults to identity (no-op).
35
35
  prepareReturn?: (handle: QuickJSHandle) => QuickJSHandle;
36
+ // Strip a host-side proxy wrapper from a target. Used to detect a wrapped class
37
+ // constructor, which would otherwise be hidden behind the proxy. Defaults to identity.
38
+ unwrap?: (target: unknown) => unknown;
39
+ // Decide whether a host function should be marshalled as an Asyncified function
40
+ // (the VM suspends until the host promise settles, so the guest gets the
41
+ // resolved value synchronously). Called with the unwrapped target. Only takes
42
+ // effect when the context supports `newAsyncifiedFunction`.
43
+ asyncify?: (target: unknown) => boolean;
36
44
  custom?: Iterable<(obj: unknown, ctx: QuickJSContext) => QuickJSHandle | undefined>;
37
45
  };
38
46
 
@@ -87,6 +95,8 @@ export function marshal(target: unknown, options: Options): QuickJSHandle {
87
95
  options.preApply,
88
96
  disposeTransient,
89
97
  options.prepareReturn,
98
+ options.unwrap,
99
+ options.asyncify,
90
100
  ) ??
91
101
  marshalMapSet(ctx, target, marshal2, pre2, disposeTransient) ??
92
102
  marshalObject(ctx, target, marshal2, pre2, disposeTransient) ??
@@ -11,27 +11,41 @@ export default function marshalMapSet(
11
11
  ): QuickJSHandle | undefined {
12
12
  if (target instanceof Map) {
13
13
  const raw = call(ctx, "() => new Map()");
14
- const handle = preMarshal(target, raw) ?? raw;
15
- for (const [k, v] of target) {
16
- const kh = marshal(k);
17
- const vh = marshal(v);
18
- call(ctx, "(m, k, v) => m.set(k, v)", undefined, raw, kh, vh).dispose();
19
- // set() has taken its own references; drop ours if they were transient.
20
- disposeTransient(kh);
21
- disposeTransient(vh);
14
+ // Own `raw` until `preMarshal` registers it; dispose it if `preMarshal`
15
+ // throws mid-flight so the fresh Map handle is not orphaned.
16
+ let ownRaw = true;
17
+ try {
18
+ const handle = preMarshal(target, raw) ?? raw;
19
+ ownRaw = false;
20
+ for (const [k, v] of target) {
21
+ const kh = marshal(k);
22
+ const vh = marshal(v);
23
+ call(ctx, "(m, k, v) => m.set(k, v)", undefined, raw, kh, vh).dispose();
24
+ // set() has taken its own references; drop ours if they were transient.
25
+ disposeTransient(kh);
26
+ disposeTransient(vh);
27
+ }
28
+ return handle;
29
+ } finally {
30
+ if (ownRaw && raw.alive) raw.dispose();
22
31
  }
23
- return handle;
24
32
  }
25
33
 
26
34
  if (target instanceof Set) {
27
35
  const raw = call(ctx, "() => new Set()");
28
- const handle = preMarshal(target, raw) ?? raw;
29
- for (const v of target) {
30
- const vh = marshal(v);
31
- call(ctx, "(s, v) => s.add(v)", undefined, raw, vh).dispose();
32
- // add() has taken its own reference; drop ours if it was transient.
33
- disposeTransient(vh);
36
+ let ownRaw = true;
37
+ try {
38
+ const handle = preMarshal(target, raw) ?? raw;
39
+ ownRaw = false;
40
+ for (const v of target) {
41
+ const vh = marshal(v);
42
+ call(ctx, "(s, v) => s.add(v)", undefined, raw, vh).dispose();
43
+ // add() has taken its own reference; drop ours if it was transient.
44
+ disposeTransient(vh);
45
+ }
46
+ return handle;
47
+ } finally {
48
+ if (ownRaw && raw.alive) raw.dispose();
34
49
  }
35
- return handle;
36
50
  }
37
51
  }
@@ -14,21 +14,30 @@ export default function marshalObject(
14
14
  if (typeof target !== "object" || target === null) return;
15
15
 
16
16
  const raw = Array.isArray(target) ? ctx.newArray() : ctx.newObject();
17
- const handle = preMarshal(target, raw) ?? raw;
17
+ // We own `raw` until `preMarshal` registers it (in the map or the transient
18
+ // set) or returns it as `handle`. If `preMarshal` throws mid-flight (e.g. an
19
+ // OOM while creating the proxy), dispose `raw` so it is not orphaned.
20
+ let ownRaw = true;
21
+ try {
22
+ const handle = preMarshal(target, raw) ?? raw;
23
+ ownRaw = false;
18
24
 
19
- // prototype
20
- const prototype = Object.getPrototypeOf(target);
21
- const prototypeHandle =
22
- prototype && prototype !== Object.prototype && prototype !== Array.prototype
23
- ? marshal(prototype)
24
- : undefined;
25
- if (prototypeHandle) {
26
- call(ctx, "Object.setPrototypeOf", undefined, handle, prototypeHandle).dispose();
27
- // setPrototypeOf has taken its own reference; drop ours if it was transient.
28
- disposeTransient(prototypeHandle);
29
- }
25
+ // prototype
26
+ const prototype = Object.getPrototypeOf(target);
27
+ const prototypeHandle =
28
+ prototype && prototype !== Object.prototype && prototype !== Array.prototype
29
+ ? marshal(prototype)
30
+ : undefined;
31
+ if (prototypeHandle) {
32
+ call(ctx, "Object.setPrototypeOf", undefined, handle, prototypeHandle).dispose();
33
+ // setPrototypeOf has taken its own reference; drop ours if it was transient.
34
+ disposeTransient(prototypeHandle);
35
+ }
30
36
 
31
- marshalProperties(ctx, target, raw, marshal, disposeTransient);
37
+ marshalProperties(ctx, target, raw, marshal, disposeTransient);
32
38
 
33
- return handle;
39
+ return handle;
40
+ } finally {
41
+ if (ownRaw && raw.alive) raw.dispose();
42
+ }
34
43
  }
@@ -1,8 +1,8 @@
1
- import {
2
- getQuickJS,
3
- type Disposable,
4
- type QuickJSDeferredPromise,
5
- type QuickJSHandle,
1
+ import { getQuickJS } from "quickjs-emscripten";
2
+ import type {
3
+ Disposable,
4
+ QuickJSDeferredPromise,
5
+ QuickJSHandle,
6
6
  } from "quickjs-emscripten";
7
7
  import { expect, test, vi } from "vitest";
8
8
 
@@ -15,15 +15,17 @@ const testPromise = (reject: boolean) => async () => {
15
15
  const ctx = (await getQuickJS()).newContext();
16
16
 
17
17
  const disposables: Disposable[] = [];
18
- const marshal = vi.fn(v => {
18
+ const marshal = vi.fn((v) => {
19
19
  const handle = json(ctx, v);
20
20
  disposables.push(handle);
21
21
  return handle;
22
22
  });
23
- const preMarshal = vi.fn((_: any, a: QuickJSDeferredPromise): QuickJSHandle => {
24
- disposables.push(a);
25
- return a.handle;
26
- });
23
+ const preMarshal = vi.fn(
24
+ (_: any, a: QuickJSDeferredPromise): QuickJSHandle => {
25
+ disposables.push(a);
26
+ return a.handle;
27
+ },
28
+ );
27
29
 
28
30
  const mockNotify = vi.fn();
29
31
  const notify = ctx.newFunction("notify", (handle1, handle2) => {
@@ -77,7 +79,7 @@ const testPromise = (reject: boolean) => async () => {
77
79
  expect(marshal).toBeCalledTimes(1);
78
80
  expect(marshal.mock.calls).toEqual([["hoge"]]);
79
81
 
80
- disposables.forEach(h => h.dispose());
82
+ disposables.forEach((h) => h.dispose());
81
83
  ctx.dispose();
82
84
  };
83
85
 
@@ -9,11 +9,19 @@ export default function marshalPromise(
9
9
  if (!(target instanceof Promise)) return;
10
10
 
11
11
  const promise = ctx.newPromise();
12
+ // Own the deferred promise until `preMarshal` registers it; dispose it (handle
13
+ // plus resolve/reject callbacks) if `preMarshal` throws mid-flight.
14
+ let owned = true;
15
+ try {
16
+ target.then(
17
+ d => promise.resolve(marshal(d)),
18
+ d => promise.reject(marshal(d)),
19
+ );
12
20
 
13
- target.then(
14
- d => promise.resolve(marshal(d)),
15
- d => promise.reject(marshal(d)),
16
- );
17
-
18
- return preMarshal(target, promise) ?? promise.handle;
21
+ const result = preMarshal(target, promise) ?? promise.handle;
22
+ owned = false;
23
+ return result;
24
+ } finally {
25
+ if (owned && promise.alive) promise.dispose();
26
+ }
19
27
  }
@@ -1,4 +1,5 @@
1
- import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
1
+ import type { QuickJSHandle } from "quickjs-emscripten";
2
+ import { getQuickJS } from "quickjs-emscripten";
2
3
  import { expect, test, vi } from "vitest";
3
4
 
4
5
  import { json } from "../vmutil";
@@ -23,7 +24,7 @@ test("works", async () => {
23
24
  );
24
25
 
25
26
  const disposables: QuickJSHandle[] = [];
26
- const marshal = vi.fn(t => {
27
+ const marshal = vi.fn((t) => {
27
28
  if (typeof t !== "function") return json(ctx, t);
28
29
  const fn = ctx.newFunction("", () => {});
29
30
  disposables.push(fn);
@@ -51,7 +52,13 @@ test("works", async () => {
51
52
  });
52
53
 
53
54
  marshalProperties(ctx, obj, handle, marshal);
54
- expect(marshal.mock.calls).toEqual([["bar"], [bar], ["foo"], [fooGet], [fooSet]]);
55
+ expect(marshal.mock.calls).toEqual([
56
+ ["bar"],
57
+ [bar],
58
+ ["foo"],
59
+ [fooGet],
60
+ [fooSet],
61
+ ]);
55
62
 
56
63
  const expected = ctx.unwrapResult(
57
64
  ctx.evalCode(`({
@@ -59,10 +66,12 @@ test("works", async () => {
59
66
  foo: { valueType: "undefined", getType: "function", setType: "function", enumerable: false, configurable: true }
60
67
  })`),
61
68
  );
62
- ctx.unwrapResult(ctx.callFunction(descTester, ctx.undefined, handle, expected));
69
+ ctx.unwrapResult(
70
+ ctx.callFunction(descTester, ctx.undefined, handle, expected),
71
+ );
63
72
 
64
73
  expected.dispose();
65
- disposables.forEach(d => d.dispose());
74
+ disposables.forEach((d) => d.dispose());
66
75
  handle.dispose();
67
76
  descTester.dispose();
68
77
  ctx.dispose();
@@ -1,6 +1,6 @@
1
1
  import type { QuickJSContext, QuickJSHandle } from "quickjs-emscripten";
2
2
 
3
- import { call } from "../vmutil";
3
+ import { call, consume } from "../vmutil";
4
4
 
5
5
  export default function marshalProperties(
6
6
  ctx: QuickJSContext,
@@ -9,12 +9,55 @@ export default function marshalProperties(
9
9
  marshal: (target: unknown) => QuickJSHandle,
10
10
  disposeTransient: (handle: QuickJSHandle) => void = () => {},
11
11
  ): void {
12
- const descs = ctx.newObject();
13
- // Property values may be transient (json copies / BigInt) with no owner; once
14
- // `Object.defineProperties` has copied them into `handle` the standalone
15
- // handles are redundant and disposed below. Owned handles are left untouched.
12
+ // `descs` aggregates only the properties that need the full descriptor path
13
+ // (accessors, non-default flags, symbol keys). It is created lazily so plain
14
+ // objects/arrays skip both the `ctx.newObject()` and the final
15
+ // `Object.defineProperties` roundtrip entirely.
16
+ let descs: QuickJSHandle | undefined;
17
+ // Descriptor-path values may be transient (json copies / BigInt) with no
18
+ // owner; once `Object.defineProperties` has copied them into `handle` the
19
+ // standalone handles are redundant and disposed below. Owned handles are left
20
+ // untouched. Fast-path values are disposed inline (see below).
16
21
  const transient: QuickJSHandle[] = [];
22
+
23
+ // `ctx.setProp` uses `[[Set]]` semantics, which walks the prototype chain and
24
+ // would invoke an inherited accessor/setter (or the `__proto__` setter)
25
+ // instead of creating an own property. That only matters when `marshalObject`
26
+ // installed a custom prototype (see marshal/object.ts): a default-prototype VM
27
+ // object (plain object / array) has no shadowing accessors, so `setProp` on a
28
+ // normal string key is identical to `defineProperty` with all flags true. For
29
+ // custom-prototype objects (e.g. class instances) we must keep the descriptor
30
+ // path, which uses `[[DefineOwnProperty]]` semantics.
31
+ const proto = Object.getPrototypeOf(target);
32
+ const defaultProto = proto === Object.prototype || proto === Array.prototype;
33
+
17
34
  const cb = (key: string | number | symbol, desc: PropertyDescriptor) => {
35
+ // Fast path: a plain data property with a string key and all flags at their
36
+ // defaults, on a default-prototype object, is semantically identical to
37
+ // `ctx.setProp` on a fresh object/array, so we skip building a descriptor
38
+ // object. `writable === true` implies a data descriptor (accessors have no
39
+ // `writable`), so there is no get/set. Symbol keys keep the descriptor path
40
+ // (setProp string-key fast path does not apply). `__proto__` is excluded
41
+ // because it resolves to the prototype setter under `[[Set]]`. The value is
42
+ // copied into `handle` by setProp immediately, so its transient handle can
43
+ // be disposed right away.
44
+ if (
45
+ defaultProto &&
46
+ typeof key === "string" &&
47
+ key !== "__proto__" &&
48
+ desc.writable === true &&
49
+ desc.enumerable === true &&
50
+ desc.configurable === true &&
51
+ desc.get === undefined &&
52
+ desc.set === undefined
53
+ ) {
54
+ const keyHandle = marshal(key);
55
+ const valueHandle = marshal(desc.value);
56
+ ctx.setProp(handle, keyHandle, valueHandle);
57
+ disposeTransient(valueHandle);
58
+ return;
59
+ }
60
+
18
61
  const keyHandle = marshal(key);
19
62
  const valueHandle = typeof desc.value === "undefined" ? undefined : marshal(desc.value);
20
63
  const getHandle = typeof desc.get === "undefined" ? undefined : marshal(desc.get);
@@ -23,7 +66,10 @@ export default function marshalProperties(
23
66
  if (getHandle) transient.push(getHandle);
24
67
  if (setHandle) transient.push(setHandle);
25
68
 
26
- ctx.newObject().consume(descObj => {
69
+ const descsHandle = (descs ??= ctx.newObject());
70
+ // `consume` disposes the descriptor object even if a `setProp` throws
71
+ // mid-flight, so the intermediate handle is not orphaned.
72
+ consume(ctx.newObject(), descObj => {
27
73
  Object.entries(desc).forEach(([k, v]) => {
28
74
  const v2 =
29
75
  k === "value"
@@ -39,7 +85,7 @@ export default function marshalProperties(
39
85
  ctx.setProp(descObj, k, v2);
40
86
  }
41
87
  });
42
- ctx.setProp(descs, keyHandle, descObj);
88
+ ctx.setProp(descsHandle, keyHandle, descObj);
43
89
  });
44
90
  };
45
91
 
@@ -48,11 +94,13 @@ export default function marshalProperties(
48
94
  Object.entries(desc).forEach(([k, v]) => cb(k, v));
49
95
  Object.getOwnPropertySymbols(desc).forEach(k => cb(k, (desc as any)[k]));
50
96
 
51
- call(ctx, `Object.defineProperties`, undefined, handle, descs).dispose();
52
- // Safe only after defineProperties has dup'd the values into `handle`; `descs`
53
- // still holds its own references until its own dispose() in the finally.
54
- for (const h of transient) disposeTransient(h);
97
+ if (descs) {
98
+ call(ctx, `Object.defineProperties`, undefined, handle, descs).dispose();
99
+ // Safe only after defineProperties has dup'd the values into `handle`;
100
+ // `descs` still holds its own references until its own dispose() below.
101
+ for (const h of transient) disposeTransient(h);
102
+ }
55
103
  } finally {
56
- descs.dispose();
104
+ descs?.dispose();
57
105
  }
58
106
  }
@@ -1,4 +1,5 @@
1
- import { getQuickJS, QuickJSHandle } from "quickjs-emscripten";
1
+ import type { QuickJSHandle } from "quickjs-emscripten";
2
+ import { getQuickJS } from "quickjs-emscripten";
2
3
  import { expect, test, vi } from "vitest";
3
4
 
4
5
  import unmarshalCustom, { defaultCustom } from "./custom";
@@ -9,7 +10,8 @@ test("symbol", async () => {
9
10
  const obj = ctx.newObject();
10
11
  const handle = ctx.unwrapResult(ctx.evalCode(`Symbol("foobar")`));
11
12
 
12
- const unmarshal = (h: QuickJSHandle): any => unmarshalCustom(ctx, h, pre, defaultCustom);
13
+ const unmarshal = (h: QuickJSHandle): any =>
14
+ unmarshalCustom(ctx, h, pre, defaultCustom);
13
15
 
14
16
  expect(unmarshal(obj)).toBe(undefined);
15
17
  expect(pre).toBeCalledTimes(0);
@@ -32,7 +34,8 @@ test("date", async () => {
32
34
  const obj = ctx.newObject();
33
35
  const handle = ctx.unwrapResult(ctx.evalCode(`new Date(2022, 7, 26)`));
34
36
 
35
- const unmarshal = (h: QuickJSHandle): any => unmarshalCustom(ctx, h, pre, defaultCustom);
37
+ const unmarshal = (h: QuickJSHandle): any =>
38
+ unmarshalCustom(ctx, h, pre, defaultCustom);
36
39
 
37
40
  expect(unmarshal(obj)).toBe(undefined);
38
41
  expect(pre).toBeCalledTimes(0);