@oxeo/visual 0.0.0-stage → 0.2.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.
Files changed (48) hide show
  1. package/README.md +99 -2
  2. package/bin/oxeo-visual.js +2 -0
  3. package/dist/build.d.ts +23 -0
  4. package/dist/build.js +58 -0
  5. package/dist/cli/build-command.d.ts +5 -0
  6. package/dist/cli/build-command.js +57 -0
  7. package/dist/cli/dev.d.ts +6 -0
  8. package/dist/cli/dev.js +274 -0
  9. package/dist/cli/index.d.ts +1 -0
  10. package/dist/cli/index.js +104 -0
  11. package/dist/cli/open.d.ts +1 -0
  12. package/dist/cli/open.js +21 -0
  13. package/dist/cli/output.d.ts +4 -0
  14. package/dist/cli/output.js +13 -0
  15. package/dist/cli/run.d.ts +7 -0
  16. package/dist/cli/run.js +55 -0
  17. package/dist/cli/worker.d.ts +20 -0
  18. package/dist/cli/worker.js +94 -0
  19. package/dist/config.d.ts +33 -0
  20. package/dist/config.js +26 -0
  21. package/dist/extract.d.ts +8 -0
  22. package/dist/extract.js +300 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.js +24 -0
  25. package/dist/layout/cards.d.ts +43 -0
  26. package/dist/layout/cards.js +121 -0
  27. package/dist/layout/place.d.ts +40 -0
  28. package/dist/layout/place.js +197 -0
  29. package/dist/layout/route.d.ts +30 -0
  30. package/dist/layout/route.js +192 -0
  31. package/dist/model.d.ts +60 -0
  32. package/dist/model.js +2 -0
  33. package/dist/project.d.ts +14 -0
  34. package/dist/project.js +11 -0
  35. package/dist/render/client.js +871 -0
  36. package/dist/render/html.d.ts +8 -0
  37. package/dist/render/html.js +211 -0
  38. package/dist/render/palette.d.ts +4 -0
  39. package/dist/render/palette.js +66 -0
  40. package/dist/render/svg.d.ts +20 -0
  41. package/dist/render/svg.js +222 -0
  42. package/dist/scheme-meta.d.ts +90 -0
  43. package/dist/scheme-meta.js +2 -0
  44. package/dist/text.d.ts +2 -0
  45. package/dist/text.js +12 -0
  46. package/dist/types.d.ts +15 -0
  47. package/dist/types.js +143 -0
  48. package/package.json +36 -3
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeBuild = exports.firstLine = exports.log = void 0;
4
+ const text_1 = require("../text");
5
+ const time = () => new Date().toTimeString().slice(0, 8);
6
+ const log = (message) => console.log(`${time()} [oxeo-visual] ${message}`);
7
+ exports.log = log;
8
+ const firstLine = (text) => text.split('\n')[0].replace(/^Error:\s*/, '');
9
+ exports.firstLine = firstLine;
10
+ const describeBuild = (metrics, ms) => `✓ карта собрана: ${(0, text_1.plural)(metrics.cards, ['коллекция', 'коллекции', 'коллекций'])}, ` +
11
+ `${(0, text_1.plural)(metrics.all.routes, ['связь', 'связи', 'связей'])}, карта ${metrics.width}×${metrics.height} · ` +
12
+ `${(ms / 1000).toFixed(1)} с`;
13
+ exports.describeBuild = describeBuild;
@@ -0,0 +1,7 @@
1
+ import { type ChildProcess } from 'child_process';
2
+ import type { WorkerMessage } from './worker';
3
+ export interface BuildRun {
4
+ child: ChildProcess;
5
+ done: Promise<WorkerMessage>;
6
+ }
7
+ export declare const startBuild: (configPath: string) => BuildRun;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.startBuild = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const path = __importStar(require("path"));
39
+ const startBuild = (configPath) => {
40
+ const child = (0, child_process_1.fork)(path.join(__dirname, 'worker.js'), [configPath], {
41
+ stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
42
+ execArgv: []
43
+ });
44
+ const done = new Promise((resolve) => {
45
+ let message = null;
46
+ child.on('message', (m) => (message = m));
47
+ child.on('error', (error) => resolve({ type: 'error', message: error.message }));
48
+ child.on('exit', (code, signal) => resolve(message ?? {
49
+ type: 'error',
50
+ message: signal ? `сборка прервана (${signal})` : `сборка завершилась с кодом ${code}`
51
+ }));
52
+ });
53
+ return { child, done };
54
+ };
55
+ exports.startBuild = startBuild;
@@ -0,0 +1,20 @@
1
+ import { type SchemeMapResult } from '../build';
2
+ export interface BuildSettings {
3
+ configDir: string;
4
+ sourceDir: string;
5
+ watch: string[];
6
+ output: {
7
+ dir: string;
8
+ name: string;
9
+ };
10
+ }
11
+ export type WorkerMessage = {
12
+ type: 'result';
13
+ html: string;
14
+ svg: string;
15
+ metrics: SchemeMapResult['metrics'];
16
+ settings: BuildSettings;
17
+ } | {
18
+ type: 'error';
19
+ message: string;
20
+ };
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const path = __importStar(require("path"));
37
+ const ts = __importStar(require("typescript"));
38
+ const build_1 = require("../build");
39
+ const isTypeScript = (file) => /\.(c|m)?ts$/.test(file);
40
+ const registerTypeScript = (configPath) => {
41
+ const dir = path.dirname(configPath);
42
+ let tsNode;
43
+ try {
44
+ tsNode = require.resolve('ts-node', { paths: [dir] });
45
+ }
46
+ catch {
47
+ throw new Error(`${path.basename(configPath)} написан на TypeScript, а ts-node в проекте не найден. Установите его: npm i -D ts-node`);
48
+ }
49
+ require(tsNode).register({
50
+ project: ts.findConfigFile(dir, ts.sys.fileExists),
51
+ transpileOnly: true,
52
+ compilerOptions: { module: 'commonjs' }
53
+ });
54
+ };
55
+ const run = async (configPath) => {
56
+ if (isTypeScript(configPath))
57
+ registerTypeScript(configPath);
58
+ const loaded = require(configPath);
59
+ const config = loaded?.default ?? loaded;
60
+ if (!config?.scheme) {
61
+ throw new Error(`${path.basename(configPath)}: не задана схема — экспортируйте defineConfig({ scheme, ... })`);
62
+ }
63
+ const dir = path.dirname(configPath);
64
+ const resolve = (p) => path.resolve(dir, p);
65
+ const sourceDir = resolve(config.sourceDir);
66
+ const tsconfig = config.tsconfig
67
+ ? resolve(config.tsconfig)
68
+ : ts.findConfigFile(dir, ts.sys.fileExists);
69
+ const result = await (0, build_1.buildSchemeMap)(config.scheme, { sourceDir, tsconfig, config });
70
+ return {
71
+ type: 'result',
72
+ html: result.html,
73
+ svg: result.svg,
74
+ metrics: result.metrics,
75
+ settings: {
76
+ configDir: dir,
77
+ sourceDir,
78
+ watch: [...new Set([dir, sourceDir, ...(config.watch ?? []).map(resolve)])],
79
+ output: { dir: resolve(config.output?.dir ?? '.'), name: config.output?.name ?? 'scheme-map' }
80
+ }
81
+ };
82
+ };
83
+ const send = (message) => {
84
+ if (process.send)
85
+ process.send(message, () => process.exit(message.type === 'result' ? 0 : 1));
86
+ else
87
+ process.exit(message.type === 'result' ? 0 : 1);
88
+ };
89
+ run(path.resolve(process.argv[2]))
90
+ .then(send)
91
+ .catch((error) => send({
92
+ type: 'error',
93
+ message: error instanceof Error ? (error.stack ?? error.message) : String(error)
94
+ }));
@@ -0,0 +1,33 @@
1
+ export interface ZoneConfig {
2
+ id: string;
3
+ title: string;
4
+ color: string;
5
+ colorDark?: string;
6
+ files: string[][];
7
+ }
8
+ export interface Spacing {
9
+ cardHeader: number;
10
+ cardRow: number;
11
+ cardPadX: number;
12
+ cardMinWidth: number;
13
+ cardGapX: number;
14
+ cardGapY: number;
15
+ filePadTop: number;
16
+ filePadX: number;
17
+ filePadBottom: number;
18
+ fileGap: number;
19
+ zonePadTop: number;
20
+ zonePadX: number;
21
+ zonePadBottom: number;
22
+ zoneGap: number;
23
+ bandGap: number;
24
+ margin: number;
25
+ }
26
+ export declare const DEFAULT_SPACING: Spacing;
27
+ export interface VisualConfig {
28
+ zones: ZoneConfig[];
29
+ bands: string[][];
30
+ hubMinSources?: number;
31
+ spacing?: Partial<Spacing>;
32
+ }
33
+ export declare const resolveSpacing: (config: VisualConfig) => Spacing;
package/dist/config.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSpacing = exports.DEFAULT_SPACING = void 0;
4
+ exports.DEFAULT_SPACING = {
5
+ cardHeader: 30,
6
+ cardRow: 20,
7
+ cardPadX: 10,
8
+ cardMinWidth: 200,
9
+ cardGapX: 56,
10
+ cardGapY: 24,
11
+ filePadTop: 44,
12
+ filePadX: 30,
13
+ filePadBottom: 30,
14
+ fileGap: 56,
15
+ zonePadTop: 80,
16
+ zonePadX: 24,
17
+ zonePadBottom: 24,
18
+ zoneGap: 96,
19
+ bandGap: 110,
20
+ margin: 40
21
+ };
22
+ const resolveSpacing = (config) => ({
23
+ ...exports.DEFAULT_SPACING,
24
+ ...config.spacing
25
+ });
26
+ exports.resolveSpacing = resolveSpacing;
@@ -0,0 +1,8 @@
1
+ import type { SchemeSource } from './scheme-meta';
2
+ import type { SchemeModel } from './model';
3
+ export interface ExtractOptions {
4
+ sourceDir: string;
5
+ tsconfig?: string;
6
+ }
7
+ export declare const reflow: (text: string) => string;
8
+ export declare const extractScheme: (dataScheme: SchemeSource, options: ExtractOptions) => SchemeModel;
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.extractScheme = exports.reflow = void 0;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const ts = __importStar(require("typescript"));
40
+ const types_1 = require("./types");
41
+ const oneLine = (text) => text
42
+ .replace(/\s+/g, ' ')
43
+ .replace(/([([]) /g, '$1')
44
+ .replace(/ ([)\]])/g, '$1');
45
+ const LIST_ITEM = /^([-*•]|\d+[.)])\s/;
46
+ const reflow = (text) => text
47
+ .split(/\n[ \t]*\n/)
48
+ .map((paragraph) => {
49
+ let out = '';
50
+ for (const line of paragraph.split('\n').map((l) => l.trim())) {
51
+ if (!line)
52
+ continue;
53
+ out = !out ? line : LIST_ITEM.test(line) ? `${out}\n${line}` : `${out} ${line}`;
54
+ }
55
+ return out;
56
+ })
57
+ .filter(Boolean)
58
+ .join('\n\n');
59
+ exports.reflow = reflow;
60
+ const jsDoc = (node) => {
61
+ const text = (0, exports.reflow)(ts
62
+ .getJSDocCommentsAndTags(node)
63
+ .filter(ts.isJSDoc)
64
+ .map((d) => ts.getTextOfJSDocComment(d.comment) ?? '')
65
+ .join('\n\n'));
66
+ return text || undefined;
67
+ };
68
+ const schemeFiles = (sourceDir) => fs.readdirSync(sourceDir).filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
69
+ const parseSources = (sourceDir, program) => {
70
+ const classes = new Map();
71
+ for (const file of schemeFiles(sourceDir)) {
72
+ const source = program.getSourceFile(path.join(sourceDir, file));
73
+ if (!source)
74
+ continue;
75
+ const visit = (node, namespace) => {
76
+ if (ts.isModuleDeclaration(node) && ts.isIdentifier(node.name)) {
77
+ ts.forEachChild(node, (child) => visit(child, node.name.getText(source)));
78
+ return;
79
+ }
80
+ if (ts.isClassDeclaration(node) && node.name) {
81
+ const decorator = (ts.getDecorators(node) ?? [])
82
+ .map((d) => ts.isCallExpression(d.expression) ? d.expression.expression.getText(source) : null)
83
+ .find((name) => name !== null);
84
+ const props = new Map();
85
+ node.members.forEach((member, order) => {
86
+ if (ts.isPropertyDeclaration(member) && member.name) {
87
+ props.set(member.name.getText(source), {
88
+ doc: jsDoc(member),
89
+ order,
90
+ tsType: member.type?.getText(source).replace(/\s+/g, ' '),
91
+ typeNode: member.type,
92
+ decorators: (ts.getDecorators(member) ?? []).map((d) => oneLine(d.getText(source)))
93
+ });
94
+ }
95
+ });
96
+ const list = classes.get(node.name.text) ?? [];
97
+ list.push({
98
+ file,
99
+ namespace,
100
+ decorator: decorator ?? null,
101
+ decorators: (ts.getDecorators(node) ?? []).map((d) => oneLine(d.getText(source))),
102
+ doc: jsDoc(node),
103
+ props
104
+ });
105
+ classes.set(node.name.text, list);
106
+ }
107
+ ts.forEachChild(node, (child) => visit(child, namespace));
108
+ };
109
+ visit(source);
110
+ }
111
+ return (target, embedded) => {
112
+ const list = classes.get(target.name);
113
+ if (!list)
114
+ return undefined;
115
+ return list.find((c) => (c.decorator === 'Embedded') === embedded) ?? list[0];
116
+ };
117
+ };
118
+ const extractScheme = (dataScheme, options) => {
119
+ const collections = dataScheme.collectionsScheme;
120
+ const models = dataScheme.models;
121
+ const keyByModel = new Map(Object.entries(collections).map(([key, info]) => [info.name, key]));
122
+ const program = ts.createProgram(schemeFiles(options.sourceDir).map((f) => path.join(options.sourceDir, f)), (0, types_1.compilerOptions)(options.tsconfig));
123
+ const catalog = new types_1.TypeCatalog(program);
124
+ const findSource = parseSources(options.sourceDir, program);
125
+ const onDeleteOf = (consumerModel, address, targetModel) => {
126
+ const inverse = models[targetModel]?.refscheme.inverseRefs.find((ref) => ref.model === consumerModel && ref.modelConsumerProperty === address);
127
+ return inverse?.onDeleteBehavior ?? 'restrict';
128
+ };
129
+ const typeOf = (primitive) => primitive.type === 'array' ? `array<${primitive.itemType}>` : primitive.type;
130
+ const buildFields = (props, prefix, refs, modelName) => {
131
+ const ordered = [];
132
+ const sourceOf = (target, key) => {
133
+ const prop = findSource(target, prefix !== '')?.props.get(key);
134
+ return {
135
+ doc: prop?.doc,
136
+ tsType: prop?.tsType,
137
+ typeNode: prop?.typeNode,
138
+ decorators: prop?.decorators.length ? prop.decorators : undefined,
139
+ order: prop?.order ?? Number.MAX_SAFE_INTEGER
140
+ };
141
+ };
142
+ for (const prop of props.properties) {
143
+ const address = prefix + prop.propertyKey;
144
+ const { doc, tsType, typeNode, decorators, order } = sourceOf(prop.target, prop.propertyKey);
145
+ const primitive = prop.primitive;
146
+ if (primitive.type === 'embedded') {
147
+ const inner = [...primitive.properties, ...primitive.references][0]?.target;
148
+ const classDoc = inner && findSource(inner, true)?.doc;
149
+ ordered.push({
150
+ order,
151
+ field: {
152
+ name: prop.propertyKey,
153
+ path: address,
154
+ kind: 'embedded',
155
+ type: inner?.name,
156
+ decorators,
157
+ doc: [doc, classDoc].filter(Boolean).join('\n\n') || undefined,
158
+ fields: buildFields(primitive, address + '.', refs, modelName)
159
+ }
160
+ });
161
+ continue;
162
+ }
163
+ ordered.push({
164
+ order,
165
+ field: {
166
+ name: prop.propertyKey,
167
+ path: address,
168
+ kind: 'prop',
169
+ type: typeOf(primitive),
170
+ tsType,
171
+ decorators,
172
+ typeRefs: primitive.type === 'json' || (primitive.type === 'array' && primitive.itemType === 'json')
173
+ ? catalog.refsOf(typeNode)
174
+ : undefined,
175
+ nullable: 'nullable' in primitive ? primitive.nullable : undefined,
176
+ values: 'values' in primitive ? primitive.values : undefined,
177
+ doc
178
+ }
179
+ });
180
+ }
181
+ for (const relation of props.references) {
182
+ const address = prefix + relation.propertyKey;
183
+ const { doc, decorators, order } = sourceOf(relation.target, relation.propertyKey);
184
+ const ref = refs[address];
185
+ const targetModel = ref?.model ?? relation.referenceModel().name;
186
+ const base = {
187
+ name: relation.propertyKey,
188
+ path: address,
189
+ target: keyByModel.get(targetModel),
190
+ decorators,
191
+ doc
192
+ };
193
+ if (ref?.refType === 'reference-to') {
194
+ ordered.push({
195
+ order,
196
+ field: {
197
+ ...base,
198
+ kind: 'ref',
199
+ nullable: ref.nullable,
200
+ onDelete: onDeleteOf(modelName, address, targetModel)
201
+ }
202
+ });
203
+ }
204
+ else if (ref?.refType === 'reference-set') {
205
+ ordered.push({
206
+ order,
207
+ field: { ...base, kind: 'set', onDelete: onDeleteOf(modelName, address, targetModel) }
208
+ });
209
+ }
210
+ else if (ref?.refType === 'has-many') {
211
+ ordered.push({
212
+ order,
213
+ field: {
214
+ ...base,
215
+ kind: 'own',
216
+ onDelete: ref.cleanupBehavior ?? 'cascade',
217
+ ownerField: ref.modelBelongsToProperty
218
+ }
219
+ });
220
+ }
221
+ else if (ref?.refType === 'belongs-to') {
222
+ ordered.push({ order, field: { ...base, kind: 'owner' } });
223
+ }
224
+ else {
225
+ console.warn(` ! ${modelName}.${address}: связь ${ref?.refType ?? relation.refType} не отображается`);
226
+ }
227
+ }
228
+ return ordered.sort((a, b) => a.order - b.order).map((item) => item.field);
229
+ };
230
+ const entities = Object.keys(collections).map((key, order) => {
231
+ const { name } = collections[key];
232
+ const { scheme, refscheme } = models[name];
233
+ const source = findSource(scheme.target, false);
234
+ const entity = {
235
+ key,
236
+ model: name,
237
+ file: source?.file ?? '?',
238
+ namespace: source?.namespace,
239
+ order,
240
+ idKey: scheme.identifier.propertyKey,
241
+ idType: scheme.identifier.options.type,
242
+ doc: source?.doc,
243
+ decorators: source?.decorators,
244
+ fields: [
245
+ {
246
+ name: scheme.identifier.propertyKey,
247
+ path: scheme.identifier.propertyKey,
248
+ kind: 'id',
249
+ type: scheme.identifier.options.type,
250
+ decorators: source?.props.get(scheme.identifier.propertyKey)?.decorators,
251
+ doc: source?.props.get(scheme.identifier.propertyKey)?.doc
252
+ },
253
+ ...buildFields(scheme, '', refscheme.commonRefs, name)
254
+ ],
255
+ variants: []
256
+ };
257
+ if (scheme.type === 'discriminated') {
258
+ entity.discriminatorKey = scheme.discriminatorKey;
259
+ entity.variants = scheme.discriminators.map((d) => ({
260
+ value: d.value,
261
+ model: d.name,
262
+ doc: findSource(d.target, false)?.doc,
263
+ decorators: findSource(d.target, false)?.decorators,
264
+ fields: buildFields(d, '', refscheme.discriminatorRefs?.[d.value] ?? {}, name)
265
+ }));
266
+ }
267
+ return entity;
268
+ });
269
+ const relations = [];
270
+ for (const entity of entities) {
271
+ const walk = (fields, variant) => {
272
+ for (const field of fields) {
273
+ if (field.fields)
274
+ walk(field.fields, variant);
275
+ if ((field.kind === 'ref' || field.kind === 'set' || field.kind === 'own') && field.target) {
276
+ relations.push({
277
+ id: `${entity.key}.${field.path}${variant ? `@${variant}` : ''}`,
278
+ source: entity.key,
279
+ target: field.target,
280
+ kind: field.kind,
281
+ field: field.path,
282
+ ownerField: field.ownerField,
283
+ onDelete: field.onDelete ?? 'restrict',
284
+ nullable: field.nullable ?? false,
285
+ variant
286
+ });
287
+ }
288
+ }
289
+ };
290
+ walk(entity.fields);
291
+ entity.variants.forEach((v) => walk(v.fields, v.value));
292
+ }
293
+ const files = [];
294
+ for (const entity of entities) {
295
+ if (!files.some((f) => f.file === entity.file))
296
+ files.push({ file: entity.file, namespace: entity.namespace });
297
+ }
298
+ return { entities, relations, files, types: catalog.types };
299
+ };
300
+ exports.extractScheme = extractScheme;
@@ -0,0 +1,6 @@
1
+ export * from './config';
2
+ export * from './model';
3
+ export * from './project';
4
+ export type { SchemeSource } from './scheme-meta';
5
+ export { extractScheme, type ExtractOptions } from './extract';
6
+ export { buildSchemeMap, type SchemeMapOptions, type SchemeMapResult } from './build';
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.buildSchemeMap = exports.extractScheme = void 0;
18
+ __exportStar(require("./config"), exports);
19
+ __exportStar(require("./model"), exports);
20
+ __exportStar(require("./project"), exports);
21
+ var extract_1 = require("./extract");
22
+ Object.defineProperty(exports, "extractScheme", { enumerable: true, get: function () { return extract_1.extractScheme; } });
23
+ var build_1 = require("./build");
24
+ Object.defineProperty(exports, "buildSchemeMap", { enumerable: true, get: function () { return build_1.buildSchemeMap; } });
@@ -0,0 +1,43 @@
1
+ import type { Spacing } from '../config';
2
+ import type { Entity, Field, FieldKind, OnDelete, Relation } from '../model';
3
+ export declare const MONO_ADVANCE = 0.6;
4
+ export declare const ROW_FONT = 11;
5
+ export declare const HEADER_FONT = 13;
6
+ export declare const BOX_STEP = 6;
7
+ export declare const BOX_GAP_BEFORE = 5;
8
+ export declare const BOX_PAD_AFTER = 6;
9
+ export declare const textWidth: (text: string, size: number) => number;
10
+ export type RowKind = Exclude<FieldKind, 'embedded'> | 'block' | 'variant';
11
+ export interface CardRow {
12
+ path: string;
13
+ label: string;
14
+ detail: string;
15
+ kind: RowKind;
16
+ depth: number;
17
+ y: number;
18
+ h: number;
19
+ boxBottom?: number;
20
+ target?: string;
21
+ onDelete?: OnDelete;
22
+ nullable?: boolean;
23
+ variant?: string;
24
+ }
25
+ export interface Card {
26
+ key: string;
27
+ entity: Entity;
28
+ rows: CardRow[];
29
+ width: number;
30
+ height: number;
31
+ }
32
+ export declare const rowLevel: (row: CardRow) => number;
33
+ export declare const boxInset: (level: number) => number;
34
+ export declare const textInset: (level: number, spacing: Spacing) => number;
35
+ export declare const ownFieldsFirst: (fields: Field[]) => Field[];
36
+ export declare const buildCard: (entity: Entity, spacing: Spacing) => Card;
37
+ export declare const rowCenter: (card: Card, path: string, spacing: Spacing) => number;
38
+ export declare const relationRows: (relation: Relation, cards: Map<string, Card>) => {
39
+ source: Card;
40
+ sourcePath: string;
41
+ target: Card;
42
+ targetPath: string;
43
+ };