dustyui 1.0.1 → 2.0.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/LICENSE +21 -0
- package/README.md +56 -1
- package/dist/Avatar/Avatar.d.ts +9 -0
- package/dist/Avatar/Avatar.js +17 -0
- package/dist/Avatar/Avatar.module.css +33 -0
- package/dist/Avatar/getInitials.d.ts +1 -0
- package/dist/Avatar/getInitials.js +13 -0
- package/dist/Badge/Badge.d.ts +7 -6
- package/dist/Badge/Badge.js +6 -7
- package/dist/Badge/Badge.module.css +25 -2
- package/dist/Button/Button.d.ts +9 -6
- package/dist/Button/Button.js +22 -7
- package/dist/Button/Button.module.css +106 -6
- package/dist/IconButton/IconButton.d.ts +7 -0
- package/dist/IconButton/IconButton.js +8 -0
- package/dist/IconButton/IconButton.module.css +17 -0
- package/dist/ObjectInspector/InspectorDetails.d.ts +6 -0
- package/dist/ObjectInspector/InspectorDetails.js +32 -0
- package/dist/ObjectInspector/InspectorDetails.module.css +47 -0
- package/dist/ObjectInspector/InspectorSearch.d.ts +9 -0
- package/dist/ObjectInspector/InspectorSearch.js +12 -0
- package/dist/ObjectInspector/InspectorSearch.module.css +39 -0
- package/dist/ObjectInspector/ObjectInspector.d.ts +8 -0
- package/dist/ObjectInspector/ObjectInspector.js +83 -0
- package/dist/ObjectInspector/ObjectInspector.module.css +46 -0
- package/dist/ObjectInspector/describe/describe.d.ts +31 -0
- package/dist/ObjectInspector/describe/describe.js +123 -0
- package/dist/ObjectInspector/describe/getChildren.d.ts +21 -0
- package/dist/ObjectInspector/describe/getChildren.js +77 -0
- package/dist/ObjectInspector/describe/getNode.d.ts +2 -0
- package/dist/ObjectInspector/describe/getNode.js +9 -0
- package/dist/ObjectInspector/describe/getPath.d.ts +26 -0
- package/dist/ObjectInspector/describe/getPath.js +58 -0
- package/dist/ObjectInspector/describe/getPreview.d.ts +10 -0
- package/dist/ObjectInspector/describe/getPreview.js +189 -0
- package/dist/ObjectInspector/describe/getValueKind.d.ts +2 -0
- package/dist/ObjectInspector/describe/getValueKind.js +64 -0
- package/dist/ObjectInspector/describe/safeRead.d.ts +12 -0
- package/dist/ObjectInspector/describe/safeRead.js +88 -0
- package/dist/ObjectInspector/getCopyText.d.ts +2 -0
- package/dist/ObjectInspector/getCopyText.js +103 -0
- package/dist/ObjectInspector/getLabelText.d.ts +2 -0
- package/dist/ObjectInspector/getLabelText.js +15 -0
- package/dist/ObjectInspector/getValueText.d.ts +2 -0
- package/dist/ObjectInspector/getValueText.js +5 -0
- package/dist/ObjectInspector/search/getHighlightParts.d.ts +5 -0
- package/dist/ObjectInspector/search/getHighlightParts.js +20 -0
- package/dist/ObjectInspector/search/getMatches.d.ts +6 -0
- package/dist/ObjectInspector/search/getMatches.js +46 -0
- package/dist/ObjectInspector/search/getSearchStatus.d.ts +2 -0
- package/dist/ObjectInspector/search/getSearchStatus.js +14 -0
- package/dist/Tree/Tree.d.ts +14 -0
- package/dist/Tree/Tree.js +166 -0
- package/dist/Tree/Tree.module.css +65 -0
- package/dist/Tree/getWindow.d.ts +22 -0
- package/dist/Tree/getWindow.js +43 -0
- package/dist/Tree/treeModel.d.ts +55 -0
- package/dist/Tree/treeModel.js +203 -0
- package/dist/Tree/useRowWindow.d.ts +3 -0
- package/dist/Tree/useRowWindow.js +78 -0
- package/dist/Tree/useTypeahead.d.ts +2 -0
- package/dist/Tree/useTypeahead.js +15 -0
- package/dist/announcer/announcer.d.ts +2 -0
- package/dist/announcer/announcer.js +41 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +10 -6
- package/dist/styles/themes/dusty.css +22 -0
- package/dist/styles/themes/mono.css +21 -0
- package/dist/styles/tokens.css +21 -0
- package/dist/styles/visuallyHidden.module.css +8 -0
- package/package.json +55 -36
- package/dist/Badge/Badge.cjs +0 -42
- package/dist/Button/Button.cjs +0 -42
- package/dist/index.cjs +0 -31
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useDeferredValue, useEffect, useMemo, useState, } from "react";
|
|
4
|
+
import { announce } from "../announcer/announcer.js";
|
|
5
|
+
import { Tree } from "../Tree/Tree.js";
|
|
6
|
+
import { describe } from "./describe/describe.js";
|
|
7
|
+
import { getNode } from "./describe/getNode.js";
|
|
8
|
+
import { getAncestorIds, ROOT_ID } from "./describe/getPath.js";
|
|
9
|
+
import { getLabelText } from "./getLabelText.js";
|
|
10
|
+
import { InspectorDetails } from "./InspectorDetails.js";
|
|
11
|
+
import { InspectorSearch } from "./InspectorSearch.js";
|
|
12
|
+
import { getHighlightParts } from "./search/getHighlightParts.js";
|
|
13
|
+
import { getMatches } from "./search/getMatches.js";
|
|
14
|
+
import { getSearchStatus } from "./search/getSearchStatus.js";
|
|
15
|
+
import styles from "./ObjectInspector.module.css";
|
|
16
|
+
const ROOT_OPEN = [ROOT_ID];
|
|
17
|
+
const SEARCH_LIMIT = 500_000;
|
|
18
|
+
const ANNOUNCE_DELAY = 700;
|
|
19
|
+
function getRowText({ node }) {
|
|
20
|
+
return node.label ? getLabelText(node.label) : "";
|
|
21
|
+
}
|
|
22
|
+
function Highlighted({ text, query }) {
|
|
23
|
+
if (query === "") {
|
|
24
|
+
return text;
|
|
25
|
+
}
|
|
26
|
+
return getHighlightParts(text, query).map((part, index) => part.isMatch ? (_jsx("mark", { className: styles.match, children: part.text }, index)) : (part.text));
|
|
27
|
+
}
|
|
28
|
+
export function ObjectInspector({ value, searchable = false, showDetails = false, expanded: controlledExpanded, defaultExpanded = ROOT_OPEN, onExpandedChange, selected: controlledSelected, onSelectedChange, ...props }) {
|
|
29
|
+
const root = useMemo(() => describe(value), [value]);
|
|
30
|
+
const [ownExpanded, setOwnExpanded] = useState(() => new Set(defaultExpanded));
|
|
31
|
+
const [ownSelected, setOwnSelected] = useState();
|
|
32
|
+
const [query, setQuery] = useState("");
|
|
33
|
+
const [position, setPosition] = useState({ matches: undefined, index: -1 });
|
|
34
|
+
const expanded = controlledExpanded ?? ownExpanded;
|
|
35
|
+
const selected = controlledSelected ?? ownSelected;
|
|
36
|
+
const searched = useDeferredValue(searchable ? query : "");
|
|
37
|
+
const matches = useMemo(() => getMatches(root, searched, SEARCH_LIMIT), [root, searched]);
|
|
38
|
+
const current = position.matches === matches ? position.index : -1;
|
|
39
|
+
const status = getSearchStatus(searched, matches, current);
|
|
40
|
+
const selectedNode = useMemo(() => showDetails && selected !== undefined
|
|
41
|
+
? (getNode(root, selected) ?? root)
|
|
42
|
+
: root, [showDetails, root, selected]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (status === "") {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const timer = setTimeout(() => announce(status), ANNOUNCE_DELAY);
|
|
48
|
+
return () => clearTimeout(timer);
|
|
49
|
+
}, [status]);
|
|
50
|
+
function changeExpanded(next) {
|
|
51
|
+
if (controlledExpanded === undefined) {
|
|
52
|
+
setOwnExpanded(next);
|
|
53
|
+
}
|
|
54
|
+
onExpandedChange?.(next);
|
|
55
|
+
}
|
|
56
|
+
function changeSelected(next) {
|
|
57
|
+
if (controlledSelected === undefined) {
|
|
58
|
+
setOwnSelected(next);
|
|
59
|
+
}
|
|
60
|
+
onSelectedChange?.(next);
|
|
61
|
+
}
|
|
62
|
+
function moveToMatch(step) {
|
|
63
|
+
const count = matches.ids.length;
|
|
64
|
+
const start = current < 0 && step < 0 ? 0 : current;
|
|
65
|
+
const index = (start + step + count) % count;
|
|
66
|
+
const id = matches.ids[index];
|
|
67
|
+
if (id === undefined) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const closed = getAncestorIds(id).filter((ancestor) => !expanded.has(ancestor));
|
|
71
|
+
if (closed.length > 0) {
|
|
72
|
+
changeExpanded(new Set([...expanded, ...closed]));
|
|
73
|
+
}
|
|
74
|
+
changeSelected(id);
|
|
75
|
+
setPosition({ matches, index });
|
|
76
|
+
}
|
|
77
|
+
const renderRow = useCallback(({ node }) => (_jsxs(_Fragment, { children: [node.label ? (_jsxs(_Fragment, { children: [_jsx("span", { children: _jsx(Highlighted, { text: getLabelText(node.label), query: searched }) }), _jsx("span", { className: styles.separator, children: ": " })] })) : null, _jsx("span", { className: styles.value, "data-kind": node.kind, children: node.childCount === 0 ? (_jsx(Highlighted, { text: node.preview, query: searched })) : (node.preview) })] })), [searched]);
|
|
78
|
+
const tree = (_jsx(Tree, { ...props, root: root, expanded: expanded, onExpandedChange: changeExpanded, selected: selected, onSelectedChange: changeSelected, renderRow: renderRow, getRowText: getRowText }));
|
|
79
|
+
if (!searchable && !showDetails) {
|
|
80
|
+
return tree;
|
|
81
|
+
}
|
|
82
|
+
return (_jsxs("div", { className: styles.inspector, children: [searchable ? (_jsx(InspectorSearch, { query: query, status: status, hasMatches: matches.ids.length > 0, onQueryChange: setQuery, onMove: moveToMatch })) : null, tree, showDetails ? _jsx(InspectorDetails, { node: selectedNode }) : null] }));
|
|
83
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
.separator {
|
|
2
|
+
color: var(--dusty-text-muted);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.value[data-kind="string"],
|
|
6
|
+
.value[data-kind="symbol"],
|
|
7
|
+
.value[data-kind="regexp"] {
|
|
8
|
+
color: var(--dusty-syntax-string);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.value[data-kind="number"],
|
|
12
|
+
.value[data-kind="bigint"],
|
|
13
|
+
.value[data-kind="date"] {
|
|
14
|
+
color: var(--dusty-syntax-number);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.value[data-kind="boolean"],
|
|
18
|
+
.value[data-kind="null"],
|
|
19
|
+
.value[data-kind="undefined"] {
|
|
20
|
+
color: var(--dusty-syntax-boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.value[data-kind="function"] {
|
|
24
|
+
color: var(--dusty-syntax-function);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.value[data-kind="getter"],
|
|
28
|
+
.value[data-kind="circular"],
|
|
29
|
+
.value[data-kind="uninspectable"] {
|
|
30
|
+
color: var(--dusty-text-muted);
|
|
31
|
+
font-style: italic;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.inspector {
|
|
35
|
+
display: grid;
|
|
36
|
+
gap: 0.5rem;
|
|
37
|
+
color: var(--dusty-text);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.match {
|
|
41
|
+
background-color: transparent;
|
|
42
|
+
color: inherit;
|
|
43
|
+
font-weight: var(--dusty-weight-semibold);
|
|
44
|
+
text-decoration: underline;
|
|
45
|
+
text-decoration-thickness: 0.125em;
|
|
46
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ValueKind } from "./getValueKind.js";
|
|
2
|
+
export type { ValueKind } from "./getValueKind.js";
|
|
3
|
+
export type NodeLabel = {
|
|
4
|
+
readonly type: "property";
|
|
5
|
+
readonly key: string;
|
|
6
|
+
} | {
|
|
7
|
+
readonly type: "symbol";
|
|
8
|
+
readonly key: symbol;
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: "index";
|
|
11
|
+
readonly index: number;
|
|
12
|
+
} | {
|
|
13
|
+
readonly type: "entry";
|
|
14
|
+
readonly index: number;
|
|
15
|
+
} | {
|
|
16
|
+
readonly type: "entryKey";
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "entryValue";
|
|
19
|
+
};
|
|
20
|
+
export interface InspectorNode {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly path: string;
|
|
23
|
+
readonly label: NodeLabel | undefined;
|
|
24
|
+
readonly value: unknown;
|
|
25
|
+
readonly kind: ValueKind;
|
|
26
|
+
readonly typeName: string | undefined;
|
|
27
|
+
readonly preview: string;
|
|
28
|
+
readonly childCount: number;
|
|
29
|
+
children(): readonly InspectorNode[];
|
|
30
|
+
}
|
|
31
|
+
export declare function describe(value: unknown): InspectorNode;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import { getChildren, getEntryChildren, } from "./getChildren.js";
|
|
3
|
+
import { getChildId, getPath, ROOT_ID } from "./getPath.js";
|
|
4
|
+
import { getPreview } from "./getPreview.js";
|
|
5
|
+
import { getValueKind } from "./getValueKind.js";
|
|
6
|
+
import { getConstructorName, getFailureReason } from "./safeRead.js";
|
|
7
|
+
const NO_CHILDREN = [];
|
|
8
|
+
function isObject(value) {
|
|
9
|
+
return ((typeof value === "object" && value !== null) || typeof value === "function");
|
|
10
|
+
}
|
|
11
|
+
function toLeaf(kind, detail = "") {
|
|
12
|
+
return { kind, typeName: undefined, detail, children: NO_CHILDREN };
|
|
13
|
+
}
|
|
14
|
+
function toLabel(step) {
|
|
15
|
+
switch (step.type) {
|
|
16
|
+
case "symbol":
|
|
17
|
+
return { type: "symbol", key: step.key };
|
|
18
|
+
case "item":
|
|
19
|
+
return { type: "index", index: step.index };
|
|
20
|
+
default:
|
|
21
|
+
return step;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function findAncestor(value, ancestors) {
|
|
25
|
+
for (let current = ancestors; current; current = current.parent) {
|
|
26
|
+
if (current.value === value) {
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function getObjectShape(target, ancestors) {
|
|
33
|
+
const repeated = findAncestor(target, ancestors);
|
|
34
|
+
if (repeated) {
|
|
35
|
+
return toLeaf("circular", repeated.path);
|
|
36
|
+
}
|
|
37
|
+
const kind = getValueKind(target);
|
|
38
|
+
return {
|
|
39
|
+
kind,
|
|
40
|
+
typeName: kind === "function" ? undefined : getConstructorName(target),
|
|
41
|
+
detail: "",
|
|
42
|
+
children: getChildren(target, kind),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function getShape(source, ancestors) {
|
|
46
|
+
switch (source.type) {
|
|
47
|
+
case "getter":
|
|
48
|
+
return toLeaf("getter");
|
|
49
|
+
case "failure":
|
|
50
|
+
return toLeaf("uninspectable", source.reason);
|
|
51
|
+
case "entry":
|
|
52
|
+
return {
|
|
53
|
+
kind: "entry",
|
|
54
|
+
typeName: undefined,
|
|
55
|
+
detail: "",
|
|
56
|
+
children: getEntryChildren(source.key, source.value),
|
|
57
|
+
};
|
|
58
|
+
case "value":
|
|
59
|
+
try {
|
|
60
|
+
return isObject(source.value)
|
|
61
|
+
? getObjectShape(source.value, ancestors)
|
|
62
|
+
: toLeaf(getValueKind(source.value));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return toLeaf("uninspectable", getFailureReason(error));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class ValueNode {
|
|
70
|
+
id;
|
|
71
|
+
path;
|
|
72
|
+
label;
|
|
73
|
+
#source;
|
|
74
|
+
#ancestors;
|
|
75
|
+
#shape;
|
|
76
|
+
#preview;
|
|
77
|
+
#children;
|
|
78
|
+
constructor(id, path, label, source, ancestors) {
|
|
79
|
+
this.id = id;
|
|
80
|
+
this.path = path;
|
|
81
|
+
this.label = label;
|
|
82
|
+
this.#source = source;
|
|
83
|
+
this.#ancestors = ancestors;
|
|
84
|
+
}
|
|
85
|
+
get #resolved() {
|
|
86
|
+
this.#shape ??= getShape(this.#source, this.#ancestors);
|
|
87
|
+
return this.#shape;
|
|
88
|
+
}
|
|
89
|
+
get value() {
|
|
90
|
+
return this.#source.type === "value" ? this.#source.value : undefined;
|
|
91
|
+
}
|
|
92
|
+
get kind() {
|
|
93
|
+
return this.#resolved.kind;
|
|
94
|
+
}
|
|
95
|
+
get typeName() {
|
|
96
|
+
return this.#resolved.typeName;
|
|
97
|
+
}
|
|
98
|
+
get preview() {
|
|
99
|
+
this.#preview ??= getPreview({
|
|
100
|
+
...this.#resolved,
|
|
101
|
+
source: this.#source,
|
|
102
|
+
});
|
|
103
|
+
return this.#preview;
|
|
104
|
+
}
|
|
105
|
+
get childCount() {
|
|
106
|
+
return this.#resolved.children.length;
|
|
107
|
+
}
|
|
108
|
+
children() {
|
|
109
|
+
this.#children ??= this.#createChildren();
|
|
110
|
+
return this.#children;
|
|
111
|
+
}
|
|
112
|
+
#createChildren() {
|
|
113
|
+
const { value } = this;
|
|
114
|
+
const ancestors = this.#source.type === "value" && isObject(value)
|
|
115
|
+
? { value, path: this.path, parent: this.#ancestors }
|
|
116
|
+
: this.#ancestors;
|
|
117
|
+
return this.#resolved.children.map(({ step, source }) => new _a(getChildId(this.id, step), getPath(this.path, step), toLabel(step), source, ancestors));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
_a = ValueNode;
|
|
121
|
+
export function describe(value) {
|
|
122
|
+
return new ValueNode(ROOT_ID, ROOT_ID, undefined, { type: "value", value }, undefined);
|
|
123
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type PathStep } from "./getPath.js";
|
|
2
|
+
import type { ValueKind } from "./getValueKind.js";
|
|
3
|
+
export type ChildSource = {
|
|
4
|
+
readonly type: "value";
|
|
5
|
+
readonly value: unknown;
|
|
6
|
+
} | {
|
|
7
|
+
readonly type: "getter";
|
|
8
|
+
} | {
|
|
9
|
+
readonly type: "failure";
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
} | {
|
|
12
|
+
readonly type: "entry";
|
|
13
|
+
readonly key: unknown;
|
|
14
|
+
readonly value: unknown;
|
|
15
|
+
};
|
|
16
|
+
export interface Child {
|
|
17
|
+
readonly step: PathStep;
|
|
18
|
+
readonly source: ChildSource;
|
|
19
|
+
}
|
|
20
|
+
export declare function getEntryChildren(key: unknown, value: unknown): Child[];
|
|
21
|
+
export declare function getChildren(target: object, kind: ValueKind): Child[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getArrayIndex } from "./getPath.js";
|
|
2
|
+
import { getFailureReason } from "./safeRead.js";
|
|
3
|
+
function getPropertySource(target, key) {
|
|
4
|
+
try {
|
|
5
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
6
|
+
if (!descriptor?.enumerable) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
return "value" in descriptor
|
|
10
|
+
? { type: "value", value: descriptor.value }
|
|
11
|
+
: { type: "getter" };
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
return { type: "failure", reason: getFailureReason(error) };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getPropertyStep(key, symbolPosition, indexed) {
|
|
18
|
+
if (typeof key === "symbol") {
|
|
19
|
+
return { type: "symbol", key, position: symbolPosition };
|
|
20
|
+
}
|
|
21
|
+
const index = indexed ? getArrayIndex(key) : undefined;
|
|
22
|
+
return index === undefined
|
|
23
|
+
? { type: "property", key }
|
|
24
|
+
: { type: "index", index };
|
|
25
|
+
}
|
|
26
|
+
function getPropertyChildren(target, indexed) {
|
|
27
|
+
const children = [];
|
|
28
|
+
let symbolPosition = 0;
|
|
29
|
+
for (const key of Reflect.ownKeys(target)) {
|
|
30
|
+
const step = getPropertyStep(key, symbolPosition, indexed);
|
|
31
|
+
if (step.type === "symbol") {
|
|
32
|
+
symbolPosition++;
|
|
33
|
+
}
|
|
34
|
+
const source = getPropertySource(target, key);
|
|
35
|
+
if (source) {
|
|
36
|
+
children.push({ step, source });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return children;
|
|
40
|
+
}
|
|
41
|
+
function getMapChildren(target) {
|
|
42
|
+
const children = [];
|
|
43
|
+
Map.prototype.forEach.call(target, (value, key) => {
|
|
44
|
+
children.push({
|
|
45
|
+
step: { type: "entry", index: children.length },
|
|
46
|
+
source: { type: "entry", key, value },
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
return children;
|
|
50
|
+
}
|
|
51
|
+
function getSetChildren(target) {
|
|
52
|
+
const children = [];
|
|
53
|
+
Set.prototype.forEach.call(target, (value) => {
|
|
54
|
+
children.push({
|
|
55
|
+
step: { type: "item", index: children.length },
|
|
56
|
+
source: { type: "value", value },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
return children;
|
|
60
|
+
}
|
|
61
|
+
export function getEntryChildren(key, value) {
|
|
62
|
+
return [
|
|
63
|
+
{ step: { type: "entryKey" }, source: { type: "value", value: key } },
|
|
64
|
+
{ step: { type: "entryValue" }, source: { type: "value", value } },
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
export function getChildren(target, kind) {
|
|
68
|
+
const properties = getPropertyChildren(target, kind === "array");
|
|
69
|
+
switch (kind) {
|
|
70
|
+
case "map":
|
|
71
|
+
return [...getMapChildren(target), ...properties];
|
|
72
|
+
case "set":
|
|
73
|
+
return [...getSetChildren(target), ...properties];
|
|
74
|
+
default:
|
|
75
|
+
return properties;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const ROOT_ID = "$";
|
|
2
|
+
export type PathStep = {
|
|
3
|
+
readonly type: "property";
|
|
4
|
+
readonly key: string;
|
|
5
|
+
} | {
|
|
6
|
+
readonly type: "symbol";
|
|
7
|
+
readonly key: symbol;
|
|
8
|
+
readonly position: number;
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: "index";
|
|
11
|
+
readonly index: number;
|
|
12
|
+
} | {
|
|
13
|
+
readonly type: "entry";
|
|
14
|
+
readonly index: number;
|
|
15
|
+
} | {
|
|
16
|
+
readonly type: "item";
|
|
17
|
+
readonly index: number;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "entryKey";
|
|
20
|
+
} | {
|
|
21
|
+
readonly type: "entryValue";
|
|
22
|
+
};
|
|
23
|
+
export declare function getArrayIndex(key: string): number | undefined;
|
|
24
|
+
export declare function getChildId(parentId: string, step: PathStep): string;
|
|
25
|
+
export declare function getPath(parentPath: string, step: PathStep): string;
|
|
26
|
+
export declare function getAncestorIds(id: string): string[];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getSymbolText } from "./safeRead.js";
|
|
2
|
+
export const ROOT_ID = "$";
|
|
3
|
+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/u;
|
|
4
|
+
const CANONICAL_INDEX = /^(?:0|[1-9]\d*)$/u;
|
|
5
|
+
const MAX_ARRAY_INDEX = 2 ** 32 - 2;
|
|
6
|
+
export function getArrayIndex(key) {
|
|
7
|
+
if (!CANONICAL_INDEX.test(key)) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const index = Number(key);
|
|
11
|
+
return index <= MAX_ARRAY_INDEX ? index : undefined;
|
|
12
|
+
}
|
|
13
|
+
function getSegment(step) {
|
|
14
|
+
switch (step.type) {
|
|
15
|
+
case "property":
|
|
16
|
+
return `.${step.key.replaceAll("~", "~0").replaceAll("/", "~1")}`;
|
|
17
|
+
case "symbol":
|
|
18
|
+
return `@${step.position}`;
|
|
19
|
+
case "index":
|
|
20
|
+
return `[${step.index}]`;
|
|
21
|
+
case "entry":
|
|
22
|
+
case "item":
|
|
23
|
+
return `#${step.index}`;
|
|
24
|
+
case "entryKey":
|
|
25
|
+
return "key";
|
|
26
|
+
case "entryValue":
|
|
27
|
+
return "value";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function getChildId(parentId, step) {
|
|
31
|
+
return `${parentId}/${getSegment(step)}`;
|
|
32
|
+
}
|
|
33
|
+
export function getPath(parentPath, step) {
|
|
34
|
+
switch (step.type) {
|
|
35
|
+
case "property":
|
|
36
|
+
return IDENTIFIER.test(step.key)
|
|
37
|
+
? `${parentPath}.${step.key}`
|
|
38
|
+
: `${parentPath}[${JSON.stringify(step.key)}]`;
|
|
39
|
+
case "symbol":
|
|
40
|
+
return `${parentPath}[${getSymbolText(step.key)}]`;
|
|
41
|
+
case "index":
|
|
42
|
+
return `${parentPath}[${step.index}]`;
|
|
43
|
+
case "entry":
|
|
44
|
+
return `${parentPath}[entry ${step.index}]`;
|
|
45
|
+
case "item":
|
|
46
|
+
return `${parentPath}[item ${step.index}]`;
|
|
47
|
+
case "entryKey":
|
|
48
|
+
return `${parentPath}.key`;
|
|
49
|
+
case "entryValue":
|
|
50
|
+
return `${parentPath}.value`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function getAncestorIds(id) {
|
|
54
|
+
const segments = id.split("/");
|
|
55
|
+
return segments
|
|
56
|
+
.slice(0, -1)
|
|
57
|
+
.map((_segment, index) => segments.slice(0, index + 1).join("/"));
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Child, ChildSource } from "./getChildren.js";
|
|
2
|
+
import { type ValueKind } from "./getValueKind.js";
|
|
3
|
+
export interface PreviewInput {
|
|
4
|
+
readonly kind: ValueKind;
|
|
5
|
+
readonly typeName: string | undefined;
|
|
6
|
+
readonly detail: string;
|
|
7
|
+
readonly source: ChildSource;
|
|
8
|
+
readonly children: readonly Child[];
|
|
9
|
+
}
|
|
10
|
+
export declare function getPreview(input: PreviewInput): string;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { getValueKind } from "./getValueKind.js";
|
|
2
|
+
import { getConstructorName, getDataProperty, getDateTime, getFailureReason, getMapSize, getRegExpText, getSetSize, getSymbolText, getTypedArrayLength, isClass, } from "./safeRead.js";
|
|
3
|
+
const MAX_TEXT_LENGTH = 80;
|
|
4
|
+
const MAX_NESTED_TEXT_LENGTH = 20;
|
|
5
|
+
const MAX_MEMBERS = 5;
|
|
6
|
+
const ELLIPSIS = "…";
|
|
7
|
+
const GETTER = "(...)";
|
|
8
|
+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/u;
|
|
9
|
+
const LINE_BREAKING = /[\p{Cc}\u2028\u2029]/gu;
|
|
10
|
+
function toSingleLine(text) {
|
|
11
|
+
return text.replace(LINE_BREAKING, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
12
|
+
}
|
|
13
|
+
function truncate(text, limit) {
|
|
14
|
+
const characters = Array.from(text);
|
|
15
|
+
return characters.length > limit
|
|
16
|
+
? `${characters.slice(0, limit).join("")}${ELLIPSIS}`
|
|
17
|
+
: text;
|
|
18
|
+
}
|
|
19
|
+
function quote(text, limit) {
|
|
20
|
+
return JSON.stringify(truncate(text, limit));
|
|
21
|
+
}
|
|
22
|
+
function withSize(name, size) {
|
|
23
|
+
return `${name}(${size})`;
|
|
24
|
+
}
|
|
25
|
+
function getArrayLength(value) {
|
|
26
|
+
try {
|
|
27
|
+
return getTypedArrayLength(value);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
const length = getDataProperty(value, "length");
|
|
31
|
+
return typeof length === "number" ? length : 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getUninspectablePreview(reason) {
|
|
35
|
+
return `Uninspectable: ${truncate(reason, MAX_TEXT_LENGTH)}`;
|
|
36
|
+
}
|
|
37
|
+
function getFunctionPreview(value) {
|
|
38
|
+
const name = getDataProperty(value, "name");
|
|
39
|
+
const label = typeof name === "string" ? truncate(name, MAX_NESTED_TEXT_LENGTH) : "";
|
|
40
|
+
return isClass(value) ? `class ${label}`.trimEnd() : `ƒ ${label}()`;
|
|
41
|
+
}
|
|
42
|
+
function getDatePreview(value) {
|
|
43
|
+
const time = getDateTime(value);
|
|
44
|
+
return Number.isNaN(time) ? "Invalid Date" : new Date(time).toISOString();
|
|
45
|
+
}
|
|
46
|
+
function getErrorPreview(value) {
|
|
47
|
+
const name = getConstructorName(value) ?? "Error";
|
|
48
|
+
const message = getDataProperty(value, "message");
|
|
49
|
+
return typeof message === "string" && message !== ""
|
|
50
|
+
? `${name}: ${truncate(message, MAX_TEXT_LENGTH)}`
|
|
51
|
+
: name;
|
|
52
|
+
}
|
|
53
|
+
function getPrimitivePreview(value, kind, textLimit) {
|
|
54
|
+
switch (kind) {
|
|
55
|
+
case "string":
|
|
56
|
+
return quote(value, textLimit);
|
|
57
|
+
case "number":
|
|
58
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
59
|
+
case "bigint":
|
|
60
|
+
return `${String(value)}n`;
|
|
61
|
+
case "boolean":
|
|
62
|
+
case "null":
|
|
63
|
+
case "undefined":
|
|
64
|
+
return String(value);
|
|
65
|
+
case "symbol":
|
|
66
|
+
return truncate(getSymbolText(value), textLimit);
|
|
67
|
+
default:
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function getNestedObjectPreview(value, kind) {
|
|
72
|
+
switch (kind) {
|
|
73
|
+
case "function":
|
|
74
|
+
return getFunctionPreview(value);
|
|
75
|
+
case "array":
|
|
76
|
+
return withSize(getConstructorName(value) ?? "Array", getArrayLength(value));
|
|
77
|
+
case "map":
|
|
78
|
+
return withSize("Map", getMapSize(value));
|
|
79
|
+
case "set":
|
|
80
|
+
return withSize("Set", getSetSize(value));
|
|
81
|
+
case "date":
|
|
82
|
+
return getDatePreview(value);
|
|
83
|
+
case "regexp":
|
|
84
|
+
return truncate(getRegExpText(value), MAX_NESTED_TEXT_LENGTH);
|
|
85
|
+
case "error":
|
|
86
|
+
return getConstructorName(value) ?? "Error";
|
|
87
|
+
default: {
|
|
88
|
+
const name = getConstructorName(value);
|
|
89
|
+
return name && name !== "Object" ? name : `{${ELLIPSIS}}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getNestedPreview(value) {
|
|
94
|
+
try {
|
|
95
|
+
const kind = getValueKind(value);
|
|
96
|
+
return (getPrimitivePreview(value, kind, MAX_NESTED_TEXT_LENGTH) ??
|
|
97
|
+
getNestedObjectPreview(value, kind));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return getUninspectablePreview(getFailureReason(error));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getSourcePreview(source) {
|
|
104
|
+
switch (source.type) {
|
|
105
|
+
case "value":
|
|
106
|
+
return getNestedPreview(source.value);
|
|
107
|
+
case "getter":
|
|
108
|
+
return GETTER;
|
|
109
|
+
case "failure":
|
|
110
|
+
return getUninspectablePreview(source.reason);
|
|
111
|
+
case "entry":
|
|
112
|
+
return `${getNestedPreview(source.key)} => ${getNestedPreview(source.value)}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function getMemberName(child) {
|
|
116
|
+
switch (child.step.type) {
|
|
117
|
+
case "property":
|
|
118
|
+
return IDENTIFIER.test(child.step.key)
|
|
119
|
+
? truncate(child.step.key, MAX_NESTED_TEXT_LENGTH)
|
|
120
|
+
: quote(child.step.key, MAX_NESTED_TEXT_LENGTH);
|
|
121
|
+
case "symbol":
|
|
122
|
+
return truncate(getSymbolText(child.step.key), MAX_NESTED_TEXT_LENGTH);
|
|
123
|
+
default:
|
|
124
|
+
return "";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function getMembersPreview(children, open, close) {
|
|
128
|
+
const members = children.slice(0, MAX_MEMBERS).map((child) => {
|
|
129
|
+
const name = getMemberName(child);
|
|
130
|
+
const value = getSourcePreview(child.source);
|
|
131
|
+
return name === "" ? value : `${name}: ${value}`;
|
|
132
|
+
});
|
|
133
|
+
if (children.length > members.length) {
|
|
134
|
+
members.push(ELLIPSIS);
|
|
135
|
+
}
|
|
136
|
+
return `${open}${members.join(", ")}${close}`;
|
|
137
|
+
}
|
|
138
|
+
function getContainerPreview(input, value) {
|
|
139
|
+
const { kind, typeName, children } = input;
|
|
140
|
+
switch (kind) {
|
|
141
|
+
case "function":
|
|
142
|
+
return getFunctionPreview(value);
|
|
143
|
+
case "date":
|
|
144
|
+
return getDatePreview(value);
|
|
145
|
+
case "regexp":
|
|
146
|
+
return truncate(getRegExpText(value), MAX_TEXT_LENGTH);
|
|
147
|
+
case "error":
|
|
148
|
+
return getErrorPreview(value);
|
|
149
|
+
case "array":
|
|
150
|
+
return `${withSize(typeName ?? "Array", getArrayLength(value))} ${getMembersPreview(children, "[", "]")}`;
|
|
151
|
+
case "map":
|
|
152
|
+
return `${withSize("Map", getMapSize(value))} ${getMembersPreview(children, "{", "}")}`;
|
|
153
|
+
case "set":
|
|
154
|
+
return `${withSize("Set", getSetSize(value))} ${getMembersPreview(children, "{", "}")}`;
|
|
155
|
+
default: {
|
|
156
|
+
const name = typeName && typeName !== "Object"
|
|
157
|
+
? `${truncate(typeName, MAX_NESTED_TEXT_LENGTH)} `
|
|
158
|
+
: "";
|
|
159
|
+
return `${name}${getMembersPreview(children, "{", "}")}`;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function getValuePreview(input) {
|
|
164
|
+
const value = input.source.type === "value" ? input.source.value : undefined;
|
|
165
|
+
try {
|
|
166
|
+
return (getPrimitivePreview(value, input.kind, MAX_TEXT_LENGTH) ??
|
|
167
|
+
getContainerPreview(input, value));
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return getUninspectablePreview(getFailureReason(error));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function getRawPreview(input) {
|
|
174
|
+
switch (input.kind) {
|
|
175
|
+
case "getter":
|
|
176
|
+
return GETTER;
|
|
177
|
+
case "uninspectable":
|
|
178
|
+
return getUninspectablePreview(input.detail);
|
|
179
|
+
case "circular":
|
|
180
|
+
return `[Circular ${truncate(input.detail, MAX_TEXT_LENGTH)}]`;
|
|
181
|
+
case "entry":
|
|
182
|
+
return getSourcePreview(input.source);
|
|
183
|
+
default:
|
|
184
|
+
return getValuePreview(input);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
export function getPreview(input) {
|
|
188
|
+
return toSingleLine(getRawPreview(input));
|
|
189
|
+
}
|