lognal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/core/filter.d.ts +29 -0
  4. package/dist/core/filter.js +82 -0
  5. package/dist/core/layout/entry-lines.d.ts +16 -0
  6. package/dist/core/layout/entry-lines.js +120 -0
  7. package/dist/core/layout/layout.d.ts +152 -0
  8. package/dist/core/layout/layout.js +784 -0
  9. package/dist/core/layout/row-index.d.ts +33 -0
  10. package/dist/core/layout/row-index.js +99 -0
  11. package/dist/core/layout/types.d.ts +128 -0
  12. package/dist/core/layout/types.js +8 -0
  13. package/dist/core/store.d.ts +96 -0
  14. package/dist/core/store.js +204 -0
  15. package/dist/core/text/ansi.d.ts +20 -0
  16. package/dist/core/text/ansi.js +187 -0
  17. package/dist/core/text/graphemes.d.ts +17 -0
  18. package/dist/core/text/graphemes.js +70 -0
  19. package/dist/core/text/line-splitter.d.ts +18 -0
  20. package/dist/core/text/line-splitter.js +56 -0
  21. package/dist/core/text/measure.d.ts +10 -0
  22. package/dist/core/text/measure.js +38 -0
  23. package/dist/core/text/shape.d.ts +24 -0
  24. package/dist/core/text/shape.js +220 -0
  25. package/dist/core/text/unicode-width-data.d.ts +54 -0
  26. package/dist/core/text/unicode-width-data.js +227 -0
  27. package/dist/core/text/width.d.ts +20 -0
  28. package/dist/core/text/width.js +157 -0
  29. package/dist/core/text/wrap.d.ts +14 -0
  30. package/dist/core/text/wrap.js +94 -0
  31. package/dist/core/time.d.ts +10 -0
  32. package/dist/core/time.js +24 -0
  33. package/dist/core/types.d.ts +131 -0
  34. package/dist/core/types.js +9 -0
  35. package/dist/core/value/preview.d.ts +16 -0
  36. package/dist/core/value/preview.js +207 -0
  37. package/dist/index.d.ts +27 -0
  38. package/dist/index.js +24 -0
  39. package/dist/lognal.css +589 -0
  40. package/dist/react/LogViewer.d.ts +32 -0
  41. package/dist/react/LogViewer.js +183 -0
  42. package/dist/react/index.d.ts +1 -0
  43. package/dist/react/index.js +2 -0
  44. package/dist/renderer/canvas/canvas-renderer.d.ts +45 -0
  45. package/dist/renderer/canvas/canvas-renderer.js +464 -0
  46. package/dist/renderer/canvas/palette.d.ts +6 -0
  47. package/dist/renderer/canvas/palette.js +25 -0
  48. package/dist/renderer/theme.d.ts +3 -0
  49. package/dist/renderer/theme.js +52 -0
  50. package/dist/renderer/types.d.ts +82 -0
  51. package/dist/renderer/types.js +1 -0
  52. package/dist/sources/console/format.d.ts +32 -0
  53. package/dist/sources/console/format.js +189 -0
  54. package/dist/sources/console/hook.d.ts +27 -0
  55. package/dist/sources/console/hook.js +94 -0
  56. package/dist/sources/console/recorder.d.ts +41 -0
  57. package/dist/sources/console/recorder.js +239 -0
  58. package/dist/sources/console/snapshot.d.ts +22 -0
  59. package/dist/sources/console/snapshot.js +547 -0
  60. package/dist/sources/console/table.d.ts +9 -0
  61. package/dist/sources/console/table.js +87 -0
  62. package/dist/sources/text/encoding.d.ts +11 -0
  63. package/dist/sources/text/encoding.js +59 -0
  64. package/dist/sources/text/follow-file.d.ts +49 -0
  65. package/dist/sources/text/follow-file.js +109 -0
  66. package/dist/sources/text/read-file.d.ts +63 -0
  67. package/dist/sources/text/read-file.js +82 -0
  68. package/dist/viewer/icons.d.ts +14 -0
  69. package/dist/viewer/icons.js +30 -0
  70. package/dist/viewer/input-line.d.ts +46 -0
  71. package/dist/viewer/input-line.js +144 -0
  72. package/dist/viewer/labels.d.ts +35 -0
  73. package/dist/viewer/labels.js +61 -0
  74. package/dist/viewer/scrollbar.d.ts +27 -0
  75. package/dist/viewer/scrollbar.js +143 -0
  76. package/dist/viewer/theme.d.ts +15 -0
  77. package/dist/viewer/theme.js +105 -0
  78. package/dist/viewer/viewer.d.ts +217 -0
  79. package/dist/viewer/viewer.js +1201 -0
  80. package/package.json +92 -0
@@ -0,0 +1,547 @@
1
+ export const DEFAULT_CAPTURE_OPTIONS = {
2
+ maxDepth: 5,
3
+ maxProperties: 100,
4
+ maxStringLength: 10000,
5
+ maxNodes: 2000
6
+ };
7
+ /** The most attributes captured from one element, and the longest attribute value. */
8
+ const MAX_ATTRIBUTES = 20;
9
+ const MAX_ATTRIBUTE_LENGTH = 200;
10
+ const getterOf = (target, key) => {
11
+ if (!target) {
12
+ return undefined;
13
+ }
14
+ return Object.getOwnPropertyDescriptor(target, key)?.get;
15
+ };
16
+ /** Calls a built-in getter or method on a value, and returns `undefined` when it throws. */
17
+ const callBuiltIn = (fn, value) => {
18
+ if (!fn) {
19
+ return undefined;
20
+ }
21
+ try {
22
+ return fn.call(value);
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ };
28
+ /** Returns whether a built-in accepts the value, which is how built-in types are told apart. */
29
+ const accepts = (fn, value) => {
30
+ if (!fn) {
31
+ return false;
32
+ }
33
+ try {
34
+ fn.call(value);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ };
41
+ const globals = globalThis;
42
+ const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
43
+ const TYPED_ARRAY_TAG = getterOf(TYPED_ARRAY_PROTOTYPE, Symbol.toStringTag);
44
+ const TYPED_ARRAY_LENGTH = getterOf(TYPED_ARRAY_PROTOTYPE, 'length');
45
+ const MAP_SIZE = getterOf(Map.prototype, 'size');
46
+ const SET_SIZE = getterOf(Set.prototype, 'size');
47
+ const ARRAY_BUFFER_LENGTH = getterOf(ArrayBuffer.prototype, 'byteLength');
48
+ const REGEXP_SOURCE = getterOf(RegExp.prototype, 'source');
49
+ const REGEXP_FLAGS = getterOf(RegExp.prototype, 'flags');
50
+ const NODE_TYPE = getterOf(globals.Node?.prototype, 'nodeType');
51
+ const WEAK_MAP_HAS = function () {
52
+ return WeakMap.prototype.has.call(this, {});
53
+ };
54
+ const WEAK_SET_HAS = function () {
55
+ return WeakSet.prototype.has.call(this, {});
56
+ };
57
+ const NATIVE_CODE = /\{\s*\[native code\]\s*\}\s*$/;
58
+ const isNativeFunction = (fn) => {
59
+ try {
60
+ return typeof fn === 'function' && NATIVE_CODE.test(Function.prototype.toString.call(fn));
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ };
66
+ /** Looks a property up along the prototype chain without running user-defined getters. */
67
+ const readProperty = (value, key) => {
68
+ let target = value;
69
+ let depth = 0;
70
+ while (target && depth < 32) {
71
+ let descriptor;
72
+ try {
73
+ descriptor = Object.getOwnPropertyDescriptor(target, key);
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ if (descriptor) {
79
+ if ('value' in descriptor) {
80
+ return descriptor.value;
81
+ }
82
+ return isNativeFunction(descriptor.get) ? callBuiltIn(descriptor.get, value) : undefined;
83
+ }
84
+ try {
85
+ target = Object.getPrototypeOf(target);
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ depth++;
91
+ }
92
+ return undefined;
93
+ };
94
+ const classNameOf = (value) => {
95
+ let prototype;
96
+ try {
97
+ prototype = Object.getPrototypeOf(value);
98
+ }
99
+ catch {
100
+ return undefined;
101
+ }
102
+ if (!prototype) {
103
+ return undefined;
104
+ }
105
+ const constructor = Object.getOwnPropertyDescriptor(prototype, 'constructor')?.value;
106
+ if (typeof constructor !== 'function') {
107
+ return undefined;
108
+ }
109
+ const name = Object.getOwnPropertyDescriptor(constructor, 'name')?.value;
110
+ return typeof name === 'string' && name ? name : undefined;
111
+ };
112
+ const functionName = (fn) => {
113
+ const name = Object.getOwnPropertyDescriptor(fn, 'name')?.value;
114
+ return typeof name === 'string' ? name : '';
115
+ };
116
+ const isError = (value) => {
117
+ const isErrorCheck = Error.isError;
118
+ if (typeof isErrorCheck === 'function') {
119
+ return isErrorCheck(value);
120
+ }
121
+ return value instanceof Error;
122
+ };
123
+ /** Removes the `Name: message` header V8 puts at the top of a stack trace. */
124
+ const stackWithoutHeader = (stack, name, message) => {
125
+ const header = message ? `${name}: ${message}` : name;
126
+ if (stack.startsWith(header)) {
127
+ return stack.slice(header.length).replace(/^\r?\n/, '');
128
+ }
129
+ return stack;
130
+ };
131
+ class Capture {
132
+ options;
133
+ nodes = 0;
134
+ path = new Set();
135
+ constructor(options) {
136
+ this.options = options;
137
+ }
138
+ value(value, depth) {
139
+ this.nodes++;
140
+ switch (typeof value) {
141
+ case 'undefined':
142
+ return { kind: 'undefined' };
143
+ case 'boolean':
144
+ return { kind: 'boolean', value: String(value) };
145
+ case 'number':
146
+ return { kind: 'number', value: Object.is(value, -0) ? '-0' : String(value) };
147
+ case 'bigint':
148
+ return {
149
+ kind: 'bigint',
150
+ value: String(callBuiltIn(BigInt.prototype.toString, value) ?? '')
151
+ };
152
+ case 'string':
153
+ return this.string(value);
154
+ case 'symbol':
155
+ return {
156
+ kind: 'symbol',
157
+ value: String(callBuiltIn(Symbol.prototype.toString, value) ?? 'Symbol()')
158
+ };
159
+ case 'function':
160
+ return this.function(value);
161
+ default:
162
+ break;
163
+ }
164
+ if (value === null) {
165
+ return { kind: 'null' };
166
+ }
167
+ const object = value;
168
+ if (this.path.has(object)) {
169
+ return { kind: 'circular' };
170
+ }
171
+ this.path.add(object);
172
+ try {
173
+ return this.object(object, depth);
174
+ }
175
+ finally {
176
+ this.path.delete(object);
177
+ }
178
+ }
179
+ string(value) {
180
+ const { maxStringLength } = this.options;
181
+ if (value.length <= maxStringLength) {
182
+ return { kind: 'string', value };
183
+ }
184
+ return {
185
+ kind: 'string',
186
+ value: value.slice(0, maxStringLength),
187
+ truncated: value.length - maxStringLength,
188
+ size: value.length
189
+ };
190
+ }
191
+ function(fn) {
192
+ let source = '';
193
+ try {
194
+ source = Function.prototype.toString.call(fn).slice(0, 6);
195
+ }
196
+ catch {
197
+ // A revoked proxy has no source.
198
+ }
199
+ return { kind: source.startsWith('class') ? 'class' : 'function', value: functionName(fn) };
200
+ }
201
+ /** Whether this value may capture its children, or is shown by name only. */
202
+ canDescend(depth) {
203
+ return depth < this.options.maxDepth && this.nodes < this.options.maxNodes;
204
+ }
205
+ object(value, depth) {
206
+ if (accepts(Date.prototype.getTime, value)) {
207
+ const time = Date.prototype.getTime.call(value);
208
+ return {
209
+ kind: 'date',
210
+ value: Number.isNaN(time) ? 'Invalid Date' : Date.prototype.toISOString.call(value)
211
+ };
212
+ }
213
+ if (accepts(REGEXP_SOURCE, value) && value !== RegExp.prototype) {
214
+ return {
215
+ kind: 'regexp',
216
+ value: `/${callBuiltIn(REGEXP_SOURCE, value)}/${callBuiltIn(REGEXP_FLAGS, value) ?? ''}`
217
+ };
218
+ }
219
+ if (isError(value)) {
220
+ return this.error(value, depth);
221
+ }
222
+ if (NODE_TYPE && accepts(NODE_TYPE, value)) {
223
+ return this.domNode(value, depth);
224
+ }
225
+ if (Array.isArray(value)) {
226
+ return this.array(value, depth);
227
+ }
228
+ if (ArrayBuffer.isView(value)) {
229
+ return this.typedArray(value, depth);
230
+ }
231
+ if (accepts(MAP_SIZE, value)) {
232
+ return this.map(value, depth);
233
+ }
234
+ if (accepts(SET_SIZE, value)) {
235
+ return this.set(value, depth);
236
+ }
237
+ if (accepts(WEAK_MAP_HAS, value)) {
238
+ return { kind: 'weak', className: 'WeakMap' };
239
+ }
240
+ if (accepts(WEAK_SET_HAS, value)) {
241
+ return { kind: 'weak', className: 'WeakSet' };
242
+ }
243
+ if (globals.WeakRef && accepts(globals.WeakRef.prototype.deref, value)) {
244
+ return { kind: 'weak', className: 'WeakRef' };
245
+ }
246
+ if (value instanceof Promise ||
247
+ (readProperty(value, Symbol.toStringTag) === 'Promise' &&
248
+ isNativeFunction(readProperty(value, 'then')))) {
249
+ return { kind: 'promise', className: classNameOf(value) ?? 'Promise' };
250
+ }
251
+ if (accepts(ARRAY_BUFFER_LENGTH, value)) {
252
+ return {
253
+ kind: 'object',
254
+ className: classNameOf(value) ?? 'ArrayBuffer',
255
+ children: [
256
+ {
257
+ key: 'byteLength',
258
+ value: { kind: 'number', value: String(callBuiltIn(ARRAY_BUFFER_LENGTH, value)) }
259
+ }
260
+ ]
261
+ };
262
+ }
263
+ return this.plainObject(value, depth);
264
+ }
265
+ plainObject(value, depth) {
266
+ const node = { kind: 'object', className: classNameOf(value) };
267
+ if (!this.canDescend(depth)) {
268
+ return node;
269
+ }
270
+ const { children, omitted } = this.properties(value, depth);
271
+ node.children = children;
272
+ if (omitted > 0) {
273
+ node.omitted = omitted;
274
+ }
275
+ return node;
276
+ }
277
+ properties(value, depth, skip) {
278
+ const children = [];
279
+ let keys;
280
+ let omitted = 0;
281
+ try {
282
+ keys = Reflect.ownKeys(value);
283
+ }
284
+ catch {
285
+ return { children, omitted };
286
+ }
287
+ for (const key of keys) {
288
+ if (typeof key === 'string' && skip?.(key)) {
289
+ continue;
290
+ }
291
+ let descriptor;
292
+ try {
293
+ descriptor = Object.getOwnPropertyDescriptor(value, key);
294
+ }
295
+ catch {
296
+ continue;
297
+ }
298
+ if (!descriptor?.enumerable) {
299
+ continue;
300
+ }
301
+ if (children.length >= this.options.maxProperties || this.nodes >= this.options.maxNodes) {
302
+ omitted++;
303
+ continue;
304
+ }
305
+ const child = typeof key === 'symbol'
306
+ ? {
307
+ key: String(callBuiltIn(Symbol.prototype.toString, key)),
308
+ keyKind: 'symbol',
309
+ value: { kind: 'undefined' }
310
+ }
311
+ : { key, keyKind: 'property', value: { kind: 'undefined' } };
312
+ child.value =
313
+ 'value' in descriptor ? this.value(descriptor.value, depth + 1) : this.accessor(descriptor);
314
+ children.push(child);
315
+ }
316
+ return { children, omitted };
317
+ }
318
+ accessor(descriptor) {
319
+ this.nodes++;
320
+ const accessor = descriptor.get && descriptor.set ? 'get-set' : descriptor.set ? 'set' : 'get';
321
+ return { kind: 'accessor', accessor };
322
+ }
323
+ array(value, depth) {
324
+ let length = 0;
325
+ try {
326
+ length = value.length;
327
+ }
328
+ catch {
329
+ // A proxy trap threw.
330
+ }
331
+ const node = {
332
+ kind: 'array',
333
+ className: classNameOf(value) ?? 'Array',
334
+ size: length
335
+ };
336
+ if (!this.canDescend(depth)) {
337
+ return node;
338
+ }
339
+ const limit = Math.min(length, this.options.maxProperties);
340
+ const children = [];
341
+ for (let index = 0; index < limit; index++) {
342
+ if (this.nodes >= this.options.maxNodes) {
343
+ break;
344
+ }
345
+ let descriptor;
346
+ try {
347
+ descriptor = Object.getOwnPropertyDescriptor(value, index);
348
+ }
349
+ catch {
350
+ descriptor = undefined;
351
+ }
352
+ const item = descriptor === undefined
353
+ ? { kind: 'undefined' }
354
+ : 'value' in descriptor
355
+ ? this.value(descriptor.value, depth + 1)
356
+ : this.accessor(descriptor);
357
+ children.push({ key: String(index), keyKind: 'index', value: item });
358
+ }
359
+ node.children = children;
360
+ if (length > children.length) {
361
+ node.omitted = length - children.length;
362
+ }
363
+ return node;
364
+ }
365
+ typedArray(value, depth) {
366
+ const className = String(callBuiltIn(TYPED_ARRAY_TAG, value) ?? classNameOf(value) ?? 'DataView');
367
+ const length = Number(callBuiltIn(TYPED_ARRAY_LENGTH, value) ?? value.byteLength);
368
+ const node = { kind: 'array', className, size: length };
369
+ if (!this.canDescend(depth) || className === 'DataView') {
370
+ return node;
371
+ }
372
+ const items = value;
373
+ const limit = Math.min(length, this.options.maxProperties);
374
+ const children = [];
375
+ for (let index = 0; index < limit; index++) {
376
+ children.push({
377
+ key: String(index),
378
+ keyKind: 'index',
379
+ value: this.value(items[index], depth + 1)
380
+ });
381
+ }
382
+ node.children = children;
383
+ if (length > limit) {
384
+ node.omitted = length - limit;
385
+ }
386
+ return node;
387
+ }
388
+ map(value, depth) {
389
+ const size = Number(callBuiltIn(MAP_SIZE, value) ?? 0);
390
+ const node = { kind: 'map', className: classNameOf(value) ?? 'Map', size };
391
+ if (!this.canDescend(depth)) {
392
+ return node;
393
+ }
394
+ const children = [];
395
+ try {
396
+ for (const [key, item] of Map.prototype.entries.call(value)) {
397
+ if (children.length >= this.options.maxProperties || this.nodes >= this.options.maxNodes) {
398
+ break;
399
+ }
400
+ children.push({ keyValue: this.value(key, depth + 1), value: this.value(item, depth + 1) });
401
+ }
402
+ }
403
+ catch {
404
+ // The map changed while it was read, or a proxy trap threw.
405
+ }
406
+ node.children = children;
407
+ if (size > children.length) {
408
+ node.omitted = size - children.length;
409
+ }
410
+ return node;
411
+ }
412
+ set(value, depth) {
413
+ const size = Number(callBuiltIn(SET_SIZE, value) ?? 0);
414
+ const node = { kind: 'set', className: classNameOf(value) ?? 'Set', size };
415
+ if (!this.canDescend(depth)) {
416
+ return node;
417
+ }
418
+ const children = [];
419
+ try {
420
+ for (const item of Set.prototype.values.call(value)) {
421
+ if (children.length >= this.options.maxProperties || this.nodes >= this.options.maxNodes) {
422
+ break;
423
+ }
424
+ children.push({ value: this.value(item, depth + 1) });
425
+ }
426
+ }
427
+ catch {
428
+ // The set changed while it was read, or a proxy trap threw.
429
+ }
430
+ node.children = children;
431
+ if (size > children.length) {
432
+ node.omitted = size - children.length;
433
+ }
434
+ return node;
435
+ }
436
+ error(value, depth) {
437
+ const name = readProperty(value, 'name');
438
+ const message = readProperty(value, 'message');
439
+ const stack = readProperty(value, 'stack');
440
+ const nameText = typeof name === 'string' && name ? name : (classNameOf(value) ?? 'Error');
441
+ const messageText = typeof message === 'string' ? message : '';
442
+ const node = { kind: 'error', className: nameText, value: messageText };
443
+ if (typeof stack === 'string' && stack) {
444
+ const trimmed = stackWithoutHeader(stack, nameText, messageText).trimEnd();
445
+ if (trimmed) {
446
+ node.stack = trimmed.slice(0, this.options.maxStringLength);
447
+ }
448
+ }
449
+ if (this.canDescend(depth)) {
450
+ const { children, omitted } = this.properties(value, depth, (key) => key === 'stack' || key === 'message');
451
+ const cause = Object.getOwnPropertyDescriptor(value, 'cause');
452
+ if (cause && 'value' in cause && !cause.enumerable) {
453
+ children.push({
454
+ key: 'cause',
455
+ keyKind: 'property',
456
+ value: this.value(cause.value, depth + 1)
457
+ });
458
+ }
459
+ if (children.length > 0) {
460
+ node.children = children;
461
+ }
462
+ if (omitted > 0) {
463
+ node.omitted = omitted;
464
+ }
465
+ }
466
+ return node;
467
+ }
468
+ domNode(value, depth) {
469
+ const type = Number(callBuiltIn(NODE_TYPE, value));
470
+ const nodePrototype = globals.Node?.prototype;
471
+ const elementPrototype = globals.Element?.prototype;
472
+ if (type === 3) {
473
+ const text = String(callBuiltIn(getterOf(nodePrototype, 'textContent'), value) ?? '');
474
+ return { kind: 'text', value: text.slice(0, this.options.maxStringLength) };
475
+ }
476
+ if (type !== 1 && type !== 9 && type !== 11) {
477
+ return { kind: 'object', className: classNameOf(value) };
478
+ }
479
+ const tag = type === 9
480
+ ? '#document'
481
+ : type === 11
482
+ ? '#document-fragment'
483
+ : String(callBuiltIn(getterOf(elementPrototype, 'localName'), value) ?? 'element');
484
+ const node = { kind: 'element', value: tag };
485
+ if (type === 1) {
486
+ node.attributes = this.attributes(value);
487
+ }
488
+ if (!this.canDescend(depth)) {
489
+ return node;
490
+ }
491
+ const childNodes = callBuiltIn(getterOf(nodePrototype, 'childNodes'), value);
492
+ const children = [];
493
+ let omitted = 0;
494
+ for (let index = 0; childNodes && index < childNodes.length; index++) {
495
+ const child = childNodes[index];
496
+ const childType = Number(callBuiltIn(NODE_TYPE, child));
497
+ if (childType === 3) {
498
+ const text = String(callBuiltIn(getterOf(nodePrototype, 'textContent'), child) ?? '').trim();
499
+ if (!text) {
500
+ continue;
501
+ }
502
+ }
503
+ else if (childType !== 1) {
504
+ continue;
505
+ }
506
+ if (children.length >= this.options.maxProperties || this.nodes >= this.options.maxNodes) {
507
+ omitted++;
508
+ continue;
509
+ }
510
+ const captured = this.value(child, depth + 1);
511
+ if (captured.kind === 'text') {
512
+ captured.value = captured.value?.trim();
513
+ }
514
+ children.push({ value: captured });
515
+ }
516
+ node.children = children;
517
+ if (omitted > 0) {
518
+ node.omitted = omitted;
519
+ }
520
+ return node;
521
+ }
522
+ attributes(value) {
523
+ const attributes = callBuiltIn(getterOf(globals.Element?.prototype, 'attributes'), value);
524
+ const attrPrototype = globals.Attr?.prototype;
525
+ const nameGetter = getterOf(attrPrototype, 'name');
526
+ const valueGetter = getterOf(attrPrototype, 'value');
527
+ const result = [];
528
+ for (let index = 0; attributes && index < Math.min(attributes.length, MAX_ATTRIBUTES); index++) {
529
+ const attribute = attributes[index];
530
+ const name = String(callBuiltIn(nameGetter, attribute) ?? '');
531
+ const text = String(callBuiltIn(valueGetter, attribute) ?? '');
532
+ result.push([name, text.slice(0, MAX_ATTRIBUTE_LENGTH)]);
533
+ }
534
+ return result;
535
+ }
536
+ }
537
+ /**
538
+ * Captures a value as plain data, at the moment of the call.
539
+ *
540
+ * Getters defined by the page are never run: an accessor property is recorded as an accessor.
541
+ * Built-in types are recognized with the engine's own methods rather than `instanceof`, so
542
+ * values from another frame are recognized too. Each limit in `options` bounds the work, and
543
+ * whatever it cuts is counted in the node's `omitted` field.
544
+ */
545
+ export const snapshotValue = (value, options = {}) => {
546
+ return new Capture({ ...DEFAULT_CAPTURE_OPTIONS, ...options }).value(value, 0);
547
+ };
@@ -0,0 +1,9 @@
1
+ import type { ValueNode } from '../../core/types.js';
2
+ /**
3
+ * Draws a captured value as a text table with box-drawing characters, the way `console.table`
4
+ * shows it: one row per property or item, one column per key of the row values, and a
5
+ * `Values` column for rows that are not objects.
6
+ *
7
+ * Returns `null` when the value has no rows, in which case the caller logs it as usual.
8
+ */
9
+ export declare const formatTable: (node: ValueNode, properties?: readonly string[]) => string | null;
@@ -0,0 +1,87 @@
1
+ import { measureCells, padCells, truncateCells } from '../../core/text/measure.js';
2
+ import { previewValue } from '../../core/value/preview.js';
3
+ /** The most rows and columns a table shows, and the widest cell. */
4
+ const MAX_ROWS = 100;
5
+ const MAX_COLUMNS = 20;
6
+ const MAX_CELL_CELLS = 40;
7
+ const INDEX_COLUMN = '(index)';
8
+ const VALUES_COLUMN = 'Values';
9
+ const cellText = (node) => {
10
+ return previewValue(node, true)
11
+ .map((span) => span.text)
12
+ .join('');
13
+ };
14
+ const keyText = (entry) => {
15
+ if (entry.keyValue) {
16
+ return cellText(entry.keyValue);
17
+ }
18
+ return entry.key ?? '';
19
+ };
20
+ const isRowObject = (node) => {
21
+ return (node.kind === 'object' || node.kind === 'array') && node.children !== undefined;
22
+ };
23
+ /**
24
+ * Draws a captured value as a text table with box-drawing characters, the way `console.table`
25
+ * shows it: one row per property or item, one column per key of the row values, and a
26
+ * `Values` column for rows that are not objects.
27
+ *
28
+ * Returns `null` when the value has no rows, in which case the caller logs it as usual.
29
+ */
30
+ export const formatTable = (node, properties) => {
31
+ if (!node.children || node.children.length === 0) {
32
+ return null;
33
+ }
34
+ const rows = node.children.slice(0, MAX_ROWS);
35
+ const columns = [];
36
+ let hasValues = false;
37
+ for (const row of rows) {
38
+ if (isRowObject(row.value)) {
39
+ for (const child of row.value.children ?? []) {
40
+ const key = child.key ?? '';
41
+ if (!columns.includes(key) && columns.length < MAX_COLUMNS) {
42
+ columns.push(key);
43
+ }
44
+ }
45
+ }
46
+ else {
47
+ hasValues = true;
48
+ }
49
+ }
50
+ const shownColumns = properties ? properties.filter((key) => columns.includes(key)) : columns;
51
+ const header = [
52
+ INDEX_COLUMN,
53
+ ...shownColumns,
54
+ ...(hasValues && !properties ? [VALUES_COLUMN] : [])
55
+ ];
56
+ const body = rows.map((row) => {
57
+ const cells = [keyText(row)];
58
+ const children = isRowObject(row.value) ? (row.value.children ?? []) : [];
59
+ for (const key of shownColumns) {
60
+ const child = children.find((item) => item.key === key);
61
+ cells.push(child ? cellText(child.value) : '');
62
+ }
63
+ if (hasValues && !properties) {
64
+ cells.push(isRowObject(row.value) ? '' : cellText(row.value));
65
+ }
66
+ return cells.map((cell) => truncateCells(cell, MAX_CELL_CELLS));
67
+ });
68
+ const widths = header.map((title, column) => Math.max(measureCells(title), ...body.map((cells) => measureCells(cells[column]))));
69
+ const line = (left, middle, right) => {
70
+ return `${left}${widths.map((width) => '─'.repeat(width + 2)).join(middle)}${right}`;
71
+ };
72
+ const row = (cells) => {
73
+ return `│${cells.map((cell, column) => ` ${padCells(cell, widths[column])} `).join('│')}│`;
74
+ };
75
+ const lines = [
76
+ line('┌', '┬', '┐'),
77
+ row(header),
78
+ line('├', '┼', '┤'),
79
+ ...body.map(row),
80
+ line('└', '┴', '┘')
81
+ ];
82
+ const hidden = node.children.length - rows.length + (node.omitted ?? 0);
83
+ if (hidden > 0) {
84
+ lines.push(`… ${hidden} more`);
85
+ }
86
+ return lines.join('\n');
87
+ };
@@ -0,0 +1,11 @@
1
+ /** Returns the legacy encoding for a language tag, such as `euc-kr` for `ko-KR`. */
2
+ export declare const legacyEncodingFor: (locale: string | undefined) => string;
3
+ /** Returns the encoding a byte order mark at the start of the bytes announces, if any. */
4
+ export declare const encodingFromBom: (bytes: Uint8Array) => string | null;
5
+ /** Returns whether the bytes are valid UTF-8. A sequence cut at the end still counts as valid. */
6
+ export declare const isValidUtf8: (bytes: Uint8Array) => boolean;
7
+ /**
8
+ * Picks the encoding of a file from its first bytes: the byte order mark if there is one,
9
+ * then UTF-8 if the bytes are valid UTF-8, and otherwise `fallback`.
10
+ */
11
+ export declare const detectEncoding: (bytes: Uint8Array, fallback: string) => string;