@ricsam/isolate-console 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,24 +1,110 @@
1
1
  # @ricsam/isolate-console
2
2
 
3
- Console API with logging, timing, counting, and grouping.
3
+ Console API with logging, timing, counting, and grouping for isolated-vm V8 sandbox.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm add @ricsam/isolate-console
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ The console module uses a single `onEntry` callback that receives structured `ConsoleEntry` objects:
4
14
 
5
15
  ```typescript
6
- import { setupConsole } from "@ricsam/isolate-console";
16
+ import { setupConsole, type ConsoleEntry } from "@ricsam/isolate-console";
7
17
 
8
18
  const handle = await setupConsole(context, {
9
- onLog: (level, ...args) => {
10
- console.log(`[${level}]`, ...args);
11
- },
12
- onTime: (label, duration) => {
13
- console.log(`${label}: ${duration}ms`);
14
- },
15
- onCount: (label, count) => {
16
- console.log(`${label}: ${count}`);
19
+ onEntry: (entry: ConsoleEntry) => {
20
+ switch (entry.type) {
21
+ case "output":
22
+ console.log(`[${entry.level}]`, ...entry.args);
23
+ break;
24
+ case "time":
25
+ console.log(`${entry.label}: ${entry.duration}ms`);
26
+ break;
27
+ case "count":
28
+ console.log(`${entry.label}: ${entry.count}`);
29
+ break;
30
+ case "group":
31
+ console.group(entry.label);
32
+ break;
33
+ case "groupEnd":
34
+ console.groupEnd();
35
+ break;
36
+ // ... handle other entry types
37
+ }
17
38
  },
18
39
  });
19
40
  ```
20
41
 
21
- **Injected Globals:**
42
+ ### Simple Console Handler
43
+
44
+ For basic use cases where you just want to route output to console methods:
45
+
46
+ ```typescript
47
+ import { setupConsole, simpleConsoleHandler } from "@ricsam/isolate-console";
48
+
49
+ const handle = await setupConsole(
50
+ context,
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),
57
+ })
58
+ );
59
+ ```
60
+
61
+ ## ConsoleEntry Types
62
+
63
+ The `ConsoleEntry` discriminated union type includes all possible console events:
64
+
65
+ ```typescript
66
+ type ConsoleEntry =
67
+ // Standard output (log, warn, error, info, debug)
68
+ | { type: "output"; level: "log" | "warn" | "error" | "info" | "debug"; args: unknown[]; groupDepth: number }
69
+
70
+ // console.dir()
71
+ | { type: "dir"; value: unknown; groupDepth: number }
72
+
73
+ // console.table()
74
+ | { type: "table"; data: unknown; columns?: string[]; groupDepth: number }
75
+
76
+ // console.timeEnd() - timer completed
77
+ | { type: "time"; label: string; duration: number; groupDepth: number }
78
+
79
+ // console.timeLog() - timer checkpoint
80
+ | { type: "timeLog"; label: string; duration: number; args: unknown[]; groupDepth: number }
81
+
82
+ // console.count()
83
+ | { type: "count"; label: string; count: number; groupDepth: number }
84
+
85
+ // console.countReset()
86
+ | { type: "countReset"; label: string; groupDepth: number }
87
+
88
+ // console.assert() - failed assertion
89
+ | { type: "assert"; args: unknown[]; groupDepth: number }
90
+
91
+ // console.group() or console.groupCollapsed()
92
+ | { type: "group"; label: string; collapsed: boolean; groupDepth: number }
93
+
94
+ // console.groupEnd()
95
+ | { type: "groupEnd"; groupDepth: number }
96
+
97
+ // console.clear()
98
+ | { type: "clear" }
99
+
100
+ // console.trace()
101
+ | { type: "trace"; args: unknown[]; stack: string; groupDepth: number };
102
+ ```
103
+
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
+
106
+ ## Injected Globals
107
+
22
108
  - `console.log`, `console.warn`, `console.error`, `console.debug`, `console.info`
23
109
  - `console.trace`, `console.dir`, `console.table`
24
110
  - `console.time`, `console.timeEnd`, `console.timeLog`
@@ -26,7 +112,7 @@ const handle = await setupConsole(context, {
26
112
  - `console.group`, `console.groupCollapsed`, `console.groupEnd`
27
113
  - `console.assert`, `console.clear`
28
114
 
29
- **Usage in Isolate:**
115
+ ## Usage in Isolate
30
116
 
31
117
  ```javascript
32
118
  // Basic logging
@@ -54,16 +140,23 @@ console.log("Age: 30");
54
140
  console.groupEnd();
55
141
  ```
56
142
 
57
- **Event Handlers:**
58
-
59
- | Handler | Description |
60
- |---------|-------------|
61
- | `onLog` | Called for log, warn, error, debug, info, trace, dir, table |
62
- | `onTime` | Called when `console.timeEnd` completes a timer |
63
- | `onTimeLog` | Called when `console.timeLog` logs without ending |
64
- | `onCount` | Called when `console.count` increments |
65
- | `onCountReset` | Called when `console.countReset` resets a counter |
66
- | `onGroup` | Called when `console.group` or `groupCollapsed` is invoked |
67
- | `onGroupEnd` | Called when `console.groupEnd` is invoked |
68
- | `onAssert` | Called when `console.assert` fails |
69
- | `onClear` | Called when `console.clear` is invoked |
143
+ ## Entry Types Reference
144
+
145
+ | Entry Type | Description | Key Properties |
146
+ |------------|-------------|----------------|
147
+ | `output` | Standard logging (log, warn, error, info, debug) | `level`, `args` |
148
+ | `dir` | Object inspection | `value` |
149
+ | `table` | Tabular data display | `data`, `columns?` |
150
+ | `time` | Timer completion (timeEnd) | `label`, `duration` |
151
+ | `timeLog` | Timer checkpoint | `label`, `duration`, `args` |
152
+ | `count` | Counter increment | `label`, `count` |
153
+ | `countReset` | Counter reset | `label` |
154
+ | `assert` | Failed assertion | `args` |
155
+ | `group` | Group start | `label`, `collapsed` |
156
+ | `groupEnd` | Group end | - |
157
+ | `clear` | Console clear | - |
158
+ | `trace` | Stack trace | `args`, `stack` |
159
+
160
+ ## License
161
+
162
+ MIT
@@ -43,31 +43,76 @@ var __export = (target, all) => {
43
43
  // packages/console/src/index.ts
44
44
  var exports_src = {};
45
45
  __export(exports_src, {
46
+ simpleConsoleHandler: () => simpleConsoleHandler,
46
47
  setupConsole: () => setupConsole
47
48
  });
48
49
  module.exports = __toCommonJS(exports_src);
49
50
  var import_isolated_vm = __toESM(require("isolated-vm"));
51
+ function simpleConsoleHandler(callbacks) {
52
+ return {
53
+ onEntry: (entry) => {
54
+ if (entry.type === "output") {
55
+ callbacks[entry.level]?.(...entry.args);
56
+ } else if (entry.type === "assert") {
57
+ callbacks.error?.("Assertion failed:", ...entry.args);
58
+ } else if (entry.type === "trace") {
59
+ callbacks.log?.(...entry.args, `
60
+ ` + entry.stack);
61
+ } else if (entry.type === "dir") {
62
+ callbacks.log?.(entry.value);
63
+ } else if (entry.type === "table") {
64
+ callbacks.log?.(entry.data);
65
+ } else if (entry.type === "time") {
66
+ callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
67
+ } else if (entry.type === "timeLog") {
68
+ callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`, ...entry.args);
69
+ } else if (entry.type === "count") {
70
+ callbacks.log?.(`${entry.label}: ${entry.count}`);
71
+ }
72
+ }
73
+ };
74
+ }
50
75
  async function setupConsole(context, options) {
51
76
  const opts = options ?? {};
52
77
  const timers = new Map;
53
78
  const counters = new Map;
54
79
  let groupDepth = 0;
55
80
  const global = context.global;
56
- const logLevels = [
57
- "log",
58
- "warn",
59
- "error",
60
- "debug",
61
- "info",
62
- "trace",
63
- "dir",
64
- "table"
65
- ];
81
+ const logLevels = ["log", "warn", "error", "debug", "info"];
66
82
  for (const level of logLevels) {
67
83
  global.setSync(`__console_${level}`, new import_isolated_vm.default.Callback((...args) => {
68
- opts.onLog?.(level, ...args);
84
+ opts.onEntry?.({
85
+ type: "output",
86
+ level,
87
+ args,
88
+ groupDepth
89
+ });
69
90
  }));
70
91
  }
92
+ global.setSync("__console_dir", new import_isolated_vm.default.Callback((value) => {
93
+ opts.onEntry?.({
94
+ type: "dir",
95
+ value,
96
+ groupDepth
97
+ });
98
+ }));
99
+ global.setSync("__console_table", new import_isolated_vm.default.Callback((data, columns) => {
100
+ opts.onEntry?.({
101
+ type: "table",
102
+ data,
103
+ columns,
104
+ groupDepth
105
+ });
106
+ }));
107
+ global.setSync("__console_trace", new import_isolated_vm.default.Callback((...args) => {
108
+ const stack = new Error().stack ?? "";
109
+ opts.onEntry?.({
110
+ type: "trace",
111
+ args,
112
+ stack,
113
+ groupDepth
114
+ });
115
+ }));
71
116
  global.setSync("__console_time", new import_isolated_vm.default.Callback((label) => {
72
117
  const l = label ?? "default";
73
118
  timers.set(l, performance.now());
@@ -78,7 +123,12 @@ async function setupConsole(context, options) {
78
123
  if (start !== undefined) {
79
124
  const duration = performance.now() - start;
80
125
  timers.delete(l);
81
- opts.onTime?.(l, duration);
126
+ opts.onEntry?.({
127
+ type: "time",
128
+ label: l,
129
+ duration,
130
+ groupDepth
131
+ });
82
132
  }
83
133
  }));
84
134
  global.setSync("__console_timeLog", new import_isolated_vm.default.Callback((label, ...args) => {
@@ -86,42 +136,74 @@ async function setupConsole(context, options) {
86
136
  const start = timers.get(l);
87
137
  if (start !== undefined) {
88
138
  const duration = performance.now() - start;
89
- opts.onTimeLog?.(l, duration, ...args);
139
+ opts.onEntry?.({
140
+ type: "timeLog",
141
+ label: l,
142
+ duration,
143
+ args,
144
+ groupDepth
145
+ });
90
146
  }
91
147
  }));
92
148
  global.setSync("__console_count", new import_isolated_vm.default.Callback((label) => {
93
149
  const l = label ?? "default";
94
150
  const count = (counters.get(l) ?? 0) + 1;
95
151
  counters.set(l, count);
96
- opts.onCount?.(l, count);
152
+ opts.onEntry?.({
153
+ type: "count",
154
+ label: l,
155
+ count,
156
+ groupDepth
157
+ });
97
158
  }));
98
159
  global.setSync("__console_countReset", new import_isolated_vm.default.Callback((label) => {
99
160
  const l = label ?? "default";
100
161
  counters.delete(l);
101
- opts.onCountReset?.(l);
162
+ opts.onEntry?.({
163
+ type: "countReset",
164
+ label: l,
165
+ groupDepth
166
+ });
102
167
  }));
103
168
  global.setSync("__console_group", new import_isolated_vm.default.Callback((label) => {
104
169
  const l = label ?? "default";
170
+ opts.onEntry?.({
171
+ type: "group",
172
+ label: l,
173
+ collapsed: false,
174
+ groupDepth
175
+ });
105
176
  groupDepth++;
106
- opts.onGroup?.(l, false);
107
177
  }));
108
178
  global.setSync("__console_groupCollapsed", new import_isolated_vm.default.Callback((label) => {
109
179
  const l = label ?? "default";
180
+ opts.onEntry?.({
181
+ type: "group",
182
+ label: l,
183
+ collapsed: true,
184
+ groupDepth
185
+ });
110
186
  groupDepth++;
111
- opts.onGroup?.(l, true);
112
187
  }));
113
188
  global.setSync("__console_groupEnd", new import_isolated_vm.default.Callback(() => {
114
189
  if (groupDepth > 0) {
115
190
  groupDepth--;
116
191
  }
117
- opts.onGroupEnd?.();
192
+ opts.onEntry?.({
193
+ type: "groupEnd",
194
+ groupDepth
195
+ });
118
196
  }));
119
197
  global.setSync("__console_clear", new import_isolated_vm.default.Callback(() => {
120
- opts.onClear?.();
198
+ opts.onEntry?.({ type: "clear" });
121
199
  }));
122
200
  global.setSync("__console_assert", new import_isolated_vm.default.Callback((condition, ...args) => {
123
201
  if (!condition) {
124
- opts.onAssert?.(condition, ...args);
202
+ opts.onEntry?.({
203
+ type: "assert",
204
+ args,
205
+ groupDepth
206
+ });
125
207
  }
126
208
  }));
127
209
  context.evalSync(`
@@ -170,4 +252,4 @@ async function setupConsole(context, options) {
170
252
  }
171
253
  })
172
254
 
173
- //# debugId=4D8B064A0F23B8BC64756E2164756E21
255
+ //# debugId=2521806AC72E527C64756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import ivm from \"isolated-vm\";\n\nexport interface ConsoleOptions {\n onLog?: (level: string, ...args: unknown[]) => void;\n onTime?: (label: string, duration: number) => void;\n onTimeLog?: (label: string, duration: number, ...args: unknown[]) => void;\n onCount?: (label: string, count: number) => void;\n onCountReset?: (label: string) => void;\n onGroup?: (label: string, collapsed: boolean) => void;\n onGroupEnd?: () => void;\n onClear?: () => void;\n onAssert?: (condition: boolean, ...args: unknown[]) => void;\n}\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 * 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 * onLog: (level, ...args) => console.log(`[${level}]`, ...args)\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\n const logLevels = [\n \"log\",\n \"warn\",\n \"error\",\n \"debug\",\n \"info\",\n \"trace\",\n \"dir\",\n \"table\",\n ];\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((...args: unknown[]) => {\n opts.onLog?.(level, ...args);\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.onTime?.(l, duration);\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.onTimeLog?.(l, duration, ...args);\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.onCount?.(l, count);\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.onCountReset?.(l);\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n groupDepth++;\n opts.onGroup?.(l, false);\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n groupDepth++;\n opts.onGroup?.(l, true);\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onGroupEnd?.();\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onClear?.();\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((condition: boolean, ...args: unknown[]) => {\n if (!condition) {\n opts.onAssert?.(condition, ...args);\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 */\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 * 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\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"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "4D8B064A0F23B8BC64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "2521806AC72E527C64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "commonjs"
5
5
  }
@@ -1,27 +1,71 @@
1
1
  // @bun
2
2
  // packages/console/src/index.ts
3
3
  import ivm from "isolated-vm";
4
+ function simpleConsoleHandler(callbacks) {
5
+ return {
6
+ onEntry: (entry) => {
7
+ if (entry.type === "output") {
8
+ callbacks[entry.level]?.(...entry.args);
9
+ } else if (entry.type === "assert") {
10
+ callbacks.error?.("Assertion failed:", ...entry.args);
11
+ } else if (entry.type === "trace") {
12
+ callbacks.log?.(...entry.args, `
13
+ ` + entry.stack);
14
+ } else if (entry.type === "dir") {
15
+ callbacks.log?.(entry.value);
16
+ } else if (entry.type === "table") {
17
+ callbacks.log?.(entry.data);
18
+ } else if (entry.type === "time") {
19
+ callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
20
+ } else if (entry.type === "timeLog") {
21
+ callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`, ...entry.args);
22
+ } else if (entry.type === "count") {
23
+ callbacks.log?.(`${entry.label}: ${entry.count}`);
24
+ }
25
+ }
26
+ };
27
+ }
4
28
  async function setupConsole(context, options) {
5
29
  const opts = options ?? {};
6
30
  const timers = new Map;
7
31
  const counters = new Map;
8
32
  let groupDepth = 0;
9
33
  const global = context.global;
10
- const logLevels = [
11
- "log",
12
- "warn",
13
- "error",
14
- "debug",
15
- "info",
16
- "trace",
17
- "dir",
18
- "table"
19
- ];
34
+ const logLevels = ["log", "warn", "error", "debug", "info"];
20
35
  for (const level of logLevels) {
21
36
  global.setSync(`__console_${level}`, new ivm.Callback((...args) => {
22
- opts.onLog?.(level, ...args);
37
+ opts.onEntry?.({
38
+ type: "output",
39
+ level,
40
+ args,
41
+ groupDepth
42
+ });
23
43
  }));
24
44
  }
45
+ global.setSync("__console_dir", new ivm.Callback((value) => {
46
+ opts.onEntry?.({
47
+ type: "dir",
48
+ value,
49
+ groupDepth
50
+ });
51
+ }));
52
+ global.setSync("__console_table", new ivm.Callback((data, columns) => {
53
+ opts.onEntry?.({
54
+ type: "table",
55
+ data,
56
+ columns,
57
+ groupDepth
58
+ });
59
+ }));
60
+ global.setSync("__console_trace", new ivm.Callback((...args) => {
61
+ const stack = new Error().stack ?? "";
62
+ opts.onEntry?.({
63
+ type: "trace",
64
+ args,
65
+ stack,
66
+ groupDepth
67
+ });
68
+ }));
25
69
  global.setSync("__console_time", new ivm.Callback((label) => {
26
70
  const l = label ?? "default";
27
71
  timers.set(l, performance.now());
@@ -32,7 +76,12 @@ async function setupConsole(context, options) {
32
76
  if (start !== undefined) {
33
77
  const duration = performance.now() - start;
34
78
  timers.delete(l);
35
- opts.onTime?.(l, duration);
79
+ opts.onEntry?.({
80
+ type: "time",
81
+ label: l,
82
+ duration,
83
+ groupDepth
84
+ });
36
85
  }
37
86
  }));
38
87
  global.setSync("__console_timeLog", new ivm.Callback((label, ...args) => {
@@ -40,42 +89,74 @@ async function setupConsole(context, options) {
40
89
  const start = timers.get(l);
41
90
  if (start !== undefined) {
42
91
  const duration = performance.now() - start;
43
- opts.onTimeLog?.(l, duration, ...args);
92
+ opts.onEntry?.({
93
+ type: "timeLog",
94
+ label: l,
95
+ duration,
96
+ args,
97
+ groupDepth
98
+ });
44
99
  }
45
100
  }));
46
101
  global.setSync("__console_count", new ivm.Callback((label) => {
47
102
  const l = label ?? "default";
48
103
  const count = (counters.get(l) ?? 0) + 1;
49
104
  counters.set(l, count);
50
- opts.onCount?.(l, count);
105
+ opts.onEntry?.({
106
+ type: "count",
107
+ label: l,
108
+ count,
109
+ groupDepth
110
+ });
51
111
  }));
52
112
  global.setSync("__console_countReset", new ivm.Callback((label) => {
53
113
  const l = label ?? "default";
54
114
  counters.delete(l);
55
- opts.onCountReset?.(l);
115
+ opts.onEntry?.({
116
+ type: "countReset",
117
+ label: l,
118
+ groupDepth
119
+ });
56
120
  }));
57
121
  global.setSync("__console_group", new ivm.Callback((label) => {
58
122
  const l = label ?? "default";
123
+ opts.onEntry?.({
124
+ type: "group",
125
+ label: l,
126
+ collapsed: false,
127
+ groupDepth
128
+ });
59
129
  groupDepth++;
60
- opts.onGroup?.(l, false);
61
130
  }));
62
131
  global.setSync("__console_groupCollapsed", new ivm.Callback((label) => {
63
132
  const l = label ?? "default";
133
+ opts.onEntry?.({
134
+ type: "group",
135
+ label: l,
136
+ collapsed: true,
137
+ groupDepth
138
+ });
64
139
  groupDepth++;
65
- opts.onGroup?.(l, true);
66
140
  }));
67
141
  global.setSync("__console_groupEnd", new ivm.Callback(() => {
68
142
  if (groupDepth > 0) {
69
143
  groupDepth--;
70
144
  }
71
- opts.onGroupEnd?.();
145
+ opts.onEntry?.({
146
+ type: "groupEnd",
147
+ groupDepth
148
+ });
72
149
  }));
73
150
  global.setSync("__console_clear", new ivm.Callback(() => {
74
- opts.onClear?.();
151
+ opts.onEntry?.({ type: "clear" });
75
152
  }));
76
153
  global.setSync("__console_assert", new ivm.Callback((condition, ...args) => {
77
154
  if (!condition) {
78
- opts.onAssert?.(condition, ...args);
155
+ opts.onEntry?.({
156
+ type: "assert",
157
+ args,
158
+ groupDepth
159
+ });
79
160
  }
80
161
  }));
81
162
  context.evalSync(`
@@ -123,7 +204,8 @@ async function setupConsole(context, options) {
123
204
  };
124
205
  }
125
206
  export {
207
+ simpleConsoleHandler,
126
208
  setupConsole
127
209
  };
128
210
 
129
- //# debugId=5ED37B6ACE7B494E64756E2164756E21
211
+ //# debugId=763E37163F3CD31764756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
4
  "sourcesContent": [
5
- "import ivm from \"isolated-vm\";\n\nexport interface ConsoleOptions {\n onLog?: (level: string, ...args: unknown[]) => void;\n onTime?: (label: string, duration: number) => void;\n onTimeLog?: (label: string, duration: number, ...args: unknown[]) => void;\n onCount?: (label: string, count: number) => void;\n onCountReset?: (label: string) => void;\n onGroup?: (label: string, collapsed: boolean) => void;\n onGroupEnd?: () => void;\n onClear?: () => void;\n onAssert?: (condition: boolean, ...args: unknown[]) => void;\n}\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 * 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 * onLog: (level, ...args) => console.log(`[${level}]`, ...args)\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\n const logLevels = [\n \"log\",\n \"warn\",\n \"error\",\n \"debug\",\n \"info\",\n \"trace\",\n \"dir\",\n \"table\",\n ];\n\n for (const level of logLevels) {\n global.setSync(\n `__console_${level}`,\n new ivm.Callback((...args: unknown[]) => {\n opts.onLog?.(level, ...args);\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.onTime?.(l, duration);\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.onTimeLog?.(l, duration, ...args);\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.onCount?.(l, count);\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.onCountReset?.(l);\n })\n );\n\n // Grouping methods\n global.setSync(\n \"__console_group\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n groupDepth++;\n opts.onGroup?.(l, false);\n })\n );\n\n global.setSync(\n \"__console_groupCollapsed\",\n new ivm.Callback((label?: string) => {\n const l = label ?? \"default\";\n groupDepth++;\n opts.onGroup?.(l, true);\n })\n );\n\n global.setSync(\n \"__console_groupEnd\",\n new ivm.Callback(() => {\n if (groupDepth > 0) {\n groupDepth--;\n }\n opts.onGroupEnd?.();\n })\n );\n\n // Other methods\n global.setSync(\n \"__console_clear\",\n new ivm.Callback(() => {\n opts.onClear?.();\n })\n );\n\n global.setSync(\n \"__console_assert\",\n new ivm.Callback((condition: boolean, ...args: unknown[]) => {\n if (!condition) {\n opts.onAssert?.(condition, ...args);\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 */\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 * 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\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"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "5ED37B6ACE7B494E64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "763E37163F3CD31764756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "type": "module"
5
5
  }
@@ -1,15 +1,81 @@
1
1
  import ivm from "isolated-vm";
2
+ /**
3
+ * Console entry types for structured console output.
4
+ * Each entry type captures the specific data needed to render like DevTools.
5
+ */
6
+ export type ConsoleEntry = {
7
+ type: "output";
8
+ level: "log" | "warn" | "error" | "info" | "debug";
9
+ args: unknown[];
10
+ groupDepth: number;
11
+ } | {
12
+ /** Browser console output (from Playwright page, not sandbox) */
13
+ type: "browserOutput";
14
+ level: string;
15
+ args: unknown[];
16
+ timestamp: number;
17
+ } | {
18
+ type: "dir";
19
+ value: unknown;
20
+ groupDepth: number;
21
+ } | {
22
+ type: "table";
23
+ data: unknown;
24
+ columns?: string[];
25
+ groupDepth: number;
26
+ } | {
27
+ type: "time";
28
+ label: string;
29
+ duration: number;
30
+ groupDepth: number;
31
+ } | {
32
+ type: "timeLog";
33
+ label: string;
34
+ duration: number;
35
+ args: unknown[];
36
+ groupDepth: number;
37
+ } | {
38
+ type: "count";
39
+ label: string;
40
+ count: number;
41
+ groupDepth: number;
42
+ } | {
43
+ type: "countReset";
44
+ label: string;
45
+ groupDepth: number;
46
+ } | {
47
+ type: "assert";
48
+ args: unknown[];
49
+ groupDepth: number;
50
+ } | {
51
+ type: "group";
52
+ label: string;
53
+ collapsed: boolean;
54
+ groupDepth: number;
55
+ } | {
56
+ type: "groupEnd";
57
+ groupDepth: number;
58
+ } | {
59
+ type: "clear";
60
+ } | {
61
+ type: "trace";
62
+ args: unknown[];
63
+ stack: string;
64
+ groupDepth: number;
65
+ };
66
+ /**
67
+ * Console options with a single structured callback.
68
+ */
2
69
  export interface ConsoleOptions {
3
- onLog?: (level: string, ...args: unknown[]) => void;
4
- onTime?: (label: string, duration: number) => void;
5
- onTimeLog?: (label: string, duration: number, ...args: unknown[]) => void;
6
- onCount?: (label: string, count: number) => void;
7
- onCountReset?: (label: string) => void;
8
- onGroup?: (label: string, collapsed: boolean) => void;
9
- onGroupEnd?: () => void;
10
- onClear?: () => void;
11
- onAssert?: (condition: boolean, ...args: unknown[]) => void;
70
+ /**
71
+ * Callback invoked for each console operation.
72
+ * Receives a structured entry with all data needed to render the output.
73
+ */
74
+ onEntry?: (entry: ConsoleEntry) => void;
12
75
  }
76
+ /**
77
+ * Console handle for accessing internal state.
78
+ */
13
79
  export interface ConsoleHandle {
14
80
  dispose(): void;
15
81
  reset(): void;
@@ -17,6 +83,32 @@ export interface ConsoleHandle {
17
83
  getCounters(): Map<string, number>;
18
84
  getGroupDepth(): number;
19
85
  }
86
+ /**
87
+ * Simple console callback interface for basic usage.
88
+ */
89
+ export interface SimpleConsoleCallbacks {
90
+ log?: (...args: unknown[]) => void;
91
+ warn?: (...args: unknown[]) => void;
92
+ error?: (...args: unknown[]) => void;
93
+ info?: (...args: unknown[]) => void;
94
+ debug?: (...args: unknown[]) => void;
95
+ }
96
+ /**
97
+ * Helper to create ConsoleOptions from simple callbacks.
98
+ * Routes log-level outputs to the appropriate callback and handles assertions.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const runtime = await createRuntime({
103
+ * console: simpleConsoleHandler({
104
+ * log: (...args) => console.log('[sandbox]', ...args),
105
+ * warn: (...args) => console.warn('[sandbox]', ...args),
106
+ * error: (...args) => console.error('[sandbox]', ...args),
107
+ * })
108
+ * });
109
+ * ```
110
+ */
111
+ export declare function simpleConsoleHandler(callbacks: SimpleConsoleCallbacks): ConsoleOptions;
20
112
  /**
21
113
  * Setup console API in an isolated-vm context
22
114
  *
@@ -27,7 +119,11 @@ export interface ConsoleHandle {
27
119
  *
28
120
  * @example
29
121
  * const handle = await setupConsole(context, {
30
- * onLog: (level, ...args) => console.log(`[${level}]`, ...args)
122
+ * onEntry: (entry) => {
123
+ * if (entry.type === 'output') {
124
+ * console.log(`[${entry.level}]`, ...entry.args);
125
+ * }
126
+ * }
31
127
  * });
32
128
  */
33
129
  export declare function setupConsole(context: ivm.Context, options?: ConsoleOptions): Promise<ConsoleHandle>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/isolate-console",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "main": "./dist/cjs/index.cjs",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "exports": {