* Updates visualization library.
* Adds support for more js debug adapters. * Minor improvements.
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -8,5 +8,6 @@
|
||||
"typescript.updateImportsOnFileMove.enabled": "prompt",
|
||||
"mochaExplorer.files": "./data-extraction/test/**/*.test.ts",
|
||||
"mochaExplorer.require": ["ts-node/register", "source-map-support/register"],
|
||||
"tasksStatusbar.taskLabelFilter": "dev"
|
||||
"tasksStatusbar.taskLabelFilter": "dev",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export type GraphEdge = {
|
||||
label?: string;
|
||||
id?: string;
|
||||
color?: string;
|
||||
dashes?: boolean;
|
||||
style?: "solid" | "dashed" | "dotted";
|
||||
};
|
||||
|
||||
export type GridVisualizationData = {
|
||||
@@ -157,12 +157,38 @@ export type MonacoTextVisualizationData = {
|
||||
* The text to show
|
||||
*/
|
||||
text: string;
|
||||
decorations?: {
|
||||
range: LineColumnRange;
|
||||
label?: string;
|
||||
}[];
|
||||
/**
|
||||
* An optional filename that might be used for chosing a syntax highlighter
|
||||
*/
|
||||
fileName?: string;
|
||||
};
|
||||
|
||||
export type LineColumnRange = {
|
||||
/**
|
||||
* The start position
|
||||
*/
|
||||
start: LineColumnPosition;
|
||||
/**
|
||||
* The end position
|
||||
*/
|
||||
end: LineColumnPosition;
|
||||
};
|
||||
|
||||
export type LineColumnPosition = {
|
||||
/**
|
||||
* The 0-based line number
|
||||
*/
|
||||
line: number;
|
||||
/**
|
||||
* The 0-based column number
|
||||
*/
|
||||
column: number;
|
||||
};
|
||||
|
||||
export type MonacoTextDiffVisualizationData = {
|
||||
kind: {
|
||||
text: true;
|
||||
|
||||
@@ -22,7 +22,8 @@ export interface DataExtractorApi {
|
||||
getData(
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, unknown>
|
||||
): JSONString<DataResult>;
|
||||
|
||||
/**
|
||||
@@ -68,6 +69,8 @@ export interface DataExtractorContext {
|
||||
* Evaluates an expression in the context of the active stack frame.
|
||||
*/
|
||||
evalFn: <TEval>(expression: string) => TEval;
|
||||
|
||||
variablesInScope: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface DataExtraction {
|
||||
|
||||
@@ -14,6 +14,9 @@ import { registerDefaultExtractors } from "./default-extractors";
|
||||
*/
|
||||
export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
public static lastEvalFn: (<T>(expression: string) => T) | undefined;
|
||||
public static lastVariablesInScope:
|
||||
| Record<string, unknown>
|
||||
| undefined = undefined;
|
||||
|
||||
private readonly extractors = new Map<string, DataExtractor>();
|
||||
|
||||
@@ -34,7 +37,8 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
public getData(
|
||||
valueFn: () => unknown,
|
||||
evalFn: <T>(expression: string) => T,
|
||||
preferredDataExtractorId: string | undefined
|
||||
preferredDataExtractorId: string | undefined,
|
||||
variablesInScope: Record<string, unknown>
|
||||
): JSONString<DataResult> {
|
||||
const extractions = new Array<DataExtraction>();
|
||||
const extractionCollector: ExtractionCollector = {
|
||||
@@ -43,13 +47,20 @@ export class DataExtractorApiImpl implements DataExtractorApi {
|
||||
},
|
||||
};
|
||||
|
||||
DataExtractorApiImpl.lastVariablesInScope = variablesInScope;
|
||||
DataExtractorApiImpl.lastEvalFn = evalFn;
|
||||
const value = valueFn();
|
||||
DataExtractorApiImpl.lastEvalFn = undefined;
|
||||
|
||||
for (const e of this.extractors.values()) {
|
||||
e.getExtractions(value, extractionCollector, { evalFn });
|
||||
e.getExtractions(value, extractionCollector, {
|
||||
evalFn,
|
||||
variablesInScope,
|
||||
});
|
||||
}
|
||||
|
||||
DataExtractorApiImpl.lastVariablesInScope = undefined;
|
||||
DataExtractorApiImpl.lastEvalFn = undefined;
|
||||
|
||||
extractions.sort((a, b) => b.priority - a.priority);
|
||||
let usedExtraction = extractions[0];
|
||||
if (!usedExtraction) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { DataExtractor, DataExtractorContext, ExtractionCollector } from "../..";
|
||||
|
||||
export class StringDiffExtractor implements DataExtractor {
|
||||
public readonly id = "string-diff";
|
||||
|
||||
public getExtractions(data: unknown,
|
||||
collector: ExtractionCollector,
|
||||
context: DataExtractorContext) {
|
||||
if (Array.isArray(data) && data.length === 2 && typeof data[0] === "string" && typeof data[1] === "string") {
|
||||
collector.addExtraction({
|
||||
id: "string-diff",
|
||||
name: "String Diff",
|
||||
priority: 1000,
|
||||
extractData: () => ({
|
||||
kind: { text: true },
|
||||
text: data[0],
|
||||
otherText: data[1]
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import { ObjectGraphExtractor } from "./ObjectGraphExtractor";
|
||||
import { getDataExtractorApi } from "../injection";
|
||||
import { GridExtractor } from "./GridExtractor";
|
||||
import { TableDataExtractor } from "./TableExtractor";
|
||||
import { StringDiffExtractor } from "./StringDiffExtractor";
|
||||
import { find } from "../../helpers";
|
||||
|
||||
/**
|
||||
* The default data extractors should be registered by VS Code automatically.
|
||||
@@ -25,7 +27,102 @@ export function registerDefaultExtractors(
|
||||
new ObjectGraphExtractor(),
|
||||
new GridExtractor(),
|
||||
new TableDataExtractor(),
|
||||
new StringDiffExtractor(),
|
||||
]) {
|
||||
api.registerExtractor(item);
|
||||
}
|
||||
|
||||
function isRange(
|
||||
value: unknown
|
||||
): value is {
|
||||
startColumn: number;
|
||||
startLineNumber: number;
|
||||
endColumn: number;
|
||||
endLineNumber: number;
|
||||
} {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"startColumn" in value &&
|
||||
"startLineNumber" in value
|
||||
);
|
||||
}
|
||||
|
||||
function isPosition(
|
||||
value: unknown
|
||||
): value is { column: number; lineNumber: number } {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
!!value &&
|
||||
"column" in value &&
|
||||
"lineNumber" in value
|
||||
);
|
||||
}
|
||||
|
||||
// hedietDbgVis.find(x => x.constructor.name === 'TextModel').getValue()
|
||||
api.registerExtractor({
|
||||
id: "positionInTextModel",
|
||||
getExtractions(data, collector, context) {
|
||||
let range: {
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
};
|
||||
if (isRange(data)) {
|
||||
range = {
|
||||
start: {
|
||||
line: data.startLineNumber - 1,
|
||||
column: data.startColumn - 1,
|
||||
},
|
||||
end: {
|
||||
line: data.endLineNumber - 1,
|
||||
column: data.endColumn - 1,
|
||||
},
|
||||
};
|
||||
} else if (isPosition(data)) {
|
||||
range = {
|
||||
start: {
|
||||
line: data.lineNumber - 1,
|
||||
column: data.column - 1,
|
||||
},
|
||||
end: {
|
||||
line: data.lineNumber - 1,
|
||||
column: data.column - 1,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRange(data) && !isPosition(data)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textModel: any = find(
|
||||
x =>
|
||||
typeof x === "object" &&
|
||||
!!x &&
|
||||
"constructor" in x &&
|
||||
(x as any).constructor.name === "TextModel"
|
||||
);
|
||||
|
||||
collector.addExtraction({
|
||||
id: "positionInTextModel",
|
||||
name: "Position In TextModel",
|
||||
extractData() {
|
||||
return {
|
||||
kind: { text: true },
|
||||
text: textModel.getValue(),
|
||||
decorations: [{ range }],
|
||||
};
|
||||
},
|
||||
priority: 1000,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
GraphVisualizationData,
|
||||
} from "../../CommonDataTypes";
|
||||
|
||||
export type CreateGraphEdge<T> = { to: T } & Omit<GraphEdge, "from" | "to">;
|
||||
export type CreateGraphEdge<T> = ({ to: T } | { from: T } | { include: T }) &
|
||||
Omit<GraphEdge, "from" | "to">;
|
||||
|
||||
/**
|
||||
* Given a list of roots, it creates a graph by following their edges recursively.
|
||||
@@ -58,13 +59,31 @@ export function createGraph<T>(
|
||||
const fromId = getId(item);
|
||||
r.nodes.push({ ...nodeInfo, id: fromId, ["edges" as any]: undefined });
|
||||
for (const e of nodeInfo.edges) {
|
||||
const toId = getId(e.to);
|
||||
r.edges.push({
|
||||
...e,
|
||||
from: fromId,
|
||||
to: toId,
|
||||
});
|
||||
let shouldPush = !processed.has(e.to);
|
||||
let other: T;
|
||||
let toId: string | undefined;
|
||||
let fromId_: string | undefined;
|
||||
if ("to" in e) {
|
||||
other = e.to;
|
||||
toId = getId(e.to);
|
||||
fromId_ = fromId;
|
||||
} else if ("from" in e) {
|
||||
other = e.from;
|
||||
toId = getId(e.from);
|
||||
fromId_ = fromId;
|
||||
} else if ("include" in e) {
|
||||
other = e.include;
|
||||
} else {
|
||||
throw new Error("Every edge must either have 'to' or 'from'");
|
||||
}
|
||||
|
||||
if (fromId_ !== undefined && toId !== undefined) {
|
||||
r.edges.push({
|
||||
...e,
|
||||
from: fromId_,
|
||||
to: toId,
|
||||
});
|
||||
}
|
||||
let shouldPush = !processed.has(other);
|
||||
if (
|
||||
options.maxSize &&
|
||||
processed.size + queue.length > options.maxSize
|
||||
@@ -72,7 +91,7 @@ export function createGraph<T>(
|
||||
shouldPush = false;
|
||||
}
|
||||
if (shouldPush) {
|
||||
queue.push({ item: e.to, dist: dist + 1 });
|
||||
queue.push({ item: other, dist: dist + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
data-extraction/src/js/helpers/find.ts
Normal file
34
data-extraction/src/js/helpers/find.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { DataExtractorApiImpl } from "../api";
|
||||
|
||||
export function find(predicate: (obj: unknown) => boolean): unknown {
|
||||
const processed = new Set();
|
||||
if (!DataExtractorApiImpl.lastVariablesInScope) {
|
||||
throw new Error("No variables in scope!");
|
||||
}
|
||||
|
||||
const values = Object.values(DataExtractorApiImpl.lastVariablesInScope);
|
||||
const queue = [...values];
|
||||
|
||||
let i = 10000;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
|
||||
const top = queue.shift();
|
||||
processed.add(top);
|
||||
|
||||
if (predicate(top)) {
|
||||
return top;
|
||||
}
|
||||
|
||||
if (typeof top === "object" && top) {
|
||||
for (const val of Object.values(top)) {
|
||||
if (!processed.has(val)) {
|
||||
processed.add(val);
|
||||
queue.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
@@ -4,3 +4,4 @@ export * from "./tryEval";
|
||||
export * from "./markedGrid";
|
||||
export * from "./cache";
|
||||
export * from "./asData";
|
||||
export * from "./find";
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Change Log
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Adds text diff and text decoration viewer.
|
||||
- Supports more js debug adapters
|
||||
|
||||
## 2.0.6
|
||||
|
||||
- Fixes grid extractor bug
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"displayName": "Debug Visualizer",
|
||||
"description": "A visual watch window that lets you visualize your data structures while debugging.",
|
||||
"icon": "docs/logo.drawio.png",
|
||||
"version": "2.0.6",
|
||||
"version": "2.1.0",
|
||||
"license": "GPL-3.0",
|
||||
"engines": {
|
||||
"vscode": "^1.46.0"
|
||||
|
||||
@@ -26,7 +26,11 @@ export class JsEvaluationEngine implements EvaluationEngine {
|
||||
"chrome",
|
||||
"pwa-chrome",
|
||||
"pwa-node",
|
||||
"pwa-extensionHost",
|
||||
"node-terminal",
|
||||
"pwa-msedge",
|
||||
];
|
||||
|
||||
if (supportedDebugAdapters.indexOf(session.session.type) !== -1) {
|
||||
return new JsEvaluator(session);
|
||||
}
|
||||
@@ -54,6 +58,31 @@ class JsEvaluator implements Evaluator {
|
||||
| { kind: "data"; result: DataExtractionResult }
|
||||
| { kind: "error"; message: FormattedMessage }
|
||||
> {
|
||||
const variableNames = new Array<string>();
|
||||
if (frameId) {
|
||||
const scopes = await this.session.getScopes({ frameId });
|
||||
const scopeVariables = await Promise.all(
|
||||
scopes
|
||||
.filter(s => !s.expensive)
|
||||
.map(s =>
|
||||
this.session.getVariables({
|
||||
variablesReference: s.variablesReference,
|
||||
})
|
||||
)
|
||||
);
|
||||
for (const variables of scopeVariables) {
|
||||
variableNames.push(
|
||||
...variables
|
||||
.filter(v => v.value !== "undefined")
|
||||
.map(v => v.name)
|
||||
);
|
||||
}
|
||||
|
||||
if (variableNames.length > 100) {
|
||||
variableNames.length = 100;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const preferredExtractorExpr = preferredExtractorId
|
||||
@@ -63,7 +92,8 @@ class JsEvaluator implements Evaluator {
|
||||
const body = `${getExpressionForDataExtractorApi()}.getData(
|
||||
e => (${expression}),
|
||||
expr => eval(expr),
|
||||
${preferredExtractorExpr}
|
||||
${preferredExtractorExpr},
|
||||
{${variableNames.map(n => `${n}: ${n}`).join(",")}},
|
||||
)`;
|
||||
|
||||
const wrappedExpr = `
|
||||
|
||||
@@ -5,11 +5,13 @@ import {
|
||||
} from "./EvaluationWatchService";
|
||||
import { observable, autorun, action } from "mobx";
|
||||
import { Disposable } from "@hediet/std/disposable";
|
||||
import { CancellationTokenSource, CancellationToken } from "vscode";
|
||||
import { DataExtractorId } from "@hediet/debug-visualizer-data-extraction";
|
||||
import { DataExtractionState, CompletionItem } from "../webviewContract";
|
||||
import { hotClass } from "@hediet/node-reload";
|
||||
import { VsCodeDebuggerView } from "../debugger/VsCodeDebuggerView";
|
||||
import { EvaluationEngine } from "./EvaluationEngine/EvaluationEngine";
|
||||
import { wait } from "@hediet/std/timer";
|
||||
|
||||
@hotClass(module)
|
||||
export class EvaluationWatchServiceImpl implements EvaluationWatchService {
|
||||
@@ -63,7 +65,25 @@ export class EvaluationWatchServiceImpl implements EvaluationWatchService {
|
||||
return evaluator.languageId;
|
||||
}
|
||||
|
||||
public async refresh(w: ObservableEvaluationWatcher): Promise<void> {
|
||||
private runningRefreshOperation: Disposable | undefined;
|
||||
|
||||
public refresh(w: ObservableEvaluationWatcher): void {
|
||||
if (this.runningRefreshOperation) {
|
||||
this.runningRefreshOperation.dispose();
|
||||
}
|
||||
const tokenSource = new CancellationTokenSource();
|
||||
this.runningRefreshOperation = {
|
||||
dispose: () => {
|
||||
tokenSource.cancel();
|
||||
},
|
||||
};
|
||||
this._refresh(w, tokenSource.token);
|
||||
}
|
||||
|
||||
private async _refresh(
|
||||
w: ObservableEvaluationWatcher,
|
||||
token: CancellationToken
|
||||
): Promise<void> {
|
||||
const session = this.vsCodeDebuggerView.activeDebugSession;
|
||||
if (!session) {
|
||||
w._state = { kind: "noDebugSession" };
|
||||
@@ -89,6 +109,15 @@ export class EvaluationWatchServiceImpl implements EvaluationWatchService {
|
||||
preferredExtractorId: w.preferredDataExtractor,
|
||||
});
|
||||
|
||||
if (result.kind === "error") {
|
||||
// give cancellation requested more time in case of errors
|
||||
await wait(330);
|
||||
}
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
||||
w._state = result;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,27 @@ export class EnhancedDebugSession {
|
||||
}
|
||||
}
|
||||
|
||||
public async getScopes(args: { frameId: number }): Promise<Scope[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("scopes", {
|
||||
frameId: args.frameId,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
return reply.scopes;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async getVariables(args: {
|
||||
variablesReference: number
|
||||
variablesReference: number;
|
||||
}): Promise<Variable[]> {
|
||||
try {
|
||||
const reply = await this.session.customRequest("variables", {
|
||||
variablesReference: args.variablesReference
|
||||
variablesReference: args.variablesReference,
|
||||
});
|
||||
if (!reply) {
|
||||
return [];
|
||||
@@ -72,16 +87,25 @@ export class EnhancedDebugSession {
|
||||
expression: string;
|
||||
frameId: number | undefined;
|
||||
context: "watch" | "repl" | "copy";
|
||||
}): Promise<{ result: string, variablesReference: number }> {
|
||||
}): Promise<{ result: string; variablesReference: number }> {
|
||||
const reply = await this.session.customRequest("evaluate", {
|
||||
expression: args.expression,
|
||||
frameId: args.frameId,
|
||||
context: args.context,
|
||||
});
|
||||
return { result: reply.result, variablesReference: reply.variablesReference };
|
||||
return {
|
||||
result: reply.result,
|
||||
variablesReference: reply.variablesReference,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface Scope {
|
||||
name: string;
|
||||
expensive: boolean;
|
||||
variablesReference: number;
|
||||
}
|
||||
|
||||
interface Variable {
|
||||
name: string;
|
||||
value: string;
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
"react-dom": "^16.12.0",
|
||||
"react-measure": "^2.3.0",
|
||||
"@hediet/debug-visualizer-data-extraction": "*",
|
||||
"@hediet/visualization-bundle": "^0.1.6",
|
||||
"@hediet/visualization-core": "^0.1.1",
|
||||
"@hediet/visualization-bundle": "^0.2.1",
|
||||
"@hediet/visualization-core": "^0.2.0",
|
||||
"@knuddels/mobx-logger": "^1.1.1",
|
||||
"less-loader": "^6.2.0"
|
||||
},
|
||||
|
||||
28
yarn.lock
28
yarn.lock
@@ -507,10 +507,10 @@
|
||||
stack-trace "^0.0.10"
|
||||
ws "^7.0.0"
|
||||
|
||||
"@hediet/semantic-json@^0.3.14":
|
||||
version "0.3.14"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/semantic-json/-/semantic-json-0.3.14.tgz#876bd5c4b646c134641ad17e9ef352595fb163e7"
|
||||
integrity sha512-u2QGfT98RNrcQjqRHwFUy+TU8+sLneBOYvHHNbkHyYNokqi8kCpE9LReTygGT+6SfMtydTWN6JT9L2ffxrvV6w==
|
||||
"@hediet/semantic-json@^0.3.15":
|
||||
version "0.3.15"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/semantic-json/-/semantic-json-0.3.15.tgz#e38f799b1a7dd2624a3ee2e05bca58cfe76cb58d"
|
||||
integrity sha512-8t1NXiYDF2wqIt1jEknxNnpucdtgFAI2MGSWnnewDhaYqWOJLMY/MxH60gDX03vycf2ySb6vEYpEwsiB6r0O3Q==
|
||||
|
||||
"@hediet/std@^0.6.0":
|
||||
version "0.6.0"
|
||||
@@ -550,16 +550,16 @@
|
||||
"@hediet/std" "^0.6.0"
|
||||
io-ts "^1.8.6"
|
||||
|
||||
"@hediet/visualization-bundle@^0.1.6":
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.1.6.tgz#a8ab11be910a30d0d26a88044caf11cab4a60f98"
|
||||
integrity sha512-IQZDORYlRTojBwJUIADpg4NRxCqK6Q+bl1H2IP7Bq28wdwsRtuERB2UcGPufn00NGhJbOw6FAH2e1zwsQAkboQ==
|
||||
"@hediet/visualization-bundle@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-bundle/-/visualization-bundle-0.2.1.tgz#397a6bff0ea12018a7a1cb1c182ef8c5de87328f"
|
||||
integrity sha512-Qfyn8WbWGwuE5J1z8U7l8E5VKuG3DpS2JJOtKhlW1QW/rhqBWOLd5avifxqjWxiMIfqh3NTShp9gLHpmI2Bysw==
|
||||
dependencies:
|
||||
"@finos/perspective-viewer" "^0.5.2"
|
||||
"@finos/perspective-viewer-d3fc" "^0.5.2"
|
||||
"@finos/perspective-viewer-datagrid" "^0.5.2"
|
||||
"@hediet/monaco-editor-react" "^0.1.1"
|
||||
"@hediet/semantic-json" "^0.3.14"
|
||||
"@hediet/semantic-json" "^0.3.15"
|
||||
"@hediet/std" "^0.6.0"
|
||||
classnames "^2.2.6"
|
||||
line-column "^1.0.2"
|
||||
@@ -576,12 +576,12 @@
|
||||
vis-network "^8.0.0"
|
||||
viz.js "^2.1.2"
|
||||
|
||||
"@hediet/visualization-core@^0.1.1":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-core/-/visualization-core-0.1.1.tgz#240b96ac48e24b7ae81f2b0360cebd57091eb600"
|
||||
integrity sha512-2WNP75TZg87bejt5XB+11jzbAoJ5EGbVrW+srSHRjUSdezZQ53x16E4XVV/q9W6HD6okTuz7wGjDrapNzK1EMw==
|
||||
"@hediet/visualization-core@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@hediet/visualization-core/-/visualization-core-0.2.0.tgz#8fca0044d5d91e8c45354984ae9c131ef7850a76"
|
||||
integrity sha512-r/SYDUipaUSdu1JXL1X0Le5ZkBXVGbKXo6jkX0jFq3NPz/60+B4MnIQnk8D7TCWLXftGFtE6MSFejrE+miup4w==
|
||||
dependencies:
|
||||
"@hediet/semantic-json" "^0.3.14"
|
||||
"@hediet/semantic-json" "^0.3.15"
|
||||
"@hediet/std" "^0.6.0"
|
||||
react "^16.12.0"
|
||||
react-dom "^16.12.0"
|
||||
|
||||
Reference in New Issue
Block a user