@ricsam/isolate-console 0.1.9 → 0.1.10

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 CHANGED
@@ -10,7 +10,7 @@ npm add @ricsam/isolate-console
10
10
 
11
11
  ## Usage
12
12
 
13
- The console module uses a single `onEntry` callback that receives structured `ConsoleEntry` objects:
13
+ The console module uses a single `onEntry` callback that receives structured `ConsoleEntry` objects. Output is pre-formatted as `stdout` strings (like Node.js console output) inside the sandbox:
14
14
 
15
15
  ```typescript
16
16
  import { setupConsole, type ConsoleEntry } from "@ricsam/isolate-console";
@@ -19,7 +19,7 @@ const handle = await setupConsole(context, {
19
19
  onEntry: (entry: ConsoleEntry) => {
20
20
  switch (entry.type) {
21
21
  case "output":
22
- console.log(`[${entry.level}]`, ...entry.args);
22
+ console.log(`[${entry.level}]`, entry.stdout);
23
23
  break;
24
24
  case "time":
25
25
  console.log(`${entry.label}: ${entry.duration}ms`);
@@ -49,35 +49,35 @@ import { setupConsole, simpleConsoleHandler } from "@ricsam/isolate-console";
49
49
  const handle = await setupConsole(
50
50
  context,
51
51
  simpleConsoleHandler({
52
- log: (...args) => console.log("[sandbox]", ...args),
53
- warn: (...args) => console.warn("[sandbox]", ...args),
54
- error: (...args) => console.error("[sandbox]", ...args),
55
- info: (...args) => console.info("[sandbox]", ...args),
56
- debug: (...args) => console.debug("[sandbox]", ...args),
52
+ log: (msg) => console.log("[sandbox]", msg),
53
+ warn: (msg) => console.warn("[sandbox]", msg),
54
+ error: (msg) => console.error("[sandbox]", msg),
55
+ info: (msg) => console.info("[sandbox]", msg),
56
+ debug: (msg) => console.debug("[sandbox]", msg),
57
57
  })
58
58
  );
59
59
  ```
60
60
 
61
61
  ## ConsoleEntry Types
62
62
 
63
- The `ConsoleEntry` discriminated union type includes all possible console events:
63
+ The `ConsoleEntry` discriminated union type includes all possible console events. Output is pre-formatted as `stdout` strings inside the sandbox (like Node.js console):
64
64
 
65
65
  ```typescript
66
66
  type ConsoleEntry =
67
67
  // Standard output (log, warn, error, info, debug)
68
- | { type: "output"; level: "log" | "warn" | "error" | "info" | "debug"; args: unknown[]; groupDepth: number }
68
+ | { type: "output"; level: "log" | "warn" | "error" | "info" | "debug"; stdout: string; groupDepth: number }
69
69
 
70
70
  // console.dir()
71
- | { type: "dir"; value: unknown; groupDepth: number }
71
+ | { type: "dir"; stdout: string; groupDepth: number }
72
72
 
73
73
  // console.table()
74
- | { type: "table"; data: unknown; columns?: string[]; groupDepth: number }
74
+ | { type: "table"; stdout: string; groupDepth: number }
75
75
 
76
76
  // console.timeEnd() - timer completed
77
77
  | { type: "time"; label: string; duration: number; groupDepth: number }
78
78
 
79
79
  // console.timeLog() - timer checkpoint
80
- | { type: "timeLog"; label: string; duration: number; args: unknown[]; groupDepth: number }
80
+ | { type: "timeLog"; label: string; duration: number; stdout: string; groupDepth: number }
81
81
 
82
82
  // console.count()
83
83
  | { type: "count"; label: string; count: number; groupDepth: number }
@@ -86,7 +86,7 @@ type ConsoleEntry =
86
86
  | { type: "countReset"; label: string; groupDepth: number }
87
87
 
88
88
  // console.assert() - failed assertion
89
- | { type: "assert"; args: unknown[]; groupDepth: number }
89
+ | { type: "assert"; stdout: string; groupDepth: number }
90
90
 
91
91
  // console.group() or console.groupCollapsed()
92
92
  | { type: "group"; label: string; collapsed: boolean; groupDepth: number }
@@ -98,11 +98,27 @@ type ConsoleEntry =
98
98
  | { type: "clear" }
99
99
 
100
100
  // console.trace()
101
- | { type: "trace"; args: unknown[]; stack: string; groupDepth: number };
101
+ | { type: "trace"; stdout: string; stack: string; groupDepth: number };
102
102
  ```
103
103
 
104
104
  Each entry includes `groupDepth` (except `clear`) which indicates the current nesting level of console groups. This allows you to render output with proper indentation without tracking state yourself.
105
105
 
106
+ ## Output Formatting
107
+
108
+ Console output is formatted inside the sandbox using Node.js-style formatting rules:
109
+
110
+ - **Strings**: passed as-is at top level, quoted in objects/arrays
111
+ - **Numbers/booleans**: converted to string
112
+ - **Functions**: `[Function: name]` or `[Function: (anonymous)]`
113
+ - **Arrays**: `[ 1, 2, 3 ]`
114
+ - **Objects**: `{ key: value }`
115
+ - **Errors**: `Error: message` with stack trace
116
+ - **Response/Request**: `Response { status: 200, ... }`
117
+ - **Map/Set**: `Map(n) { ... }`, `Set(n) { ... }`
118
+ - **Date**: ISO string
119
+ - **Circular refs**: `[Circular]`
120
+ - **Deep objects**: `[Object]` at depth limit (default 2)
121
+
106
122
  ## Injected Globals
107
123
 
108
124
  - `console.log`, `console.warn`, `console.error`, `console.debug`, `console.info`
@@ -116,7 +132,7 @@ Each entry includes `groupDepth` (except `clear`) which indicates the current ne
116
132
 
117
133
  ```javascript
118
134
  // Basic logging
119
- console.log("Hello", { name: "World" });
135
+ console.log("Hello", { name: "World" }); // "Hello { name: 'World' }"
120
136
  console.warn("Warning message");
121
137
  console.error("Error occurred");
122
138
 
@@ -144,18 +160,18 @@ console.groupEnd();
144
160
 
145
161
  | Entry Type | Description | Key Properties |
146
162
  |------------|-------------|----------------|
147
- | `output` | Standard logging (log, warn, error, info, debug) | `level`, `args` |
148
- | `dir` | Object inspection | `value` |
149
- | `table` | Tabular data display | `data`, `columns?` |
163
+ | `output` | Standard logging (log, warn, error, info, debug) | `level`, `stdout` |
164
+ | `dir` | Object inspection | `stdout` |
165
+ | `table` | Tabular data display (ASCII table) | `stdout` |
150
166
  | `time` | Timer completion (timeEnd) | `label`, `duration` |
151
- | `timeLog` | Timer checkpoint | `label`, `duration`, `args` |
167
+ | `timeLog` | Timer checkpoint | `label`, `duration`, `stdout` |
152
168
  | `count` | Counter increment | `label`, `count` |
153
169
  | `countReset` | Counter reset | `label` |
154
- | `assert` | Failed assertion | `args` |
170
+ | `assert` | Failed assertion | `stdout` |
155
171
  | `group` | Group start | `label`, `collapsed` |
156
172
  | `groupEnd` | Group end | - |
157
173
  | `clear` | Console clear | - |
158
- | `trace` | Stack trace | `args`, `stack` |
174
+ | `trace` | Stack trace | `stdout`, `stack` |
159
175
 
160
176
  ## License
161
177
 
@@ -54,35 +54,33 @@ async function setupConsole(context, options) {
54
54
  const global = context.global;
55
55
  const logLevels = ["log", "warn", "error", "debug", "info"];
56
56
  for (const level of logLevels) {
57
- global.setSync(`__console_${level}`, new import_isolated_vm.default.Callback((...args) => {
57
+ global.setSync(`__console_${level}`, new import_isolated_vm.default.Callback((stdout) => {
58
58
  opts.onEntry?.({
59
59
  type: "output",
60
60
  level,
61
- args,
61
+ stdout,
62
62
  groupDepth
63
63
  });
64
64
  }));
65
65
  }
66
- global.setSync("__console_dir", new import_isolated_vm.default.Callback((value) => {
66
+ global.setSync("__console_dir", new import_isolated_vm.default.Callback((stdout) => {
67
67
  opts.onEntry?.({
68
68
  type: "dir",
69
- value,
69
+ stdout,
70
70
  groupDepth
71
71
  });
72
72
  }));
73
- global.setSync("__console_table", new import_isolated_vm.default.Callback((data, columns) => {
73
+ global.setSync("__console_table", new import_isolated_vm.default.Callback((stdout) => {
74
74
  opts.onEntry?.({
75
75
  type: "table",
76
- data,
77
- columns,
76
+ stdout,
78
77
  groupDepth
79
78
  });
80
79
  }));
81
- global.setSync("__console_trace", new import_isolated_vm.default.Callback((...args) => {
82
- const stack = new Error().stack ?? "";
80
+ global.setSync("__console_trace", new import_isolated_vm.default.Callback((stdout, stack) => {
83
81
  opts.onEntry?.({
84
82
  type: "trace",
85
- args,
83
+ stdout,
86
84
  stack,
87
85
  groupDepth
88
86
  });
@@ -105,16 +103,16 @@ async function setupConsole(context, options) {
105
103
  });
106
104
  }
107
105
  }));
108
- global.setSync("__console_timeLog", new import_isolated_vm.default.Callback((label, ...args) => {
106
+ global.setSync("__console_timeLog", new import_isolated_vm.default.Callback((label, duration, stdout) => {
109
107
  const l = label ?? "default";
110
108
  const start = timers.get(l);
111
109
  if (start !== undefined) {
112
- const duration = performance.now() - start;
110
+ const actualDuration = performance.now() - start;
113
111
  opts.onEntry?.({
114
112
  type: "timeLog",
115
113
  label: l,
116
- duration,
117
- args,
114
+ duration: actualDuration,
115
+ stdout,
118
116
  groupDepth
119
117
  });
120
118
  }
@@ -171,35 +169,264 @@ async function setupConsole(context, options) {
171
169
  global.setSync("__console_clear", new import_isolated_vm.default.Callback(() => {
172
170
  opts.onEntry?.({ type: "clear" });
173
171
  }));
174
- global.setSync("__console_assert", new import_isolated_vm.default.Callback((condition, ...args) => {
175
- if (!condition) {
176
- opts.onEntry?.({
177
- type: "assert",
178
- args,
179
- groupDepth
180
- });
181
- }
172
+ global.setSync("__console_assert", new import_isolated_vm.default.Callback((stdout) => {
173
+ opts.onEntry?.({
174
+ type: "assert",
175
+ stdout,
176
+ groupDepth
177
+ });
182
178
  }));
183
179
  context.evalSync(`
180
+ // Format a single value for console output (Node.js style)
181
+ function __formatForConsole(value, options = {}) {
182
+ const { depth = 2, currentDepth = 0, seen = new WeakSet(), inObject = false } = options;
183
+
184
+ // Handle null/undefined
185
+ if (value === null) return 'null';
186
+ if (value === undefined) return 'undefined';
187
+
188
+ // Handle primitives
189
+ const type = typeof value;
190
+ if (type === 'string') {
191
+ // Strings: quoted when inside objects/arrays, raw when top-level console.log arg
192
+ return inObject ? "'" + value.replace(/'/g, "\\\\'") + "'" : value;
193
+ }
194
+ if (type === 'number' || type === 'boolean') {
195
+ return String(value);
196
+ }
197
+ if (type === 'bigint') {
198
+ return value.toString() + 'n';
199
+ }
200
+ if (type === 'symbol') {
201
+ return value.toString();
202
+ }
203
+ if (type === 'function') {
204
+ const name = value.name || '(anonymous)';
205
+ return '[Function: ' + name + ']';
206
+ }
207
+
208
+ // Handle objects
209
+ if (type === 'object') {
210
+ // Handle circular references BEFORE depth check (Node.js behavior)
211
+ if (seen.has(value)) {
212
+ return '[Circular]';
213
+ }
214
+ seen.add(value);
215
+
216
+ // Check depth limit
217
+ if (currentDepth >= depth) {
218
+ if (Array.isArray(value)) return '[Array]';
219
+ return '[Object]';
220
+ }
221
+
222
+ const nextOptions = { depth, currentDepth: currentDepth + 1, seen, inObject: true };
223
+
224
+ // Handle Error objects
225
+ if (value instanceof Error) {
226
+ let result = value.name + ': ' + value.message;
227
+ if (value.stack) {
228
+ // Get stack lines after the first line (which is the error message)
229
+ const stackLines = value.stack.split('\\n').slice(1);
230
+ if (stackLines.length > 0) {
231
+ result += '\\n' + stackLines.join('\\n');
232
+ }
233
+ }
234
+ return result;
235
+ }
236
+
237
+ // Handle Response objects
238
+ if (typeof Response !== 'undefined' && value instanceof Response) {
239
+ return 'Response { status: ' + value.status + ', statusText: ' + __formatForConsole(value.statusText, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';
240
+ }
241
+
242
+ // Handle Request objects
243
+ if (typeof Request !== 'undefined' && value instanceof Request) {
244
+ return 'Request { method: ' + __formatForConsole(value.method, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';
245
+ }
246
+
247
+ // Handle Headers objects
248
+ if (typeof Headers !== 'undefined' && value instanceof Headers) {
249
+ const entries = [];
250
+ value.forEach((v, k) => {
251
+ entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));
252
+ });
253
+ return 'Headers { ' + entries.join(', ') + ' }';
254
+ }
255
+
256
+ // Handle Date objects
257
+ if (value instanceof Date) {
258
+ return value.toISOString();
259
+ }
260
+
261
+ // Handle RegExp
262
+ if (value instanceof RegExp) {
263
+ return value.toString();
264
+ }
265
+
266
+ // Handle Map
267
+ if (value instanceof Map) {
268
+ const entries = [];
269
+ value.forEach((v, k) => {
270
+ entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));
271
+ });
272
+ return 'Map(' + value.size + ') { ' + entries.join(', ') + ' }';
273
+ }
274
+
275
+ // Handle Set
276
+ if (value instanceof Set) {
277
+ const entries = [];
278
+ value.forEach((v) => {
279
+ entries.push(__formatForConsole(v, nextOptions));
280
+ });
281
+ return 'Set(' + value.size + ') { ' + entries.join(', ') + ' }';
282
+ }
283
+
284
+ // Handle ArrayBuffer and TypedArrays
285
+ if (value instanceof ArrayBuffer) {
286
+ return 'ArrayBuffer { byteLength: ' + value.byteLength + ' }';
287
+ }
288
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
289
+ const typedArray = value;
290
+ const name = typedArray.constructor.name;
291
+ const length = typedArray.length;
292
+ if (length <= 10) {
293
+ const items = Array.from(typedArray).map(v => __formatForConsole(v, nextOptions));
294
+ return name + '(' + length + ') [ ' + items.join(', ') + ' ]';
295
+ }
296
+ return name + '(' + length + ') [ ... ]';
297
+ }
298
+
299
+ // Handle Promise
300
+ if (value instanceof Promise) {
301
+ return 'Promise { <pending> }';
302
+ }
303
+
304
+ // Handle arrays
305
+ if (Array.isArray(value)) {
306
+ if (value.length === 0) return '[]';
307
+ const items = value.map(item => __formatForConsole(item, nextOptions));
308
+ return '[ ' + items.join(', ') + ' ]';
309
+ }
310
+
311
+ // Handle plain objects
312
+ const keys = Object.keys(value);
313
+ if (keys.length === 0) return '{}';
314
+ const entries = keys.map(key => {
315
+ const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : __formatForConsole(key, nextOptions);
316
+ return formattedKey + ': ' + __formatForConsole(value[key], nextOptions);
317
+ });
318
+ return '{ ' + entries.join(', ') + ' }';
319
+ }
320
+
321
+ return String(value);
322
+ }
323
+
324
+ // Format multiple args with space separation (like console.log)
325
+ function __formatArgs(args) {
326
+ return args.map((arg, i) => __formatForConsole(arg, { inObject: false })).join(' ');
327
+ }
328
+
329
+ // Format data for console.table - creates ASCII table
330
+ function __formatTable(data, columns) {
331
+ if (data === null || data === undefined) {
332
+ return __formatForConsole(data);
333
+ }
334
+
335
+ // Convert to array of objects
336
+ let rows = [];
337
+ let headers = new Set();
338
+
339
+ if (Array.isArray(data)) {
340
+ rows = data.map((item, index) => {
341
+ if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
342
+ Object.keys(item).forEach(k => headers.add(k));
343
+ return { __index: index, ...item };
344
+ }
345
+ return { __index: index, Values: item };
346
+ });
347
+ headers.add('Values');
348
+ } else if (typeof data === 'object') {
349
+ Object.keys(data).forEach(key => {
350
+ const item = data[key];
351
+ if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
352
+ Object.keys(item).forEach(k => headers.add(k));
353
+ rows.push({ __index: key, ...item });
354
+ } else {
355
+ rows.push({ __index: key, Values: item });
356
+ headers.add('Values');
357
+ }
358
+ });
359
+ } else {
360
+ return __formatForConsole(data);
361
+ }
362
+
363
+ // Filter headers by columns if provided
364
+ let headerList = ['(index)', ...headers];
365
+ headerList = headerList.filter(h => h !== '__index');
366
+ if (columns && Array.isArray(columns)) {
367
+ headerList = ['(index)', ...columns.filter(c => headers.has(c))];
368
+ }
369
+
370
+ // Calculate column widths
371
+ const colWidths = {};
372
+ headerList.forEach(h => {
373
+ colWidths[h] = h === '(index)' ? 7 : h.length;
374
+ });
375
+ rows.forEach(row => {
376
+ headerList.forEach(h => {
377
+ const key = h === '(index)' ? '__index' : h;
378
+ const val = row[key];
379
+ const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';
380
+ colWidths[h] = Math.max(colWidths[h], formatted.length);
381
+ });
382
+ });
383
+
384
+ // Build table
385
+ const sep = '+' + headerList.map(h => '-'.repeat(colWidths[h] + 2)).join('+') + '+';
386
+ const headerRow = '|' + headerList.map(h => ' ' + h.padEnd(colWidths[h]) + ' ').join('|') + '|';
387
+
388
+ const dataRows = rows.map(row => {
389
+ return '|' + headerList.map(h => {
390
+ const key = h === '(index)' ? '__index' : h;
391
+ const val = row[key];
392
+ const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';
393
+ return ' ' + formatted.padEnd(colWidths[h]) + ' ';
394
+ }).join('|') + '|';
395
+ });
396
+
397
+ return [sep, headerRow, sep, ...dataRows, sep].join('\\n');
398
+ }
399
+
184
400
  globalThis.console = {
185
- log: __console_log,
186
- warn: __console_warn,
187
- error: __console_error,
188
- debug: __console_debug,
189
- info: __console_info,
190
- trace: __console_trace,
191
- dir: __console_dir,
192
- table: __console_table,
401
+ log: (...args) => __console_log(__formatArgs(args)),
402
+ warn: (...args) => __console_warn(__formatArgs(args)),
403
+ error: (...args) => __console_error(__formatArgs(args)),
404
+ debug: (...args) => __console_debug(__formatArgs(args)),
405
+ info: (...args) => __console_info(__formatArgs(args)),
406
+ trace: (...args) => {
407
+ const err = new Error();
408
+ const stack = err.stack || '';
409
+ // Remove the first two lines (Error and the trace call itself)
410
+ const stackLines = stack.split('\\n').slice(2).join('\\n');
411
+ __console_trace(__formatArgs(args), 'Trace' + (args.length > 0 ? ': ' + __formatArgs(args) : '') + '\\n' + stackLines);
412
+ },
413
+ dir: (value, options) => __console_dir(__formatForConsole(value, { depth: options?.depth ?? 2, inObject: true })),
414
+ table: (data, columns) => __console_table(__formatTable(data, columns)),
193
415
  time: __console_time,
194
416
  timeEnd: __console_timeEnd,
195
- timeLog: __console_timeLog,
417
+ timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),
196
418
  count: __console_count,
197
419
  countReset: __console_countReset,
198
420
  group: __console_group,
199
421
  groupCollapsed: __console_groupCollapsed,
200
422
  groupEnd: __console_groupEnd,
201
423
  clear: __console_clear,
202
- assert: __console_assert,
424
+ assert: (condition, ...args) => {
425
+ if (!condition) {
426
+ const msg = args.length > 0 ? __formatArgs(args) : 'console.assert';
427
+ __console_assert('Assertion failed: ' + msg);
428
+ }
429
+ },
203
430
  };
204
431
  `);
205
432
  return {
@@ -225,4 +452,4 @@ async function setupConsole(context, options) {
225
452
  };
226
453
  }
227
454
 
228
- //# debugId=B7EF6485733B186B64756E2164756E21
455
+ //# debugId=C79A16572D336D2464756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import ivm from \"isolated-vm\";\n\n/**\n * Console entry types for structured console output.\n * Each entry type captures the specific data needed to render like DevTools.\n */\nexport type ConsoleEntry =\n | {\n type: \"output\";\n level: \"log\" | \"warn\" | \"error\" | \"info\" | \"debug\";\n args: unknown[];\n groupDepth: number;\n }\n | {\n /** Browser console output (from Playwright page, not sandbox) */\n type: \"browserOutput\";\n level: string;\n args: unknown[];\n timestamp: number;\n }\n | { type: \"dir\"; value: unknown; groupDepth: number }\n | { type: \"table\"; data: unknown; columns?: string[]; groupDepth: number }\n | { type: \"time\"; label: string; duration: number; groupDepth: number }\n | {\n type: \"timeLog\";\n label: string;\n duration: number;\n args: unknown[];\n groupDepth: number;\n }\n | { type: \"count\"; label: string; count: number; groupDepth: number }\n | { type: \"countReset\"; label: string; groupDepth: number }\n | { type: \"assert\"; args: unknown[]; groupDepth: number }\n | {\n type: \"group\";\n label: string;\n collapsed: boolean;\n groupDepth: number;\n }\n | { type: \"groupEnd\"; groupDepth: number }\n | { type: \"clear\" }\n | { type: \"trace\"; args: unknown[]; stack: string; groupDepth: number };\n\n/**\n * Console options with a single structured callback.\n */\nexport interface ConsoleOptions {\n /**\n * Callback invoked for each console operation.\n * Receives a structured entry with all data needed to render the output.\n */\n onEntry?: (entry: ConsoleEntry) => void;\n}\n\n/**\n * Console handle for accessing internal state.\n */\nexport interface ConsoleHandle {\n dispose(): void;\n reset(): void;\n getTimers(): Map<string, number>;\n getCounters(): Map<string, number>;\n getGroupDepth(): number;\n}\n\n\n/**\n * Setup console API in an isolated-vm context\n *\n * Injects console.log, console.warn, console.error, console.info, console.debug,\n * console.trace, console.dir, console.table, console.time, console.timeEnd,\n * console.timeLog, console.count, console.countReset, console.group,\n * console.groupCollapsed, console.groupEnd, console.clear, console.assert\n *\n * @example\n * const handle = await setupConsole(context, {\n * onEntry: (entry) => {\n * if (entry.type === 'output') {\n * console.log(`[${entry.level}]`, ...entry.args);\n * }\n * }\n * });\n */\nexport async function setupConsole(\n context: ivm.Context,\n options?: ConsoleOptions\n): Promise<ConsoleHandle> {\n const opts = options ?? {};\n\n // State management\n const timers = new Map<string, number>();\n const counters = new Map<string, number>();\n let groupDepth = 0;\n\n const global = context.global;\n\n // Log-level methods (output type)\n const logLevels = [\"log\", \"warn\", \"error\", \"debug\", \"info\"] as const;\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((...args: unknown[]) => {\n opts.onEntry?.({\n type: \"output\",\n level,\n args,\n groupDepth,\n });\n })\n );\n }\n\n // dir method\n global.setSync(\n \"__console_dir\",\n new ivm.Callback((value: unknown) => {\n opts.onEntry?.({\n type: \"dir\",\n value,\n groupDepth,\n });\n })\n );\n\n // table method\n global.setSync(\n \"__console_table\",\n new ivm.Callback((data: unknown, columns?: string[]) => {\n opts.onEntry?.({\n type: \"table\",\n data,\n columns,\n groupDepth,\n });\n })\n );\n\n // trace method (includes stack)\n global.setSync(\n \"__console_trace\",\n new ivm.Callback((...args: unknown[]) => {\n const stack = new Error().stack ?? \"\";\n opts.onEntry?.({\n type: \"trace\",\n args,\n stack,\n groupDepth,\n });\n })\n );\n\n // Timing methods\n global.setSync(\n \"__console_time\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n timers.set(l, performance.now());\n })\n );\n\n global.setSync(\n \"__console_timeEnd\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n timers.delete(l);\n opts.onEntry?.({\n type: \"time\",\n label: l,\n duration,\n groupDepth,\n });\n }\n })\n );\n\n global.setSync(\n \"__console_timeLog\",\n new ivm.Callback((label?: string, ...args: unknown[]) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n opts.onEntry?.({\n type: \"timeLog\",\n label: l,\n duration,\n args,\n groupDepth,\n });\n }\n })\n );\n\n // Counting methods\n global.setSync(\n \"__console_count\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const count = (counters.get(l) ?? 0) + 1;\n counters.set(l, count);\n opts.onEntry?.({\n type: \"count\",\n label: l,\n count,\n groupDepth,\n });\n })\n );\n\n global.setSync(\n \"__console_countReset\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n counters.delete(l);\n opts.onEntry?.({\n type: \"countReset\",\n label: l,\n groupDepth,\n });\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: false,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: true,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onEntry?.({\n type: \"groupEnd\",\n groupDepth,\n });\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onEntry?.({ type: \"clear\" });\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((condition: boolean, ...args: unknown[]) => {\n if (!condition) {\n opts.onEntry?.({\n type: \"assert\",\n args,\n groupDepth,\n });\n }\n })\n );\n\n // Inject console object\n context.evalSync(`\n globalThis.console = {\n log: __console_log,\n warn: __console_warn,\n error: __console_error,\n debug: __console_debug,\n info: __console_info,\n trace: __console_trace,\n dir: __console_dir,\n table: __console_table,\n time: __console_time,\n timeEnd: __console_timeEnd,\n timeLog: __console_timeLog,\n count: __console_count,\n countReset: __console_countReset,\n group: __console_group,\n groupCollapsed: __console_groupCollapsed,\n groupEnd: __console_groupEnd,\n clear: __console_clear,\n assert: __console_assert,\n };\n `);\n\n return {\n dispose() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n reset() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n getTimers() {\n return new Map(timers);\n },\n getCounters() {\n return new Map(counters);\n },\n getGroupDepth() {\n return groupDepth;\n },\n };\n}\n"
5
+ "import ivm from \"isolated-vm\";\n\n/**\n * Console entry types for structured console output.\n * Each entry type captures the specific data needed to render like DevTools.\n * Output is pre-formatted as stdout strings (like Node.js console) inside the sandbox.\n */\nexport type ConsoleEntry =\n | {\n type: \"output\";\n level: \"log\" | \"warn\" | \"error\" | \"info\" | \"debug\";\n stdout: string;\n groupDepth: number;\n }\n | {\n /** Browser console output (from Playwright page, not sandbox) */\n type: \"browserOutput\";\n level: string;\n stdout: string;\n timestamp: number;\n }\n | { type: \"dir\"; stdout: string; groupDepth: number }\n | { type: \"table\"; stdout: string; groupDepth: number }\n | { type: \"time\"; label: string; duration: number; groupDepth: number }\n | {\n type: \"timeLog\";\n label: string;\n duration: number;\n stdout: string;\n groupDepth: number;\n }\n | { type: \"count\"; label: string; count: number; groupDepth: number }\n | { type: \"countReset\"; label: string; groupDepth: number }\n | { type: \"assert\"; stdout: string; groupDepth: number }\n | {\n type: \"group\";\n label: string;\n collapsed: boolean;\n groupDepth: number;\n }\n | { type: \"groupEnd\"; groupDepth: number }\n | { type: \"clear\" }\n | { type: \"trace\"; stdout: string; stack: string; groupDepth: number };\n\n/**\n * Console options with a single structured callback.\n */\nexport interface ConsoleOptions {\n /**\n * Callback invoked for each console operation.\n * Receives a structured entry with all data needed to render the output.\n */\n onEntry?: (entry: ConsoleEntry) => void;\n}\n\n/**\n * Console handle for accessing internal state.\n */\nexport interface ConsoleHandle {\n dispose(): void;\n reset(): void;\n getTimers(): Map<string, number>;\n getCounters(): Map<string, number>;\n getGroupDepth(): number;\n}\n\n\n/**\n * Setup console API in an isolated-vm context\n *\n * Injects console.log, console.warn, console.error, console.info, console.debug,\n * console.trace, console.dir, console.table, console.time, console.timeEnd,\n * console.timeLog, console.count, console.countReset, console.group,\n * console.groupCollapsed, console.groupEnd, console.clear, console.assert\n *\n * @example\n * const handle = await setupConsole(context, {\n * onEntry: (entry) => {\n * if (entry.type === 'output') {\n * console.log(`[${entry.level}]`, ...entry.args);\n * }\n * }\n * });\n */\nexport async function setupConsole(\n context: ivm.Context,\n options?: ConsoleOptions\n): Promise<ConsoleHandle> {\n const opts = options ?? {};\n\n // State management\n const timers = new Map<string, number>();\n const counters = new Map<string, number>();\n let groupDepth = 0;\n\n const global = context.global;\n\n // Log-level methods (output type)\n const logLevels = [\"log\", \"warn\", \"error\", \"debug\", \"info\"] as const;\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"output\",\n level,\n stdout,\n groupDepth,\n });\n })\n );\n }\n\n // dir method\n global.setSync(\n \"__console_dir\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"dir\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // table method\n global.setSync(\n \"__console_table\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"table\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // trace method (includes stack)\n global.setSync(\n \"__console_trace\",\n new ivm.Callback((stdout: string, stack: string) => {\n opts.onEntry?.({\n type: \"trace\",\n stdout,\n stack,\n groupDepth,\n });\n })\n );\n\n // Timing methods\n global.setSync(\n \"__console_time\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n timers.set(l, performance.now());\n })\n );\n\n global.setSync(\n \"__console_timeEnd\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n timers.delete(l);\n opts.onEntry?.({\n type: \"time\",\n label: l,\n duration,\n groupDepth,\n });\n }\n })\n );\n\n global.setSync(\n \"__console_timeLog\",\n new ivm.Callback((label: string, duration: number, stdout: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const actualDuration = performance.now() - start;\n opts.onEntry?.({\n type: \"timeLog\",\n label: l,\n duration: actualDuration,\n stdout,\n groupDepth,\n });\n }\n })\n );\n\n // Counting methods\n global.setSync(\n \"__console_count\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const count = (counters.get(l) ?? 0) + 1;\n counters.set(l, count);\n opts.onEntry?.({\n type: \"count\",\n label: l,\n count,\n groupDepth,\n });\n })\n );\n\n global.setSync(\n \"__console_countReset\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n counters.delete(l);\n opts.onEntry?.({\n type: \"countReset\",\n label: l,\n groupDepth,\n });\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: false,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: true,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onEntry?.({\n type: \"groupEnd\",\n groupDepth,\n });\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onEntry?.({ type: \"clear\" });\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"assert\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // Inject console object with Node.js-style formatting\n context.evalSync(`\n // Format a single value for console output (Node.js style)\n function __formatForConsole(value, options = {}) {\n const { depth = 2, currentDepth = 0, seen = new WeakSet(), inObject = false } = options;\n\n // Handle null/undefined\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n // Handle primitives\n const type = typeof value;\n if (type === 'string') {\n // Strings: quoted when inside objects/arrays, raw when top-level console.log arg\n return inObject ? \"'\" + value.replace(/'/g, \"\\\\\\\\'\") + \"'\" : value;\n }\n if (type === 'number' || type === 'boolean') {\n return String(value);\n }\n if (type === 'bigint') {\n return value.toString() + 'n';\n }\n if (type === 'symbol') {\n return value.toString();\n }\n if (type === 'function') {\n const name = value.name || '(anonymous)';\n return '[Function: ' + name + ']';\n }\n\n // Handle objects\n if (type === 'object') {\n // Handle circular references BEFORE depth check (Node.js behavior)\n if (seen.has(value)) {\n return '[Circular]';\n }\n seen.add(value);\n\n // Check depth limit\n if (currentDepth >= depth) {\n if (Array.isArray(value)) return '[Array]';\n return '[Object]';\n }\n\n const nextOptions = { depth, currentDepth: currentDepth + 1, seen, inObject: true };\n\n // Handle Error objects\n if (value instanceof Error) {\n let result = value.name + ': ' + value.message;\n if (value.stack) {\n // Get stack lines after the first line (which is the error message)\n const stackLines = value.stack.split('\\\\n').slice(1);\n if (stackLines.length > 0) {\n result += '\\\\n' + stackLines.join('\\\\n');\n }\n }\n return result;\n }\n\n // Handle Response objects\n if (typeof Response !== 'undefined' && value instanceof Response) {\n return 'Response { status: ' + value.status + ', statusText: ' + __formatForConsole(value.statusText, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';\n }\n\n // Handle Request objects\n if (typeof Request !== 'undefined' && value instanceof Request) {\n return 'Request { method: ' + __formatForConsole(value.method, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';\n }\n\n // Handle Headers objects\n if (typeof Headers !== 'undefined' && value instanceof Headers) {\n const entries = [];\n value.forEach((v, k) => {\n entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));\n });\n return 'Headers { ' + entries.join(', ') + ' }';\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // Handle RegExp\n if (value instanceof RegExp) {\n return value.toString();\n }\n\n // Handle Map\n if (value instanceof Map) {\n const entries = [];\n value.forEach((v, k) => {\n entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));\n });\n return 'Map(' + value.size + ') { ' + entries.join(', ') + ' }';\n }\n\n // Handle Set\n if (value instanceof Set) {\n const entries = [];\n value.forEach((v) => {\n entries.push(__formatForConsole(v, nextOptions));\n });\n return 'Set(' + value.size + ') { ' + entries.join(', ') + ' }';\n }\n\n // Handle ArrayBuffer and TypedArrays\n if (value instanceof ArrayBuffer) {\n return 'ArrayBuffer { byteLength: ' + value.byteLength + ' }';\n }\n if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {\n const typedArray = value;\n const name = typedArray.constructor.name;\n const length = typedArray.length;\n if (length <= 10) {\n const items = Array.from(typedArray).map(v => __formatForConsole(v, nextOptions));\n return name + '(' + length + ') [ ' + items.join(', ') + ' ]';\n }\n return name + '(' + length + ') [ ... ]';\n }\n\n // Handle Promise\n if (value instanceof Promise) {\n return 'Promise { <pending> }';\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]';\n const items = value.map(item => __formatForConsole(item, nextOptions));\n return '[ ' + items.join(', ') + ' ]';\n }\n\n // Handle plain objects\n const keys = Object.keys(value);\n if (keys.length === 0) return '{}';\n const entries = keys.map(key => {\n const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : __formatForConsole(key, nextOptions);\n return formattedKey + ': ' + __formatForConsole(value[key], nextOptions);\n });\n return '{ ' + entries.join(', ') + ' }';\n }\n\n return String(value);\n }\n\n // Format multiple args with space separation (like console.log)\n function __formatArgs(args) {\n return args.map((arg, i) => __formatForConsole(arg, { inObject: false })).join(' ');\n }\n\n // Format data for console.table - creates ASCII table\n function __formatTable(data, columns) {\n if (data === null || data === undefined) {\n return __formatForConsole(data);\n }\n\n // Convert to array of objects\n let rows = [];\n let headers = new Set();\n\n if (Array.isArray(data)) {\n rows = data.map((item, index) => {\n if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n Object.keys(item).forEach(k => headers.add(k));\n return { __index: index, ...item };\n }\n return { __index: index, Values: item };\n });\n headers.add('Values');\n } else if (typeof data === 'object') {\n Object.keys(data).forEach(key => {\n const item = data[key];\n if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n Object.keys(item).forEach(k => headers.add(k));\n rows.push({ __index: key, ...item });\n } else {\n rows.push({ __index: key, Values: item });\n headers.add('Values');\n }\n });\n } else {\n return __formatForConsole(data);\n }\n\n // Filter headers by columns if provided\n let headerList = ['(index)', ...headers];\n headerList = headerList.filter(h => h !== '__index');\n if (columns && Array.isArray(columns)) {\n headerList = ['(index)', ...columns.filter(c => headers.has(c))];\n }\n\n // Calculate column widths\n const colWidths = {};\n headerList.forEach(h => {\n colWidths[h] = h === '(index)' ? 7 : h.length;\n });\n rows.forEach(row => {\n headerList.forEach(h => {\n const key = h === '(index)' ? '__index' : h;\n const val = row[key];\n const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';\n colWidths[h] = Math.max(colWidths[h], formatted.length);\n });\n });\n\n // Build table\n const sep = '+' + headerList.map(h => '-'.repeat(colWidths[h] + 2)).join('+') + '+';\n const headerRow = '|' + headerList.map(h => ' ' + h.padEnd(colWidths[h]) + ' ').join('|') + '|';\n\n const dataRows = rows.map(row => {\n return '|' + headerList.map(h => {\n const key = h === '(index)' ? '__index' : h;\n const val = row[key];\n const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';\n return ' ' + formatted.padEnd(colWidths[h]) + ' ';\n }).join('|') + '|';\n });\n\n return [sep, headerRow, sep, ...dataRows, sep].join('\\\\n');\n }\n\n globalThis.console = {\n log: (...args) => __console_log(__formatArgs(args)),\n warn: (...args) => __console_warn(__formatArgs(args)),\n error: (...args) => __console_error(__formatArgs(args)),\n debug: (...args) => __console_debug(__formatArgs(args)),\n info: (...args) => __console_info(__formatArgs(args)),\n trace: (...args) => {\n const err = new Error();\n const stack = err.stack || '';\n // Remove the first two lines (Error and the trace call itself)\n const stackLines = stack.split('\\\\n').slice(2).join('\\\\n');\n __console_trace(__formatArgs(args), 'Trace' + (args.length > 0 ? ': ' + __formatArgs(args) : '') + '\\\\n' + stackLines);\n },\n dir: (value, options) => __console_dir(__formatForConsole(value, { depth: options?.depth ?? 2, inObject: true })),\n table: (data, columns) => __console_table(__formatTable(data, columns)),\n time: __console_time,\n timeEnd: __console_timeEnd,\n timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),\n count: __console_count,\n countReset: __console_countReset,\n group: __console_group,\n groupCollapsed: __console_groupCollapsed,\n groupEnd: __console_groupEnd,\n clear: __console_clear,\n assert: (condition, ...args) => {\n if (!condition) {\n const msg = args.length > 0 ? __formatArgs(args) : 'console.assert';\n __console_assert('Assertion failed: ' + msg);\n }\n },\n };\n `);\n\n return {\n dispose() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n reset() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n getTimers() {\n return new Map(timers);\n },\n getCounters() {\n return new Map(counters);\n },\n getGroupDepth() {\n return groupDepth;\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "B7EF6485733B186B64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C79A16572D336D2464756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "commonjs"
5
5
  }
@@ -36,20 +36,20 @@ function simpleConsoleHandler(callbacks) {
36
36
  return {
37
37
  onEntry: (entry) => {
38
38
  if (entry.type === "output") {
39
- callbacks[entry.level]?.(...entry.args);
39
+ callbacks[entry.level]?.(entry.stdout);
40
40
  } else if (entry.type === "assert") {
41
- callbacks.error?.("Assertion failed:", ...entry.args);
41
+ callbacks.error?.(entry.stdout);
42
42
  } else if (entry.type === "trace") {
43
- callbacks.log?.(...entry.args, `
44
- ` + entry.stack);
43
+ callbacks.log?.(entry.stack);
45
44
  } else if (entry.type === "dir") {
46
- callbacks.log?.(entry.value);
45
+ callbacks.log?.(entry.stdout);
47
46
  } else if (entry.type === "table") {
48
- callbacks.log?.(entry.data);
47
+ callbacks.log?.(entry.stdout);
49
48
  } else if (entry.type === "time") {
50
49
  callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
51
50
  } else if (entry.type === "timeLog") {
52
- callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`, ...entry.args);
51
+ const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;
52
+ callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);
53
53
  } else if (entry.type === "count") {
54
54
  callbacks.log?.(`${entry.label}: ${entry.count}`);
55
55
  }
@@ -57,4 +57,4 @@ function simpleConsoleHandler(callbacks) {
57
57
  };
58
58
  }
59
59
 
60
- //# debugId=F5761AFCE3BD4E6F64756E2164756E21
60
+ //# debugId=83CDB12CE97F61ED64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils.ts"],
4
4
  "sourcesContent": [
5
- "import type { ConsoleOptions } from \"./index.cjs\";\n\n/**\n * Simple console callback interface for basic usage.\n */\nexport interface SimpleConsoleCallbacks {\n log?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n info?: (...args: unknown[]) => void;\n debug?: (...args: unknown[]) => void;\n}\n\n/**\n * Helper to create ConsoleOptions from simple callbacks.\n * Routes log-level outputs to the appropriate callback and handles assertions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: simpleConsoleHandler({\n * log: (...args) => console.log('[sandbox]', ...args),\n * warn: (...args) => console.warn('[sandbox]', ...args),\n * error: (...args) => console.error('[sandbox]', ...args),\n * })\n * });\n * ```\n */\nexport function simpleConsoleHandler(\n callbacks: SimpleConsoleCallbacks\n): ConsoleOptions {\n return {\n onEntry: (entry) => {\n if (entry.type === \"output\") {\n callbacks[entry.level]?.(...entry.args);\n } else if (entry.type === \"assert\") {\n callbacks.error?.(\"Assertion failed:\", ...entry.args);\n } else if (entry.type === \"trace\") {\n callbacks.log?.(...entry.args, \"\\n\" + entry.stack);\n } else if (entry.type === \"dir\") {\n callbacks.log?.(entry.value);\n } else if (entry.type === \"table\") {\n callbacks.log?.(entry.data);\n } else if (entry.type === \"time\") {\n callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);\n } else if (entry.type === \"timeLog\") {\n callbacks.log?.(\n `${entry.label}: ${entry.duration.toFixed(2)}ms`,\n ...entry.args\n );\n } else if (entry.type === \"count\") {\n callbacks.log?.(`${entry.label}: ${entry.count}`);\n }\n // group, groupEnd, groupEnd, countReset, clear are silently ignored\n },\n };\n}\n"
5
+ "import type { ConsoleOptions } from \"./index.cjs\";\n\n/**\n * Simple console callback interface for basic usage.\n */\nexport interface SimpleConsoleCallbacks {\n log?: (message: string) => void;\n warn?: (message: string) => void;\n error?: (message: string) => void;\n info?: (message: string) => void;\n debug?: (message: string) => void;\n}\n\n/**\n * Helper to create ConsoleOptions from simple callbacks.\n * Routes log-level outputs to the appropriate callback and handles assertions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: simpleConsoleHandler({\n * log: (msg) => console.log('[sandbox]', msg),\n * warn: (msg) => console.warn('[sandbox]', msg),\n * error: (msg) => console.error('[sandbox]', msg),\n * })\n * });\n * ```\n */\nexport function simpleConsoleHandler(\n callbacks: SimpleConsoleCallbacks\n): ConsoleOptions {\n return {\n onEntry: (entry) => {\n if (entry.type === \"output\") {\n callbacks[entry.level]?.(entry.stdout);\n } else if (entry.type === \"assert\") {\n callbacks.error?.(entry.stdout);\n } else if (entry.type === \"trace\") {\n callbacks.log?.(entry.stack);\n } else if (entry.type === \"dir\") {\n callbacks.log?.(entry.stdout);\n } else if (entry.type === \"table\") {\n callbacks.log?.(entry.stdout);\n } else if (entry.type === \"time\") {\n callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);\n } else if (entry.type === \"timeLog\") {\n const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;\n callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);\n } else if (entry.type === \"count\") {\n callbacks.log?.(`${entry.label}: ${entry.count}`);\n }\n // group, groupEnd, countReset, clear are silently ignored\n },\n };\n}\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BO,SAAS,oBAAoB,CAClC,WACgB;AAAA,EAChB,OAAO;AAAA,IACL,SAAS,CAAC,UAAU;AAAA,MAClB,IAAI,MAAM,SAAS,UAAU;AAAA,QAC3B,UAAU,MAAM,SAAS,GAAG,MAAM,IAAI;AAAA,MACxC,EAAO,SAAI,MAAM,SAAS,UAAU;AAAA,QAClC,UAAU,QAAQ,qBAAqB,GAAG,MAAM,IAAI;AAAA,MACtD,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,MAAM;AAAA,IAAO,MAAM,KAAK;AAAA,MACnD,EAAO,SAAI,MAAM,SAAS,OAAO;AAAA,QAC/B,UAAU,MAAM,MAAM,KAAK;AAAA,MAC7B,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,IAAI;AAAA,MAC5B,EAAO,SAAI,MAAM,SAAS,QAAQ;AAAA,QAChC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,KAAK;AAAA,MAClE,EAAO,SAAI,MAAM,SAAS,WAAW;AAAA,QACnC,UAAU,MACR,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,OAC3C,GAAG,MAAM,IACX;AAAA,MACF,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,OAAO;AAAA,MAClD;AAAA;AAAA,EAGJ;AAAA;",
8
- "debugId": "F5761AFCE3BD4E6F64756E2164756E21",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BO,SAAS,oBAAoB,CAClC,WACgB;AAAA,EAChB,OAAO;AAAA,IACL,SAAS,CAAC,UAAU;AAAA,MAClB,IAAI,MAAM,SAAS,UAAU;AAAA,QAC3B,UAAU,MAAM,SAAS,MAAM,MAAM;AAAA,MACvC,EAAO,SAAI,MAAM,SAAS,UAAU;AAAA,QAClC,UAAU,QAAQ,MAAM,MAAM;AAAA,MAChC,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,KAAK;AAAA,MAC7B,EAAO,SAAI,MAAM,SAAS,OAAO;AAAA,QAC/B,UAAU,MAAM,MAAM,MAAM;AAAA,MAC9B,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,MAAM;AAAA,MAC9B,EAAO,SAAI,MAAM,SAAS,QAAQ;AAAA,QAChC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,KAAK;AAAA,MAClE,EAAO,SAAI,MAAM,SAAS,WAAW;AAAA,QACnC,MAAM,UAAU,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC;AAAA,QAC3D,UAAU,MAAM,MAAM,SAAS,GAAG,WAAW,MAAM,WAAW,OAAO;AAAA,MACvE,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,OAAO;AAAA,MAClD;AAAA;AAAA,EAGJ;AAAA;",
8
+ "debugId": "83CDB12CE97F61ED64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -8,35 +8,33 @@ async function setupConsole(context, options) {
8
8
  const global = context.global;
9
9
  const logLevels = ["log", "warn", "error", "debug", "info"];
10
10
  for (const level of logLevels) {
11
- global.setSync(`__console_${level}`, new ivm.Callback((...args) => {
11
+ global.setSync(`__console_${level}`, new ivm.Callback((stdout) => {
12
12
  opts.onEntry?.({
13
13
  type: "output",
14
14
  level,
15
- args,
15
+ stdout,
16
16
  groupDepth
17
17
  });
18
18
  }));
19
19
  }
20
- global.setSync("__console_dir", new ivm.Callback((value) => {
20
+ global.setSync("__console_dir", new ivm.Callback((stdout) => {
21
21
  opts.onEntry?.({
22
22
  type: "dir",
23
- value,
23
+ stdout,
24
24
  groupDepth
25
25
  });
26
26
  }));
27
- global.setSync("__console_table", new ivm.Callback((data, columns) => {
27
+ global.setSync("__console_table", new ivm.Callback((stdout) => {
28
28
  opts.onEntry?.({
29
29
  type: "table",
30
- data,
31
- columns,
30
+ stdout,
32
31
  groupDepth
33
32
  });
34
33
  }));
35
- global.setSync("__console_trace", new ivm.Callback((...args) => {
36
- const stack = new Error().stack ?? "";
34
+ global.setSync("__console_trace", new ivm.Callback((stdout, stack) => {
37
35
  opts.onEntry?.({
38
36
  type: "trace",
39
- args,
37
+ stdout,
40
38
  stack,
41
39
  groupDepth
42
40
  });
@@ -59,16 +57,16 @@ async function setupConsole(context, options) {
59
57
  });
60
58
  }
61
59
  }));
62
- global.setSync("__console_timeLog", new ivm.Callback((label, ...args) => {
60
+ global.setSync("__console_timeLog", new ivm.Callback((label, duration, stdout) => {
63
61
  const l = label ?? "default";
64
62
  const start = timers.get(l);
65
63
  if (start !== undefined) {
66
- const duration = performance.now() - start;
64
+ const actualDuration = performance.now() - start;
67
65
  opts.onEntry?.({
68
66
  type: "timeLog",
69
67
  label: l,
70
- duration,
71
- args,
68
+ duration: actualDuration,
69
+ stdout,
72
70
  groupDepth
73
71
  });
74
72
  }
@@ -125,35 +123,264 @@ async function setupConsole(context, options) {
125
123
  global.setSync("__console_clear", new ivm.Callback(() => {
126
124
  opts.onEntry?.({ type: "clear" });
127
125
  }));
128
- global.setSync("__console_assert", new ivm.Callback((condition, ...args) => {
129
- if (!condition) {
130
- opts.onEntry?.({
131
- type: "assert",
132
- args,
133
- groupDepth
134
- });
135
- }
126
+ global.setSync("__console_assert", new ivm.Callback((stdout) => {
127
+ opts.onEntry?.({
128
+ type: "assert",
129
+ stdout,
130
+ groupDepth
131
+ });
136
132
  }));
137
133
  context.evalSync(`
134
+ // Format a single value for console output (Node.js style)
135
+ function __formatForConsole(value, options = {}) {
136
+ const { depth = 2, currentDepth = 0, seen = new WeakSet(), inObject = false } = options;
137
+
138
+ // Handle null/undefined
139
+ if (value === null) return 'null';
140
+ if (value === undefined) return 'undefined';
141
+
142
+ // Handle primitives
143
+ const type = typeof value;
144
+ if (type === 'string') {
145
+ // Strings: quoted when inside objects/arrays, raw when top-level console.log arg
146
+ return inObject ? "'" + value.replace(/'/g, "\\\\'") + "'" : value;
147
+ }
148
+ if (type === 'number' || type === 'boolean') {
149
+ return String(value);
150
+ }
151
+ if (type === 'bigint') {
152
+ return value.toString() + 'n';
153
+ }
154
+ if (type === 'symbol') {
155
+ return value.toString();
156
+ }
157
+ if (type === 'function') {
158
+ const name = value.name || '(anonymous)';
159
+ return '[Function: ' + name + ']';
160
+ }
161
+
162
+ // Handle objects
163
+ if (type === 'object') {
164
+ // Handle circular references BEFORE depth check (Node.js behavior)
165
+ if (seen.has(value)) {
166
+ return '[Circular]';
167
+ }
168
+ seen.add(value);
169
+
170
+ // Check depth limit
171
+ if (currentDepth >= depth) {
172
+ if (Array.isArray(value)) return '[Array]';
173
+ return '[Object]';
174
+ }
175
+
176
+ const nextOptions = { depth, currentDepth: currentDepth + 1, seen, inObject: true };
177
+
178
+ // Handle Error objects
179
+ if (value instanceof Error) {
180
+ let result = value.name + ': ' + value.message;
181
+ if (value.stack) {
182
+ // Get stack lines after the first line (which is the error message)
183
+ const stackLines = value.stack.split('\\n').slice(1);
184
+ if (stackLines.length > 0) {
185
+ result += '\\n' + stackLines.join('\\n');
186
+ }
187
+ }
188
+ return result;
189
+ }
190
+
191
+ // Handle Response objects
192
+ if (typeof Response !== 'undefined' && value instanceof Response) {
193
+ return 'Response { status: ' + value.status + ', statusText: ' + __formatForConsole(value.statusText, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';
194
+ }
195
+
196
+ // Handle Request objects
197
+ if (typeof Request !== 'undefined' && value instanceof Request) {
198
+ return 'Request { method: ' + __formatForConsole(value.method, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';
199
+ }
200
+
201
+ // Handle Headers objects
202
+ if (typeof Headers !== 'undefined' && value instanceof Headers) {
203
+ const entries = [];
204
+ value.forEach((v, k) => {
205
+ entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));
206
+ });
207
+ return 'Headers { ' + entries.join(', ') + ' }';
208
+ }
209
+
210
+ // Handle Date objects
211
+ if (value instanceof Date) {
212
+ return value.toISOString();
213
+ }
214
+
215
+ // Handle RegExp
216
+ if (value instanceof RegExp) {
217
+ return value.toString();
218
+ }
219
+
220
+ // Handle Map
221
+ if (value instanceof Map) {
222
+ const entries = [];
223
+ value.forEach((v, k) => {
224
+ entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));
225
+ });
226
+ return 'Map(' + value.size + ') { ' + entries.join(', ') + ' }';
227
+ }
228
+
229
+ // Handle Set
230
+ if (value instanceof Set) {
231
+ const entries = [];
232
+ value.forEach((v) => {
233
+ entries.push(__formatForConsole(v, nextOptions));
234
+ });
235
+ return 'Set(' + value.size + ') { ' + entries.join(', ') + ' }';
236
+ }
237
+
238
+ // Handle ArrayBuffer and TypedArrays
239
+ if (value instanceof ArrayBuffer) {
240
+ return 'ArrayBuffer { byteLength: ' + value.byteLength + ' }';
241
+ }
242
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
243
+ const typedArray = value;
244
+ const name = typedArray.constructor.name;
245
+ const length = typedArray.length;
246
+ if (length <= 10) {
247
+ const items = Array.from(typedArray).map(v => __formatForConsole(v, nextOptions));
248
+ return name + '(' + length + ') [ ' + items.join(', ') + ' ]';
249
+ }
250
+ return name + '(' + length + ') [ ... ]';
251
+ }
252
+
253
+ // Handle Promise
254
+ if (value instanceof Promise) {
255
+ return 'Promise { <pending> }';
256
+ }
257
+
258
+ // Handle arrays
259
+ if (Array.isArray(value)) {
260
+ if (value.length === 0) return '[]';
261
+ const items = value.map(item => __formatForConsole(item, nextOptions));
262
+ return '[ ' + items.join(', ') + ' ]';
263
+ }
264
+
265
+ // Handle plain objects
266
+ const keys = Object.keys(value);
267
+ if (keys.length === 0) return '{}';
268
+ const entries = keys.map(key => {
269
+ const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : __formatForConsole(key, nextOptions);
270
+ return formattedKey + ': ' + __formatForConsole(value[key], nextOptions);
271
+ });
272
+ return '{ ' + entries.join(', ') + ' }';
273
+ }
274
+
275
+ return String(value);
276
+ }
277
+
278
+ // Format multiple args with space separation (like console.log)
279
+ function __formatArgs(args) {
280
+ return args.map((arg, i) => __formatForConsole(arg, { inObject: false })).join(' ');
281
+ }
282
+
283
+ // Format data for console.table - creates ASCII table
284
+ function __formatTable(data, columns) {
285
+ if (data === null || data === undefined) {
286
+ return __formatForConsole(data);
287
+ }
288
+
289
+ // Convert to array of objects
290
+ let rows = [];
291
+ let headers = new Set();
292
+
293
+ if (Array.isArray(data)) {
294
+ rows = data.map((item, index) => {
295
+ if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
296
+ Object.keys(item).forEach(k => headers.add(k));
297
+ return { __index: index, ...item };
298
+ }
299
+ return { __index: index, Values: item };
300
+ });
301
+ headers.add('Values');
302
+ } else if (typeof data === 'object') {
303
+ Object.keys(data).forEach(key => {
304
+ const item = data[key];
305
+ if (item !== null && typeof item === 'object' && !Array.isArray(item)) {
306
+ Object.keys(item).forEach(k => headers.add(k));
307
+ rows.push({ __index: key, ...item });
308
+ } else {
309
+ rows.push({ __index: key, Values: item });
310
+ headers.add('Values');
311
+ }
312
+ });
313
+ } else {
314
+ return __formatForConsole(data);
315
+ }
316
+
317
+ // Filter headers by columns if provided
318
+ let headerList = ['(index)', ...headers];
319
+ headerList = headerList.filter(h => h !== '__index');
320
+ if (columns && Array.isArray(columns)) {
321
+ headerList = ['(index)', ...columns.filter(c => headers.has(c))];
322
+ }
323
+
324
+ // Calculate column widths
325
+ const colWidths = {};
326
+ headerList.forEach(h => {
327
+ colWidths[h] = h === '(index)' ? 7 : h.length;
328
+ });
329
+ rows.forEach(row => {
330
+ headerList.forEach(h => {
331
+ const key = h === '(index)' ? '__index' : h;
332
+ const val = row[key];
333
+ const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';
334
+ colWidths[h] = Math.max(colWidths[h], formatted.length);
335
+ });
336
+ });
337
+
338
+ // Build table
339
+ const sep = '+' + headerList.map(h => '-'.repeat(colWidths[h] + 2)).join('+') + '+';
340
+ const headerRow = '|' + headerList.map(h => ' ' + h.padEnd(colWidths[h]) + ' ').join('|') + '|';
341
+
342
+ const dataRows = rows.map(row => {
343
+ return '|' + headerList.map(h => {
344
+ const key = h === '(index)' ? '__index' : h;
345
+ const val = row[key];
346
+ const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';
347
+ return ' ' + formatted.padEnd(colWidths[h]) + ' ';
348
+ }).join('|') + '|';
349
+ });
350
+
351
+ return [sep, headerRow, sep, ...dataRows, sep].join('\\n');
352
+ }
353
+
138
354
  globalThis.console = {
139
- log: __console_log,
140
- warn: __console_warn,
141
- error: __console_error,
142
- debug: __console_debug,
143
- info: __console_info,
144
- trace: __console_trace,
145
- dir: __console_dir,
146
- table: __console_table,
355
+ log: (...args) => __console_log(__formatArgs(args)),
356
+ warn: (...args) => __console_warn(__formatArgs(args)),
357
+ error: (...args) => __console_error(__formatArgs(args)),
358
+ debug: (...args) => __console_debug(__formatArgs(args)),
359
+ info: (...args) => __console_info(__formatArgs(args)),
360
+ trace: (...args) => {
361
+ const err = new Error();
362
+ const stack = err.stack || '';
363
+ // Remove the first two lines (Error and the trace call itself)
364
+ const stackLines = stack.split('\\n').slice(2).join('\\n');
365
+ __console_trace(__formatArgs(args), 'Trace' + (args.length > 0 ? ': ' + __formatArgs(args) : '') + '\\n' + stackLines);
366
+ },
367
+ dir: (value, options) => __console_dir(__formatForConsole(value, { depth: options?.depth ?? 2, inObject: true })),
368
+ table: (data, columns) => __console_table(__formatTable(data, columns)),
147
369
  time: __console_time,
148
370
  timeEnd: __console_timeEnd,
149
- timeLog: __console_timeLog,
371
+ timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),
150
372
  count: __console_count,
151
373
  countReset: __console_countReset,
152
374
  group: __console_group,
153
375
  groupCollapsed: __console_groupCollapsed,
154
376
  groupEnd: __console_groupEnd,
155
377
  clear: __console_clear,
156
- assert: __console_assert,
378
+ assert: (condition, ...args) => {
379
+ if (!condition) {
380
+ const msg = args.length > 0 ? __formatArgs(args) : 'console.assert';
381
+ __console_assert('Assertion failed: ' + msg);
382
+ }
383
+ },
157
384
  };
158
385
  `);
159
386
  return {
@@ -182,4 +409,4 @@ export {
182
409
  setupConsole
183
410
  };
184
411
 
185
- //# debugId=4056CE9A9E9B4E5D64756E2164756E21
412
+ //# debugId=2FC6087CD8BA651864756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import ivm from \"isolated-vm\";\n\n/**\n * Console entry types for structured console output.\n * Each entry type captures the specific data needed to render like DevTools.\n */\nexport type ConsoleEntry =\n | {\n type: \"output\";\n level: \"log\" | \"warn\" | \"error\" | \"info\" | \"debug\";\n args: unknown[];\n groupDepth: number;\n }\n | {\n /** Browser console output (from Playwright page, not sandbox) */\n type: \"browserOutput\";\n level: string;\n args: unknown[];\n timestamp: number;\n }\n | { type: \"dir\"; value: unknown; groupDepth: number }\n | { type: \"table\"; data: unknown; columns?: string[]; groupDepth: number }\n | { type: \"time\"; label: string; duration: number; groupDepth: number }\n | {\n type: \"timeLog\";\n label: string;\n duration: number;\n args: unknown[];\n groupDepth: number;\n }\n | { type: \"count\"; label: string; count: number; groupDepth: number }\n | { type: \"countReset\"; label: string; groupDepth: number }\n | { type: \"assert\"; args: unknown[]; groupDepth: number }\n | {\n type: \"group\";\n label: string;\n collapsed: boolean;\n groupDepth: number;\n }\n | { type: \"groupEnd\"; groupDepth: number }\n | { type: \"clear\" }\n | { type: \"trace\"; args: unknown[]; stack: string; groupDepth: number };\n\n/**\n * Console options with a single structured callback.\n */\nexport interface ConsoleOptions {\n /**\n * Callback invoked for each console operation.\n * Receives a structured entry with all data needed to render the output.\n */\n onEntry?: (entry: ConsoleEntry) => void;\n}\n\n/**\n * Console handle for accessing internal state.\n */\nexport interface ConsoleHandle {\n dispose(): void;\n reset(): void;\n getTimers(): Map<string, number>;\n getCounters(): Map<string, number>;\n getGroupDepth(): number;\n}\n\n\n/**\n * Setup console API in an isolated-vm context\n *\n * Injects console.log, console.warn, console.error, console.info, console.debug,\n * console.trace, console.dir, console.table, console.time, console.timeEnd,\n * console.timeLog, console.count, console.countReset, console.group,\n * console.groupCollapsed, console.groupEnd, console.clear, console.assert\n *\n * @example\n * const handle = await setupConsole(context, {\n * onEntry: (entry) => {\n * if (entry.type === 'output') {\n * console.log(`[${entry.level}]`, ...entry.args);\n * }\n * }\n * });\n */\nexport async function setupConsole(\n context: ivm.Context,\n options?: ConsoleOptions\n): Promise<ConsoleHandle> {\n const opts = options ?? {};\n\n // State management\n const timers = new Map<string, number>();\n const counters = new Map<string, number>();\n let groupDepth = 0;\n\n const global = context.global;\n\n // Log-level methods (output type)\n const logLevels = [\"log\", \"warn\", \"error\", \"debug\", \"info\"] as const;\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((...args: unknown[]) => {\n opts.onEntry?.({\n type: \"output\",\n level,\n args,\n groupDepth,\n });\n })\n );\n }\n\n // dir method\n global.setSync(\n \"__console_dir\",\n new ivm.Callback((value: unknown) => {\n opts.onEntry?.({\n type: \"dir\",\n value,\n groupDepth,\n });\n })\n );\n\n // table method\n global.setSync(\n \"__console_table\",\n new ivm.Callback((data: unknown, columns?: string[]) => {\n opts.onEntry?.({\n type: \"table\",\n data,\n columns,\n groupDepth,\n });\n })\n );\n\n // trace method (includes stack)\n global.setSync(\n \"__console_trace\",\n new ivm.Callback((...args: unknown[]) => {\n const stack = new Error().stack ?? \"\";\n opts.onEntry?.({\n type: \"trace\",\n args,\n stack,\n groupDepth,\n });\n })\n );\n\n // Timing methods\n global.setSync(\n \"__console_time\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n timers.set(l, performance.now());\n })\n );\n\n global.setSync(\n \"__console_timeEnd\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n timers.delete(l);\n opts.onEntry?.({\n type: \"time\",\n label: l,\n duration,\n groupDepth,\n });\n }\n })\n );\n\n global.setSync(\n \"__console_timeLog\",\n new ivm.Callback((label?: string, ...args: unknown[]) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n opts.onEntry?.({\n type: \"timeLog\",\n label: l,\n duration,\n args,\n groupDepth,\n });\n }\n })\n );\n\n // Counting methods\n global.setSync(\n \"__console_count\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const count = (counters.get(l) ?? 0) + 1;\n counters.set(l, count);\n opts.onEntry?.({\n type: \"count\",\n label: l,\n count,\n groupDepth,\n });\n })\n );\n\n global.setSync(\n \"__console_countReset\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n counters.delete(l);\n opts.onEntry?.({\n type: \"countReset\",\n label: l,\n groupDepth,\n });\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: false,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: true,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onEntry?.({\n type: \"groupEnd\",\n groupDepth,\n });\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onEntry?.({ type: \"clear\" });\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((condition: boolean, ...args: unknown[]) => {\n if (!condition) {\n opts.onEntry?.({\n type: \"assert\",\n args,\n groupDepth,\n });\n }\n })\n );\n\n // Inject console object\n context.evalSync(`\n globalThis.console = {\n log: __console_log,\n warn: __console_warn,\n error: __console_error,\n debug: __console_debug,\n info: __console_info,\n trace: __console_trace,\n dir: __console_dir,\n table: __console_table,\n time: __console_time,\n timeEnd: __console_timeEnd,\n timeLog: __console_timeLog,\n count: __console_count,\n countReset: __console_countReset,\n group: __console_group,\n groupCollapsed: __console_groupCollapsed,\n groupEnd: __console_groupEnd,\n clear: __console_clear,\n assert: __console_assert,\n };\n `);\n\n return {\n dispose() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n reset() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n getTimers() {\n return new Map(timers);\n },\n getCounters() {\n return new Map(counters);\n },\n getGroupDepth() {\n return groupDepth;\n },\n };\n}\n"
5
+ "import ivm from \"isolated-vm\";\n\n/**\n * Console entry types for structured console output.\n * Each entry type captures the specific data needed to render like DevTools.\n * Output is pre-formatted as stdout strings (like Node.js console) inside the sandbox.\n */\nexport type ConsoleEntry =\n | {\n type: \"output\";\n level: \"log\" | \"warn\" | \"error\" | \"info\" | \"debug\";\n stdout: string;\n groupDepth: number;\n }\n | {\n /** Browser console output (from Playwright page, not sandbox) */\n type: \"browserOutput\";\n level: string;\n stdout: string;\n timestamp: number;\n }\n | { type: \"dir\"; stdout: string; groupDepth: number }\n | { type: \"table\"; stdout: string; groupDepth: number }\n | { type: \"time\"; label: string; duration: number; groupDepth: number }\n | {\n type: \"timeLog\";\n label: string;\n duration: number;\n stdout: string;\n groupDepth: number;\n }\n | { type: \"count\"; label: string; count: number; groupDepth: number }\n | { type: \"countReset\"; label: string; groupDepth: number }\n | { type: \"assert\"; stdout: string; groupDepth: number }\n | {\n type: \"group\";\n label: string;\n collapsed: boolean;\n groupDepth: number;\n }\n | { type: \"groupEnd\"; groupDepth: number }\n | { type: \"clear\" }\n | { type: \"trace\"; stdout: string; stack: string; groupDepth: number };\n\n/**\n * Console options with a single structured callback.\n */\nexport interface ConsoleOptions {\n /**\n * Callback invoked for each console operation.\n * Receives a structured entry with all data needed to render the output.\n */\n onEntry?: (entry: ConsoleEntry) => void;\n}\n\n/**\n * Console handle for accessing internal state.\n */\nexport interface ConsoleHandle {\n dispose(): void;\n reset(): void;\n getTimers(): Map<string, number>;\n getCounters(): Map<string, number>;\n getGroupDepth(): number;\n}\n\n\n/**\n * Setup console API in an isolated-vm context\n *\n * Injects console.log, console.warn, console.error, console.info, console.debug,\n * console.trace, console.dir, console.table, console.time, console.timeEnd,\n * console.timeLog, console.count, console.countReset, console.group,\n * console.groupCollapsed, console.groupEnd, console.clear, console.assert\n *\n * @example\n * const handle = await setupConsole(context, {\n * onEntry: (entry) => {\n * if (entry.type === 'output') {\n * console.log(`[${entry.level}]`, ...entry.args);\n * }\n * }\n * });\n */\nexport async function setupConsole(\n context: ivm.Context,\n options?: ConsoleOptions\n): Promise<ConsoleHandle> {\n const opts = options ?? {};\n\n // State management\n const timers = new Map<string, number>();\n const counters = new Map<string, number>();\n let groupDepth = 0;\n\n const global = context.global;\n\n // Log-level methods (output type)\n const logLevels = [\"log\", \"warn\", \"error\", \"debug\", \"info\"] as const;\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"output\",\n level,\n stdout,\n groupDepth,\n });\n })\n );\n }\n\n // dir method\n global.setSync(\n \"__console_dir\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"dir\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // table method\n global.setSync(\n \"__console_table\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"table\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // trace method (includes stack)\n global.setSync(\n \"__console_trace\",\n new ivm.Callback((stdout: string, stack: string) => {\n opts.onEntry?.({\n type: \"trace\",\n stdout,\n stack,\n groupDepth,\n });\n })\n );\n\n // Timing methods\n global.setSync(\n \"__console_time\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n timers.set(l, performance.now());\n })\n );\n\n global.setSync(\n \"__console_timeEnd\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const duration = performance.now() - start;\n timers.delete(l);\n opts.onEntry?.({\n type: \"time\",\n label: l,\n duration,\n groupDepth,\n });\n }\n })\n );\n\n global.setSync(\n \"__console_timeLog\",\n new ivm.Callback((label: string, duration: number, stdout: string) => {\n const l = label ?? \"default\";\n const start = timers.get(l);\n if (start !== undefined) {\n const actualDuration = performance.now() - start;\n opts.onEntry?.({\n type: \"timeLog\",\n label: l,\n duration: actualDuration,\n stdout,\n groupDepth,\n });\n }\n })\n );\n\n // Counting methods\n global.setSync(\n \"__console_count\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n const count = (counters.get(l) ?? 0) + 1;\n counters.set(l, count);\n opts.onEntry?.({\n type: \"count\",\n label: l,\n count,\n groupDepth,\n });\n })\n );\n\n global.setSync(\n \"__console_countReset\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n counters.delete(l);\n opts.onEntry?.({\n type: \"countReset\",\n label: l,\n groupDepth,\n });\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: false,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n opts.onEntry?.({\n type: \"group\",\n label: l,\n collapsed: true,\n groupDepth,\n });\n groupDepth++;\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onEntry?.({\n type: \"groupEnd\",\n groupDepth,\n });\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onEntry?.({ type: \"clear\" });\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((stdout: string) => {\n opts.onEntry?.({\n type: \"assert\",\n stdout,\n groupDepth,\n });\n })\n );\n\n // Inject console object with Node.js-style formatting\n context.evalSync(`\n // Format a single value for console output (Node.js style)\n function __formatForConsole(value, options = {}) {\n const { depth = 2, currentDepth = 0, seen = new WeakSet(), inObject = false } = options;\n\n // Handle null/undefined\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n\n // Handle primitives\n const type = typeof value;\n if (type === 'string') {\n // Strings: quoted when inside objects/arrays, raw when top-level console.log arg\n return inObject ? \"'\" + value.replace(/'/g, \"\\\\\\\\'\") + \"'\" : value;\n }\n if (type === 'number' || type === 'boolean') {\n return String(value);\n }\n if (type === 'bigint') {\n return value.toString() + 'n';\n }\n if (type === 'symbol') {\n return value.toString();\n }\n if (type === 'function') {\n const name = value.name || '(anonymous)';\n return '[Function: ' + name + ']';\n }\n\n // Handle objects\n if (type === 'object') {\n // Handle circular references BEFORE depth check (Node.js behavior)\n if (seen.has(value)) {\n return '[Circular]';\n }\n seen.add(value);\n\n // Check depth limit\n if (currentDepth >= depth) {\n if (Array.isArray(value)) return '[Array]';\n return '[Object]';\n }\n\n const nextOptions = { depth, currentDepth: currentDepth + 1, seen, inObject: true };\n\n // Handle Error objects\n if (value instanceof Error) {\n let result = value.name + ': ' + value.message;\n if (value.stack) {\n // Get stack lines after the first line (which is the error message)\n const stackLines = value.stack.split('\\\\n').slice(1);\n if (stackLines.length > 0) {\n result += '\\\\n' + stackLines.join('\\\\n');\n }\n }\n return result;\n }\n\n // Handle Response objects\n if (typeof Response !== 'undefined' && value instanceof Response) {\n return 'Response { status: ' + value.status + ', statusText: ' + __formatForConsole(value.statusText, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';\n }\n\n // Handle Request objects\n if (typeof Request !== 'undefined' && value instanceof Request) {\n return 'Request { method: ' + __formatForConsole(value.method, nextOptions) + ', url: ' + __formatForConsole(value.url, nextOptions) + ' }';\n }\n\n // Handle Headers objects\n if (typeof Headers !== 'undefined' && value instanceof Headers) {\n const entries = [];\n value.forEach((v, k) => {\n entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));\n });\n return 'Headers { ' + entries.join(', ') + ' }';\n }\n\n // Handle Date objects\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // Handle RegExp\n if (value instanceof RegExp) {\n return value.toString();\n }\n\n // Handle Map\n if (value instanceof Map) {\n const entries = [];\n value.forEach((v, k) => {\n entries.push(__formatForConsole(k, nextOptions) + ' => ' + __formatForConsole(v, nextOptions));\n });\n return 'Map(' + value.size + ') { ' + entries.join(', ') + ' }';\n }\n\n // Handle Set\n if (value instanceof Set) {\n const entries = [];\n value.forEach((v) => {\n entries.push(__formatForConsole(v, nextOptions));\n });\n return 'Set(' + value.size + ') { ' + entries.join(', ') + ' }';\n }\n\n // Handle ArrayBuffer and TypedArrays\n if (value instanceof ArrayBuffer) {\n return 'ArrayBuffer { byteLength: ' + value.byteLength + ' }';\n }\n if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {\n const typedArray = value;\n const name = typedArray.constructor.name;\n const length = typedArray.length;\n if (length <= 10) {\n const items = Array.from(typedArray).map(v => __formatForConsole(v, nextOptions));\n return name + '(' + length + ') [ ' + items.join(', ') + ' ]';\n }\n return name + '(' + length + ') [ ... ]';\n }\n\n // Handle Promise\n if (value instanceof Promise) {\n return 'Promise { <pending> }';\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]';\n const items = value.map(item => __formatForConsole(item, nextOptions));\n return '[ ' + items.join(', ') + ' ]';\n }\n\n // Handle plain objects\n const keys = Object.keys(value);\n if (keys.length === 0) return '{}';\n const entries = keys.map(key => {\n const formattedKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : __formatForConsole(key, nextOptions);\n return formattedKey + ': ' + __formatForConsole(value[key], nextOptions);\n });\n return '{ ' + entries.join(', ') + ' }';\n }\n\n return String(value);\n }\n\n // Format multiple args with space separation (like console.log)\n function __formatArgs(args) {\n return args.map((arg, i) => __formatForConsole(arg, { inObject: false })).join(' ');\n }\n\n // Format data for console.table - creates ASCII table\n function __formatTable(data, columns) {\n if (data === null || data === undefined) {\n return __formatForConsole(data);\n }\n\n // Convert to array of objects\n let rows = [];\n let headers = new Set();\n\n if (Array.isArray(data)) {\n rows = data.map((item, index) => {\n if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n Object.keys(item).forEach(k => headers.add(k));\n return { __index: index, ...item };\n }\n return { __index: index, Values: item };\n });\n headers.add('Values');\n } else if (typeof data === 'object') {\n Object.keys(data).forEach(key => {\n const item = data[key];\n if (item !== null && typeof item === 'object' && !Array.isArray(item)) {\n Object.keys(item).forEach(k => headers.add(k));\n rows.push({ __index: key, ...item });\n } else {\n rows.push({ __index: key, Values: item });\n headers.add('Values');\n }\n });\n } else {\n return __formatForConsole(data);\n }\n\n // Filter headers by columns if provided\n let headerList = ['(index)', ...headers];\n headerList = headerList.filter(h => h !== '__index');\n if (columns && Array.isArray(columns)) {\n headerList = ['(index)', ...columns.filter(c => headers.has(c))];\n }\n\n // Calculate column widths\n const colWidths = {};\n headerList.forEach(h => {\n colWidths[h] = h === '(index)' ? 7 : h.length;\n });\n rows.forEach(row => {\n headerList.forEach(h => {\n const key = h === '(index)' ? '__index' : h;\n const val = row[key];\n const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';\n colWidths[h] = Math.max(colWidths[h], formatted.length);\n });\n });\n\n // Build table\n const sep = '+' + headerList.map(h => '-'.repeat(colWidths[h] + 2)).join('+') + '+';\n const headerRow = '|' + headerList.map(h => ' ' + h.padEnd(colWidths[h]) + ' ').join('|') + '|';\n\n const dataRows = rows.map(row => {\n return '|' + headerList.map(h => {\n const key = h === '(index)' ? '__index' : h;\n const val = row[key];\n const formatted = val !== undefined ? __formatForConsole(val, { depth: 1, inObject: true }) : '';\n return ' ' + formatted.padEnd(colWidths[h]) + ' ';\n }).join('|') + '|';\n });\n\n return [sep, headerRow, sep, ...dataRows, sep].join('\\\\n');\n }\n\n globalThis.console = {\n log: (...args) => __console_log(__formatArgs(args)),\n warn: (...args) => __console_warn(__formatArgs(args)),\n error: (...args) => __console_error(__formatArgs(args)),\n debug: (...args) => __console_debug(__formatArgs(args)),\n info: (...args) => __console_info(__formatArgs(args)),\n trace: (...args) => {\n const err = new Error();\n const stack = err.stack || '';\n // Remove the first two lines (Error and the trace call itself)\n const stackLines = stack.split('\\\\n').slice(2).join('\\\\n');\n __console_trace(__formatArgs(args), 'Trace' + (args.length > 0 ? ': ' + __formatArgs(args) : '') + '\\\\n' + stackLines);\n },\n dir: (value, options) => __console_dir(__formatForConsole(value, { depth: options?.depth ?? 2, inObject: true })),\n table: (data, columns) => __console_table(__formatTable(data, columns)),\n time: __console_time,\n timeEnd: __console_timeEnd,\n timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),\n count: __console_count,\n countReset: __console_countReset,\n group: __console_group,\n groupCollapsed: __console_groupCollapsed,\n groupEnd: __console_groupEnd,\n clear: __console_clear,\n assert: (condition, ...args) => {\n if (!condition) {\n const msg = args.length > 0 ? __formatArgs(args) : 'console.assert';\n __console_assert('Assertion failed: ' + msg);\n }\n },\n };\n `);\n\n return {\n dispose() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n reset() {\n timers.clear();\n counters.clear();\n groupDepth = 0;\n },\n getTimers() {\n return new Map(timers);\n },\n getCounters() {\n return new Map(counters);\n },\n getGroupDepth() {\n return groupDepth;\n },\n };\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "4056CE9A9E9B4E5D64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "2FC6087CD8BA651864756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "type": "module"
5
5
  }
@@ -3,20 +3,20 @@ function simpleConsoleHandler(callbacks) {
3
3
  return {
4
4
  onEntry: (entry) => {
5
5
  if (entry.type === "output") {
6
- callbacks[entry.level]?.(...entry.args);
6
+ callbacks[entry.level]?.(entry.stdout);
7
7
  } else if (entry.type === "assert") {
8
- callbacks.error?.("Assertion failed:", ...entry.args);
8
+ callbacks.error?.(entry.stdout);
9
9
  } else if (entry.type === "trace") {
10
- callbacks.log?.(...entry.args, `
11
- ` + entry.stack);
10
+ callbacks.log?.(entry.stack);
12
11
  } else if (entry.type === "dir") {
13
- callbacks.log?.(entry.value);
12
+ callbacks.log?.(entry.stdout);
14
13
  } else if (entry.type === "table") {
15
- callbacks.log?.(entry.data);
14
+ callbacks.log?.(entry.stdout);
16
15
  } else if (entry.type === "time") {
17
16
  callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
18
17
  } else if (entry.type === "timeLog") {
19
- callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`, ...entry.args);
18
+ const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;
19
+ callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);
20
20
  } else if (entry.type === "count") {
21
21
  callbacks.log?.(`${entry.label}: ${entry.count}`);
22
22
  }
@@ -27,4 +27,4 @@ export {
27
27
  simpleConsoleHandler
28
28
  };
29
29
 
30
- //# debugId=F4BB11E7A05AFC3364756E2164756E21
30
+ //# debugId=BB0862A33450F0FB64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils.ts"],
4
4
  "sourcesContent": [
5
- "import type { ConsoleOptions } from \"./index.mjs\";\n\n/**\n * Simple console callback interface for basic usage.\n */\nexport interface SimpleConsoleCallbacks {\n log?: (...args: unknown[]) => void;\n warn?: (...args: unknown[]) => void;\n error?: (...args: unknown[]) => void;\n info?: (...args: unknown[]) => void;\n debug?: (...args: unknown[]) => void;\n}\n\n/**\n * Helper to create ConsoleOptions from simple callbacks.\n * Routes log-level outputs to the appropriate callback and handles assertions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: simpleConsoleHandler({\n * log: (...args) => console.log('[sandbox]', ...args),\n * warn: (...args) => console.warn('[sandbox]', ...args),\n * error: (...args) => console.error('[sandbox]', ...args),\n * })\n * });\n * ```\n */\nexport function simpleConsoleHandler(\n callbacks: SimpleConsoleCallbacks\n): ConsoleOptions {\n return {\n onEntry: (entry) => {\n if (entry.type === \"output\") {\n callbacks[entry.level]?.(...entry.args);\n } else if (entry.type === \"assert\") {\n callbacks.error?.(\"Assertion failed:\", ...entry.args);\n } else if (entry.type === \"trace\") {\n callbacks.log?.(...entry.args, \"\\n\" + entry.stack);\n } else if (entry.type === \"dir\") {\n callbacks.log?.(entry.value);\n } else if (entry.type === \"table\") {\n callbacks.log?.(entry.data);\n } else if (entry.type === \"time\") {\n callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);\n } else if (entry.type === \"timeLog\") {\n callbacks.log?.(\n `${entry.label}: ${entry.duration.toFixed(2)}ms`,\n ...entry.args\n );\n } else if (entry.type === \"count\") {\n callbacks.log?.(`${entry.label}: ${entry.count}`);\n }\n // group, groupEnd, groupEnd, countReset, clear are silently ignored\n },\n };\n}\n"
5
+ "import type { ConsoleOptions } from \"./index.mjs\";\n\n/**\n * Simple console callback interface for basic usage.\n */\nexport interface SimpleConsoleCallbacks {\n log?: (message: string) => void;\n warn?: (message: string) => void;\n error?: (message: string) => void;\n info?: (message: string) => void;\n debug?: (message: string) => void;\n}\n\n/**\n * Helper to create ConsoleOptions from simple callbacks.\n * Routes log-level outputs to the appropriate callback and handles assertions.\n *\n * @example\n * ```typescript\n * const runtime = await createRuntime({\n * console: simpleConsoleHandler({\n * log: (msg) => console.log('[sandbox]', msg),\n * warn: (msg) => console.warn('[sandbox]', msg),\n * error: (msg) => console.error('[sandbox]', msg),\n * })\n * });\n * ```\n */\nexport function simpleConsoleHandler(\n callbacks: SimpleConsoleCallbacks\n): ConsoleOptions {\n return {\n onEntry: (entry) => {\n if (entry.type === \"output\") {\n callbacks[entry.level]?.(entry.stdout);\n } else if (entry.type === \"assert\") {\n callbacks.error?.(entry.stdout);\n } else if (entry.type === \"trace\") {\n callbacks.log?.(entry.stack);\n } else if (entry.type === \"dir\") {\n callbacks.log?.(entry.stdout);\n } else if (entry.type === \"table\") {\n callbacks.log?.(entry.stdout);\n } else if (entry.type === \"time\") {\n callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);\n } else if (entry.type === \"timeLog\") {\n const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;\n callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);\n } else if (entry.type === \"count\") {\n callbacks.log?.(`${entry.label}: ${entry.count}`);\n }\n // group, groupEnd, countReset, clear are silently ignored\n },\n };\n}\n"
6
6
  ],
7
- "mappings": ";AA4BO,SAAS,oBAAoB,CAClC,WACgB;AAAA,EAChB,OAAO;AAAA,IACL,SAAS,CAAC,UAAU;AAAA,MAClB,IAAI,MAAM,SAAS,UAAU;AAAA,QAC3B,UAAU,MAAM,SAAS,GAAG,MAAM,IAAI;AAAA,MACxC,EAAO,SAAI,MAAM,SAAS,UAAU;AAAA,QAClC,UAAU,QAAQ,qBAAqB,GAAG,MAAM,IAAI;AAAA,MACtD,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,MAAM;AAAA,IAAO,MAAM,KAAK;AAAA,MACnD,EAAO,SAAI,MAAM,SAAS,OAAO;AAAA,QAC/B,UAAU,MAAM,MAAM,KAAK;AAAA,MAC7B,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,IAAI;AAAA,MAC5B,EAAO,SAAI,MAAM,SAAS,QAAQ;AAAA,QAChC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,KAAK;AAAA,MAClE,EAAO,SAAI,MAAM,SAAS,WAAW;AAAA,QACnC,UAAU,MACR,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,OAC3C,GAAG,MAAM,IACX;AAAA,MACF,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,OAAO;AAAA,MAClD;AAAA;AAAA,EAGJ;AAAA;",
8
- "debugId": "F4BB11E7A05AFC3364756E2164756E21",
7
+ "mappings": ";AA4BO,SAAS,oBAAoB,CAClC,WACgB;AAAA,EAChB,OAAO;AAAA,IACL,SAAS,CAAC,UAAU;AAAA,MAClB,IAAI,MAAM,SAAS,UAAU;AAAA,QAC3B,UAAU,MAAM,SAAS,MAAM,MAAM;AAAA,MACvC,EAAO,SAAI,MAAM,SAAS,UAAU;AAAA,QAClC,UAAU,QAAQ,MAAM,MAAM;AAAA,MAChC,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,KAAK;AAAA,MAC7B,EAAO,SAAI,MAAM,SAAS,OAAO;AAAA,QAC/B,UAAU,MAAM,MAAM,MAAM;AAAA,MAC9B,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,MAAM,MAAM;AAAA,MAC9B,EAAO,SAAI,MAAM,SAAS,QAAQ;AAAA,QAChC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC,KAAK;AAAA,MAClE,EAAO,SAAI,MAAM,SAAS,WAAW;AAAA,QACnC,MAAM,UAAU,GAAG,MAAM,UAAU,MAAM,SAAS,QAAQ,CAAC;AAAA,QAC3D,UAAU,MAAM,MAAM,SAAS,GAAG,WAAW,MAAM,WAAW,OAAO;AAAA,MACvE,EAAO,SAAI,MAAM,SAAS,SAAS;AAAA,QACjC,UAAU,MAAM,GAAG,MAAM,UAAU,MAAM,OAAO;AAAA,MAClD;AAAA;AAAA,EAGJ;AAAA;",
8
+ "debugId": "BB0862A33450F0FB64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -2,26 +2,26 @@ import ivm from "isolated-vm";
2
2
  /**
3
3
  * Console entry types for structured console output.
4
4
  * Each entry type captures the specific data needed to render like DevTools.
5
+ * Output is pre-formatted as stdout strings (like Node.js console) inside the sandbox.
5
6
  */
6
7
  export type ConsoleEntry = {
7
8
  type: "output";
8
9
  level: "log" | "warn" | "error" | "info" | "debug";
9
- args: unknown[];
10
+ stdout: string;
10
11
  groupDepth: number;
11
12
  } | {
12
13
  /** Browser console output (from Playwright page, not sandbox) */
13
14
  type: "browserOutput";
14
15
  level: string;
15
- args: unknown[];
16
+ stdout: string;
16
17
  timestamp: number;
17
18
  } | {
18
19
  type: "dir";
19
- value: unknown;
20
+ stdout: string;
20
21
  groupDepth: number;
21
22
  } | {
22
23
  type: "table";
23
- data: unknown;
24
- columns?: string[];
24
+ stdout: string;
25
25
  groupDepth: number;
26
26
  } | {
27
27
  type: "time";
@@ -32,7 +32,7 @@ export type ConsoleEntry = {
32
32
  type: "timeLog";
33
33
  label: string;
34
34
  duration: number;
35
- args: unknown[];
35
+ stdout: string;
36
36
  groupDepth: number;
37
37
  } | {
38
38
  type: "count";
@@ -45,7 +45,7 @@ export type ConsoleEntry = {
45
45
  groupDepth: number;
46
46
  } | {
47
47
  type: "assert";
48
- args: unknown[];
48
+ stdout: string;
49
49
  groupDepth: number;
50
50
  } | {
51
51
  type: "group";
@@ -59,7 +59,7 @@ export type ConsoleEntry = {
59
59
  type: "clear";
60
60
  } | {
61
61
  type: "trace";
62
- args: unknown[];
62
+ stdout: string;
63
63
  stack: string;
64
64
  groupDepth: number;
65
65
  };
@@ -3,11 +3,11 @@ import type { ConsoleOptions } from "./index.ts";
3
3
  * Simple console callback interface for basic usage.
4
4
  */
5
5
  export interface SimpleConsoleCallbacks {
6
- log?: (...args: unknown[]) => void;
7
- warn?: (...args: unknown[]) => void;
8
- error?: (...args: unknown[]) => void;
9
- info?: (...args: unknown[]) => void;
10
- debug?: (...args: unknown[]) => void;
6
+ log?: (message: string) => void;
7
+ warn?: (message: string) => void;
8
+ error?: (message: string) => void;
9
+ info?: (message: string) => void;
10
+ debug?: (message: string) => void;
11
11
  }
12
12
  /**
13
13
  * Helper to create ConsoleOptions from simple callbacks.
@@ -17,9 +17,9 @@ export interface SimpleConsoleCallbacks {
17
17
  * ```typescript
18
18
  * const runtime = await createRuntime({
19
19
  * console: simpleConsoleHandler({
20
- * log: (...args) => console.log('[sandbox]', ...args),
21
- * warn: (...args) => console.warn('[sandbox]', ...args),
22
- * error: (...args) => console.error('[sandbox]', ...args),
20
+ * log: (msg) => console.log('[sandbox]', msg),
21
+ * warn: (msg) => console.warn('[sandbox]', msg),
22
+ * error: (msg) => console.error('[sandbox]', msg),
23
23
  * })
24
24
  * });
25
25
  * ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "main": "./dist/cjs/index.cjs",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "exports": {