@ricsam/isolate-console 0.1.8 → 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 +37 -21
- package/dist/cjs/index.cjs +261 -36
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/utils.cjs +9 -11
- package/dist/cjs/utils.cjs.map +3 -3
- package/dist/mjs/index.mjs +260 -34
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/utils.mjs +8 -9
- package/dist/mjs/utils.mjs.map +3 -3
- package/dist/types/index.d.ts +8 -8
- package/dist/types/utils.d.ts +8 -8
- package/package.json +1 -1
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}]`,
|
|
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: (
|
|
53
|
-
warn: (
|
|
54
|
-
error: (
|
|
55
|
-
info: (
|
|
56
|
-
debug: (
|
|
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";
|
|
68
|
+
| { type: "output"; level: "log" | "warn" | "error" | "info" | "debug"; stdout: string; groupDepth: number }
|
|
69
69
|
|
|
70
70
|
// console.dir()
|
|
71
|
-
| { type: "dir";
|
|
71
|
+
| { type: "dir"; stdout: string; groupDepth: number }
|
|
72
72
|
|
|
73
73
|
// console.table()
|
|
74
|
-
| { type: "table";
|
|
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;
|
|
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";
|
|
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";
|
|
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`, `
|
|
148
|
-
| `dir` | Object inspection | `
|
|
149
|
-
| `table` | Tabular data display | `
|
|
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`, `
|
|
167
|
+
| `timeLog` | Timer checkpoint | `label`, `duration`, `stdout` |
|
|
152
168
|
| `count` | Counter increment | `label`, `count` |
|
|
153
169
|
| `countReset` | Counter reset | `label` |
|
|
154
|
-
| `assert` | Failed assertion | `
|
|
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 | `
|
|
174
|
+
| `trace` | Stack trace | `stdout`, `stack` |
|
|
159
175
|
|
|
160
176
|
## License
|
|
161
177
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
|
1
|
+
var __create = Object.create;
|
|
3
2
|
var __getProtoOf = Object.getPrototypeOf;
|
|
4
3
|
var __defProp = Object.defineProperty;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -55,35 +54,33 @@ async function setupConsole(context, options) {
|
|
|
55
54
|
const global = context.global;
|
|
56
55
|
const logLevels = ["log", "warn", "error", "debug", "info"];
|
|
57
56
|
for (const level of logLevels) {
|
|
58
|
-
global.setSync(`__console_${level}`, new import_isolated_vm.default.Callback((
|
|
57
|
+
global.setSync(`__console_${level}`, new import_isolated_vm.default.Callback((stdout) => {
|
|
59
58
|
opts.onEntry?.({
|
|
60
59
|
type: "output",
|
|
61
60
|
level,
|
|
62
|
-
|
|
61
|
+
stdout,
|
|
63
62
|
groupDepth
|
|
64
63
|
});
|
|
65
64
|
}));
|
|
66
65
|
}
|
|
67
|
-
global.setSync("__console_dir", new import_isolated_vm.default.Callback((
|
|
66
|
+
global.setSync("__console_dir", new import_isolated_vm.default.Callback((stdout) => {
|
|
68
67
|
opts.onEntry?.({
|
|
69
68
|
type: "dir",
|
|
70
|
-
|
|
69
|
+
stdout,
|
|
71
70
|
groupDepth
|
|
72
71
|
});
|
|
73
72
|
}));
|
|
74
|
-
global.setSync("__console_table", new import_isolated_vm.default.Callback((
|
|
73
|
+
global.setSync("__console_table", new import_isolated_vm.default.Callback((stdout) => {
|
|
75
74
|
opts.onEntry?.({
|
|
76
75
|
type: "table",
|
|
77
|
-
|
|
78
|
-
columns,
|
|
76
|
+
stdout,
|
|
79
77
|
groupDepth
|
|
80
78
|
});
|
|
81
79
|
}));
|
|
82
|
-
global.setSync("__console_trace", new import_isolated_vm.default.Callback((
|
|
83
|
-
const stack = new Error().stack ?? "";
|
|
80
|
+
global.setSync("__console_trace", new import_isolated_vm.default.Callback((stdout, stack) => {
|
|
84
81
|
opts.onEntry?.({
|
|
85
82
|
type: "trace",
|
|
86
|
-
|
|
83
|
+
stdout,
|
|
87
84
|
stack,
|
|
88
85
|
groupDepth
|
|
89
86
|
});
|
|
@@ -106,16 +103,16 @@ async function setupConsole(context, options) {
|
|
|
106
103
|
});
|
|
107
104
|
}
|
|
108
105
|
}));
|
|
109
|
-
global.setSync("__console_timeLog", new import_isolated_vm.default.Callback((label,
|
|
106
|
+
global.setSync("__console_timeLog", new import_isolated_vm.default.Callback((label, duration, stdout) => {
|
|
110
107
|
const l = label ?? "default";
|
|
111
108
|
const start = timers.get(l);
|
|
112
109
|
if (start !== undefined) {
|
|
113
|
-
const
|
|
110
|
+
const actualDuration = performance.now() - start;
|
|
114
111
|
opts.onEntry?.({
|
|
115
112
|
type: "timeLog",
|
|
116
113
|
label: l,
|
|
117
|
-
duration,
|
|
118
|
-
|
|
114
|
+
duration: actualDuration,
|
|
115
|
+
stdout,
|
|
119
116
|
groupDepth
|
|
120
117
|
});
|
|
121
118
|
}
|
|
@@ -172,35 +169,264 @@ async function setupConsole(context, options) {
|
|
|
172
169
|
global.setSync("__console_clear", new import_isolated_vm.default.Callback(() => {
|
|
173
170
|
opts.onEntry?.({ type: "clear" });
|
|
174
171
|
}));
|
|
175
|
-
global.setSync("__console_assert", new import_isolated_vm.default.Callback((
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
});
|
|
182
|
-
}
|
|
172
|
+
global.setSync("__console_assert", new import_isolated_vm.default.Callback((stdout) => {
|
|
173
|
+
opts.onEntry?.({
|
|
174
|
+
type: "assert",
|
|
175
|
+
stdout,
|
|
176
|
+
groupDepth
|
|
177
|
+
});
|
|
183
178
|
}));
|
|
184
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
|
+
|
|
185
400
|
globalThis.console = {
|
|
186
|
-
log: __console_log,
|
|
187
|
-
warn: __console_warn,
|
|
188
|
-
error: __console_error,
|
|
189
|
-
debug: __console_debug,
|
|
190
|
-
info: __console_info,
|
|
191
|
-
trace:
|
|
192
|
-
|
|
193
|
-
|
|
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)),
|
|
194
415
|
time: __console_time,
|
|
195
416
|
timeEnd: __console_timeEnd,
|
|
196
|
-
timeLog: __console_timeLog,
|
|
417
|
+
timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),
|
|
197
418
|
count: __console_count,
|
|
198
419
|
countReset: __console_countReset,
|
|
199
420
|
group: __console_group,
|
|
200
421
|
groupCollapsed: __console_groupCollapsed,
|
|
201
422
|
groupEnd: __console_groupEnd,
|
|
202
423
|
clear: __console_clear,
|
|
203
|
-
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
|
+
},
|
|
204
430
|
};
|
|
205
431
|
`);
|
|
206
432
|
return {
|
|
@@ -225,6 +451,5 @@ async function setupConsole(context, options) {
|
|
|
225
451
|
}
|
|
226
452
|
};
|
|
227
453
|
}
|
|
228
|
-
})
|
|
229
454
|
|
|
230
|
-
//# debugId=
|
|
455
|
+
//# debugId=C79A16572D336D2464756E2164756E21
|
package/dist/cjs/index.cjs.map
CHANGED
|
@@ -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": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "C79A16572D336D2464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/cjs/package.json
CHANGED
package/dist/cjs/utils.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
(function(exports, require, module, __filename, __dirname) {var __defProp = Object.defineProperty;
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -37,26 +36,25 @@ function simpleConsoleHandler(callbacks) {
|
|
|
37
36
|
return {
|
|
38
37
|
onEntry: (entry) => {
|
|
39
38
|
if (entry.type === "output") {
|
|
40
|
-
callbacks[entry.level]?.(
|
|
39
|
+
callbacks[entry.level]?.(entry.stdout);
|
|
41
40
|
} else if (entry.type === "assert") {
|
|
42
|
-
callbacks.error?.(
|
|
41
|
+
callbacks.error?.(entry.stdout);
|
|
43
42
|
} else if (entry.type === "trace") {
|
|
44
|
-
callbacks.log?.(
|
|
45
|
-
` + entry.stack);
|
|
43
|
+
callbacks.log?.(entry.stack);
|
|
46
44
|
} else if (entry.type === "dir") {
|
|
47
|
-
callbacks.log?.(entry.
|
|
45
|
+
callbacks.log?.(entry.stdout);
|
|
48
46
|
} else if (entry.type === "table") {
|
|
49
|
-
callbacks.log?.(entry.
|
|
47
|
+
callbacks.log?.(entry.stdout);
|
|
50
48
|
} else if (entry.type === "time") {
|
|
51
49
|
callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
|
|
52
50
|
} else if (entry.type === "timeLog") {
|
|
53
|
-
|
|
51
|
+
const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;
|
|
52
|
+
callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);
|
|
54
53
|
} else if (entry.type === "count") {
|
|
55
54
|
callbacks.log?.(`${entry.label}: ${entry.count}`);
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
};
|
|
59
58
|
}
|
|
60
|
-
})
|
|
61
59
|
|
|
62
|
-
//# debugId=
|
|
60
|
+
//# debugId=83CDB12CE97F61ED64756E2164756E21
|
package/dist/cjs/utils.cjs.map
CHANGED
|
@@ -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?: (
|
|
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": "
|
|
8
|
-
"debugId": "
|
|
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
|
}
|
package/dist/mjs/index.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// @bun
|
|
2
1
|
// packages/console/src/index.ts
|
|
3
2
|
import ivm from "isolated-vm";
|
|
4
3
|
async function setupConsole(context, options) {
|
|
@@ -9,35 +8,33 @@ async function setupConsole(context, options) {
|
|
|
9
8
|
const global = context.global;
|
|
10
9
|
const logLevels = ["log", "warn", "error", "debug", "info"];
|
|
11
10
|
for (const level of logLevels) {
|
|
12
|
-
global.setSync(`__console_${level}`, new ivm.Callback((
|
|
11
|
+
global.setSync(`__console_${level}`, new ivm.Callback((stdout) => {
|
|
13
12
|
opts.onEntry?.({
|
|
14
13
|
type: "output",
|
|
15
14
|
level,
|
|
16
|
-
|
|
15
|
+
stdout,
|
|
17
16
|
groupDepth
|
|
18
17
|
});
|
|
19
18
|
}));
|
|
20
19
|
}
|
|
21
|
-
global.setSync("__console_dir", new ivm.Callback((
|
|
20
|
+
global.setSync("__console_dir", new ivm.Callback((stdout) => {
|
|
22
21
|
opts.onEntry?.({
|
|
23
22
|
type: "dir",
|
|
24
|
-
|
|
23
|
+
stdout,
|
|
25
24
|
groupDepth
|
|
26
25
|
});
|
|
27
26
|
}));
|
|
28
|
-
global.setSync("__console_table", new ivm.Callback((
|
|
27
|
+
global.setSync("__console_table", new ivm.Callback((stdout) => {
|
|
29
28
|
opts.onEntry?.({
|
|
30
29
|
type: "table",
|
|
31
|
-
|
|
32
|
-
columns,
|
|
30
|
+
stdout,
|
|
33
31
|
groupDepth
|
|
34
32
|
});
|
|
35
33
|
}));
|
|
36
|
-
global.setSync("__console_trace", new ivm.Callback((
|
|
37
|
-
const stack = new Error().stack ?? "";
|
|
34
|
+
global.setSync("__console_trace", new ivm.Callback((stdout, stack) => {
|
|
38
35
|
opts.onEntry?.({
|
|
39
36
|
type: "trace",
|
|
40
|
-
|
|
37
|
+
stdout,
|
|
41
38
|
stack,
|
|
42
39
|
groupDepth
|
|
43
40
|
});
|
|
@@ -60,16 +57,16 @@ async function setupConsole(context, options) {
|
|
|
60
57
|
});
|
|
61
58
|
}
|
|
62
59
|
}));
|
|
63
|
-
global.setSync("__console_timeLog", new ivm.Callback((label,
|
|
60
|
+
global.setSync("__console_timeLog", new ivm.Callback((label, duration, stdout) => {
|
|
64
61
|
const l = label ?? "default";
|
|
65
62
|
const start = timers.get(l);
|
|
66
63
|
if (start !== undefined) {
|
|
67
|
-
const
|
|
64
|
+
const actualDuration = performance.now() - start;
|
|
68
65
|
opts.onEntry?.({
|
|
69
66
|
type: "timeLog",
|
|
70
67
|
label: l,
|
|
71
|
-
duration,
|
|
72
|
-
|
|
68
|
+
duration: actualDuration,
|
|
69
|
+
stdout,
|
|
73
70
|
groupDepth
|
|
74
71
|
});
|
|
75
72
|
}
|
|
@@ -126,35 +123,264 @@ async function setupConsole(context, options) {
|
|
|
126
123
|
global.setSync("__console_clear", new ivm.Callback(() => {
|
|
127
124
|
opts.onEntry?.({ type: "clear" });
|
|
128
125
|
}));
|
|
129
|
-
global.setSync("__console_assert", new ivm.Callback((
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
126
|
+
global.setSync("__console_assert", new ivm.Callback((stdout) => {
|
|
127
|
+
opts.onEntry?.({
|
|
128
|
+
type: "assert",
|
|
129
|
+
stdout,
|
|
130
|
+
groupDepth
|
|
131
|
+
});
|
|
137
132
|
}));
|
|
138
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
|
+
|
|
139
354
|
globalThis.console = {
|
|
140
|
-
log: __console_log,
|
|
141
|
-
warn: __console_warn,
|
|
142
|
-
error: __console_error,
|
|
143
|
-
debug: __console_debug,
|
|
144
|
-
info: __console_info,
|
|
145
|
-
trace:
|
|
146
|
-
|
|
147
|
-
|
|
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)),
|
|
148
369
|
time: __console_time,
|
|
149
370
|
timeEnd: __console_timeEnd,
|
|
150
|
-
timeLog: __console_timeLog,
|
|
371
|
+
timeLog: (label, ...args) => __console_timeLog(label ?? 'default', 0, __formatArgs(args)),
|
|
151
372
|
count: __console_count,
|
|
152
373
|
countReset: __console_countReset,
|
|
153
374
|
group: __console_group,
|
|
154
375
|
groupCollapsed: __console_groupCollapsed,
|
|
155
376
|
groupEnd: __console_groupEnd,
|
|
156
377
|
clear: __console_clear,
|
|
157
|
-
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
|
+
},
|
|
158
384
|
};
|
|
159
385
|
`);
|
|
160
386
|
return {
|
|
@@ -183,4 +409,4 @@ export {
|
|
|
183
409
|
setupConsole
|
|
184
410
|
};
|
|
185
411
|
|
|
186
|
-
//# debugId=
|
|
412
|
+
//# debugId=2FC6087CD8BA651864756E2164756E21
|
package/dist/mjs/index.mjs.map
CHANGED
|
@@ -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": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAAA;AAoFA,eAAsB,YAAY,CAChC,SACA,SACwB;AAAA,EACxB,MAAM,OAAO,WAAW,CAAC;AAAA,EAGzB,MAAM,SAAS,IAAI;AAAA,EACnB,MAAM,WAAW,IAAI;AAAA,EACrB,IAAI,aAAa;AAAA,EAEjB,MAAM,SAAS,QAAQ;AAAA,EAGvB,MAAM,YAAY,CAAC,OAAO,QAAQ,SAAS,SAAS,MAAM;AAAA,EAE1D,WAAW,SAAS,WAAW;AAAA,IAC7B,OAAO,QACL,aAAa,SACb,IAAI,IAAI,SAAS,CAAC,WAAmB;AAAA,MACnC,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,KACF,CACH;AAAA,EACF;AAAA,EAGA,OAAO,QACL,iBACA,IAAI,IAAI,SAAS,CAAC,WAAmB;AAAA,IACnC,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,OAAO,QACL,mBACA,IAAI,IAAI,SAAS,CAAC,WAAmB;AAAA,IACnC,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,OAAO,QACL,mBACA,IAAI,IAAI,SAAS,CAAC,QAAgB,UAAkB;AAAA,IAClD,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,OAAO,QACL,kBACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,OAAO,IAAI,GAAG,YAAY,IAAI,CAAC;AAAA,GAChC,CACH;AAAA,EAEA,OAAO,QACL,qBACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,MAAM,QAAQ,OAAO,IAAI,CAAC;AAAA,IAC1B,IAAI,UAAU,WAAW;AAAA,MACvB,MAAM,WAAW,YAAY,IAAI,IAAI;AAAA,MACrC,OAAO,OAAO,CAAC;AAAA,MACf,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,GACD,CACH;AAAA,EAEA,OAAO,QACL,qBACA,IAAI,IAAI,SAAS,CAAC,OAAe,UAAkB,WAAmB;AAAA,IACpE,MAAM,IAAI,SAAS;AAAA,IACnB,MAAM,QAAQ,OAAO,IAAI,CAAC;AAAA,IAC1B,IAAI,UAAU,WAAW;AAAA,MACvB,MAAM,iBAAiB,YAAY,IAAI,IAAI;AAAA,MAC3C,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,GACD,CACH;AAAA,EAGA,OAAO,QACL,mBACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,MAAM,SAAS,SAAS,IAAI,CAAC,KAAK,KAAK;AAAA,IACvC,SAAS,IAAI,GAAG,KAAK;AAAA,IACrB,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAEA,OAAO,QACL,wBACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,SAAS,OAAO,CAAC;AAAA,IACjB,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,OAAO,QACL,mBACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AAAA,IACD;AAAA,GACD,CACH;AAAA,EAEA,OAAO,QACL,4BACA,IAAI,IAAI,SAAS,CAAC,UAAmB;AAAA,IACnC,MAAM,IAAI,SAAS;AAAA,IACnB,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,OAAO;AAAA,MACP,WAAW;AAAA,MACX;AAAA,IACF,CAAC;AAAA,IACD;AAAA,GACD,CACH;AAAA,EAEA,OAAO,QACL,sBACA,IAAI,IAAI,SAAS,MAAM;AAAA,IACrB,IAAI,aAAa,GAAG;AAAA,MAClB;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,OAAO,QACL,mBACA,IAAI,IAAI,SAAS,MAAM;AAAA,IACrB,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAAA,GACjC,CACH;AAAA,EAEA,OAAO,QACL,oBACA,IAAI,IAAI,SAAS,CAAC,WAAmB;AAAA,IACnC,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF,CAAC;AAAA,GACF,CACH;AAAA,EAGA,QAAQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA4PhB;AAAA,EAED,OAAO;AAAA,IACL,OAAO,GAAG;AAAA,MACR,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,aAAa;AAAA;AAAA,IAEf,KAAK,GAAG;AAAA,MACN,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,aAAa;AAAA;AAAA,IAEf,SAAS,GAAG;AAAA,MACV,OAAO,IAAI,IAAI,MAAM;AAAA;AAAA,IAEvB,WAAW,GAAG;AAAA,MACZ,OAAO,IAAI,IAAI,QAAQ;AAAA;AAAA,IAEzB,aAAa,GAAG;AAAA,MACd,OAAO;AAAA;AAAA,EAEX;AAAA;",
|
|
8
|
+
"debugId": "2FC6087CD8BA651864756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/package.json
CHANGED
package/dist/mjs/utils.mjs
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
// @bun
|
|
2
1
|
// packages/console/src/utils.ts
|
|
3
2
|
function simpleConsoleHandler(callbacks) {
|
|
4
3
|
return {
|
|
5
4
|
onEntry: (entry) => {
|
|
6
5
|
if (entry.type === "output") {
|
|
7
|
-
callbacks[entry.level]?.(
|
|
6
|
+
callbacks[entry.level]?.(entry.stdout);
|
|
8
7
|
} else if (entry.type === "assert") {
|
|
9
|
-
callbacks.error?.(
|
|
8
|
+
callbacks.error?.(entry.stdout);
|
|
10
9
|
} else if (entry.type === "trace") {
|
|
11
|
-
callbacks.log?.(
|
|
12
|
-
` + entry.stack);
|
|
10
|
+
callbacks.log?.(entry.stack);
|
|
13
11
|
} else if (entry.type === "dir") {
|
|
14
|
-
callbacks.log?.(entry.
|
|
12
|
+
callbacks.log?.(entry.stdout);
|
|
15
13
|
} else if (entry.type === "table") {
|
|
16
|
-
callbacks.log?.(entry.
|
|
14
|
+
callbacks.log?.(entry.stdout);
|
|
17
15
|
} else if (entry.type === "time") {
|
|
18
16
|
callbacks.log?.(`${entry.label}: ${entry.duration.toFixed(2)}ms`);
|
|
19
17
|
} else if (entry.type === "timeLog") {
|
|
20
|
-
|
|
18
|
+
const timeMsg = `${entry.label}: ${entry.duration.toFixed(2)}ms`;
|
|
19
|
+
callbacks.log?.(entry.stdout ? `${timeMsg} ${entry.stdout}` : timeMsg);
|
|
21
20
|
} else if (entry.type === "count") {
|
|
22
21
|
callbacks.log?.(`${entry.label}: ${entry.count}`);
|
|
23
22
|
}
|
|
@@ -28,4 +27,4 @@ export {
|
|
|
28
27
|
simpleConsoleHandler
|
|
29
28
|
};
|
|
30
29
|
|
|
31
|
-
//# debugId=
|
|
30
|
+
//# debugId=BB0862A33450F0FB64756E2164756E21
|
package/dist/mjs/utils.mjs.map
CHANGED
|
@@ -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?: (
|
|
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": "
|
|
8
|
-
"debugId": "
|
|
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
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
16
|
+
stdout: string;
|
|
16
17
|
timestamp: number;
|
|
17
18
|
} | {
|
|
18
19
|
type: "dir";
|
|
19
|
-
|
|
20
|
+
stdout: string;
|
|
20
21
|
groupDepth: number;
|
|
21
22
|
} | {
|
|
22
23
|
type: "table";
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
+
stdout: string;
|
|
63
63
|
stack: string;
|
|
64
64
|
groupDepth: number;
|
|
65
65
|
};
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -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?: (
|
|
7
|
-
warn?: (
|
|
8
|
-
error?: (
|
|
9
|
-
info?: (
|
|
10
|
-
debug?: (
|
|
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: (
|
|
21
|
-
* warn: (
|
|
22
|
-
* error: (
|
|
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
|
* ```
|