quickjs-emscripten-sync 1.5.2 → 1.7.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.
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import type {
4
4
  QuickJSContext,
5
5
  SuccessOrFail,
6
6
  VmCallResult,
7
+ Intrinsics,
7
8
  } from "quickjs-emscripten";
8
9
 
9
10
  import { wrapContext, QuickJSContextEx } from "./contextex";
@@ -31,6 +32,8 @@ export {
31
32
  consumeAll,
32
33
  };
33
34
 
35
+ export type { Intrinsics };
36
+
34
37
  export type Options = {
35
38
  /** A callback that returns a boolean value that determines whether an object is marshalled or not. If false, no marshaling will be done and undefined will be passed to the QuickJS VM, otherwise marshaling will be done. By default, all objects will be marshalled. */
36
39
  isMarshalable?: boolean | "json" | ((target: any) => boolean | "json");
@@ -103,6 +106,43 @@ export class Arena {
103
106
  return this._unwrapResultAndUnmarshal(handle);
104
107
  }
105
108
 
109
+ /**
110
+ * Evaluate ES module code in the VM and get the module's exports.
111
+ *
112
+ * Requires quickjs-emscripten >= 0.29.0 for export access.
113
+ *
114
+ * @param code - The ES module code to evaluate
115
+ * @param filename - Optional filename for debugging purposes (default: "module.js")
116
+ * @returns The module's exports object, or a Promise resolving to exports if using top-level await
117
+ *
118
+ * @example
119
+ * ```js
120
+ * // Simple module with exports
121
+ * const exports = arena.evalModule(`
122
+ * export const value = 42;
123
+ * export function greet(name) {
124
+ * return "Hello, " + name;
125
+ * }
126
+ * `);
127
+ * console.log(exports.value); // 42
128
+ * console.log(exports.greet("World")); // "Hello, World"
129
+ *
130
+ * // Module with default export
131
+ * const mod = arena.evalModule('export default function(x) { return x * 2; }');
132
+ * console.log(mod.default(21)); // 42
133
+ *
134
+ * // Module with top-level await
135
+ * const promise = arena.evalModule('export const data = await Promise.resolve(123);');
136
+ * arena.executePendingJobs();
137
+ * const exports = await promise;
138
+ * console.log(exports.data); // 123
139
+ * ```
140
+ */
141
+ evalModule<T = any>(code: string, filename = "module.js"): T | Promise<T> {
142
+ const handle = this.context.evalCode(code, filename, { type: "module" });
143
+ return this._unwrapResultAndUnmarshal(handle);
144
+ }
145
+
106
146
  /**
107
147
  * Almost same as `vm.executePendingJobs()`, but it converts and re-throws error objects when an error is thrown during evaluation.
108
148
  */
@@ -114,6 +154,121 @@ export class Arena {
114
154
  throw this._unwrapIfNotSynced(result.error.consume(this._unmarshal));
115
155
  }
116
156
 
157
+ /**
158
+ * Set the max memory this runtime can allocate.
159
+ * To remove the limit, set to `-1`.
160
+ *
161
+ * This is useful for preventing runaway memory usage in untrusted code.
162
+ *
163
+ * @param limitBytes - Maximum memory in bytes, or -1 to remove limit
164
+ *
165
+ * @example
166
+ * ```js
167
+ * // Limit sandbox to 10MB
168
+ * arena.setMemoryLimit(10 * 1024 * 1024);
169
+ *
170
+ * try {
171
+ * arena.evalCode(`const huge = new Array(1000000000);`);
172
+ * } catch (e) {
173
+ * console.log("Memory limit exceeded");
174
+ * }
175
+ * ```
176
+ */
177
+ setMemoryLimit(limitBytes: number): void {
178
+ this.context.runtime.setMemoryLimit(limitBytes);
179
+ }
180
+
181
+ /**
182
+ * Set the max stack size for this runtime, in bytes.
183
+ * To remove the limit, set to `0`.
184
+ *
185
+ * This is useful for preventing stack overflow from deeply nested calls or recursion.
186
+ *
187
+ * @param stackSize - Maximum stack size in bytes, or 0 to remove limit
188
+ *
189
+ * @example
190
+ * ```js
191
+ * // Limit stack to 512KB
192
+ * arena.setMaxStackSize(512 * 1024);
193
+ *
194
+ * try {
195
+ * arena.evalCode(`function recurse() { recurse(); } recurse();`);
196
+ * } catch (e) {
197
+ * console.log("Stack overflow prevented");
198
+ * }
199
+ * ```
200
+ */
201
+ setMaxStackSize(stackSize: number): void {
202
+ this.context.runtime.setMaxStackSize(stackSize);
203
+ }
204
+
205
+ /**
206
+ * Get detailed memory usage statistics for this runtime.
207
+ *
208
+ * @returns An object containing detailed memory allocation information
209
+ *
210
+ * @example
211
+ * ```js
212
+ * const stats = arena.getMemoryUsage();
213
+ * console.log(`Memory used: ${stats.memory_used_size} bytes`);
214
+ * console.log(`Object count: ${stats.obj_count}`);
215
+ * console.log(`Memory limit: ${stats.malloc_limit}`);
216
+ * ```
217
+ */
218
+ getMemoryUsage(): {
219
+ malloc_limit: number;
220
+ memory_used_size: number;
221
+ malloc_count: number;
222
+ memory_used_count: number;
223
+ atom_count: number;
224
+ atom_size: number;
225
+ str_count: number;
226
+ str_size: number;
227
+ obj_count: number;
228
+ obj_size: number;
229
+ prop_count: number;
230
+ prop_size: number;
231
+ shape_count: number;
232
+ shape_size: number;
233
+ js_func_count: number;
234
+ js_func_size: number;
235
+ js_func_code_size: number;
236
+ js_func_pc2line_count: number;
237
+ js_func_pc2line_size: number;
238
+ c_func_count: number;
239
+ array_count: number;
240
+ fast_array_count: number;
241
+ fast_array_elements: number;
242
+ binary_object_count: number;
243
+ binary_object_size: number;
244
+ } {
245
+ const handle = this.context.runtime.computeMemoryUsage();
246
+ try {
247
+ return this.context.dump(handle);
248
+ } finally {
249
+ handle.dispose();
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Get a human-readable description of memory usage in this runtime.
255
+ *
256
+ * @returns A formatted string showing memory statistics
257
+ *
258
+ * @example
259
+ * ```js
260
+ * console.log(arena.dumpMemoryUsage());
261
+ * // Output:
262
+ * // QuickJS memory usage:
263
+ * // malloc_limit: 4294967295
264
+ * // memory_used_size: 67078
265
+ * // ...
266
+ * ```
267
+ */
268
+ dumpMemoryUsage(): string {
269
+ return this.context.runtime.dumpMemoryUsage();
270
+ }
271
+
117
272
  /**
118
273
  * Expose objects as global objects in the VM.
119
274
  *
package/src/vmutil.ts CHANGED
@@ -10,10 +10,12 @@ export function fn(
10
10
  code: string,
11
11
  ): ((thisArg: QuickJSHandle | undefined, ...args: QuickJSHandle[]) => QuickJSHandle) & Disposable {
12
12
  const handle = ctx.unwrapResult(ctx.evalCode(code));
13
- const f = (thisArg: QuickJSHandle | undefined, ...args: QuickJSHandle[]): any => {
13
+ const f: any = (thisArg: QuickJSHandle | undefined, ...args: QuickJSHandle[]): any => {
14
14
  return ctx.unwrapResult(ctx.callFunction(handle, thisArg ?? ctx.undefined, ...args));
15
15
  };
16
- f.dispose = () => handle.dispose();
16
+ const disposeFn = () => handle.dispose();
17
+ f.dispose = disposeFn;
18
+ f[Symbol.dispose] = disposeFn;
17
19
  f.alive = true;
18
20
  Object.defineProperty(f, "alive", {
19
21
  get: () => handle.alive,