ts-prorm-orm 1.2.2 → 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/CHANGELOG.md +209 -0
- package/README.md +32 -24
- package/dist/compliance/index.d.ts +2 -0
- package/dist/compliance/index.js +13 -1
- package/dist/connection-manager.js +13 -1
- package/dist/core/expressions.d.ts +58 -0
- package/dist/core/expressions.js +83 -0
- package/dist/core/hooks-manager.d.ts +179 -0
- package/dist/core/hooks-manager.js +291 -0
- package/dist/core/types.d.ts +235 -0
- package/dist/core/types.js +9 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +11 -1
- package/dist/diagrams/chen-diagram.d.ts +15 -12
- package/dist/diagrams/chen-diagram.js +76 -85
- package/dist/diagrams/class-diagram.d.ts +9 -8
- package/dist/diagrams/class-diagram.js +60 -82
- package/dist/diagrams/core/index.d.ts +13 -0
- package/dist/diagrams/core/index.js +33 -0
- package/dist/diagrams/core/layout.d.ts +104 -0
- package/dist/diagrams/core/layout.js +223 -0
- package/dist/diagrams/{svg-dom.d.ts → core/svg.d.ts} +8 -20
- package/dist/diagrams/{svg-dom.js → core/svg.js} +21 -29
- package/dist/diagrams/core/theme.d.ts +84 -0
- package/dist/diagrams/core/theme.js +62 -0
- package/dist/diagrams/dependency-diagram.d.ts +15 -14
- package/dist/diagrams/dependency-diagram.js +71 -105
- package/dist/diagrams/er-diagram.d.ts +13 -26
- package/dist/diagrams/er-diagram.js +180 -259
- package/dist/diagrams/flow-diagram.d.ts +16 -11
- package/dist/diagrams/flow-diagram.js +96 -91
- package/dist/diagrams/gantt-diagram.d.ts +10 -5
- package/dist/diagrams/gantt-diagram.js +77 -103
- package/dist/diagrams/index-diagram.d.ts +10 -10
- package/dist/diagrams/index-diagram.js +106 -116
- package/dist/diagrams/index.d.ts +6 -9
- package/dist/diagrams/index.js +9 -24
- package/dist/diagrams/migration-diagram.d.ts +8 -4
- package/dist/diagrams/migration-diagram.js +189 -161
- package/dist/diagrams/model-diagram.d.ts +34 -34
- package/dist/diagrams/model-diagram.js +106 -485
- package/dist/diagrams/package-diagram.d.ts +9 -6
- package/dist/diagrams/package-diagram.js +64 -101
- package/dist/diagrams/relational-diagram.d.ts +12 -10
- package/dist/diagrams/relational-diagram.js +87 -114
- package/dist/diagrams/schemadoc-diagram.d.ts +8 -8
- package/dist/diagrams/schemadoc-diagram.js +71 -93
- package/dist/diagrams/sequence-diagram.d.ts +10 -10
- package/dist/diagrams/sequence-diagram.js +64 -89
- package/dist/diagrams/state-diagram.d.ts +10 -9
- package/dist/diagrams/state-diagram.js +69 -92
- package/dist/diagrams/tree-diagram.d.ts +11 -5
- package/dist/diagrams/tree-diagram.js +41 -65
- package/dist/dialects/clickhouse/index.js +15 -0
- package/dist/dialects/cockroachdb/index.d.ts +1 -1
- package/dist/dialects/cockroachdb/index.js +32 -63
- package/dist/dialects/db2/index.js +23 -8
- package/dist/dialects/dialect.d.ts +29 -5
- package/dist/dialects/duckdb/index.js +9 -2
- package/dist/dialects/hana/index.js +22 -5
- package/dist/dialects/lock-clause-helper.d.ts +167 -0
- package/dist/dialects/lock-clause-helper.js +316 -0
- package/dist/dialects/mariadb/index.d.ts +7 -4
- package/dist/dialects/mariadb/index.js +36 -60
- package/dist/dialects/mssql/index.js +34 -3
- package/dist/dialects/mysql/index.d.ts +1 -1
- package/dist/dialects/mysql/index.js +17 -45
- package/dist/dialects/oracle/index.d.ts +14 -3
- package/dist/dialects/oracle/index.js +66 -36
- package/dist/dialects/order-expression-helper.d.ts +72 -0
- package/dist/dialects/order-expression-helper.js +154 -0
- package/dist/dialects/partial-index.d.ts +52 -0
- package/dist/dialects/partial-index.js +251 -0
- package/dist/dialects/postgres/index.d.ts +1 -1
- package/dist/dialects/postgres/index.js +43 -68
- package/dist/dialects/redshift/index.js +9 -2
- package/dist/dialects/snowflake/index.js +17 -0
- package/dist/dialects/spanner/index.js +7 -2
- package/dist/dialects/sqlite/driver.d.ts +55 -0
- package/dist/dialects/sqlite/driver.js +102 -0
- package/dist/dialects/sqlite/index.d.ts +1 -1
- package/dist/dialects/sqlite/index.js +49 -27
- package/dist/graph/base-graph-dialect.d.ts +24 -3
- package/dist/graph/base-graph-dialect.js +43 -2
- package/dist/graph/dgraph/index.js +6 -0
- package/dist/graph/gremlin/index.d.ts +6 -0
- package/dist/graph/gremlin/index.js +12 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/neo4j/index.d.ts +2 -0
- package/dist/graph/neo4j/index.js +6 -1
- package/dist/graph/types.d.ts +12 -1
- package/dist/index.d.ts +13 -4
- package/dist/index.js +54 -7
- package/dist/logging/index.d.ts +10 -0
- package/dist/logging/index.js +23 -0
- package/dist/logging/query-logging.d.ts +82 -0
- package/dist/logging/query-logging.js +102 -0
- package/dist/models/decorators.d.ts +39 -3
- package/dist/models/decorators.js +74 -0
- package/dist/models/eager-load.d.ts +99 -0
- package/dist/models/eager-load.js +348 -5
- package/dist/models/indexes.d.ts +3 -2
- package/dist/models/indexes.js +7 -1
- package/dist/models/model.js +69 -43
- package/dist/prorm.d.ts +175 -409
- package/dist/prorm.js +1142 -557
- package/dist/query-builders/order-limit-builder.js +12 -10
- package/dist/query-builders/sql-compiler.d.ts +10 -3
- package/dist/query-builders/sql-compiler.js +14 -43
- package/dist/query-interface.d.ts +2 -1
- package/dist/sql-constants.d.ts +44 -0
- package/dist/sql-constants.js +138 -1
- package/dist/types/index.d.ts +53 -17
- package/dist/types/index.js +10 -15
- package/dist/types/query-types.d.ts +26 -0
- package/dist/types/query-types.js +30 -0
- package/package.json +32 -5
- package/dist/decorators/belongs-to-many.js +0 -115
- package/dist/decorators/belongs-to.js +0 -115
- package/dist/decorators/has-many.js +0 -127
- package/dist/decorators/has-one.js +0 -116
- package/dist/diagrams/palette.d.ts +0 -138
- package/dist/diagrams/palette.js +0 -194
- package/dist/hooks/hooks-manager.d.ts +0 -189
- package/dist/hooks/hooks-manager.js +0 -350
- package/dist/hooks/index.d.ts +0 -8
- package/dist/hooks/index.js +0 -37
- /package/dist/{logging.d.ts → logging/logger.d.ts} +0 -0
- /package/dist/{logging.js → logging/logger.js} +0 -0
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* StateDiagram
|
|
2
|
+
* StateDiagram - generates a UML state-machine diagram as an SVG.
|
|
3
3
|
*
|
|
4
4
|
* Renders:
|
|
5
|
-
* - normal states as
|
|
6
|
-
* - an initial pseudo-state as a filled
|
|
5
|
+
* - normal states as square hairline boxes
|
|
6
|
+
* - an initial pseudo-state as a filled circle
|
|
7
7
|
* - a final state as a ringed (double) circle
|
|
8
8
|
* - transitions as arrows between states, labeled with their trigger/event
|
|
9
9
|
* - self-transitions as loop arrows
|
|
10
10
|
*
|
|
11
|
-
* Great for showing a record lifecycle (e.g. draft
|
|
11
|
+
* Great for showing a record lifecycle (e.g. draft -> active -> archived ->
|
|
12
12
|
* deleted). States are auto-placed left-to-right in a wrapping grid unless
|
|
13
13
|
* explicit x/y hints are supplied per state. The SVG viewBox is auto-sized.
|
|
14
14
|
*
|
|
15
|
-
* Both `--theme light|dark` and `--color <name|hex>` genuinely change the
|
|
16
|
-
* output: the accent drives state fills + arrowheads, the theme drives the
|
|
17
|
-
* canvas background, text and borders.
|
|
18
|
-
*
|
|
19
15
|
* Usage:
|
|
20
16
|
* const diag = new StateDiagram({
|
|
21
17
|
* states: [
|
|
@@ -56,8 +52,12 @@ export interface StateDiagramOptions {
|
|
|
56
52
|
states: StateNode[];
|
|
57
53
|
transitions: StateTransition[];
|
|
58
54
|
title?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Accepted for backward compatibility and ignored: diagrams render in a
|
|
57
|
+
* single monochrome theme.
|
|
58
|
+
*/
|
|
59
59
|
theme?: 'light' | 'dark';
|
|
60
|
-
/**
|
|
60
|
+
/** Accepted for backward compatibility and ignored. */
|
|
61
61
|
color?: string;
|
|
62
62
|
}
|
|
63
63
|
export declare class StateDiagram {
|
|
@@ -74,6 +74,7 @@ export declare class StateDiagram {
|
|
|
74
74
|
constructor(options: StateDiagramOptions);
|
|
75
75
|
private layout;
|
|
76
76
|
private edgePoint;
|
|
77
|
+
private build;
|
|
77
78
|
generate(): string;
|
|
78
79
|
private drawTransition;
|
|
79
80
|
private drawSelfLoop;
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* StateDiagram
|
|
3
|
+
* StateDiagram - generates a UML state-machine diagram as an SVG.
|
|
4
4
|
*
|
|
5
5
|
* Renders:
|
|
6
|
-
* - normal states as
|
|
7
|
-
* - an initial pseudo-state as a filled
|
|
6
|
+
* - normal states as square hairline boxes
|
|
7
|
+
* - an initial pseudo-state as a filled circle
|
|
8
8
|
* - a final state as a ringed (double) circle
|
|
9
9
|
* - transitions as arrows between states, labeled with their trigger/event
|
|
10
10
|
* - self-transitions as loop arrows
|
|
11
11
|
*
|
|
12
|
-
* Great for showing a record lifecycle (e.g. draft
|
|
12
|
+
* Great for showing a record lifecycle (e.g. draft -> active -> archived ->
|
|
13
13
|
* deleted). States are auto-placed left-to-right in a wrapping grid unless
|
|
14
14
|
* explicit x/y hints are supplied per state. The SVG viewBox is auto-sized.
|
|
15
15
|
*
|
|
16
|
-
* Both `--theme light|dark` and `--color <name|hex>` genuinely change the
|
|
17
|
-
* output: the accent drives state fills + arrowheads, the theme drives the
|
|
18
|
-
* canvas background, text and borders.
|
|
19
|
-
*
|
|
20
16
|
* Usage:
|
|
21
17
|
* const diag = new StateDiagram({
|
|
22
18
|
* states: [
|
|
@@ -73,18 +69,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
73
69
|
exports.StateDiagram = void 0;
|
|
74
70
|
const fs = __importStar(require("fs"));
|
|
75
71
|
const path = __importStar(require("path"));
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
//
|
|
80
|
-
// corners are fixed by the Industry spec; accent + surface colours still route
|
|
81
|
-
// through resolveAccent / resolveTheme so --color / --theme keep working.
|
|
82
|
-
const IND_FONT_HEAD = '"Barlow Condensed", system-ui, sans-serif';
|
|
83
|
-
const IND_FONT_BODY = '"Barlow", system-ui, sans-serif';
|
|
84
|
-
const IND_FONT_MONO = 'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace';
|
|
85
|
-
const IND_HAIRLINE = 'rgba(29,31,32,0.16)';
|
|
86
|
-
const IND_FONT_IMPORT = "@import url('https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;700&family=Barlow+Condensed:wght@400;600&family=JetBrains+Mono&display=swap');";
|
|
87
|
-
// ─── Main class ────────────────────────────────────────────────────────────
|
|
72
|
+
const svg_1 = require("./core/svg");
|
|
73
|
+
const theme_1 = require("./core/theme");
|
|
74
|
+
const layout_1 = require("./core/layout");
|
|
75
|
+
// -- Main class ----------------------------------------------------------------
|
|
88
76
|
class StateDiagram {
|
|
89
77
|
constructor(options) {
|
|
90
78
|
this.opts = {
|
|
@@ -95,7 +83,7 @@ class StateDiagram {
|
|
|
95
83
|
color: options.color ?? '',
|
|
96
84
|
};
|
|
97
85
|
}
|
|
98
|
-
//
|
|
86
|
+
// -- Auto-place states in a wrapping row/grid (honoring x/y hints) ---------
|
|
99
87
|
layout() {
|
|
100
88
|
const { states } = this.opts;
|
|
101
89
|
const PAD = StateDiagram.PAD;
|
|
@@ -131,7 +119,7 @@ class StateDiagram {
|
|
|
131
119
|
const height = maxY + PAD;
|
|
132
120
|
return { placed, width, height };
|
|
133
121
|
}
|
|
134
|
-
//
|
|
122
|
+
// -- Compute a point on the box perimeter toward a target point ------------
|
|
135
123
|
edgePoint(p, tx, ty) {
|
|
136
124
|
const dx = tx - p.cx;
|
|
137
125
|
const dy = ty - p.cy;
|
|
@@ -142,109 +130,94 @@ class StateDiagram {
|
|
|
142
130
|
const s = Math.min(scaleX, scaleY);
|
|
143
131
|
return { x: p.cx + dx * s, y: p.cy + dy * s };
|
|
144
132
|
}
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
const { title
|
|
148
|
-
const accent = (0, palette_1.resolveAccent)(color);
|
|
149
|
-
const t = (0, palette_1.resolveTheme)(theme);
|
|
133
|
+
// -- Build the SVG document -------------------------------------------------
|
|
134
|
+
build() {
|
|
135
|
+
const { title } = this.opts;
|
|
150
136
|
const { placed, width, height } = this.layout();
|
|
151
137
|
const PAD = StateDiagram.PAD;
|
|
152
|
-
const
|
|
153
|
-
const doc = new svg_dom_1.SvgDocument(width, height);
|
|
138
|
+
const doc = new svg_1.SvgDocument(width, height);
|
|
154
139
|
const svg = doc.root;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// ── Defs: arrowhead marker (solid accent states — no gradient) ──────
|
|
159
|
-
const marker = doc.defs().child('marker', {
|
|
160
|
-
id: 'arrow',
|
|
161
|
-
markerWidth: 10,
|
|
162
|
-
markerHeight: 10,
|
|
163
|
-
refX: 8,
|
|
164
|
-
refY: 3,
|
|
165
|
-
orient: 'auto',
|
|
166
|
-
markerUnits: 'strokeWidth',
|
|
167
|
-
});
|
|
168
|
-
marker.child('path', { d: 'M0,0 L8,3 L0,6 Z', fill: accent.mid });
|
|
169
|
-
// ── Title ───────────────────────────────────────────────────────────
|
|
140
|
+
svg.child('rect', { width, height, fill: theme_1.INK.ground });
|
|
141
|
+
// Defs: the single shared arrowhead marker.
|
|
142
|
+
(0, layout_1.defineArrowhead)(doc.defs(), 'arrow');
|
|
170
143
|
if (title) {
|
|
171
144
|
svg
|
|
172
145
|
.child('text', {
|
|
173
146
|
x: PAD,
|
|
174
147
|
y: PAD + 8,
|
|
175
|
-
'font-family':
|
|
176
|
-
'font-size':
|
|
177
|
-
'font-weight':
|
|
178
|
-
fill:
|
|
148
|
+
'font-family': theme_1.FONT.family,
|
|
149
|
+
'font-size': theme_1.FONT.title + 9,
|
|
150
|
+
'font-weight': 'bold',
|
|
151
|
+
fill: theme_1.INK.line,
|
|
179
152
|
})
|
|
180
153
|
.text(title);
|
|
181
154
|
}
|
|
182
|
-
//
|
|
155
|
+
// Transitions, drawn first so states sit on top.
|
|
183
156
|
this.opts.transitions.forEach((tr) => {
|
|
184
157
|
const from = placed.get(tr.from);
|
|
185
158
|
const to = placed.get(tr.to);
|
|
186
159
|
if (!from || !to)
|
|
187
160
|
return;
|
|
188
161
|
if (tr.from === tr.to) {
|
|
189
|
-
this.drawSelfLoop(svg, from, tr.event
|
|
162
|
+
this.drawSelfLoop(svg, from, tr.event);
|
|
190
163
|
}
|
|
191
164
|
else {
|
|
192
|
-
this.drawTransition(svg, from, to, tr.event
|
|
165
|
+
this.drawTransition(svg, from, to, tr.event);
|
|
193
166
|
}
|
|
194
167
|
});
|
|
195
|
-
//
|
|
168
|
+
// States.
|
|
196
169
|
placed.forEach((p) => {
|
|
197
170
|
const node = p.node;
|
|
198
171
|
if (node.initial) {
|
|
199
|
-
// Filled
|
|
172
|
+
// Filled circle (initial pseudo-state).
|
|
200
173
|
svg.child('circle', {
|
|
201
174
|
cx: p.cx,
|
|
202
175
|
cy: p.cy,
|
|
203
176
|
r: StateDiagram.PSEUDO_R,
|
|
204
|
-
fill:
|
|
205
|
-
stroke:
|
|
206
|
-
'stroke-width':
|
|
177
|
+
fill: theme_1.INK.line,
|
|
178
|
+
stroke: theme_1.INK.line,
|
|
179
|
+
'stroke-width': theme_1.STROKE.hair,
|
|
207
180
|
});
|
|
208
181
|
}
|
|
209
182
|
else if (node.final) {
|
|
210
|
-
// Ringed circle (final state): outer ring + filled
|
|
183
|
+
// Ringed circle (final state): outer ring + filled core.
|
|
211
184
|
svg.child('circle', {
|
|
212
185
|
cx: p.cx,
|
|
213
186
|
cy: p.cy,
|
|
214
187
|
r: StateDiagram.PSEUDO_R,
|
|
215
188
|
fill: 'none',
|
|
216
|
-
stroke:
|
|
217
|
-
'stroke-width':
|
|
189
|
+
stroke: theme_1.INK.line,
|
|
190
|
+
'stroke-width': theme_1.STROKE.heavy,
|
|
218
191
|
});
|
|
219
192
|
svg.child('circle', {
|
|
220
193
|
cx: p.cx,
|
|
221
194
|
cy: p.cy,
|
|
222
195
|
r: StateDiagram.PSEUDO_R - 6,
|
|
223
|
-
fill:
|
|
196
|
+
fill: theme_1.INK.line,
|
|
224
197
|
});
|
|
225
198
|
}
|
|
226
199
|
else {
|
|
227
|
-
// Normal state: square box
|
|
200
|
+
// Normal state: square hairline box.
|
|
228
201
|
svg.child('rect', {
|
|
229
202
|
x: p.cx - p.hw,
|
|
230
203
|
y: p.cy - p.hh,
|
|
231
204
|
width: p.hw * 2,
|
|
232
205
|
height: p.hh * 2,
|
|
233
|
-
fill:
|
|
234
|
-
stroke:
|
|
235
|
-
'stroke-width':
|
|
206
|
+
fill: theme_1.INK.wash,
|
|
207
|
+
stroke: theme_1.INK.line,
|
|
208
|
+
'stroke-width': theme_1.STROKE.hair,
|
|
236
209
|
});
|
|
237
210
|
svg
|
|
238
211
|
.child('text', {
|
|
239
212
|
x: p.cx,
|
|
240
213
|
y: p.cy + 5,
|
|
241
|
-
'font-family':
|
|
242
|
-
'font-size':
|
|
243
|
-
'font-weight':
|
|
244
|
-
fill:
|
|
214
|
+
'font-family': theme_1.FONT.family,
|
|
215
|
+
'font-size': theme_1.FONT.body + 2,
|
|
216
|
+
'font-weight': 'bold',
|
|
217
|
+
fill: theme_1.INK.line,
|
|
245
218
|
'text-anchor': 'middle',
|
|
246
219
|
})
|
|
247
|
-
.text(node.name);
|
|
220
|
+
.text((0, layout_1.truncate)(node.name, p.hw * 2 - 12, theme_1.FONT.body + 2));
|
|
248
221
|
}
|
|
249
222
|
// Label markers (initial/final) beneath the circle.
|
|
250
223
|
if (node.initial || node.final) {
|
|
@@ -252,19 +225,22 @@ class StateDiagram {
|
|
|
252
225
|
.child('text', {
|
|
253
226
|
x: p.cx,
|
|
254
227
|
y: p.cy + StateDiagram.PSEUDO_R + 16,
|
|
255
|
-
'font-family':
|
|
256
|
-
'font-size':
|
|
257
|
-
'font-weight':
|
|
258
|
-
fill:
|
|
228
|
+
'font-family': theme_1.FONT.family,
|
|
229
|
+
'font-size': theme_1.FONT.small + 2,
|
|
230
|
+
'font-weight': 'bold',
|
|
231
|
+
fill: theme_1.INK.muted,
|
|
259
232
|
'text-anchor': 'middle',
|
|
260
233
|
})
|
|
261
234
|
.text(node.name);
|
|
262
235
|
}
|
|
263
236
|
});
|
|
264
|
-
return doc
|
|
237
|
+
return doc;
|
|
238
|
+
}
|
|
239
|
+
generate() {
|
|
240
|
+
return this.build().toString('Generated by ts-prorm-orm StateDiagram');
|
|
265
241
|
}
|
|
266
|
-
//
|
|
267
|
-
drawTransition(svg, from, to, event
|
|
242
|
+
// -- Draw a straight transition arrow with an optional event label ---------
|
|
243
|
+
drawTransition(svg, from, to, event) {
|
|
268
244
|
const start = this.edgePoint(from, to.cx, to.cy);
|
|
269
245
|
const end = this.edgePoint(to, from.cx, from.cy);
|
|
270
246
|
svg.child('line', {
|
|
@@ -272,18 +248,18 @@ class StateDiagram {
|
|
|
272
248
|
y1: start.y,
|
|
273
249
|
x2: end.x,
|
|
274
250
|
y2: end.y,
|
|
275
|
-
stroke,
|
|
276
|
-
'stroke-width':
|
|
251
|
+
stroke: theme_1.INK.line,
|
|
252
|
+
'stroke-width': theme_1.STROKE.heavy,
|
|
277
253
|
'marker-end': 'url(#arrow)',
|
|
278
254
|
});
|
|
279
255
|
if (event) {
|
|
280
256
|
const mx = (start.x + end.x) / 2;
|
|
281
257
|
const my = (start.y + end.y) / 2;
|
|
282
|
-
this.drawEventLabel(svg, mx, my - 6, event
|
|
258
|
+
this.drawEventLabel(svg, mx, my - 6, event);
|
|
283
259
|
}
|
|
284
260
|
}
|
|
285
|
-
//
|
|
286
|
-
drawSelfLoop(svg, p, event
|
|
261
|
+
// -- Draw a self-transition loop arrow above the state ---------------------
|
|
262
|
+
drawSelfLoop(svg, p, event) {
|
|
287
263
|
const topY = p.cy - p.hh;
|
|
288
264
|
const x1 = p.cx - p.hw * 0.4;
|
|
289
265
|
const x2 = p.cx + p.hw * 0.4;
|
|
@@ -292,33 +268,34 @@ class StateDiagram {
|
|
|
292
268
|
svg.child('path', {
|
|
293
269
|
d,
|
|
294
270
|
fill: 'none',
|
|
295
|
-
stroke,
|
|
296
|
-
'stroke-width':
|
|
271
|
+
stroke: theme_1.INK.line,
|
|
272
|
+
'stroke-width': theme_1.STROKE.heavy,
|
|
297
273
|
'marker-end': 'url(#arrow)',
|
|
298
274
|
});
|
|
299
275
|
if (event) {
|
|
300
|
-
this.drawEventLabel(svg, p.cx, topY - loopH - 4, event
|
|
276
|
+
this.drawEventLabel(svg, p.cx, topY - loopH - 4, event);
|
|
301
277
|
}
|
|
302
278
|
}
|
|
303
|
-
//
|
|
304
|
-
drawEventLabel(svg, x, y, event
|
|
279
|
+
// -- Draw an event label with a background pill for legibility -------------
|
|
280
|
+
drawEventLabel(svg, x, y, event) {
|
|
305
281
|
const w = event.length * 7 + 12;
|
|
306
282
|
svg.child('rect', {
|
|
307
283
|
x: x - w / 2,
|
|
308
284
|
y: y - 13,
|
|
309
285
|
width: w,
|
|
310
286
|
height: 18,
|
|
311
|
-
fill:
|
|
312
|
-
|
|
287
|
+
fill: theme_1.INK.ground,
|
|
288
|
+
stroke: theme_1.INK.line,
|
|
289
|
+
'stroke-width': theme_1.STROKE.hair,
|
|
313
290
|
});
|
|
314
291
|
svg
|
|
315
292
|
.child('text', {
|
|
316
293
|
x,
|
|
317
294
|
y: y - 1,
|
|
318
|
-
'font-family':
|
|
319
|
-
'font-size':
|
|
320
|
-
'font-weight':
|
|
321
|
-
fill:
|
|
295
|
+
'font-family': theme_1.FONT.mono,
|
|
296
|
+
'font-size': theme_1.FONT.small + 2,
|
|
297
|
+
'font-weight': 'bold',
|
|
298
|
+
fill: theme_1.INK.line,
|
|
322
299
|
'text-anchor': 'middle',
|
|
323
300
|
})
|
|
324
301
|
.text(event);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TreeDiagram
|
|
2
|
+
* TreeDiagram - renders a recursive hierarchical tree / mind-map as SVG.
|
|
3
3
|
*
|
|
4
4
|
* A `TreeNode` tree of arbitrary depth is laid out with a classic "tidy tree"
|
|
5
5
|
* algorithm: each node's cross-axis span equals the number of its leaf
|
|
6
6
|
* descendants, so siblings never overlap. The main axis is driven by depth
|
|
7
7
|
* (columns for `horizontal`, rows for `vertical`). Parents connect to children
|
|
8
|
-
* with
|
|
9
|
-
*
|
|
10
|
-
* light/dark theme.
|
|
8
|
+
* with cubic-Bezier <path> connectors. Nodes are square hairline label boxes;
|
|
9
|
+
* the root is distinguished with a heavier stroke and a wash fill.
|
|
11
10
|
*
|
|
12
11
|
* Usage:
|
|
13
12
|
* const diag = new TreeDiagram({ root: { name: 'db', children: [...] } });
|
|
14
13
|
* diag.save('./diagrams/tree.svg'); // or: diag.generate()
|
|
15
14
|
*/
|
|
15
|
+
import { measureText } from './core/layout';
|
|
16
16
|
export interface TreeNode {
|
|
17
17
|
name: string;
|
|
18
18
|
children?: TreeNode[];
|
|
@@ -21,7 +21,12 @@ export interface TreeNode {
|
|
|
21
21
|
export interface TreeDiagramOptions {
|
|
22
22
|
root: TreeNode;
|
|
23
23
|
title?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Accepted for backward compatibility and ignored: diagrams render in a
|
|
26
|
+
* single monochrome theme.
|
|
27
|
+
*/
|
|
24
28
|
theme?: 'light' | 'dark';
|
|
29
|
+
/** Accepted for backward compatibility and ignored. */
|
|
25
30
|
color?: string;
|
|
26
31
|
direction?: 'horizontal' | 'vertical';
|
|
27
32
|
}
|
|
@@ -29,7 +34,6 @@ export declare class TreeDiagram {
|
|
|
29
34
|
private opts;
|
|
30
35
|
private static readonly BOX_W;
|
|
31
36
|
private static readonly BOX_H;
|
|
32
|
-
private static readonly R;
|
|
33
37
|
private static readonly PAD;
|
|
34
38
|
private static readonly TITLE_H;
|
|
35
39
|
/** Distance between successive depth columns/rows (center-to-center). */
|
|
@@ -40,8 +44,10 @@ export declare class TreeDiagram {
|
|
|
40
44
|
private layout;
|
|
41
45
|
private flatten;
|
|
42
46
|
private connectorPath;
|
|
47
|
+
private build;
|
|
43
48
|
generate(): string;
|
|
44
49
|
/** Write the SVG to a file, creating parent directories as needed. */
|
|
45
50
|
save(outputPath: string): string;
|
|
46
51
|
}
|
|
47
52
|
export default TreeDiagram;
|
|
53
|
+
export { measureText };
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* TreeDiagram
|
|
3
|
+
* TreeDiagram - renders a recursive hierarchical tree / mind-map as SVG.
|
|
4
4
|
*
|
|
5
5
|
* A `TreeNode` tree of arbitrary depth is laid out with a classic "tidy tree"
|
|
6
6
|
* algorithm: each node's cross-axis span equals the number of its leaf
|
|
7
7
|
* descendants, so siblings never overlap. The main axis is driven by depth
|
|
8
8
|
* (columns for `horizontal`, rows for `vertical`). Parents connect to children
|
|
9
|
-
* with
|
|
10
|
-
*
|
|
11
|
-
* light/dark theme.
|
|
9
|
+
* with cubic-Bezier <path> connectors. Nodes are square hairline label boxes;
|
|
10
|
+
* the root is distinguished with a heavier stroke and a wash fill.
|
|
12
11
|
*
|
|
13
12
|
* Usage:
|
|
14
13
|
* const diag = new TreeDiagram({ root: { name: 'db', children: [...] } });
|
|
@@ -48,20 +47,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
48
47
|
};
|
|
49
48
|
})();
|
|
50
49
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.TreeDiagram = void 0;
|
|
50
|
+
exports.measureText = exports.TreeDiagram = void 0;
|
|
52
51
|
const fs = __importStar(require("fs"));
|
|
53
52
|
const path = __importStar(require("path"));
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// corners are fixed by the Industry spec; accent + surface colours still route
|
|
59
|
-
// through resolveAccent / resolveTheme so --color / --theme keep working.
|
|
60
|
-
const IND_FONT_HEAD = '"Barlow Condensed", system-ui, sans-serif';
|
|
61
|
-
const IND_FONT_BODY = '"Barlow", system-ui, sans-serif';
|
|
62
|
-
const IND_FONT_MONO = 'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace';
|
|
63
|
-
const IND_HAIRLINE = 'rgba(29,31,32,0.16)';
|
|
64
|
-
const IND_FONT_IMPORT = "@import url('https://fonts.googleapis.com/css2?family=Barlow:wght@400;500;700&family=Barlow+Condensed:wght@400;600&family=JetBrains+Mono&display=swap');";
|
|
53
|
+
const svg_1 = require("./core/svg");
|
|
54
|
+
const theme_1 = require("./core/theme");
|
|
55
|
+
const layout_1 = require("./core/layout");
|
|
56
|
+
Object.defineProperty(exports, "measureText", { enumerable: true, get: function () { return layout_1.measureText; } });
|
|
65
57
|
class TreeDiagram {
|
|
66
58
|
constructor(options) {
|
|
67
59
|
this.opts = {
|
|
@@ -72,8 +64,8 @@ class TreeDiagram {
|
|
|
72
64
|
...options,
|
|
73
65
|
};
|
|
74
66
|
}
|
|
75
|
-
//
|
|
76
|
-
// A node's cross span = the sum of its leaf descendants. `
|
|
67
|
+
// -- Recursive subtree-height layout --------------------------------------
|
|
68
|
+
// A node's cross span = the sum of its leaf descendants. `cursor` tracks
|
|
77
69
|
// the running leaf-slot cursor so siblings tile without overlap.
|
|
78
70
|
layout(node, depth, cursor) {
|
|
79
71
|
const kids = node.children ?? [];
|
|
@@ -93,7 +85,7 @@ class TreeDiagram {
|
|
|
93
85
|
for (const c of n.children)
|
|
94
86
|
this.flatten(c, out);
|
|
95
87
|
}
|
|
96
|
-
//
|
|
88
|
+
// -- Cubic-Bezier connector between two centers ---------------------------
|
|
97
89
|
connectorPath(parent, child, horizontal) {
|
|
98
90
|
if (horizontal) {
|
|
99
91
|
const x1 = parent.x + TreeDiagram.BOX_W / 2;
|
|
@@ -110,15 +102,11 @@ class TreeDiagram {
|
|
|
110
102
|
const my = (y1 + y2) / 2;
|
|
111
103
|
return `M ${x1} ${y1} C ${x1} ${my}, ${x2} ${my}, ${x2} ${y2}`;
|
|
112
104
|
}
|
|
113
|
-
|
|
114
|
-
const { root, title,
|
|
105
|
+
build() {
|
|
106
|
+
const { root, title, direction } = this.opts;
|
|
115
107
|
const horizontal = direction !== 'vertical';
|
|
116
|
-
const accent = (0, palette_1.resolveAccent)(color);
|
|
117
|
-
const t = (0, palette_1.resolveTheme)(theme);
|
|
118
|
-
const palettes = (0, palette_1.rotatedPalettes)(color);
|
|
119
108
|
const BOX_W = TreeDiagram.BOX_W;
|
|
120
109
|
const BOX_H = TreeDiagram.BOX_H;
|
|
121
|
-
const R = TreeDiagram.R;
|
|
122
110
|
const PAD = TreeDiagram.PAD;
|
|
123
111
|
const DEPTH_GAP = TreeDiagram.DEPTH_GAP;
|
|
124
112
|
const LEAF_GAP = TreeDiagram.LEAF_GAP;
|
|
@@ -129,7 +117,7 @@ class TreeDiagram {
|
|
|
129
117
|
this.flatten(tree, nodes);
|
|
130
118
|
const maxDepth = nodes.reduce((m, n) => Math.max(m, n.depth), 0);
|
|
131
119
|
const totalLeaves = tree.leaves;
|
|
132
|
-
// 2. Map abstract coords
|
|
120
|
+
// 2. Map abstract coords to pixels.
|
|
133
121
|
const originX = PAD + BOX_W / 2;
|
|
134
122
|
const originY = PAD + titleH + BOX_H / 2;
|
|
135
123
|
for (const n of nodes) {
|
|
@@ -147,75 +135,62 @@ class TreeDiagram {
|
|
|
147
135
|
// 3. Auto-size viewBox.
|
|
148
136
|
const spanDepth = maxDepth * DEPTH_GAP;
|
|
149
137
|
const spanCross = Math.max(0, totalLeaves - 1) * LEAF_GAP;
|
|
150
|
-
const svgW = horizontal
|
|
151
|
-
? 2 * PAD + BOX_W + spanDepth
|
|
152
|
-
: 2 * PAD + BOX_W + spanCross;
|
|
138
|
+
const svgW = horizontal ? 2 * PAD + BOX_W + spanDepth : 2 * PAD + BOX_W + spanCross;
|
|
153
139
|
const svgH = horizontal
|
|
154
140
|
? 2 * PAD + titleH + BOX_H + spanCross
|
|
155
141
|
: 2 * PAD + titleH + BOX_H + spanDepth;
|
|
156
|
-
const doc = new
|
|
142
|
+
const doc = new svg_1.SvgDocument(svgW, svgH);
|
|
157
143
|
const svg = doc.root;
|
|
158
|
-
|
|
159
|
-
svg.child('rect', { width: svgW, height: svgH, fill: t.canvasBg });
|
|
160
|
-
svg.child('style').text(IND_FONT_IMPORT);
|
|
161
|
-
// ── Title ────────────────────────────────────────────────────────────
|
|
144
|
+
svg.child('rect', { width: svgW, height: svgH, fill: theme_1.INK.ground });
|
|
162
145
|
if (title) {
|
|
163
146
|
svg
|
|
164
147
|
.child('text', {
|
|
165
148
|
x: PAD,
|
|
166
149
|
y: PAD + 24,
|
|
167
|
-
'font-family':
|
|
168
|
-
'font-size':
|
|
169
|
-
'font-weight':
|
|
170
|
-
fill:
|
|
150
|
+
'font-family': theme_1.FONT.family,
|
|
151
|
+
'font-size': theme_1.FONT.title + 3,
|
|
152
|
+
'font-weight': 'bold',
|
|
153
|
+
fill: theme_1.INK.line,
|
|
171
154
|
})
|
|
172
155
|
.text(title);
|
|
173
156
|
}
|
|
174
|
-
//
|
|
157
|
+
// Connectors drawn first so boxes sit on top.
|
|
175
158
|
const drawConnectors = (n) => {
|
|
176
|
-
const depthPalette = palettes[(n.depth + 1) % palettes.length];
|
|
177
159
|
for (const c of n.children) {
|
|
178
160
|
svg.child('path', {
|
|
179
161
|
d: this.connectorPath(n, c, horizontal),
|
|
180
162
|
fill: 'none',
|
|
181
|
-
stroke:
|
|
182
|
-
'stroke-width':
|
|
183
|
-
'stroke-opacity': 0.55,
|
|
184
|
-
'stroke-linecap': 'round',
|
|
163
|
+
stroke: theme_1.INK.line,
|
|
164
|
+
'stroke-width': theme_1.STROKE.hair,
|
|
185
165
|
});
|
|
186
166
|
drawConnectors(c);
|
|
187
167
|
}
|
|
188
168
|
};
|
|
189
169
|
drawConnectors(tree);
|
|
190
|
-
//
|
|
170
|
+
// Node boxes: square hairline frames; the root gets a heavier stroke and
|
|
171
|
+
// a wash fill to stand out without introducing colour.
|
|
191
172
|
for (const n of nodes) {
|
|
192
|
-
const pal = palettes[n.depth % palettes.length];
|
|
193
173
|
const x = n.x - BOX_W / 2;
|
|
194
174
|
const y = n.y - BOX_H / 2;
|
|
195
|
-
// Root box uses the primary accent's gradient-ish solid; deeper nodes use
|
|
196
|
-
// their depth palette. `light`/`text` keep contrast readable per theme.
|
|
197
175
|
const isRoot = n.depth === 0;
|
|
198
|
-
const boxFill = isRoot ? accent.mid : pal.light;
|
|
199
|
-
const boxStroke = isRoot ? accent.from : pal.mid;
|
|
200
|
-
const labelFill = isRoot ? '#FFFFFF' : pal.text;
|
|
201
176
|
svg.child('rect', {
|
|
202
177
|
x,
|
|
203
178
|
y,
|
|
204
179
|
width: BOX_W,
|
|
205
180
|
height: BOX_H,
|
|
206
|
-
fill:
|
|
207
|
-
stroke:
|
|
208
|
-
'stroke-width':
|
|
181
|
+
fill: isRoot ? theme_1.INK.wash : theme_1.INK.ground,
|
|
182
|
+
stroke: theme_1.INK.line,
|
|
183
|
+
'stroke-width': isRoot ? theme_1.STROKE.heavy : theme_1.STROKE.hair,
|
|
209
184
|
});
|
|
210
|
-
const label = n.node.name
|
|
185
|
+
const label = (0, layout_1.truncate)(n.node.name, BOX_W - theme_1.SPACE.lg, theme_1.FONT.body);
|
|
211
186
|
svg
|
|
212
187
|
.child('text', {
|
|
213
188
|
x: n.x,
|
|
214
189
|
y: n.y + (n.node.badge ? -1 : 5),
|
|
215
|
-
'font-family':
|
|
216
|
-
'font-size':
|
|
217
|
-
'font-weight':
|
|
218
|
-
fill:
|
|
190
|
+
'font-family': theme_1.FONT.family,
|
|
191
|
+
'font-size': theme_1.FONT.body,
|
|
192
|
+
'font-weight': 'bold',
|
|
193
|
+
fill: theme_1.INK.line,
|
|
219
194
|
'text-anchor': 'middle',
|
|
220
195
|
})
|
|
221
196
|
.text(label);
|
|
@@ -224,16 +199,18 @@ class TreeDiagram {
|
|
|
224
199
|
.child('text', {
|
|
225
200
|
x: n.x,
|
|
226
201
|
y: n.y + 15,
|
|
227
|
-
'font-family':
|
|
228
|
-
'font-size':
|
|
229
|
-
|
|
230
|
-
fill: isRoot ? 'rgba(255,255,255,0.8)' : pal.mid,
|
|
202
|
+
'font-family': theme_1.FONT.mono,
|
|
203
|
+
'font-size': theme_1.FONT.small,
|
|
204
|
+
fill: theme_1.INK.muted,
|
|
231
205
|
'text-anchor': 'middle',
|
|
232
206
|
})
|
|
233
207
|
.text(n.node.badge);
|
|
234
208
|
}
|
|
235
209
|
}
|
|
236
|
-
return doc
|
|
210
|
+
return doc;
|
|
211
|
+
}
|
|
212
|
+
generate() {
|
|
213
|
+
return this.build().toString('Generated by ts-prorm-orm TreeDiagram');
|
|
237
214
|
}
|
|
238
215
|
/** Write the SVG to a file, creating parent directories as needed. */
|
|
239
216
|
save(outputPath) {
|
|
@@ -249,7 +226,6 @@ exports.TreeDiagram = TreeDiagram;
|
|
|
249
226
|
// Layout constants (px)
|
|
250
227
|
TreeDiagram.BOX_W = 168;
|
|
251
228
|
TreeDiagram.BOX_H = 46;
|
|
252
|
-
TreeDiagram.R = 12;
|
|
253
229
|
TreeDiagram.PAD = 40;
|
|
254
230
|
TreeDiagram.TITLE_H = 44;
|
|
255
231
|
/** Distance between successive depth columns/rows (center-to-center). */
|