|
|
|
|
import type { BuildVisibleJsonRowsOptions, JsonViewNode, JsonViewNodeType } from "./types";
|
|
|
|
|
import {
|
|
|
|
|
formatCircularReference,
|
|
|
|
|
isCircularReferenceMarker,
|
|
|
|
|
normalizeVueValue,
|
|
|
|
|
} from "./normalizeValue";
|
|
|
|
|
|
|
|
|
|
type PlainObject = Record<string, unknown>;
|
|
|
|
|
type JsonContainerKind = "object" | "array" | "map" | "set";
|
|
|
|
|
type JsonContainerValue = PlainObject | unknown[] | Map<unknown, unknown> | Set<unknown>;
|
|
|
|
|
|
|
|
|
|
interface RowContext {
|
|
|
|
|
path: string;
|
|
|
|
|
level: number;
|
|
|
|
|
key?: string;
|
|
|
|
|
displayKey?: string;
|
|
|
|
|
keyValue?: unknown;
|
|
|
|
|
index?: number;
|
|
|
|
|
showComma: boolean;
|
|
|
|
|
posInSet: number;
|
|
|
|
|
setSize: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface LineCounter {
|
|
|
|
|
nextLineNumber: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toDisplayPath(path: string, rootPath: string) {
|
|
|
|
|
if (path === rootPath) {
|
|
|
|
|
return "$";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.startsWith(`${rootPath}.`)) {
|
|
|
|
|
return `$${path.slice(rootPath.length)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (path.startsWith(`${rootPath}[`)) {
|
|
|
|
|
return `$${path.slice(rootPath.length)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isRecordLike(value: unknown): value is Record<string, unknown> {
|
|
|
|
|
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildNodeId(path: string, type: JsonViewNodeType) {
|
|
|
|
|
return `${path}:${type}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildObjectPath(parentPath: string, key: string) {
|
|
|
|
|
if (/^[A-Za-z_$][\w$]*$/.test(key)) {
|
|
|
|
|
return `${parentPath}.${key}`;
|
|
|
|
|
}
|
|
|
|
|
return `${parentPath}[${JSON.stringify(key)}]`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildArrayPath(parentPath: string, index: number) {
|
|
|
|
|
return `${parentPath}[${index}]`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildMapPath(parentPath: string, key: unknown, index: number) {
|
|
|
|
|
if (typeof key === "string") {
|
|
|
|
|
return `${parentPath}.get(${JSON.stringify(key)})`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof key === "number" || typeof key === "boolean" || typeof key === "bigint") {
|
|
|
|
|
return `${parentPath}.get(${String(key)})`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key === null) {
|
|
|
|
|
return `${parentPath}.get(null)`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (key === undefined) {
|
|
|
|
|
return `${parentPath}.get(undefined)`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof key === "symbol") {
|
|
|
|
|
return `${parentPath}.get(${String(key)})`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return `${parentPath}.entries[${index}]`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCollapsedByDefault(level: number, length: number, options: BuildVisibleJsonRowsOptions) {
|
|
|
|
|
const deepCollapsed = typeof options.deep === "number" ? level + 1 > options.deep : false;
|
|
|
|
|
const lengthCollapsed =
|
|
|
|
|
typeof options.collapsedNodeLength === "number" ? length > options.collapsedNodeLength : false;
|
|
|
|
|
return deepCollapsed || lengthCollapsed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isExpanded(path: string, level: number, length: number, options: BuildVisibleJsonRowsOptions) {
|
|
|
|
|
const explicit = options.expandedState.get(path);
|
|
|
|
|
if (explicit !== undefined) {
|
|
|
|
|
return explicit;
|
|
|
|
|
}
|
|
|
|
|
return !isCollapsedByDefault(level, length, options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatKeyDisplay(value: unknown) {
|
|
|
|
|
const normalizedValue = normalizeVueValue(value);
|
|
|
|
|
if (isCircularReferenceMarker(normalizedValue)) {
|
|
|
|
|
return formatCircularReference(normalizedValue.path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = normalizedValue;
|
|
|
|
|
|
|
|
|
|
if (typeof value === "string") {
|
|
|
|
|
return JSON.stringify(value);
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
|
|
|
return String(value);
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "bigint") {
|
|
|
|
|
return `${String(value)}n`;
|
|
|
|
|
}
|
|
|
|
|
if (value === null) {
|
|
|
|
|
return "null";
|
|
|
|
|
}
|
|
|
|
|
if (value === undefined) {
|
|
|
|
|
return "undefined";
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "symbol") {
|
|
|
|
|
return value.toString();
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "function") {
|
|
|
|
|
return value.name ? `[Function ${value.name}]` : "[Function]";
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
return `Array(${value.length})`;
|
|
|
|
|
}
|
|
|
|
|
if (value instanceof Map) {
|
|
|
|
|
return `Map(${value.size})`;
|
|
|
|
|
}
|
|
|
|
|
if (value instanceof Set) {
|
|
|
|
|
return `Set(${value.size})`;
|
|
|
|
|
}
|
|
|
|
|
return "Object";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatPrimitiveDisplay(value: unknown) {
|
|
|
|
|
const normalizedValue = normalizeVueValue(value);
|
|
|
|
|
if (isCircularReferenceMarker(normalizedValue)) {
|
|
|
|
|
return formatCircularReference(normalizedValue.path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = normalizedValue;
|
|
|
|
|
|
|
|
|
|
if (typeof value === "string") {
|
|
|
|
|
return JSON.stringify(value);
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "number" || typeof value === "boolean") {
|
|
|
|
|
return String(value);
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "bigint") {
|
|
|
|
|
return `${String(value)}n`;
|
|
|
|
|
}
|
|
|
|
|
if (value === null) {
|
|
|
|
|
return "null";
|
|
|
|
|
}
|
|
|
|
|
if (value === undefined) {
|
|
|
|
|
return "undefined";
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "symbol") {
|
|
|
|
|
return value.toString();
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === "function") {
|
|
|
|
|
return value.name ? `[Function ${value.name}]` : "[Function]";
|
|
|
|
|
}
|
|
|
|
|
return String(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getContainerKind(value: unknown): JsonContainerKind | null {
|
|
|
|
|
value = normalizeVueValue(value);
|
|
|
|
|
if (isCircularReferenceMarker(value)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
return "array";
|
|
|
|
|
}
|
|
|
|
|
if (value instanceof Map) {
|
|
|
|
|
return "map";
|
|
|
|
|
}
|
|
|
|
|
if (value instanceof Set) {
|
|
|
|
|
return "set";
|
|
|
|
|
}
|
|
|
|
|
if (isRecordLike(value)) {
|
|
|
|
|
return "object";
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getContainerLength(value: JsonContainerValue, kind: JsonContainerKind) {
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case "array":
|
|
|
|
|
return (value as unknown[]).length;
|
|
|
|
|
case "map":
|
|
|
|
|
return (value as Map<unknown, unknown>).size;
|
|
|
|
|
case "set":
|
|
|
|
|
return (value as Set<unknown>).size;
|
|
|
|
|
case "object":
|
|
|
|
|
default:
|
|
|
|
|
return Object.keys(value as PlainObject).length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getContainerContent(kind: JsonContainerKind, state: "start" | "end" | "collapsed") {
|
|
|
|
|
switch (kind) {
|
|
|
|
|
case "map":
|
|
|
|
|
if (state === "start") return "Map {";
|
|
|
|
|
if (state === "end") return "}";
|
|
|
|
|
return "Map {...}";
|
|
|
|
|
case "set":
|
|
|
|
|
if (state === "start") return "Set [";
|
|
|
|
|
if (state === "end") return "]";
|
|
|
|
|
return "Set [...]";
|
|
|
|
|
case "object":
|
|
|
|
|
if (state === "start") return "{";
|
|
|
|
|
if (state === "end") return "}";
|
|
|
|
|
return "{...}";
|
|
|
|
|
case "array":
|
|
|
|
|
default:
|
|
|
|
|
if (state === "start") return "[";
|
|
|
|
|
if (state === "end") return "]";
|
|
|
|
|
return "[...]";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getContainerNodeType(kind: JsonContainerKind, state: "start" | "end" | "collapsed"): JsonViewNodeType {
|
|
|
|
|
if (kind === "object" || kind === "map") {
|
|
|
|
|
if (state === "start") return "objectStart";
|
|
|
|
|
if (state === "end") return "objectEnd";
|
|
|
|
|
return "objectCollapsed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state === "start") return "arrayStart";
|
|
|
|
|
if (state === "end") return "arrayEnd";
|
|
|
|
|
return "arrayCollapsed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function countValueLines(value: unknown, ancestors = new WeakMap<object, true>()): number {
|
|
|
|
|
value = normalizeVueValue(value);
|
|
|
|
|
if (isCircularReferenceMarker(value)) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const kind = getContainerKind(value);
|
|
|
|
|
if (!kind) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const reference = value as object;
|
|
|
|
|
if (ancestors.has(reference)) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ancestors.set(reference, true);
|
|
|
|
|
|
|
|
|
|
const total =
|
|
|
|
|
kind === "array"
|
|
|
|
|
? 2 + (value as unknown[]).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0)
|
|
|
|
|
: kind === "set"
|
|
|
|
|
? 2 + Array.from(value as Set<unknown>).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0)
|
|
|
|
|
: kind === "map"
|
|
|
|
|
? 2 +
|
|
|
|
|
Array.from((value as Map<unknown, unknown>).values()).reduce<number>(
|
|
|
|
|
(sum, entry) => sum + countValueLines(entry, ancestors),
|
|
|
|
|
0
|
|
|
|
|
)
|
|
|
|
|
: 2 +
|
|
|
|
|
Object.values(value as PlainObject).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
|
|
|
|
|
|
|
|
|
|
ancestors.delete(reference);
|
|
|
|
|
return total;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function countContainerChildLines(kind: JsonContainerKind, value: JsonContainerValue, ancestors: WeakMap<object, true>) {
|
|
|
|
|
if (kind === "array") {
|
|
|
|
|
return (value as unknown[]).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kind === "set") {
|
|
|
|
|
return Array.from(value as Set<unknown>).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kind === "map") {
|
|
|
|
|
return Array.from((value as Map<unknown, unknown>).values()).reduce<number>(
|
|
|
|
|
(sum, entry) => sum + countValueLines(entry, ancestors),
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Object.values(value as PlainObject).reduce<number>((sum, entry) => sum + countValueLines(entry, ancestors), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createNode(
|
|
|
|
|
counter: LineCounter,
|
|
|
|
|
context: RowContext,
|
|
|
|
|
type: JsonViewNodeType,
|
|
|
|
|
content: string,
|
|
|
|
|
value: unknown,
|
|
|
|
|
extras?: Partial<JsonViewNode>
|
|
|
|
|
): JsonViewNode {
|
|
|
|
|
return {
|
|
|
|
|
id: buildNodeId(context.path, type),
|
|
|
|
|
path: context.path,
|
|
|
|
|
lineNumber: counter.nextLineNumber++,
|
|
|
|
|
level: context.level,
|
|
|
|
|
key: context.key,
|
|
|
|
|
displayKey: context.displayKey,
|
|
|
|
|
keyValue: context.keyValue,
|
|
|
|
|
index: context.index,
|
|
|
|
|
content,
|
|
|
|
|
showComma: context.showComma,
|
|
|
|
|
type,
|
|
|
|
|
value,
|
|
|
|
|
isLeaf: type === "content",
|
|
|
|
|
hasToggle: false,
|
|
|
|
|
isExpanded: false,
|
|
|
|
|
posInSet: context.posInSet,
|
|
|
|
|
setSize: context.setSize,
|
|
|
|
|
...extras,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function appendValueRows(
|
|
|
|
|
value: unknown,
|
|
|
|
|
context: RowContext,
|
|
|
|
|
rows: JsonViewNode[],
|
|
|
|
|
options: BuildVisibleJsonRowsOptions,
|
|
|
|
|
counter: LineCounter,
|
|
|
|
|
ancestors: WeakMap<object, string>
|
|
|
|
|
) {
|
|
|
|
|
const normalizedValue = normalizeVueValue(value, toDisplayPath(context.path, options.rootPath));
|
|
|
|
|
if (isCircularReferenceMarker(normalizedValue)) {
|
|
|
|
|
rows.push(
|
|
|
|
|
createNode(
|
|
|
|
|
counter,
|
|
|
|
|
context,
|
|
|
|
|
"content",
|
|
|
|
|
formatCircularReference(normalizedValue.path ?? toDisplayPath(context.path, options.rootPath)),
|
|
|
|
|
normalizedValue,
|
|
|
|
|
{ isLeaf: true }
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value = normalizedValue;
|
|
|
|
|
const kind = getContainerKind(value);
|
|
|
|
|
if (!kind) {
|
|
|
|
|
rows.push(createNode(counter, context, "content", formatPrimitiveDisplay(value), value, { isLeaf: true }));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const reference = value as object;
|
|
|
|
|
const existingPath = ancestors.get(reference);
|
|
|
|
|
if (existingPath) {
|
|
|
|
|
rows.push(
|
|
|
|
|
createNode(counter, context, "content", formatCircularReference(toDisplayPath(existingPath, options.rootPath)), value, {
|
|
|
|
|
isLeaf: true,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ancestors.set(reference, context.path);
|
|
|
|
|
appendContainerRows(kind, value as JsonContainerValue, context, rows, options, counter, ancestors);
|
|
|
|
|
ancestors.delete(reference);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function appendContainerRows(
|
|
|
|
|
kind: JsonContainerKind,
|
|
|
|
|
value: JsonContainerValue,
|
|
|
|
|
context: RowContext,
|
|
|
|
|
rows: JsonViewNode[],
|
|
|
|
|
options: BuildVisibleJsonRowsOptions,
|
|
|
|
|
counter: LineCounter,
|
|
|
|
|
ancestors: WeakMap<object, string>
|
|
|
|
|
) {
|
|
|
|
|
const length = getContainerLength(value, kind);
|
|
|
|
|
const expanded = isExpanded(context.path, context.level, length, options);
|
|
|
|
|
const totalLineCount = 2 + countContainerChildLines(kind, value, new WeakMap<object, true>());
|
|
|
|
|
|
|
|
|
|
if (!expanded) {
|
|
|
|
|
rows.push(
|
|
|
|
|
createNode(counter, context, getContainerNodeType(kind, "collapsed"), getContainerContent(kind, "collapsed"), value, {
|
|
|
|
|
length,
|
|
|
|
|
hasToggle: true,
|
|
|
|
|
isExpanded: false,
|
|
|
|
|
isLeaf: false,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
counter.nextLineNumber += totalLineCount - 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rows.push(
|
|
|
|
|
createNode(counter, context, getContainerNodeType(kind, "start"), getContainerContent(kind, "start"), value, {
|
|
|
|
|
length,
|
|
|
|
|
hasToggle: true,
|
|
|
|
|
isExpanded: true,
|
|
|
|
|
isLeaf: false,
|
|
|
|
|
showComma: false,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (kind === "object") {
|
|
|
|
|
Object.entries(value as PlainObject).forEach(([key, childValue], index) => {
|
|
|
|
|
appendValueRows(
|
|
|
|
|
childValue,
|
|
|
|
|
{
|
|
|
|
|
path: buildObjectPath(context.path, key),
|
|
|
|
|
level: context.level + 1,
|
|
|
|
|
key,
|
|
|
|
|
showComma: index < length - 1,
|
|
|
|
|
posInSet: index + 1,
|
|
|
|
|
setSize: length,
|
|
|
|
|
},
|
|
|
|
|
rows,
|
|
|
|
|
options,
|
|
|
|
|
counter,
|
|
|
|
|
ancestors
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
} else if (kind === "array") {
|
|
|
|
|
(value as unknown[]).forEach((childValue, index) => {
|
|
|
|
|
appendValueRows(
|
|
|
|
|
childValue,
|
|
|
|
|
{
|
|
|
|
|
path: buildArrayPath(context.path, index),
|
|
|
|
|
level: context.level + 1,
|
|
|
|
|
index,
|
|
|
|
|
showComma: index < length - 1,
|
|
|
|
|
posInSet: index + 1,
|
|
|
|
|
setSize: length,
|
|
|
|
|
},
|
|
|
|
|
rows,
|
|
|
|
|
options,
|
|
|
|
|
counter,
|
|
|
|
|
ancestors
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
} else if (kind === "set") {
|
|
|
|
|
Array.from(value as Set<unknown>).forEach((childValue, index) => {
|
|
|
|
|
appendValueRows(
|
|
|
|
|
childValue,
|
|
|
|
|
{
|
|
|
|
|
path: buildArrayPath(context.path, index),
|
|
|
|
|
level: context.level + 1,
|
|
|
|
|
index,
|
|
|
|
|
showComma: index < length - 1,
|
|
|
|
|
posInSet: index + 1,
|
|
|
|
|
setSize: length,
|
|
|
|
|
},
|
|
|
|
|
rows,
|
|
|
|
|
options,
|
|
|
|
|
counter,
|
|
|
|
|
ancestors
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
Array.from(value as Map<unknown, unknown>).forEach(([keyValue, childValue], index) => {
|
|
|
|
|
const normalizedKeyValue = normalizeVueValue(keyValue, toDisplayPath(context.path, options.rootPath));
|
|
|
|
|
appendValueRows(
|
|
|
|
|
childValue,
|
|
|
|
|
{
|
|
|
|
|
path: buildMapPath(context.path, normalizedKeyValue, index),
|
|
|
|
|
level: context.level + 1,
|
|
|
|
|
displayKey: formatKeyDisplay(normalizedKeyValue),
|
|
|
|
|
keyValue: normalizedKeyValue,
|
|
|
|
|
showComma: index < length - 1,
|
|
|
|
|
posInSet: index + 1,
|
|
|
|
|
setSize: length,
|
|
|
|
|
},
|
|
|
|
|
rows,
|
|
|
|
|
options,
|
|
|
|
|
counter,
|
|
|
|
|
ancestors
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rows.push(
|
|
|
|
|
createNode(counter, context, getContainerNodeType(kind, "end"), getContainerContent(kind, "end"), value, {
|
|
|
|
|
length,
|
|
|
|
|
hasToggle: false,
|
|
|
|
|
isExpanded: expanded,
|
|
|
|
|
isLeaf: false,
|
|
|
|
|
key: undefined,
|
|
|
|
|
displayKey: undefined,
|
|
|
|
|
keyValue: undefined,
|
|
|
|
|
index: undefined,
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function buildVisibleJsonRows(value: unknown, options: BuildVisibleJsonRowsOptions) {
|
|
|
|
|
const rows: JsonViewNode[] = [];
|
|
|
|
|
const counter: LineCounter = { nextLineNumber: 1 };
|
|
|
|
|
appendValueRows(
|
|
|
|
|
value,
|
|
|
|
|
{
|
|
|
|
|
path: options.rootPath,
|
|
|
|
|
level: 0,
|
|
|
|
|
showComma: false,
|
|
|
|
|
posInSet: 1,
|
|
|
|
|
setSize: 1,
|
|
|
|
|
},
|
|
|
|
|
rows,
|
|
|
|
|
options,
|
|
|
|
|
counter,
|
|
|
|
|
new WeakMap<object, string>()
|
|
|
|
|
);
|
|
|
|
|
return rows;
|
|
|
|
|
}
|