@power-maverick/tool-erd-generator 0.0.7 → 0.0.8
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/MIGRATION_COMPLETE.md +181 -0
- package/README.md +118 -359
- package/index.html +18 -1
- package/package.json +12 -27
- package/tsconfig.json +20 -15
- package/{webview/vite.config.ts → vite.config.ts} +1 -1
- package/CONVERSION_SUMMARY.md +0 -288
- package/REFACTORING_COMPLETE.md +0 -352
- package/TYPESCRIPT_NOTES.md +0 -57
- package/dist/src/components/ERDGenerator.d.ts +0 -44
- package/dist/src/components/ERDGenerator.d.ts.map +0 -1
- package/dist/src/components/ERDGenerator.js +0 -232
- package/dist/src/components/ERDGenerator.js.map +0 -1
- package/dist/src/dvdtIntegration/integration.d.ts +0 -47
- package/dist/src/dvdtIntegration/integration.d.ts.map +0 -1
- package/dist/src/dvdtIntegration/integration.js +0 -223
- package/dist/src/dvdtIntegration/integration.js.map +0 -1
- package/dist/src/index.d.ts +0 -6
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -26
- package/dist/src/index.js.map +0 -1
- package/dist/src/models/interfaces.d.ts +0 -84
- package/dist/src/models/interfaces.d.ts.map +0 -1
- package/dist/src/models/interfaces.js +0 -3
- package/dist/src/models/interfaces.js.map +0 -1
- package/dist/src/models/platformApi.d.ts +0 -92
- package/dist/src/models/platformApi.d.ts.map +0 -1
- package/dist/src/models/platformApi.js +0 -213
- package/dist/src/models/platformApi.js.map +0 -1
- package/dist/src/utils/Constants.d.ts +0 -3
- package/dist/src/utils/Constants.d.ts.map +0 -1
- package/dist/src/utils/Constants.js +0 -6
- package/dist/src/utils/Constants.js.map +0 -1
- package/dist/src/utils/DataverseClient.d.ts +0 -53
- package/dist/src/utils/DataverseClient.d.ts.map +0 -1
- package/dist/src/utils/DataverseClient.js +0 -236
- package/dist/src/utils/DataverseClient.js.map +0 -1
- package/dist/webview/index.css +0 -1
- package/dist/webview/index.html +0 -13
- package/dist/webview/index.js +0 -49
- package/tsconfig.webview.json +0 -24
- package/ui/test.html +0 -326
- package/webview/App.tsx +0 -412
- package/webview/index.html +0 -12
- package/webview/main.tsx +0 -10
- package/webview/styles.css +0 -288
- package/webview/tsconfig.json +0 -35
- /package/{webview/tsconfig.node.json → tsconfig.node.json} +0 -0
package/webview/App.tsx
DELETED
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import { ERDGenerator } from "../src/components/ERDGenerator";
|
|
3
|
-
import { DataverseSolution } from "../src/models/interfaces";
|
|
4
|
-
import { IPlatformAPI, PlatformAPIFactory, ToolContext } from "../src/models/platformApi";
|
|
5
|
-
import "../src/types/pptb.d.ts";
|
|
6
|
-
import { DataverseClient } from "../src/utils/DataverseClient";
|
|
7
|
-
|
|
8
|
-
// Declare Mermaid library (loaded externally for visualization)
|
|
9
|
-
declare global {
|
|
10
|
-
interface Window {
|
|
11
|
-
mermaid?: {
|
|
12
|
-
initialize: (config: any) => void;
|
|
13
|
-
init: (config: any, element: HTMLElement | null) => void;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Solution {
|
|
19
|
-
uniqueName: string;
|
|
20
|
-
displayName: string;
|
|
21
|
-
version: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function App() {
|
|
25
|
-
// Platform API - automatically detects PPTB or DVDT
|
|
26
|
-
const [platformAPI, setPlatformAPI] = useState<IPlatformAPI | null>(null);
|
|
27
|
-
const [platformName, setPlatformName] = useState<string>("");
|
|
28
|
-
|
|
29
|
-
const [connectionUrl, setConnectionUrl] = useState<string>("");
|
|
30
|
-
const [accessToken, setAccessToken] = useState<string>("");
|
|
31
|
-
const [solutions, setSolutions] = useState<Solution[]>([]);
|
|
32
|
-
const [selectedSolution, setSelectedSolution] = useState<string>("");
|
|
33
|
-
const [selectedFormat, setSelectedFormat] = useState<'mermaid' | 'plantuml' | 'graphviz'>('mermaid');
|
|
34
|
-
const [loading, setLoading] = useState<boolean>(true);
|
|
35
|
-
const [error, setError] = useState<string>("");
|
|
36
|
-
const [generatedDiagram, setGeneratedDiagram] = useState<string>("");
|
|
37
|
-
const [viewMode, setViewMode] = useState<'visual' | 'text'>('visual');
|
|
38
|
-
|
|
39
|
-
// Configuration options
|
|
40
|
-
const [includeAttributes, setIncludeAttributes] = useState<boolean>(true);
|
|
41
|
-
const [includeRelationships, setIncludeRelationships] = useState<boolean>(true);
|
|
42
|
-
const [maxAttributesPerTable, setMaxAttributesPerTable] = useState<number>(10);
|
|
43
|
-
|
|
44
|
-
// Initialize platform API and detect environment
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
const initializePlatform = async () => {
|
|
47
|
-
setLoading(true);
|
|
48
|
-
|
|
49
|
-
// For PPTB: Listen for TOOLBOX_CONTEXT via postMessage
|
|
50
|
-
if (PlatformAPIFactory.isPPTB()) {
|
|
51
|
-
const handlePPTBMessage = (event: MessageEvent) => {
|
|
52
|
-
if (event.data && event.data.type === 'TOOLBOX_CONTEXT') {
|
|
53
|
-
window.TOOLBOX_CONTEXT = event.data.data;
|
|
54
|
-
console.log('[App] Received TOOLBOX_CONTEXT:', window.TOOLBOX_CONTEXT);
|
|
55
|
-
|
|
56
|
-
// Re-create platform API with updated context
|
|
57
|
-
const api = PlatformAPIFactory.create();
|
|
58
|
-
setPlatformAPI(api);
|
|
59
|
-
setPlatformName(api.getPlatformName());
|
|
60
|
-
|
|
61
|
-
// Load context
|
|
62
|
-
loadPlatformContext(api);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
window.addEventListener('message', handlePPTBMessage);
|
|
67
|
-
|
|
68
|
-
// Try to create API immediately (context might already be available)
|
|
69
|
-
try {
|
|
70
|
-
const api = PlatformAPIFactory.create();
|
|
71
|
-
setPlatformAPI(api);
|
|
72
|
-
setPlatformName(api.getPlatformName());
|
|
73
|
-
await loadPlatformContext(api);
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.error('[App] Failed to create PPTB API:', error);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return () => {
|
|
79
|
-
window.removeEventListener('message', handlePPTBMessage);
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
// For DVDT: Listen for credentials message
|
|
83
|
-
else {
|
|
84
|
-
const handleDVDTMessage = (event: MessageEvent) => {
|
|
85
|
-
const message = event.data;
|
|
86
|
-
if (message.command === 'setCredentials') {
|
|
87
|
-
console.log('[App] Received DVDT credentials');
|
|
88
|
-
|
|
89
|
-
const context: ToolContext = {
|
|
90
|
-
toolId: null,
|
|
91
|
-
connectionUrl: message.environmentUrl,
|
|
92
|
-
accessToken: message.accessToken,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const api = PlatformAPIFactory.create(context);
|
|
96
|
-
setPlatformAPI(api);
|
|
97
|
-
setPlatformName(api.getPlatformName());
|
|
98
|
-
|
|
99
|
-
setConnectionUrl(message.environmentUrl);
|
|
100
|
-
setAccessToken(message.accessToken);
|
|
101
|
-
setLoading(false);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
window.addEventListener('message', handleDVDTMessage);
|
|
106
|
-
|
|
107
|
-
// Signal to DVDT that we're ready
|
|
108
|
-
console.log('[App] Running in DVDT mode, waiting for credentials...');
|
|
109
|
-
|
|
110
|
-
return () => {
|
|
111
|
-
window.removeEventListener('message', handleDVDTMessage);
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
initializePlatform();
|
|
117
|
-
}, []);
|
|
118
|
-
|
|
119
|
-
// Helper to load context from platform API
|
|
120
|
-
const loadPlatformContext = async (api: IPlatformAPI) => {
|
|
121
|
-
try {
|
|
122
|
-
const context = await api.getToolContext();
|
|
123
|
-
console.log('[App] Loaded platform context:', context);
|
|
124
|
-
|
|
125
|
-
setConnectionUrl(context.connectionUrl || "");
|
|
126
|
-
setAccessToken(context.accessToken || "");
|
|
127
|
-
setLoading(false);
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error('[App] Failed to load platform context:', error);
|
|
130
|
-
showError('Failed to load connection context');
|
|
131
|
-
setLoading(false);
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Load solutions when credentials are available
|
|
136
|
-
useEffect(() => {
|
|
137
|
-
if (connectionUrl && accessToken) {
|
|
138
|
-
loadSolutions();
|
|
139
|
-
}
|
|
140
|
-
}, [connectionUrl, accessToken]);
|
|
141
|
-
|
|
142
|
-
const loadSolutions = async () => {
|
|
143
|
-
try {
|
|
144
|
-
const client = new DataverseClient({
|
|
145
|
-
environmentUrl: connectionUrl,
|
|
146
|
-
accessToken: accessToken
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const solutionList = await client.listSolutions();
|
|
150
|
-
setSolutions(solutionList);
|
|
151
|
-
} catch (error: any) {
|
|
152
|
-
showError(`Failed to load solutions: ${error.message}`);
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const showError = (message: string) => {
|
|
157
|
-
setError(message);
|
|
158
|
-
setTimeout(() => setError(""), 5000);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const showNotification = async (title: string, body: string, type: 'success' | 'error' | 'info') => {
|
|
162
|
-
if (platformAPI) {
|
|
163
|
-
await platformAPI.showNotification({ title, body, type });
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
const handleGenerateERD = async () => {
|
|
168
|
-
if (!selectedSolution) {
|
|
169
|
-
showError('Please select a solution first');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
setLoading(true);
|
|
175
|
-
|
|
176
|
-
const client = new DataverseClient({
|
|
177
|
-
environmentUrl: connectionUrl,
|
|
178
|
-
accessToken: accessToken
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
const solution: DataverseSolution = await client.fetchSolution(selectedSolution);
|
|
182
|
-
|
|
183
|
-
const generator = new ERDGenerator({
|
|
184
|
-
format: selectedFormat,
|
|
185
|
-
includeAttributes,
|
|
186
|
-
includeRelationships,
|
|
187
|
-
maxAttributesPerTable
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const diagram = generator.generate(solution);
|
|
191
|
-
setGeneratedDiagram(diagram);
|
|
192
|
-
|
|
193
|
-
await showNotification('Success', 'ERD generated successfully', 'success');
|
|
194
|
-
} catch (error: any) {
|
|
195
|
-
showError(`Failed to generate ERD: ${error.message}`);
|
|
196
|
-
} finally {
|
|
197
|
-
setLoading(false);
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const handleDownload = async () => {
|
|
202
|
-
if (!generatedDiagram || !platformAPI) return;
|
|
203
|
-
|
|
204
|
-
const extensions: Record<string, string> = {
|
|
205
|
-
'mermaid': 'mmd',
|
|
206
|
-
'plantuml': 'puml',
|
|
207
|
-
'graphviz': 'dot'
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
const fileName = `${selectedSolution}-erd.${extensions[selectedFormat]}`;
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
const savedPath = await platformAPI.saveFile(fileName, generatedDiagram);
|
|
214
|
-
if (savedPath) {
|
|
215
|
-
await showNotification('Success', 'File saved successfully', 'success');
|
|
216
|
-
}
|
|
217
|
-
} catch (error: any) {
|
|
218
|
-
showError(`Failed to save file: ${error.message}`);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const handleCopyToClipboard = async () => {
|
|
223
|
-
if (!generatedDiagram || !platformAPI) return;
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
await platformAPI.copyToClipboard(generatedDiagram);
|
|
227
|
-
await showNotification('Success', 'Copied to clipboard', 'success');
|
|
228
|
-
} catch (error: any) {
|
|
229
|
-
showError(`Failed to copy: ${error.message}`);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const renderDiagram = () => {
|
|
234
|
-
if (!generatedDiagram) return null;
|
|
235
|
-
|
|
236
|
-
if (viewMode === 'visual' && selectedFormat === 'mermaid') {
|
|
237
|
-
// Render Mermaid diagram
|
|
238
|
-
return (
|
|
239
|
-
<div
|
|
240
|
-
className="mermaid-container"
|
|
241
|
-
dangerouslySetInnerHTML={{ __html: generatedDiagram }}
|
|
242
|
-
ref={(el) => {
|
|
243
|
-
if (el && window.mermaid) {
|
|
244
|
-
try {
|
|
245
|
-
window.mermaid.init(undefined, el);
|
|
246
|
-
} catch (error) {
|
|
247
|
-
console.error('Mermaid rendering error:', error);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}}
|
|
251
|
-
/>
|
|
252
|
-
);
|
|
253
|
-
} else {
|
|
254
|
-
// Show text view
|
|
255
|
-
return (
|
|
256
|
-
<pre className="diagram-text">
|
|
257
|
-
{generatedDiagram}
|
|
258
|
-
</pre>
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
if (loading) {
|
|
264
|
-
return (
|
|
265
|
-
<div className="container">
|
|
266
|
-
<div className="loading">Loading...</div>
|
|
267
|
-
</div>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return (
|
|
272
|
-
<div className="container">
|
|
273
|
-
<header className="header">
|
|
274
|
-
<h1>🗺️ Dataverse ERD Generator</h1>
|
|
275
|
-
<p>Generate Entity Relationship Diagrams from your Dataverse solutions</p>
|
|
276
|
-
</header>
|
|
277
|
-
|
|
278
|
-
{error && (
|
|
279
|
-
<div className="error">
|
|
280
|
-
{error}
|
|
281
|
-
</div>
|
|
282
|
-
)}
|
|
283
|
-
|
|
284
|
-
<div className="card">
|
|
285
|
-
<div className="info-message">
|
|
286
|
-
<strong>✓ Connected to Dataverse</strong><br />
|
|
287
|
-
Environment: <span>{connectionUrl || "Not connected"}</span>
|
|
288
|
-
</div>
|
|
289
|
-
|
|
290
|
-
<div className="form-group">
|
|
291
|
-
<label htmlFor="solutionSelect">Select a Solution</label>
|
|
292
|
-
<select
|
|
293
|
-
id="solutionSelect"
|
|
294
|
-
value={selectedSolution}
|
|
295
|
-
onChange={(e) => setSelectedSolution(e.target.value)}
|
|
296
|
-
disabled={solutions.length === 0}
|
|
297
|
-
>
|
|
298
|
-
<option value="">-- Select a Solution --</option>
|
|
299
|
-
{solutions.map((solution) => (
|
|
300
|
-
<option key={solution.uniqueName} value={solution.uniqueName}>
|
|
301
|
-
{solution.displayName} ({solution.version})
|
|
302
|
-
</option>
|
|
303
|
-
))}
|
|
304
|
-
</select>
|
|
305
|
-
</div>
|
|
306
|
-
|
|
307
|
-
<div className="generate-section">
|
|
308
|
-
<h2>Generate ERD</h2>
|
|
309
|
-
<div className="format-selector">
|
|
310
|
-
<label style={{ fontWeight: 600, marginRight: '10px' }}>Output Format:</label>
|
|
311
|
-
<button
|
|
312
|
-
className={`format-btn ${selectedFormat === 'mermaid' ? 'active' : ''}`}
|
|
313
|
-
onClick={() => setSelectedFormat('mermaid')}
|
|
314
|
-
>
|
|
315
|
-
Mermaid
|
|
316
|
-
</button>
|
|
317
|
-
<button
|
|
318
|
-
className={`format-btn ${selectedFormat === 'plantuml' ? 'active' : ''}`}
|
|
319
|
-
onClick={() => setSelectedFormat('plantuml')}
|
|
320
|
-
>
|
|
321
|
-
PlantUML
|
|
322
|
-
</button>
|
|
323
|
-
<button
|
|
324
|
-
className={`format-btn ${selectedFormat === 'graphviz' ? 'active' : ''}`}
|
|
325
|
-
onClick={() => setSelectedFormat('graphviz')}
|
|
326
|
-
>
|
|
327
|
-
Graphviz
|
|
328
|
-
</button>
|
|
329
|
-
</div>
|
|
330
|
-
|
|
331
|
-
<div className="config-section">
|
|
332
|
-
<h3>Configuration</h3>
|
|
333
|
-
|
|
334
|
-
<div className="config-group">
|
|
335
|
-
<label>
|
|
336
|
-
<input
|
|
337
|
-
type="checkbox"
|
|
338
|
-
checked={includeAttributes}
|
|
339
|
-
onChange={(e) => setIncludeAttributes(e.target.checked)}
|
|
340
|
-
/>
|
|
341
|
-
<span>Include Attributes</span>
|
|
342
|
-
</label>
|
|
343
|
-
<div className="config-help">Show table columns/fields in the diagram</div>
|
|
344
|
-
</div>
|
|
345
|
-
|
|
346
|
-
<div className="config-group">
|
|
347
|
-
<label>
|
|
348
|
-
<input
|
|
349
|
-
type="checkbox"
|
|
350
|
-
checked={includeRelationships}
|
|
351
|
-
onChange={(e) => setIncludeRelationships(e.target.checked)}
|
|
352
|
-
/>
|
|
353
|
-
<span>Include Relationships</span>
|
|
354
|
-
</label>
|
|
355
|
-
<div className="config-help">Show relationships between tables</div>
|
|
356
|
-
</div>
|
|
357
|
-
|
|
358
|
-
<div className="config-group">
|
|
359
|
-
<div className="config-label-group">
|
|
360
|
-
<label htmlFor="maxAttributesInput">Max Attributes per Table:</label>
|
|
361
|
-
<input
|
|
362
|
-
type="number"
|
|
363
|
-
id="maxAttributesInput"
|
|
364
|
-
value={maxAttributesPerTable}
|
|
365
|
-
onChange={(e) => setMaxAttributesPerTable(parseInt(e.target.value) || 0)}
|
|
366
|
-
min="0"
|
|
367
|
-
max="100"
|
|
368
|
-
/>
|
|
369
|
-
</div>
|
|
370
|
-
<div className="config-help">Maximum number of attributes to display per table (0 = show all)</div>
|
|
371
|
-
</div>
|
|
372
|
-
</div>
|
|
373
|
-
|
|
374
|
-
<button
|
|
375
|
-
className="btn btn-primary"
|
|
376
|
-
onClick={handleGenerateERD}
|
|
377
|
-
disabled={!selectedSolution || loading}
|
|
378
|
-
>
|
|
379
|
-
Generate ERD
|
|
380
|
-
</button>
|
|
381
|
-
</div>
|
|
382
|
-
</div>
|
|
383
|
-
|
|
384
|
-
{generatedDiagram && (
|
|
385
|
-
<div className="card">
|
|
386
|
-
<h2>Generated ERD</h2>
|
|
387
|
-
<div className="diagram-controls">
|
|
388
|
-
{selectedFormat === 'mermaid' && (
|
|
389
|
-
<button
|
|
390
|
-
className="btn btn-secondary"
|
|
391
|
-
onClick={() => setViewMode(viewMode === 'visual' ? 'text' : 'visual')}
|
|
392
|
-
>
|
|
393
|
-
{viewMode === 'visual' ? '📝 Show Text' : '🎨 Show Visual'}
|
|
394
|
-
</button>
|
|
395
|
-
)}
|
|
396
|
-
<button className="btn btn-secondary" onClick={handleDownload}>
|
|
397
|
-
📥 Download Source
|
|
398
|
-
</button>
|
|
399
|
-
<button className="btn btn-secondary" onClick={handleCopyToClipboard}>
|
|
400
|
-
📋 Copy to Clipboard
|
|
401
|
-
</button>
|
|
402
|
-
</div>
|
|
403
|
-
<div className="diagram-container">
|
|
404
|
-
{renderDiagram()}
|
|
405
|
-
</div>
|
|
406
|
-
</div>
|
|
407
|
-
)}
|
|
408
|
-
</div>
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
export default App;
|
package/webview/index.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Dataverse ERD Generator</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
package/webview/main.tsx
DELETED