@indiekitai/inspect 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.
- package/README.md +78 -0
- package/dist/cli.mjs +311 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +295 -0
- package/dist/index.mjs +268 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @indiekitai/inspect
|
|
2
|
+
|
|
3
|
+
Rich-style `inspect()` for JavaScript/TypeScript. Pretty-print any object's attributes, methods, and docs with colored output. Zero dependencies.
|
|
4
|
+
|
|
5
|
+
Inspired by Python's [Rich](https://github.com/Textualize/rich) `inspect()` function.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @indiekitai/inspect
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { inspect } from '@indiekitai/inspect';
|
|
17
|
+
|
|
18
|
+
class User {
|
|
19
|
+
/** User's full name */
|
|
20
|
+
name: string = 'Alice';
|
|
21
|
+
/** User's age */
|
|
22
|
+
age: number = 30;
|
|
23
|
+
|
|
24
|
+
greet() { return `Hi, I'm ${this.name}`; }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
inspect(new User());
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Output:
|
|
31
|
+
```
|
|
32
|
+
╭──────────────────────────────────────────────────────────────╮
|
|
33
|
+
│ User │
|
|
34
|
+
├──────────────────────────────────────────────────────────────┤
|
|
35
|
+
│ • age number = 30 │
|
|
36
|
+
│ • name string = "Alice" │
|
|
37
|
+
│ λ greet function = ƒ greet() │
|
|
38
|
+
╰──────────────────────────────────────────────────────────────╯
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Options
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
inspect(obj, {
|
|
45
|
+
methods: true, // Show methods (default: true)
|
|
46
|
+
private: false, // Show _private members (default: false)
|
|
47
|
+
values: true, // Show property values (default: true)
|
|
48
|
+
sort: true, // Sort alphabetically (default: true)
|
|
49
|
+
maxStringLength: 80, // Truncate long strings (default: 80)
|
|
50
|
+
maxDepth: 2, // Nested object depth (default: 2)
|
|
51
|
+
json: false, // Output as JSON (default: false)
|
|
52
|
+
color: true, // ANSI colors (default: auto-detect)
|
|
53
|
+
title: '', // Custom title
|
|
54
|
+
print: true, // Print to console (default: true)
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## CLI
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx @indiekitai/inspect '{"hello":"world"}'
|
|
62
|
+
npx @indiekitai/inspect --json '{"hello":"world"}'
|
|
63
|
+
npx @indiekitai/inspect --no-color '{"hello":"world"}'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Supports
|
|
67
|
+
|
|
68
|
+
- Class instances with prototype chain display
|
|
69
|
+
- Plain objects, Arrays, Maps, Sets
|
|
70
|
+
- Functions (with argument signatures)
|
|
71
|
+
- Dates, RegExps, Errors
|
|
72
|
+
- Getters/setters
|
|
73
|
+
- Nested objects with depth control
|
|
74
|
+
- BigInt, Symbol
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/ansi.ts
|
|
4
|
+
var ESC = "\x1B[";
|
|
5
|
+
var RESET = `${ESC}0m`;
|
|
6
|
+
var color = (code) => (s) => `${ESC}${code}m${s}${RESET}`;
|
|
7
|
+
var bold = color(1);
|
|
8
|
+
var dim = color(2);
|
|
9
|
+
var italic = color(3);
|
|
10
|
+
var underline = color(4);
|
|
11
|
+
var red = color(31);
|
|
12
|
+
var green = color(32);
|
|
13
|
+
var yellow = color(33);
|
|
14
|
+
var blue = color(34);
|
|
15
|
+
var magenta = color(35);
|
|
16
|
+
var cyan = color(36);
|
|
17
|
+
var white = color(37);
|
|
18
|
+
var brightBlack = color(90);
|
|
19
|
+
var brightRed = color(91);
|
|
20
|
+
var brightGreen = color(92);
|
|
21
|
+
var brightYellow = color(93);
|
|
22
|
+
var brightBlue = color(94);
|
|
23
|
+
var brightMagenta = color(95);
|
|
24
|
+
var brightCyan = color(96);
|
|
25
|
+
function stripAnsi(s) {
|
|
26
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
27
|
+
}
|
|
28
|
+
function isColorSupported() {
|
|
29
|
+
if (typeof process === "undefined") return false;
|
|
30
|
+
if (process.env.NO_COLOR) return false;
|
|
31
|
+
if (process.env.FORCE_COLOR) return true;
|
|
32
|
+
if (process.stdout && "isTTY" in process.stdout) return !!process.stdout.isTTY;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function withColor(enabled) {
|
|
36
|
+
if (enabled) return { bold, dim, italic, underline, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan };
|
|
37
|
+
const id = (s) => s;
|
|
38
|
+
return { bold: id, dim: id, italic: id, underline: id, red: id, green: id, yellow: id, blue: id, magenta: id, cyan: id, white: id, brightBlack: id, brightRed: id, brightGreen: id, brightYellow: id, brightBlue: id, brightMagenta: id, brightCyan: id };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/inspect.ts
|
|
42
|
+
function getTypeName(obj) {
|
|
43
|
+
if (obj === null) return "null";
|
|
44
|
+
if (obj === void 0) return "undefined";
|
|
45
|
+
if (Array.isArray(obj)) return "Array";
|
|
46
|
+
if (obj instanceof Map) return "Map";
|
|
47
|
+
if (obj instanceof Set) return "Set";
|
|
48
|
+
if (obj instanceof Date) return "Date";
|
|
49
|
+
if (obj instanceof RegExp) return "RegExp";
|
|
50
|
+
if (obj instanceof Error) return obj.constructor.name;
|
|
51
|
+
if (typeof obj === "function") return obj.name ? `function ${obj.name}` : "function";
|
|
52
|
+
const proto = Object.getPrototypeOf(obj);
|
|
53
|
+
if (proto && proto.constructor && proto.constructor.name !== "Object") {
|
|
54
|
+
return proto.constructor.name;
|
|
55
|
+
}
|
|
56
|
+
return typeof obj;
|
|
57
|
+
}
|
|
58
|
+
function formatValue(val, maxLen, depth, maxDepth) {
|
|
59
|
+
if (depth > maxDepth) return "{\u2026}";
|
|
60
|
+
if (val === null) return "null";
|
|
61
|
+
if (val === void 0) return "undefined";
|
|
62
|
+
if (typeof val === "string") {
|
|
63
|
+
const s = val.length > maxLen ? val.slice(0, maxLen) + "\u2026" : val;
|
|
64
|
+
return JSON.stringify(s);
|
|
65
|
+
}
|
|
66
|
+
if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") return String(val);
|
|
67
|
+
if (typeof val === "symbol") return val.toString();
|
|
68
|
+
if (typeof val === "function") {
|
|
69
|
+
const name = val.name || "anonymous";
|
|
70
|
+
const src = Function.prototype.toString.call(val);
|
|
71
|
+
const argsMatch = src.match(/\(([^)]*)\)/);
|
|
72
|
+
const args2 = argsMatch ? argsMatch[1] : "";
|
|
73
|
+
return `\u0192 ${name}(${args2})`;
|
|
74
|
+
}
|
|
75
|
+
if (val instanceof Date) return val.toISOString();
|
|
76
|
+
if (val instanceof RegExp) return val.toString();
|
|
77
|
+
if (val instanceof Error) return `${val.constructor.name}: ${val.message}`;
|
|
78
|
+
if (val instanceof Map) {
|
|
79
|
+
const entries2 = [...val.entries()].slice(0, 5).map(([k, v]) => `${formatValue(k, maxLen, depth + 1, maxDepth)} => ${formatValue(v, maxLen, depth + 1, maxDepth)}`);
|
|
80
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
81
|
+
return `Map(${val.size}) { ${entries2.join(", ")}${suffix2} }`;
|
|
82
|
+
}
|
|
83
|
+
if (val instanceof Set) {
|
|
84
|
+
const items = [...val].slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
85
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
86
|
+
return `Set(${val.size}) { ${items.join(", ")}${suffix2} }`;
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(val)) {
|
|
89
|
+
const items = val.slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
90
|
+
const suffix2 = val.length > 5 ? `, \u2026 +${val.length - 5}` : "";
|
|
91
|
+
return `[${items.join(", ")}${suffix2}]`;
|
|
92
|
+
}
|
|
93
|
+
const keys = Object.keys(val).slice(0, 5);
|
|
94
|
+
const entries = keys.map((k) => `${k}: ${formatValue(val[k], maxLen, depth + 1, maxDepth)}`);
|
|
95
|
+
const allKeys = Object.keys(val);
|
|
96
|
+
const suffix = allKeys.length > 5 ? `, \u2026 +${allKeys.length - 5}` : "";
|
|
97
|
+
return `{ ${entries.join(", ")}${suffix} }`;
|
|
98
|
+
}
|
|
99
|
+
function getPrototypeChain(obj) {
|
|
100
|
+
const chain = [];
|
|
101
|
+
if (obj === null || obj === void 0 || typeof obj !== "object") return chain;
|
|
102
|
+
let proto = Object.getPrototypeOf(obj);
|
|
103
|
+
while (proto && proto !== Object.prototype) {
|
|
104
|
+
const name = proto.constructor?.name;
|
|
105
|
+
if (name && name !== "Object") chain.push(name);
|
|
106
|
+
proto = Object.getPrototypeOf(proto);
|
|
107
|
+
}
|
|
108
|
+
return chain;
|
|
109
|
+
}
|
|
110
|
+
function collectMembers(obj, opts2) {
|
|
111
|
+
const members = [];
|
|
112
|
+
if (obj === null || obj === void 0) return members;
|
|
113
|
+
const seen = /* @__PURE__ */ new Set();
|
|
114
|
+
const processProperty = (name, target) => {
|
|
115
|
+
if (seen.has(name)) return;
|
|
116
|
+
if (!opts2.private && name.startsWith("_") && !name.startsWith("__")) return;
|
|
117
|
+
if (!opts2.dunder && name.startsWith("__")) return;
|
|
118
|
+
seen.add(name);
|
|
119
|
+
const desc = Object.getOwnPropertyDescriptor(target, name);
|
|
120
|
+
if (!desc) return;
|
|
121
|
+
if (desc.get || desc.set) {
|
|
122
|
+
let val = "";
|
|
123
|
+
let type2 = "unknown";
|
|
124
|
+
if (desc.get) {
|
|
125
|
+
try {
|
|
126
|
+
const v = desc.get.call(obj);
|
|
127
|
+
val = formatValue(v, opts2.maxStringLength, 0, opts2.maxDepth);
|
|
128
|
+
type2 = v === null ? "null" : typeof v;
|
|
129
|
+
} catch {
|
|
130
|
+
val = "<error>";
|
|
131
|
+
type2 = "getter";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
members.push({ name, kind: desc.get ? "getter" : "setter", type: type2, value: val });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (typeof desc.value === "function") {
|
|
138
|
+
if (!opts2.methods) return;
|
|
139
|
+
members.push({
|
|
140
|
+
name,
|
|
141
|
+
kind: "method",
|
|
142
|
+
type: "function",
|
|
143
|
+
value: formatValue(desc.value, opts2.maxStringLength, 0, opts2.maxDepth)
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const type = desc.value === null ? "null" : typeof desc.value;
|
|
148
|
+
members.push({
|
|
149
|
+
name,
|
|
150
|
+
kind: "property",
|
|
151
|
+
type: Array.isArray(desc.value) ? "Array" : desc.value instanceof Map ? "Map" : desc.value instanceof Set ? "Set" : type,
|
|
152
|
+
value: opts2.values ? formatValue(desc.value, opts2.maxStringLength, 0, opts2.maxDepth) : ""
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
if (typeof obj === "object" || typeof obj === "function") {
|
|
156
|
+
for (const name of Object.getOwnPropertyNames(obj)) {
|
|
157
|
+
if (name === "constructor") continue;
|
|
158
|
+
processProperty(name, obj);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (typeof obj === "object" && obj !== null) {
|
|
162
|
+
let proto = Object.getPrototypeOf(obj);
|
|
163
|
+
while (proto && proto !== Object.prototype) {
|
|
164
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
165
|
+
if (name === "constructor") continue;
|
|
166
|
+
processProperty(name, proto);
|
|
167
|
+
}
|
|
168
|
+
proto = Object.getPrototypeOf(proto);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (opts2.sort) {
|
|
172
|
+
members.sort((a, b) => {
|
|
173
|
+
if (a.kind !== b.kind) {
|
|
174
|
+
const order = { property: 0, getter: 1, setter: 2, method: 3 };
|
|
175
|
+
return order[a.kind] - order[b.kind];
|
|
176
|
+
}
|
|
177
|
+
return a.name.localeCompare(b.name);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return members;
|
|
181
|
+
}
|
|
182
|
+
function renderTable(title, members, protoChain, c) {
|
|
183
|
+
const lines = [];
|
|
184
|
+
const nameWidth = Math.max(4, ...members.map((m) => m.name.length));
|
|
185
|
+
const typeWidth = Math.max(4, ...members.map((m) => m.type.length));
|
|
186
|
+
const totalInner = Math.max(60, nameWidth + typeWidth + 20);
|
|
187
|
+
lines.push(c.dim(`\u256D${"\u2500".repeat(totalInner + 2)}\u256E`));
|
|
188
|
+
const titlePad = totalInner - stripAnsi(title).length;
|
|
189
|
+
const leftPad = Math.floor(titlePad / 2);
|
|
190
|
+
const rightPad = titlePad - leftPad;
|
|
191
|
+
lines.push(c.dim("\u2502") + " ".repeat(leftPad + 1) + c.bold(c.cyan(title)) + " ".repeat(rightPad + 1) + c.dim("\u2502"));
|
|
192
|
+
if (protoChain.length > 0) {
|
|
193
|
+
const chain = `extends ${protoChain.join(" \u2192 ")}`;
|
|
194
|
+
const chainPad = totalInner - chain.length;
|
|
195
|
+
const clp = Math.floor(chainPad / 2);
|
|
196
|
+
const crp = chainPad - clp;
|
|
197
|
+
lines.push(c.dim("\u2502") + " ".repeat(clp + 1) + c.dim(chain) + " ".repeat(crp + 1) + c.dim("\u2502"));
|
|
198
|
+
}
|
|
199
|
+
lines.push(c.dim(`\u251C${"\u2500".repeat(totalInner + 2)}\u2524`));
|
|
200
|
+
if (members.length === 0) {
|
|
201
|
+
const msg = "(no members)";
|
|
202
|
+
const pad = totalInner - msg.length;
|
|
203
|
+
const lp = Math.floor(pad / 2);
|
|
204
|
+
const rp = pad - lp;
|
|
205
|
+
lines.push(c.dim("\u2502") + " ".repeat(lp + 1) + c.dim(msg) + " ".repeat(rp + 1) + c.dim("\u2502"));
|
|
206
|
+
} else {
|
|
207
|
+
for (const m of members) {
|
|
208
|
+
const icon = m.kind === "method" ? c.yellow("\u03BB") : m.kind === "getter" ? c.magenta("\u25CF") : m.kind === "setter" ? c.magenta("\u25CB") : c.green("\u2022");
|
|
209
|
+
const name = m.kind === "method" ? c.yellow(m.name) : c.white(m.name);
|
|
210
|
+
const type = c.dim(m.type);
|
|
211
|
+
const value2 = m.value ? ` ${c.brightBlack("=")} ${c.cyan(m.value)}` : "";
|
|
212
|
+
const content = ` ${icon} ${name} ${type}${value2}`;
|
|
213
|
+
const contentLen = stripAnsi(content).length;
|
|
214
|
+
const rightSpace = Math.max(0, totalInner - contentLen);
|
|
215
|
+
lines.push(c.dim("\u2502") + content + " ".repeat(rightSpace + 2) + c.dim("\u2502"));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
lines.push(c.dim(`\u2570${"\u2500".repeat(totalInner + 2)}\u256F`));
|
|
219
|
+
return lines.join("\n");
|
|
220
|
+
}
|
|
221
|
+
function renderJson(title, members, protoChain) {
|
|
222
|
+
return JSON.stringify({
|
|
223
|
+
type: title,
|
|
224
|
+
prototypeChain: protoChain,
|
|
225
|
+
members: members.map((m) => ({
|
|
226
|
+
name: m.name,
|
|
227
|
+
kind: m.kind,
|
|
228
|
+
type: m.type,
|
|
229
|
+
...m.value ? { value: m.value } : {},
|
|
230
|
+
...m.doc ? { doc: m.doc } : {}
|
|
231
|
+
}))
|
|
232
|
+
}, null, 2);
|
|
233
|
+
}
|
|
234
|
+
var defaults = {
|
|
235
|
+
methods: true,
|
|
236
|
+
private: false,
|
|
237
|
+
dunder: false,
|
|
238
|
+
docs: true,
|
|
239
|
+
values: true,
|
|
240
|
+
sort: true,
|
|
241
|
+
maxStringLength: 80,
|
|
242
|
+
maxDepth: 2,
|
|
243
|
+
json: false,
|
|
244
|
+
color: true,
|
|
245
|
+
title: "",
|
|
246
|
+
print: true
|
|
247
|
+
};
|
|
248
|
+
function inspect(obj, options) {
|
|
249
|
+
const opts2 = { ...defaults, ...options };
|
|
250
|
+
if (opts2.color === true && options?.color === void 0) {
|
|
251
|
+
opts2.color = isColorSupported();
|
|
252
|
+
}
|
|
253
|
+
const title = opts2.title || getTypeName(obj);
|
|
254
|
+
const members = collectMembers(obj, opts2);
|
|
255
|
+
const protoChain = getPrototypeChain(obj);
|
|
256
|
+
let output;
|
|
257
|
+
if (opts2.json) {
|
|
258
|
+
output = renderJson(title, members, protoChain);
|
|
259
|
+
} else {
|
|
260
|
+
const c = withColor(opts2.color);
|
|
261
|
+
output = renderTable(title, members, protoChain, c);
|
|
262
|
+
}
|
|
263
|
+
if (opts2.print) {
|
|
264
|
+
console.log(output);
|
|
265
|
+
}
|
|
266
|
+
return output;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/cli.ts
|
|
270
|
+
var args = process.argv.slice(2);
|
|
271
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
272
|
+
console.log(`Usage: inspect [options] <value>
|
|
273
|
+
|
|
274
|
+
Pretty-print any JavaScript value.
|
|
275
|
+
|
|
276
|
+
Arguments:
|
|
277
|
+
<value> JSON string, or a literal value to inspect
|
|
278
|
+
|
|
279
|
+
Options:
|
|
280
|
+
--no-methods Hide methods
|
|
281
|
+
--no-values Hide values
|
|
282
|
+
--private Show private members (starting with _)
|
|
283
|
+
--no-sort Don't sort members
|
|
284
|
+
--json Output as JSON
|
|
285
|
+
--no-color Disable color
|
|
286
|
+
--title <name> Custom title
|
|
287
|
+
-h, --help Show this help
|
|
288
|
+
`);
|
|
289
|
+
process.exit(0);
|
|
290
|
+
}
|
|
291
|
+
var opts = {};
|
|
292
|
+
var positional = [];
|
|
293
|
+
for (let i = 0; i < args.length; i++) {
|
|
294
|
+
const arg = args[i];
|
|
295
|
+
if (arg === "--no-methods") opts.methods = false;
|
|
296
|
+
else if (arg === "--no-values") opts.values = false;
|
|
297
|
+
else if (arg === "--private") opts.private = true;
|
|
298
|
+
else if (arg === "--no-sort") opts.sort = false;
|
|
299
|
+
else if (arg === "--json") opts.json = true;
|
|
300
|
+
else if (arg === "--no-color") opts.color = false;
|
|
301
|
+
else if (arg === "--title" && i + 1 < args.length) opts.title = args[++i];
|
|
302
|
+
else positional.push(arg);
|
|
303
|
+
}
|
|
304
|
+
var input = positional.join(" ");
|
|
305
|
+
var value;
|
|
306
|
+
try {
|
|
307
|
+
value = JSON.parse(input);
|
|
308
|
+
} catch {
|
|
309
|
+
value = input;
|
|
310
|
+
}
|
|
311
|
+
inspect(value, opts);
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface InspectOptions {
|
|
2
|
+
/** Show methods (default: true) */
|
|
3
|
+
methods?: boolean;
|
|
4
|
+
/** Show private members starting with _ (default: false) */
|
|
5
|
+
private?: boolean;
|
|
6
|
+
/** Show dunder members starting with __ (default: false) */
|
|
7
|
+
dunder?: boolean;
|
|
8
|
+
/** Show docs/comments (default: true) */
|
|
9
|
+
docs?: boolean;
|
|
10
|
+
/** Show values of properties (default: true) */
|
|
11
|
+
values?: boolean;
|
|
12
|
+
/** Sort members alphabetically (default: true) */
|
|
13
|
+
sort?: boolean;
|
|
14
|
+
/** Maximum string value length before truncating (default: 80) */
|
|
15
|
+
maxStringLength?: number;
|
|
16
|
+
/** Maximum depth for nested objects (default: 2) */
|
|
17
|
+
maxDepth?: number;
|
|
18
|
+
/** Output as JSON instead of pretty table */
|
|
19
|
+
json?: boolean;
|
|
20
|
+
/** Enable color output (default: auto-detect) */
|
|
21
|
+
color?: boolean;
|
|
22
|
+
/** Title override */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Print to console (default: true). If false, returns string. */
|
|
25
|
+
print?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare function inspect(obj: unknown, options?: InspectOptions): string;
|
|
28
|
+
|
|
29
|
+
export { type InspectOptions, inspect };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
interface InspectOptions {
|
|
2
|
+
/** Show methods (default: true) */
|
|
3
|
+
methods?: boolean;
|
|
4
|
+
/** Show private members starting with _ (default: false) */
|
|
5
|
+
private?: boolean;
|
|
6
|
+
/** Show dunder members starting with __ (default: false) */
|
|
7
|
+
dunder?: boolean;
|
|
8
|
+
/** Show docs/comments (default: true) */
|
|
9
|
+
docs?: boolean;
|
|
10
|
+
/** Show values of properties (default: true) */
|
|
11
|
+
values?: boolean;
|
|
12
|
+
/** Sort members alphabetically (default: true) */
|
|
13
|
+
sort?: boolean;
|
|
14
|
+
/** Maximum string value length before truncating (default: 80) */
|
|
15
|
+
maxStringLength?: number;
|
|
16
|
+
/** Maximum depth for nested objects (default: 2) */
|
|
17
|
+
maxDepth?: number;
|
|
18
|
+
/** Output as JSON instead of pretty table */
|
|
19
|
+
json?: boolean;
|
|
20
|
+
/** Enable color output (default: auto-detect) */
|
|
21
|
+
color?: boolean;
|
|
22
|
+
/** Title override */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Print to console (default: true). If false, returns string. */
|
|
25
|
+
print?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare function inspect(obj: unknown, options?: InspectOptions): string;
|
|
28
|
+
|
|
29
|
+
export { type InspectOptions, inspect };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
inspect: () => inspect
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/ansi.ts
|
|
28
|
+
var ESC = "\x1B[";
|
|
29
|
+
var RESET = `${ESC}0m`;
|
|
30
|
+
var color = (code) => (s) => `${ESC}${code}m${s}${RESET}`;
|
|
31
|
+
var bold = color(1);
|
|
32
|
+
var dim = color(2);
|
|
33
|
+
var italic = color(3);
|
|
34
|
+
var underline = color(4);
|
|
35
|
+
var red = color(31);
|
|
36
|
+
var green = color(32);
|
|
37
|
+
var yellow = color(33);
|
|
38
|
+
var blue = color(34);
|
|
39
|
+
var magenta = color(35);
|
|
40
|
+
var cyan = color(36);
|
|
41
|
+
var white = color(37);
|
|
42
|
+
var brightBlack = color(90);
|
|
43
|
+
var brightRed = color(91);
|
|
44
|
+
var brightGreen = color(92);
|
|
45
|
+
var brightYellow = color(93);
|
|
46
|
+
var brightBlue = color(94);
|
|
47
|
+
var brightMagenta = color(95);
|
|
48
|
+
var brightCyan = color(96);
|
|
49
|
+
function stripAnsi(s) {
|
|
50
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
51
|
+
}
|
|
52
|
+
function isColorSupported() {
|
|
53
|
+
if (typeof process === "undefined") return false;
|
|
54
|
+
if (process.env.NO_COLOR) return false;
|
|
55
|
+
if (process.env.FORCE_COLOR) return true;
|
|
56
|
+
if (process.stdout && "isTTY" in process.stdout) return !!process.stdout.isTTY;
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
function withColor(enabled) {
|
|
60
|
+
if (enabled) return { bold, dim, italic, underline, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan };
|
|
61
|
+
const id = (s) => s;
|
|
62
|
+
return { bold: id, dim: id, italic: id, underline: id, red: id, green: id, yellow: id, blue: id, magenta: id, cyan: id, white: id, brightBlack: id, brightRed: id, brightGreen: id, brightYellow: id, brightBlue: id, brightMagenta: id, brightCyan: id };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/inspect.ts
|
|
66
|
+
function getTypeName(obj) {
|
|
67
|
+
if (obj === null) return "null";
|
|
68
|
+
if (obj === void 0) return "undefined";
|
|
69
|
+
if (Array.isArray(obj)) return "Array";
|
|
70
|
+
if (obj instanceof Map) return "Map";
|
|
71
|
+
if (obj instanceof Set) return "Set";
|
|
72
|
+
if (obj instanceof Date) return "Date";
|
|
73
|
+
if (obj instanceof RegExp) return "RegExp";
|
|
74
|
+
if (obj instanceof Error) return obj.constructor.name;
|
|
75
|
+
if (typeof obj === "function") return obj.name ? `function ${obj.name}` : "function";
|
|
76
|
+
const proto = Object.getPrototypeOf(obj);
|
|
77
|
+
if (proto && proto.constructor && proto.constructor.name !== "Object") {
|
|
78
|
+
return proto.constructor.name;
|
|
79
|
+
}
|
|
80
|
+
return typeof obj;
|
|
81
|
+
}
|
|
82
|
+
function formatValue(val, maxLen, depth, maxDepth) {
|
|
83
|
+
if (depth > maxDepth) return "{\u2026}";
|
|
84
|
+
if (val === null) return "null";
|
|
85
|
+
if (val === void 0) return "undefined";
|
|
86
|
+
if (typeof val === "string") {
|
|
87
|
+
const s = val.length > maxLen ? val.slice(0, maxLen) + "\u2026" : val;
|
|
88
|
+
return JSON.stringify(s);
|
|
89
|
+
}
|
|
90
|
+
if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") return String(val);
|
|
91
|
+
if (typeof val === "symbol") return val.toString();
|
|
92
|
+
if (typeof val === "function") {
|
|
93
|
+
const name = val.name || "anonymous";
|
|
94
|
+
const src = Function.prototype.toString.call(val);
|
|
95
|
+
const argsMatch = src.match(/\(([^)]*)\)/);
|
|
96
|
+
const args = argsMatch ? argsMatch[1] : "";
|
|
97
|
+
return `\u0192 ${name}(${args})`;
|
|
98
|
+
}
|
|
99
|
+
if (val instanceof Date) return val.toISOString();
|
|
100
|
+
if (val instanceof RegExp) return val.toString();
|
|
101
|
+
if (val instanceof Error) return `${val.constructor.name}: ${val.message}`;
|
|
102
|
+
if (val instanceof Map) {
|
|
103
|
+
const entries2 = [...val.entries()].slice(0, 5).map(([k, v]) => `${formatValue(k, maxLen, depth + 1, maxDepth)} => ${formatValue(v, maxLen, depth + 1, maxDepth)}`);
|
|
104
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
105
|
+
return `Map(${val.size}) { ${entries2.join(", ")}${suffix2} }`;
|
|
106
|
+
}
|
|
107
|
+
if (val instanceof Set) {
|
|
108
|
+
const items = [...val].slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
109
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
110
|
+
return `Set(${val.size}) { ${items.join(", ")}${suffix2} }`;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(val)) {
|
|
113
|
+
const items = val.slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
114
|
+
const suffix2 = val.length > 5 ? `, \u2026 +${val.length - 5}` : "";
|
|
115
|
+
return `[${items.join(", ")}${suffix2}]`;
|
|
116
|
+
}
|
|
117
|
+
const keys = Object.keys(val).slice(0, 5);
|
|
118
|
+
const entries = keys.map((k) => `${k}: ${formatValue(val[k], maxLen, depth + 1, maxDepth)}`);
|
|
119
|
+
const allKeys = Object.keys(val);
|
|
120
|
+
const suffix = allKeys.length > 5 ? `, \u2026 +${allKeys.length - 5}` : "";
|
|
121
|
+
return `{ ${entries.join(", ")}${suffix} }`;
|
|
122
|
+
}
|
|
123
|
+
function getPrototypeChain(obj) {
|
|
124
|
+
const chain = [];
|
|
125
|
+
if (obj === null || obj === void 0 || typeof obj !== "object") return chain;
|
|
126
|
+
let proto = Object.getPrototypeOf(obj);
|
|
127
|
+
while (proto && proto !== Object.prototype) {
|
|
128
|
+
const name = proto.constructor?.name;
|
|
129
|
+
if (name && name !== "Object") chain.push(name);
|
|
130
|
+
proto = Object.getPrototypeOf(proto);
|
|
131
|
+
}
|
|
132
|
+
return chain;
|
|
133
|
+
}
|
|
134
|
+
function collectMembers(obj, opts) {
|
|
135
|
+
const members = [];
|
|
136
|
+
if (obj === null || obj === void 0) return members;
|
|
137
|
+
const seen = /* @__PURE__ */ new Set();
|
|
138
|
+
const processProperty = (name, target) => {
|
|
139
|
+
if (seen.has(name)) return;
|
|
140
|
+
if (!opts.private && name.startsWith("_") && !name.startsWith("__")) return;
|
|
141
|
+
if (!opts.dunder && name.startsWith("__")) return;
|
|
142
|
+
seen.add(name);
|
|
143
|
+
const desc = Object.getOwnPropertyDescriptor(target, name);
|
|
144
|
+
if (!desc) return;
|
|
145
|
+
if (desc.get || desc.set) {
|
|
146
|
+
let val = "";
|
|
147
|
+
let type2 = "unknown";
|
|
148
|
+
if (desc.get) {
|
|
149
|
+
try {
|
|
150
|
+
const v = desc.get.call(obj);
|
|
151
|
+
val = formatValue(v, opts.maxStringLength, 0, opts.maxDepth);
|
|
152
|
+
type2 = v === null ? "null" : typeof v;
|
|
153
|
+
} catch {
|
|
154
|
+
val = "<error>";
|
|
155
|
+
type2 = "getter";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
members.push({ name, kind: desc.get ? "getter" : "setter", type: type2, value: val });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (typeof desc.value === "function") {
|
|
162
|
+
if (!opts.methods) return;
|
|
163
|
+
members.push({
|
|
164
|
+
name,
|
|
165
|
+
kind: "method",
|
|
166
|
+
type: "function",
|
|
167
|
+
value: formatValue(desc.value, opts.maxStringLength, 0, opts.maxDepth)
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const type = desc.value === null ? "null" : typeof desc.value;
|
|
172
|
+
members.push({
|
|
173
|
+
name,
|
|
174
|
+
kind: "property",
|
|
175
|
+
type: Array.isArray(desc.value) ? "Array" : desc.value instanceof Map ? "Map" : desc.value instanceof Set ? "Set" : type,
|
|
176
|
+
value: opts.values ? formatValue(desc.value, opts.maxStringLength, 0, opts.maxDepth) : ""
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
if (typeof obj === "object" || typeof obj === "function") {
|
|
180
|
+
for (const name of Object.getOwnPropertyNames(obj)) {
|
|
181
|
+
if (name === "constructor") continue;
|
|
182
|
+
processProperty(name, obj);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (typeof obj === "object" && obj !== null) {
|
|
186
|
+
let proto = Object.getPrototypeOf(obj);
|
|
187
|
+
while (proto && proto !== Object.prototype) {
|
|
188
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
189
|
+
if (name === "constructor") continue;
|
|
190
|
+
processProperty(name, proto);
|
|
191
|
+
}
|
|
192
|
+
proto = Object.getPrototypeOf(proto);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (opts.sort) {
|
|
196
|
+
members.sort((a, b) => {
|
|
197
|
+
if (a.kind !== b.kind) {
|
|
198
|
+
const order = { property: 0, getter: 1, setter: 2, method: 3 };
|
|
199
|
+
return order[a.kind] - order[b.kind];
|
|
200
|
+
}
|
|
201
|
+
return a.name.localeCompare(b.name);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return members;
|
|
205
|
+
}
|
|
206
|
+
function renderTable(title, members, protoChain, c) {
|
|
207
|
+
const lines = [];
|
|
208
|
+
const nameWidth = Math.max(4, ...members.map((m) => m.name.length));
|
|
209
|
+
const typeWidth = Math.max(4, ...members.map((m) => m.type.length));
|
|
210
|
+
const totalInner = Math.max(60, nameWidth + typeWidth + 20);
|
|
211
|
+
lines.push(c.dim(`\u256D${"\u2500".repeat(totalInner + 2)}\u256E`));
|
|
212
|
+
const titlePad = totalInner - stripAnsi(title).length;
|
|
213
|
+
const leftPad = Math.floor(titlePad / 2);
|
|
214
|
+
const rightPad = titlePad - leftPad;
|
|
215
|
+
lines.push(c.dim("\u2502") + " ".repeat(leftPad + 1) + c.bold(c.cyan(title)) + " ".repeat(rightPad + 1) + c.dim("\u2502"));
|
|
216
|
+
if (protoChain.length > 0) {
|
|
217
|
+
const chain = `extends ${protoChain.join(" \u2192 ")}`;
|
|
218
|
+
const chainPad = totalInner - chain.length;
|
|
219
|
+
const clp = Math.floor(chainPad / 2);
|
|
220
|
+
const crp = chainPad - clp;
|
|
221
|
+
lines.push(c.dim("\u2502") + " ".repeat(clp + 1) + c.dim(chain) + " ".repeat(crp + 1) + c.dim("\u2502"));
|
|
222
|
+
}
|
|
223
|
+
lines.push(c.dim(`\u251C${"\u2500".repeat(totalInner + 2)}\u2524`));
|
|
224
|
+
if (members.length === 0) {
|
|
225
|
+
const msg = "(no members)";
|
|
226
|
+
const pad = totalInner - msg.length;
|
|
227
|
+
const lp = Math.floor(pad / 2);
|
|
228
|
+
const rp = pad - lp;
|
|
229
|
+
lines.push(c.dim("\u2502") + " ".repeat(lp + 1) + c.dim(msg) + " ".repeat(rp + 1) + c.dim("\u2502"));
|
|
230
|
+
} else {
|
|
231
|
+
for (const m of members) {
|
|
232
|
+
const icon = m.kind === "method" ? c.yellow("\u03BB") : m.kind === "getter" ? c.magenta("\u25CF") : m.kind === "setter" ? c.magenta("\u25CB") : c.green("\u2022");
|
|
233
|
+
const name = m.kind === "method" ? c.yellow(m.name) : c.white(m.name);
|
|
234
|
+
const type = c.dim(m.type);
|
|
235
|
+
const value = m.value ? ` ${c.brightBlack("=")} ${c.cyan(m.value)}` : "";
|
|
236
|
+
const content = ` ${icon} ${name} ${type}${value}`;
|
|
237
|
+
const contentLen = stripAnsi(content).length;
|
|
238
|
+
const rightSpace = Math.max(0, totalInner - contentLen);
|
|
239
|
+
lines.push(c.dim("\u2502") + content + " ".repeat(rightSpace + 2) + c.dim("\u2502"));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
lines.push(c.dim(`\u2570${"\u2500".repeat(totalInner + 2)}\u256F`));
|
|
243
|
+
return lines.join("\n");
|
|
244
|
+
}
|
|
245
|
+
function renderJson(title, members, protoChain) {
|
|
246
|
+
return JSON.stringify({
|
|
247
|
+
type: title,
|
|
248
|
+
prototypeChain: protoChain,
|
|
249
|
+
members: members.map((m) => ({
|
|
250
|
+
name: m.name,
|
|
251
|
+
kind: m.kind,
|
|
252
|
+
type: m.type,
|
|
253
|
+
...m.value ? { value: m.value } : {},
|
|
254
|
+
...m.doc ? { doc: m.doc } : {}
|
|
255
|
+
}))
|
|
256
|
+
}, null, 2);
|
|
257
|
+
}
|
|
258
|
+
var defaults = {
|
|
259
|
+
methods: true,
|
|
260
|
+
private: false,
|
|
261
|
+
dunder: false,
|
|
262
|
+
docs: true,
|
|
263
|
+
values: true,
|
|
264
|
+
sort: true,
|
|
265
|
+
maxStringLength: 80,
|
|
266
|
+
maxDepth: 2,
|
|
267
|
+
json: false,
|
|
268
|
+
color: true,
|
|
269
|
+
title: "",
|
|
270
|
+
print: true
|
|
271
|
+
};
|
|
272
|
+
function inspect(obj, options) {
|
|
273
|
+
const opts = { ...defaults, ...options };
|
|
274
|
+
if (opts.color === true && options?.color === void 0) {
|
|
275
|
+
opts.color = isColorSupported();
|
|
276
|
+
}
|
|
277
|
+
const title = opts.title || getTypeName(obj);
|
|
278
|
+
const members = collectMembers(obj, opts);
|
|
279
|
+
const protoChain = getPrototypeChain(obj);
|
|
280
|
+
let output;
|
|
281
|
+
if (opts.json) {
|
|
282
|
+
output = renderJson(title, members, protoChain);
|
|
283
|
+
} else {
|
|
284
|
+
const c = withColor(opts.color);
|
|
285
|
+
output = renderTable(title, members, protoChain, c);
|
|
286
|
+
}
|
|
287
|
+
if (opts.print) {
|
|
288
|
+
console.log(output);
|
|
289
|
+
}
|
|
290
|
+
return output;
|
|
291
|
+
}
|
|
292
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
293
|
+
0 && (module.exports = {
|
|
294
|
+
inspect
|
|
295
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
// src/ansi.ts
|
|
2
|
+
var ESC = "\x1B[";
|
|
3
|
+
var RESET = `${ESC}0m`;
|
|
4
|
+
var color = (code) => (s) => `${ESC}${code}m${s}${RESET}`;
|
|
5
|
+
var bold = color(1);
|
|
6
|
+
var dim = color(2);
|
|
7
|
+
var italic = color(3);
|
|
8
|
+
var underline = color(4);
|
|
9
|
+
var red = color(31);
|
|
10
|
+
var green = color(32);
|
|
11
|
+
var yellow = color(33);
|
|
12
|
+
var blue = color(34);
|
|
13
|
+
var magenta = color(35);
|
|
14
|
+
var cyan = color(36);
|
|
15
|
+
var white = color(37);
|
|
16
|
+
var brightBlack = color(90);
|
|
17
|
+
var brightRed = color(91);
|
|
18
|
+
var brightGreen = color(92);
|
|
19
|
+
var brightYellow = color(93);
|
|
20
|
+
var brightBlue = color(94);
|
|
21
|
+
var brightMagenta = color(95);
|
|
22
|
+
var brightCyan = color(96);
|
|
23
|
+
function stripAnsi(s) {
|
|
24
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
25
|
+
}
|
|
26
|
+
function isColorSupported() {
|
|
27
|
+
if (typeof process === "undefined") return false;
|
|
28
|
+
if (process.env.NO_COLOR) return false;
|
|
29
|
+
if (process.env.FORCE_COLOR) return true;
|
|
30
|
+
if (process.stdout && "isTTY" in process.stdout) return !!process.stdout.isTTY;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function withColor(enabled) {
|
|
34
|
+
if (enabled) return { bold, dim, italic, underline, red, green, yellow, blue, magenta, cyan, white, brightBlack, brightRed, brightGreen, brightYellow, brightBlue, brightMagenta, brightCyan };
|
|
35
|
+
const id = (s) => s;
|
|
36
|
+
return { bold: id, dim: id, italic: id, underline: id, red: id, green: id, yellow: id, blue: id, magenta: id, cyan: id, white: id, brightBlack: id, brightRed: id, brightGreen: id, brightYellow: id, brightBlue: id, brightMagenta: id, brightCyan: id };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/inspect.ts
|
|
40
|
+
function getTypeName(obj) {
|
|
41
|
+
if (obj === null) return "null";
|
|
42
|
+
if (obj === void 0) return "undefined";
|
|
43
|
+
if (Array.isArray(obj)) return "Array";
|
|
44
|
+
if (obj instanceof Map) return "Map";
|
|
45
|
+
if (obj instanceof Set) return "Set";
|
|
46
|
+
if (obj instanceof Date) return "Date";
|
|
47
|
+
if (obj instanceof RegExp) return "RegExp";
|
|
48
|
+
if (obj instanceof Error) return obj.constructor.name;
|
|
49
|
+
if (typeof obj === "function") return obj.name ? `function ${obj.name}` : "function";
|
|
50
|
+
const proto = Object.getPrototypeOf(obj);
|
|
51
|
+
if (proto && proto.constructor && proto.constructor.name !== "Object") {
|
|
52
|
+
return proto.constructor.name;
|
|
53
|
+
}
|
|
54
|
+
return typeof obj;
|
|
55
|
+
}
|
|
56
|
+
function formatValue(val, maxLen, depth, maxDepth) {
|
|
57
|
+
if (depth > maxDepth) return "{\u2026}";
|
|
58
|
+
if (val === null) return "null";
|
|
59
|
+
if (val === void 0) return "undefined";
|
|
60
|
+
if (typeof val === "string") {
|
|
61
|
+
const s = val.length > maxLen ? val.slice(0, maxLen) + "\u2026" : val;
|
|
62
|
+
return JSON.stringify(s);
|
|
63
|
+
}
|
|
64
|
+
if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") return String(val);
|
|
65
|
+
if (typeof val === "symbol") return val.toString();
|
|
66
|
+
if (typeof val === "function") {
|
|
67
|
+
const name = val.name || "anonymous";
|
|
68
|
+
const src = Function.prototype.toString.call(val);
|
|
69
|
+
const argsMatch = src.match(/\(([^)]*)\)/);
|
|
70
|
+
const args = argsMatch ? argsMatch[1] : "";
|
|
71
|
+
return `\u0192 ${name}(${args})`;
|
|
72
|
+
}
|
|
73
|
+
if (val instanceof Date) return val.toISOString();
|
|
74
|
+
if (val instanceof RegExp) return val.toString();
|
|
75
|
+
if (val instanceof Error) return `${val.constructor.name}: ${val.message}`;
|
|
76
|
+
if (val instanceof Map) {
|
|
77
|
+
const entries2 = [...val.entries()].slice(0, 5).map(([k, v]) => `${formatValue(k, maxLen, depth + 1, maxDepth)} => ${formatValue(v, maxLen, depth + 1, maxDepth)}`);
|
|
78
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
79
|
+
return `Map(${val.size}) { ${entries2.join(", ")}${suffix2} }`;
|
|
80
|
+
}
|
|
81
|
+
if (val instanceof Set) {
|
|
82
|
+
const items = [...val].slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
83
|
+
const suffix2 = val.size > 5 ? `, \u2026 +${val.size - 5}` : "";
|
|
84
|
+
return `Set(${val.size}) { ${items.join(", ")}${suffix2} }`;
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(val)) {
|
|
87
|
+
const items = val.slice(0, 5).map((v) => formatValue(v, maxLen, depth + 1, maxDepth));
|
|
88
|
+
const suffix2 = val.length > 5 ? `, \u2026 +${val.length - 5}` : "";
|
|
89
|
+
return `[${items.join(", ")}${suffix2}]`;
|
|
90
|
+
}
|
|
91
|
+
const keys = Object.keys(val).slice(0, 5);
|
|
92
|
+
const entries = keys.map((k) => `${k}: ${formatValue(val[k], maxLen, depth + 1, maxDepth)}`);
|
|
93
|
+
const allKeys = Object.keys(val);
|
|
94
|
+
const suffix = allKeys.length > 5 ? `, \u2026 +${allKeys.length - 5}` : "";
|
|
95
|
+
return `{ ${entries.join(", ")}${suffix} }`;
|
|
96
|
+
}
|
|
97
|
+
function getPrototypeChain(obj) {
|
|
98
|
+
const chain = [];
|
|
99
|
+
if (obj === null || obj === void 0 || typeof obj !== "object") return chain;
|
|
100
|
+
let proto = Object.getPrototypeOf(obj);
|
|
101
|
+
while (proto && proto !== Object.prototype) {
|
|
102
|
+
const name = proto.constructor?.name;
|
|
103
|
+
if (name && name !== "Object") chain.push(name);
|
|
104
|
+
proto = Object.getPrototypeOf(proto);
|
|
105
|
+
}
|
|
106
|
+
return chain;
|
|
107
|
+
}
|
|
108
|
+
function collectMembers(obj, opts) {
|
|
109
|
+
const members = [];
|
|
110
|
+
if (obj === null || obj === void 0) return members;
|
|
111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
112
|
+
const processProperty = (name, target) => {
|
|
113
|
+
if (seen.has(name)) return;
|
|
114
|
+
if (!opts.private && name.startsWith("_") && !name.startsWith("__")) return;
|
|
115
|
+
if (!opts.dunder && name.startsWith("__")) return;
|
|
116
|
+
seen.add(name);
|
|
117
|
+
const desc = Object.getOwnPropertyDescriptor(target, name);
|
|
118
|
+
if (!desc) return;
|
|
119
|
+
if (desc.get || desc.set) {
|
|
120
|
+
let val = "";
|
|
121
|
+
let type2 = "unknown";
|
|
122
|
+
if (desc.get) {
|
|
123
|
+
try {
|
|
124
|
+
const v = desc.get.call(obj);
|
|
125
|
+
val = formatValue(v, opts.maxStringLength, 0, opts.maxDepth);
|
|
126
|
+
type2 = v === null ? "null" : typeof v;
|
|
127
|
+
} catch {
|
|
128
|
+
val = "<error>";
|
|
129
|
+
type2 = "getter";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
members.push({ name, kind: desc.get ? "getter" : "setter", type: type2, value: val });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (typeof desc.value === "function") {
|
|
136
|
+
if (!opts.methods) return;
|
|
137
|
+
members.push({
|
|
138
|
+
name,
|
|
139
|
+
kind: "method",
|
|
140
|
+
type: "function",
|
|
141
|
+
value: formatValue(desc.value, opts.maxStringLength, 0, opts.maxDepth)
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const type = desc.value === null ? "null" : typeof desc.value;
|
|
146
|
+
members.push({
|
|
147
|
+
name,
|
|
148
|
+
kind: "property",
|
|
149
|
+
type: Array.isArray(desc.value) ? "Array" : desc.value instanceof Map ? "Map" : desc.value instanceof Set ? "Set" : type,
|
|
150
|
+
value: opts.values ? formatValue(desc.value, opts.maxStringLength, 0, opts.maxDepth) : ""
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
if (typeof obj === "object" || typeof obj === "function") {
|
|
154
|
+
for (const name of Object.getOwnPropertyNames(obj)) {
|
|
155
|
+
if (name === "constructor") continue;
|
|
156
|
+
processProperty(name, obj);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (typeof obj === "object" && obj !== null) {
|
|
160
|
+
let proto = Object.getPrototypeOf(obj);
|
|
161
|
+
while (proto && proto !== Object.prototype) {
|
|
162
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
163
|
+
if (name === "constructor") continue;
|
|
164
|
+
processProperty(name, proto);
|
|
165
|
+
}
|
|
166
|
+
proto = Object.getPrototypeOf(proto);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (opts.sort) {
|
|
170
|
+
members.sort((a, b) => {
|
|
171
|
+
if (a.kind !== b.kind) {
|
|
172
|
+
const order = { property: 0, getter: 1, setter: 2, method: 3 };
|
|
173
|
+
return order[a.kind] - order[b.kind];
|
|
174
|
+
}
|
|
175
|
+
return a.name.localeCompare(b.name);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return members;
|
|
179
|
+
}
|
|
180
|
+
function renderTable(title, members, protoChain, c) {
|
|
181
|
+
const lines = [];
|
|
182
|
+
const nameWidth = Math.max(4, ...members.map((m) => m.name.length));
|
|
183
|
+
const typeWidth = Math.max(4, ...members.map((m) => m.type.length));
|
|
184
|
+
const totalInner = Math.max(60, nameWidth + typeWidth + 20);
|
|
185
|
+
lines.push(c.dim(`\u256D${"\u2500".repeat(totalInner + 2)}\u256E`));
|
|
186
|
+
const titlePad = totalInner - stripAnsi(title).length;
|
|
187
|
+
const leftPad = Math.floor(titlePad / 2);
|
|
188
|
+
const rightPad = titlePad - leftPad;
|
|
189
|
+
lines.push(c.dim("\u2502") + " ".repeat(leftPad + 1) + c.bold(c.cyan(title)) + " ".repeat(rightPad + 1) + c.dim("\u2502"));
|
|
190
|
+
if (protoChain.length > 0) {
|
|
191
|
+
const chain = `extends ${protoChain.join(" \u2192 ")}`;
|
|
192
|
+
const chainPad = totalInner - chain.length;
|
|
193
|
+
const clp = Math.floor(chainPad / 2);
|
|
194
|
+
const crp = chainPad - clp;
|
|
195
|
+
lines.push(c.dim("\u2502") + " ".repeat(clp + 1) + c.dim(chain) + " ".repeat(crp + 1) + c.dim("\u2502"));
|
|
196
|
+
}
|
|
197
|
+
lines.push(c.dim(`\u251C${"\u2500".repeat(totalInner + 2)}\u2524`));
|
|
198
|
+
if (members.length === 0) {
|
|
199
|
+
const msg = "(no members)";
|
|
200
|
+
const pad = totalInner - msg.length;
|
|
201
|
+
const lp = Math.floor(pad / 2);
|
|
202
|
+
const rp = pad - lp;
|
|
203
|
+
lines.push(c.dim("\u2502") + " ".repeat(lp + 1) + c.dim(msg) + " ".repeat(rp + 1) + c.dim("\u2502"));
|
|
204
|
+
} else {
|
|
205
|
+
for (const m of members) {
|
|
206
|
+
const icon = m.kind === "method" ? c.yellow("\u03BB") : m.kind === "getter" ? c.magenta("\u25CF") : m.kind === "setter" ? c.magenta("\u25CB") : c.green("\u2022");
|
|
207
|
+
const name = m.kind === "method" ? c.yellow(m.name) : c.white(m.name);
|
|
208
|
+
const type = c.dim(m.type);
|
|
209
|
+
const value = m.value ? ` ${c.brightBlack("=")} ${c.cyan(m.value)}` : "";
|
|
210
|
+
const content = ` ${icon} ${name} ${type}${value}`;
|
|
211
|
+
const contentLen = stripAnsi(content).length;
|
|
212
|
+
const rightSpace = Math.max(0, totalInner - contentLen);
|
|
213
|
+
lines.push(c.dim("\u2502") + content + " ".repeat(rightSpace + 2) + c.dim("\u2502"));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
lines.push(c.dim(`\u2570${"\u2500".repeat(totalInner + 2)}\u256F`));
|
|
217
|
+
return lines.join("\n");
|
|
218
|
+
}
|
|
219
|
+
function renderJson(title, members, protoChain) {
|
|
220
|
+
return JSON.stringify({
|
|
221
|
+
type: title,
|
|
222
|
+
prototypeChain: protoChain,
|
|
223
|
+
members: members.map((m) => ({
|
|
224
|
+
name: m.name,
|
|
225
|
+
kind: m.kind,
|
|
226
|
+
type: m.type,
|
|
227
|
+
...m.value ? { value: m.value } : {},
|
|
228
|
+
...m.doc ? { doc: m.doc } : {}
|
|
229
|
+
}))
|
|
230
|
+
}, null, 2);
|
|
231
|
+
}
|
|
232
|
+
var defaults = {
|
|
233
|
+
methods: true,
|
|
234
|
+
private: false,
|
|
235
|
+
dunder: false,
|
|
236
|
+
docs: true,
|
|
237
|
+
values: true,
|
|
238
|
+
sort: true,
|
|
239
|
+
maxStringLength: 80,
|
|
240
|
+
maxDepth: 2,
|
|
241
|
+
json: false,
|
|
242
|
+
color: true,
|
|
243
|
+
title: "",
|
|
244
|
+
print: true
|
|
245
|
+
};
|
|
246
|
+
function inspect(obj, options) {
|
|
247
|
+
const opts = { ...defaults, ...options };
|
|
248
|
+
if (opts.color === true && options?.color === void 0) {
|
|
249
|
+
opts.color = isColorSupported();
|
|
250
|
+
}
|
|
251
|
+
const title = opts.title || getTypeName(obj);
|
|
252
|
+
const members = collectMembers(obj, opts);
|
|
253
|
+
const protoChain = getPrototypeChain(obj);
|
|
254
|
+
let output;
|
|
255
|
+
if (opts.json) {
|
|
256
|
+
output = renderJson(title, members, protoChain);
|
|
257
|
+
} else {
|
|
258
|
+
const c = withColor(opts.color);
|
|
259
|
+
output = renderTable(title, members, protoChain, c);
|
|
260
|
+
}
|
|
261
|
+
if (opts.print) {
|
|
262
|
+
console.log(output);
|
|
263
|
+
}
|
|
264
|
+
return output;
|
|
265
|
+
}
|
|
266
|
+
export {
|
|
267
|
+
inspect
|
|
268
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@indiekitai/inspect",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Rich-style inspect() for JavaScript/TypeScript - pretty-print any object's attributes, methods, and docs",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"inspect": "./dist/cli.mjs"
|
|
22
|
+
},
|
|
23
|
+
"files": ["dist"],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": ["inspect", "debug", "pretty-print", "rich", "developer-tools"],
|
|
30
|
+
"author": "IndieKit AI",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"tsup": "^8.0.0",
|
|
34
|
+
"typescript": "^5.3.0",
|
|
35
|
+
"vitest": "^1.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|