modscape 2.0.3 → 2.1.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/README.ja.md +189 -5
- package/README.md +185 -3
- package/package.json +1 -1
- package/src/layout.js +47 -26
- package/src/templates/rules.md +97 -22
- package/visualizer/package.json +1 -1
- package/visualizer-dist/assets/index-CVn9qVGW.css +1 -0
- package/visualizer-dist/assets/index-Crci4Usl.js +432 -0
- package/visualizer-dist/index.html +2 -2
- package/visualizer-dist/assets/index-nIGQ5zIu.js +0 -432
- package/visualizer-dist/assets/index-zFK5TNtm.css +0 -1
package/README.ja.md
CHANGED
|
@@ -62,6 +62,8 @@ npm install -g modscape
|
|
|
62
62
|
```
|
|
63
63
|
`.modscape/rules.md`(YAMLスキーマのルール)と `.modscape/codegen-rules.md`(実装コード生成のルール)、および各エージェント用のコマンドファイルが生成されます。
|
|
64
64
|
|
|
65
|
+
> **ルールの更新**: Modscape をアップグレードした後は、`modscape init` を再実行することで `.modscape/rules.md` と `.modscape/codegen-rules.md` を最新版に上書きできます。
|
|
66
|
+
|
|
65
67
|
2. **起動**: ビジュアライザーを起動します。
|
|
66
68
|
```bash
|
|
67
69
|
modscape dev model.yaml
|
|
@@ -73,10 +75,12 @@ npm install -g modscape
|
|
|
73
75
|
4. **実装コードの生成** — `/modscape:codegen` でYAMLをdbt / SQLMesh / Spark SQLに変換します。
|
|
74
76
|
> *".modscape/codegen-rules.md に従って、model.yaml からdbtモデルを生成して。"*
|
|
75
77
|
|
|
76
|
-
エージェントは `lineage
|
|
78
|
+
エージェントは `lineage` セクションを元に依存関係の順でモデルを生成し、YAMLで定義しきれない箇所には `-- TODO:` コメントを残します。
|
|
77
79
|
|
|
78
80
|
### B: 手動モデリング
|
|
79
|
-
|
|
81
|
+
アーキテクチャを直接コントロールしたい場合に最適です。
|
|
82
|
+
|
|
83
|
+
1. **YAML作成**: `model.yaml` ファイルを作成します([YAMLリファレンス](#モデルの定義-yaml) を参照)。
|
|
80
84
|
2. **起動**: ビジュアライザーを起動します。
|
|
81
85
|
```bash
|
|
82
86
|
modscape dev model.yaml
|
|
@@ -95,6 +99,7 @@ relationships – テーブル間のERカーディナリティ
|
|
|
95
99
|
lineage – データの流れ / 変換パス
|
|
96
100
|
annotations – キャンバス上のスティッキーノート・吹き出し
|
|
97
101
|
layout – 全座標データ(tables/domains の中に x/y を書いてはいけない)
|
|
102
|
+
consumers – データの下流消費者(BIダッシュボード・MLモデル・アプリケーション等)
|
|
98
103
|
```
|
|
99
104
|
|
|
100
105
|
### Domains(ドメイン)
|
|
@@ -105,8 +110,7 @@ domains:
|
|
|
105
110
|
name: "主要売上"
|
|
106
111
|
description: "営業チームのトランザクションデータ。" # 任意
|
|
107
112
|
color: "rgba(59, 130, 246, 0.1)" # 背景色
|
|
108
|
-
|
|
109
|
-
isLocked: false # true にするとキャンバスでの誤ドラッグを防止
|
|
113
|
+
members: [orders, dim_customers] # 論理的な所属リスト
|
|
110
114
|
```
|
|
111
115
|
|
|
112
116
|
### Tables(テーブル)
|
|
@@ -193,6 +197,31 @@ relationships:
|
|
|
193
197
|
|
|
194
198
|
> **ER関係** vs **リネージ**: 構造的な結合(外部キーなど)には `relationships` を、データの加工・変換の流れには `lineage` を使用してください。両方に同じ接続を記述しないでください。
|
|
195
199
|
|
|
200
|
+
### Consumers(コンシューマー)
|
|
201
|
+
|
|
202
|
+
コンシューマーはデータモデルの下流消費者を表します。BIダッシュボード、MLモデル、アプリケーションなど、データを利用するあらゆるシステムを定義できます。キャンバス上に独自のノードとして表示され、リネージ矢印で接続されます。
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
consumers:
|
|
206
|
+
- id: revenue_dashboard # 一意のID — lineageやlayoutで使用
|
|
207
|
+
name: "Revenue Dashboard" # 表示名
|
|
208
|
+
description: "財務チーム向け月次KPIダッシュボード" # 任意
|
|
209
|
+
appearance:
|
|
210
|
+
icon: "📊" # 任意(デフォルト: 📊)
|
|
211
|
+
color: "#e0f2fe" # 任意のアクセントカラー
|
|
212
|
+
url: "https://bi.example.com/revenue" # 任意のリンク
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
コンシューマーへのリネージは `lineage.to` にコンシューマーIDを指定します:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
lineage:
|
|
219
|
+
- from: mart_monthly_revenue
|
|
220
|
+
to: revenue_dashboard # コンシューマーID
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
テーブルと同様に、ドメインの `members` リストにも追加できます。
|
|
224
|
+
|
|
196
225
|
### Annotations(アノテーション)
|
|
197
226
|
|
|
198
227
|
```yaml
|
|
@@ -220,7 +249,6 @@ layout:
|
|
|
220
249
|
y: 0
|
|
221
250
|
width: 880
|
|
222
251
|
height: 480
|
|
223
|
-
isLocked: false # true でキャンバスのドラッグを防止
|
|
224
252
|
|
|
225
253
|
# ドメイン内のテーブル – 座標はドメインの原点からの相対値
|
|
226
254
|
orders:
|
|
@@ -261,6 +289,162 @@ modscape build ./models -o docs-site
|
|
|
261
289
|
modscape export ./models -o docs/ARCHITECTURE.md
|
|
262
290
|
```
|
|
263
291
|
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## dbt連携
|
|
295
|
+
|
|
296
|
+
既存のdbtプロジェクトを `manifest.json` から直接インポートできます。
|
|
297
|
+
|
|
298
|
+
### 事前準備
|
|
299
|
+
|
|
300
|
+
コマンドを実行する前に、dbtプロジェクトで `dbt parse`(または `target/manifest.json` を生成する任意のdbtコマンド)を実行してください。
|
|
301
|
+
|
|
302
|
+
### dbtプロジェクトのインポート
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
modscape dbt import [project-dir] [オプション]
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
| オプション | 説明 |
|
|
309
|
+
|-----------|------|
|
|
310
|
+
| `-o, --output <dir>` | 出力ディレクトリ(デフォルト: `modscape-<プロジェクト名>`) |
|
|
311
|
+
| `--split-by <key>` | `schema`、`tag`、`folder` のいずれかでYAMLファイルを分割 |
|
|
312
|
+
|
|
313
|
+
**使用例:**
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# カレントディレクトリからインポート
|
|
317
|
+
modscape dbt import
|
|
318
|
+
|
|
319
|
+
# 特定のdbtプロジェクトパスを指定
|
|
320
|
+
modscape dbt import ./my_dbt_project
|
|
321
|
+
|
|
322
|
+
# スキーマ別にYAMLファイルを分割して出力
|
|
323
|
+
modscape dbt import --split-by schema
|
|
324
|
+
|
|
325
|
+
# dbtタグ別に分割し、出力先ディレクトリを指定
|
|
326
|
+
modscape dbt import --split-by tag -o ./modscape-models
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
インポート後は以下でビジュアライザーを起動できます:
|
|
330
|
+
```bash
|
|
331
|
+
modscape dev modscape-my_project
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
> **インポートされる内容:** `manifest.json` 内の `model`、`seed`、`snapshot`、`source` ノード(カラム、説明文、`depends_on` によるリネージ含む)。
|
|
335
|
+
> **分割モード:** `--split-by` 指定時はグループごとに別YAMLファイルへ出力されます。自己完結率(self-contained rate)が80%未満のファイルは、クロスファイルのリネージ参照が単体では表示されないため注意してください。
|
|
336
|
+
|
|
337
|
+
### dbt変更の同期
|
|
338
|
+
|
|
339
|
+
dbtプロジェクトを更新した後、既存のModscape YAMLファイルへ差分を反映できます。手動で追加したレイアウト・外観・アノテーション・リレーションシップは保持されます。
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
modscape dbt sync [project-dir] [オプション]
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
| オプション | 説明 |
|
|
346
|
+
|-----------|------|
|
|
347
|
+
| `-o, --output <dir>` | 同期対象のModscape YAMLが置かれたディレクトリ(デフォルト: `modscape-<プロジェクト名>`) |
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# カレントディレクトリのdbtプロジェクトを同期
|
|
351
|
+
modscape dbt sync
|
|
352
|
+
|
|
353
|
+
# パスを指定して同期
|
|
354
|
+
modscape dbt sync ./my_dbt_project -o ./modscape-models
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
> **sync と import の違い:** `import` はYAMLをゼロから生成します。`sync` は既存ファイルを更新するため、手動で加えたテーブル種別・ビジネス定義・サンプルデータなどの情報が失われません。
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## モデルファイル操作
|
|
362
|
+
|
|
363
|
+
### YAMLファイルのマージ
|
|
364
|
+
|
|
365
|
+
複数のYAMLモデルを1ファイルに統合します。テーブル/ドメインIDが重複した場合は先勝ちで処理されます。
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
modscape merge model-a.yaml model-b.yaml -o merged.yaml
|
|
369
|
+
|
|
370
|
+
# ディレクトリ内のすべてのYAMLをマージ
|
|
371
|
+
modscape merge ./models -o merged.yaml
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### テーブルの抽出
|
|
375
|
+
|
|
376
|
+
特定のテーブル(関連するリレーションシップ・リネージも含む)を新しいYAMLファイルへ切り出します。
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
modscape extract model.yaml --tables orders,dim_customers -o subset.yaml
|
|
380
|
+
|
|
381
|
+
# 複数ファイルから抽出
|
|
382
|
+
modscape extract ./models --tables fct_sales,dim_dates -o extracted.yaml
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 自動レイアウト
|
|
386
|
+
|
|
387
|
+
テーブルのリレーションシップをもとに、座標を自動計算してYAMLに書き込みます。
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
modscape layout model.yaml
|
|
391
|
+
|
|
392
|
+
# 別ファイルに出力
|
|
393
|
+
modscape layout model.yaml -o model-with-layout.yaml
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## アトミックモデル操作コマンド
|
|
399
|
+
|
|
400
|
+
AIエージェントやスクリプトから、YAMLモデルファイルに対して精確な変更を加えるためのコマンドです。すべてのコマンドで `--json` オプションによる機械可読な出力が利用できます。
|
|
401
|
+
|
|
402
|
+
### テーブルコマンド
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
modscape table list <file> # テーブルID一覧を表示
|
|
406
|
+
modscape table get <file> --id <id> # 指定テーブルをJSONで取得
|
|
407
|
+
modscape table add <file> --data <json> # テーブルを追加
|
|
408
|
+
modscape table update <file> --id <id> --data <json> # テーブルを更新
|
|
409
|
+
modscape table remove <file> --id <id> # テーブルを削除
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### カラムコマンド
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
modscape column add <file> --table <id> --data <json>
|
|
416
|
+
modscape column update <file> --table <id> --id <col-id> --data <json>
|
|
417
|
+
modscape column remove <file> --table <id> --id <col-id>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### リレーションシップコマンド
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
modscape relationship list <file>
|
|
424
|
+
modscape relationship add <file> --data <json>
|
|
425
|
+
modscape relationship remove <file> --index <n>
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### リネージコマンド
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
modscape lineage list <file>
|
|
432
|
+
modscape lineage add <file> --from <table-id> --to <table-id>
|
|
433
|
+
modscape lineage remove <file> --from <table-id> --to <table-id>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### ドメインコマンド
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
modscape domain list <file>
|
|
440
|
+
modscape domain get <file> --id <id>
|
|
441
|
+
modscape domain add <file> --data <json>
|
|
442
|
+
modscape domain update <file> --id <id> --data <json>
|
|
443
|
+
modscape domain remove <file> --id <id>
|
|
444
|
+
modscape domain member add <file> --domain <id> --table <table-id>
|
|
445
|
+
modscape domain member remove <file> --domain <id> --table <table-id>
|
|
446
|
+
```
|
|
447
|
+
|
|
264
448
|
## クレジット
|
|
265
449
|
|
|
266
450
|
Modscape は以下の素晴らしいオープンソースプロジェクトによって支えられています:
|
package/README.md
CHANGED
|
@@ -63,6 +63,8 @@ Leverage AI coding assistants (**Gemini CLI, Claude Code, or Codex**) to build y
|
|
|
63
63
|
```
|
|
64
64
|
This creates `.modscape/rules.md` (YAML schema rules) and `.modscape/codegen-rules.md` (code generation rules), plus agent-specific command files.
|
|
65
65
|
|
|
66
|
+
> **Updating rules**: After upgrading Modscape, re-run `modscape init` to overwrite `.modscape/rules.md` and `.modscape/codegen-rules.md` with the latest bundled version.
|
|
67
|
+
|
|
66
68
|
2. **Start Dev**: Launch the visualizer.
|
|
67
69
|
```bash
|
|
68
70
|
modscape dev model.yaml
|
|
@@ -98,6 +100,7 @@ relationships – ER cardinality between tables
|
|
|
98
100
|
lineage – data flow / transformation paths
|
|
99
101
|
annotations – sticky notes / callouts on the canvas
|
|
100
102
|
layout – ALL coordinate data (never put x/y inside tables or domains)
|
|
103
|
+
consumers – downstream consumers (BI dashboards, ML models, applications)
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
### Domains
|
|
@@ -108,8 +111,7 @@ domains:
|
|
|
108
111
|
name: "Core Sales"
|
|
109
112
|
description: "Transactional data for the sales team." # optional
|
|
110
113
|
color: "rgba(59, 130, 246, 0.1)" # background fill
|
|
111
|
-
|
|
112
|
-
isLocked: false # prevent accidental drag when true
|
|
114
|
+
members: [orders, dim_customers] # logical membership
|
|
113
115
|
```
|
|
114
116
|
|
|
115
117
|
### Tables
|
|
@@ -196,6 +198,31 @@ relationships:
|
|
|
196
198
|
|
|
197
199
|
> **ER Relationships** vs **Lineage**: Use `relationships` for structural joins (FKs) and `lineage` for data flow (transformations). Do not duplicate them.
|
|
198
200
|
|
|
201
|
+
### Consumers
|
|
202
|
+
|
|
203
|
+
Consumers represent the downstream users of your data model — BI dashboards, ML models, applications, or any other system that consumes the data. They appear as distinct nodes on the canvas and can receive lineage edges.
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
consumers:
|
|
207
|
+
- id: revenue_dashboard # unique ID — used in lineage and layout
|
|
208
|
+
name: "Revenue Dashboard" # display name
|
|
209
|
+
description: "Monthly KPI dashboard for the finance team." # optional
|
|
210
|
+
appearance:
|
|
211
|
+
icon: "📊" # optional (defaults to 📊)
|
|
212
|
+
color: "#e0f2fe" # optional accent color
|
|
213
|
+
url: "https://bi.example.com/revenue" # optional link
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Connect a consumer with lineage by using its `id` as the `to` field:
|
|
217
|
+
|
|
218
|
+
```yaml
|
|
219
|
+
lineage:
|
|
220
|
+
- from: mart_monthly_revenue
|
|
221
|
+
to: revenue_dashboard # consumer ID
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Consumers can also be added to domain `members` lists just like tables.
|
|
225
|
+
|
|
199
226
|
### Annotations
|
|
200
227
|
|
|
201
228
|
```yaml
|
|
@@ -223,7 +250,6 @@ layout:
|
|
|
223
250
|
y: 0
|
|
224
251
|
width: 880
|
|
225
252
|
height: 480
|
|
226
|
-
isLocked: false # prevent drag in canvas
|
|
227
253
|
|
|
228
254
|
# Table inside a domain – coordinates are relative to domain origin
|
|
229
255
|
orders:
|
|
@@ -264,6 +290,162 @@ modscape build ./models -o docs-site
|
|
|
264
290
|
modscape export ./models -o docs/ARCHITECTURE.md
|
|
265
291
|
```
|
|
266
292
|
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## dbt Integration
|
|
296
|
+
|
|
297
|
+
Modscape can import your existing dbt project directly from its compiled `manifest.json`.
|
|
298
|
+
|
|
299
|
+
### Prerequisites
|
|
300
|
+
|
|
301
|
+
Run `dbt parse` (or any dbt command that produces `target/manifest.json`) in your dbt project before using these commands.
|
|
302
|
+
|
|
303
|
+
### Import dbt Project
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
modscape dbt import [project-dir] [options]
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
| Option | Description |
|
|
310
|
+
|--------|-------------|
|
|
311
|
+
| `-o, --output <dir>` | Output directory (default: `modscape-<project-name>`) |
|
|
312
|
+
| `--split-by <key>` | Split output files by `schema`, `tag`, or `folder` |
|
|
313
|
+
|
|
314
|
+
**Examples:**
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
# Import from current directory, output to modscape-my_project/
|
|
318
|
+
modscape dbt import
|
|
319
|
+
|
|
320
|
+
# Import from a specific dbt project path
|
|
321
|
+
modscape dbt import ./my_dbt_project
|
|
322
|
+
|
|
323
|
+
# Split into separate YAML files by schema
|
|
324
|
+
modscape dbt import --split-by schema
|
|
325
|
+
|
|
326
|
+
# Split by dbt tag, with custom output directory
|
|
327
|
+
modscape dbt import --split-by tag -o ./modscape-models
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
After import, visualize with:
|
|
331
|
+
```bash
|
|
332
|
+
modscape dev modscape-my_project
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
> **What gets imported:** All `model`, `seed`, `snapshot`, and `source` nodes from `manifest.json`, including columns, descriptions, and lineage (`depends_on`).
|
|
336
|
+
> **Split mode:** When `--split-by` is used, each group is written to a separate YAML file. A self-containment score is shown — files below 80% have cross-file lineage edges that won't render in isolation.
|
|
337
|
+
|
|
338
|
+
### Sync dbt Changes
|
|
339
|
+
|
|
340
|
+
After modifying your dbt project, sync the changes into existing Modscape YAML files without losing any manual edits (layout, appearance, annotations, relationships):
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
modscape dbt sync [project-dir] [options]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
| Option | Description |
|
|
347
|
+
|--------|-------------|
|
|
348
|
+
| `-o, --output <dir>` | Target directory containing existing Modscape YAML files (default: `modscape-<project-name>`) |
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Sync changes from current dbt project
|
|
352
|
+
modscape dbt sync
|
|
353
|
+
|
|
354
|
+
# Sync from a specific path
|
|
355
|
+
modscape dbt sync ./my_dbt_project -o ./modscape-models
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
> **sync vs import:** `import` creates YAML files from scratch; `sync` updates existing files, preserving your manual enrichments (table types, business definitions, sample data, etc.).
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Model File Operations
|
|
363
|
+
|
|
364
|
+
### Merge YAML Files
|
|
365
|
+
|
|
366
|
+
Combine multiple YAML models into one. Duplicate table/domain IDs are resolved with first-wins semantics.
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
modscape merge model-a.yaml model-b.yaml -o merged.yaml
|
|
370
|
+
|
|
371
|
+
# Merge all YAMLs in a directory
|
|
372
|
+
modscape merge ./models -o merged.yaml
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Extract Tables
|
|
376
|
+
|
|
377
|
+
Extract a subset of tables (and their relationships/lineage) into a new YAML file.
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
modscape extract model.yaml --tables orders,dim_customers -o subset.yaml
|
|
381
|
+
|
|
382
|
+
# Extract from multiple files
|
|
383
|
+
modscape extract ./models --tables fct_sales,dim_dates -o extracted.yaml
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Auto-Layout
|
|
387
|
+
|
|
388
|
+
Automatically calculate and write layout coordinates based on table relationships.
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
modscape layout model.yaml
|
|
392
|
+
|
|
393
|
+
# Write to a separate output file
|
|
394
|
+
modscape layout model.yaml -o model-with-layout.yaml
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Atomic Model Mutation Commands
|
|
400
|
+
|
|
401
|
+
These commands let AI agents (or scripts) make precise, targeted changes to a YAML model file. All commands support `--json` for machine-readable output.
|
|
402
|
+
|
|
403
|
+
### Table Commands
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
modscape table list <file> # List all table IDs
|
|
407
|
+
modscape table get <file> --id <id> # Get a single table as JSON
|
|
408
|
+
modscape table add <file> --data <json> # Add a new table
|
|
409
|
+
modscape table update <file> --id <id> --data <json> # Update a table
|
|
410
|
+
modscape table remove <file> --id <id> # Remove a table
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Column Commands
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
modscape column add <file> --table <id> --data <json>
|
|
417
|
+
modscape column update <file> --table <id> --id <col-id> --data <json>
|
|
418
|
+
modscape column remove <file> --table <id> --id <col-id>
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Relationship Commands
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
modscape relationship list <file>
|
|
425
|
+
modscape relationship add <file> --data <json>
|
|
426
|
+
modscape relationship remove <file> --index <n>
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Lineage Commands
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
modscape lineage list <file>
|
|
433
|
+
modscape lineage add <file> --from <table-id> --to <table-id>
|
|
434
|
+
modscape lineage remove <file> --from <table-id> --to <table-id>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Domain Commands
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
modscape domain list <file>
|
|
441
|
+
modscape domain get <file> --id <id>
|
|
442
|
+
modscape domain add <file> --data <json>
|
|
443
|
+
modscape domain update <file> --id <id> --data <json>
|
|
444
|
+
modscape domain remove <file> --id <id>
|
|
445
|
+
modscape domain member add <file> --domain <id> --table <table-id>
|
|
446
|
+
modscape domain member remove <file> --domain <id> --table <table-id>
|
|
447
|
+
```
|
|
448
|
+
|
|
267
449
|
## Credits
|
|
268
450
|
|
|
269
451
|
Modscape is made possible by these incredible open-source projects:
|
package/package.json
CHANGED
package/src/layout.js
CHANGED
|
@@ -28,7 +28,15 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const consumers = Array.isArray(schema.consumers) ? schema.consumers : [];
|
|
32
|
+
const totalNodes = schema.tables.length + consumers.length;
|
|
33
|
+
console.log(` 🏗️ Calculating layout for ${schema.tables.length} tables and ${consumers.length} consumers...`);
|
|
34
|
+
|
|
35
|
+
// Normalize domain members: support both `members` (new) and `tables` (legacy)
|
|
36
|
+
const domains = (schema.domains || []).map(d => ({
|
|
37
|
+
...d,
|
|
38
|
+
members: Array.isArray(d.members) ? d.members : (Array.isArray(d.tables) ? d.tables : []),
|
|
39
|
+
}));
|
|
32
40
|
|
|
33
41
|
// Initialize Dagre Graph
|
|
34
42
|
const g = new dagre.graphlib.Graph({ compound: true });
|
|
@@ -43,11 +51,15 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
43
51
|
|
|
44
52
|
// 1. Add Tables
|
|
45
53
|
schema.tables.forEach((table) => {
|
|
46
|
-
// Standard table size in canvas units
|
|
47
54
|
g.setNode(table.id, { width: 280, height: 160 });
|
|
48
55
|
});
|
|
49
56
|
|
|
50
|
-
// 2. Add
|
|
57
|
+
// 2. Add Consumer nodes
|
|
58
|
+
consumers.forEach((uc) => {
|
|
59
|
+
g.setNode(uc.id, { width: 160, height: 60 });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 3. Add Lineage Edges (tables and consumers)
|
|
51
63
|
if (schema.lineage) {
|
|
52
64
|
schema.lineage.forEach((edge) => {
|
|
53
65
|
if (g.hasNode(edge.from) && g.hasNode(edge.to)) {
|
|
@@ -56,7 +68,7 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
56
68
|
});
|
|
57
69
|
}
|
|
58
70
|
|
|
59
|
-
//
|
|
71
|
+
// 4. Add ER Relationships
|
|
60
72
|
if (schema.relationships) {
|
|
61
73
|
schema.relationships.forEach((rel) => {
|
|
62
74
|
if (g.hasNode(rel.from.table) && g.hasNode(rel.to.table)) {
|
|
@@ -65,34 +77,32 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
65
77
|
});
|
|
66
78
|
}
|
|
67
79
|
|
|
68
|
-
//
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
schema.domains.forEach((domain) => {
|
|
80
|
+
// 5. Setup Domains (members can be tables or consumers)
|
|
81
|
+
if (domains.length > 0) {
|
|
82
|
+
domains.forEach((domain) => {
|
|
72
83
|
g.setNode(domain.id, { label: domain.name, cluster: true });
|
|
73
|
-
domain.
|
|
74
|
-
if (g.hasNode(
|
|
75
|
-
g.setParent(
|
|
84
|
+
domain.members.forEach((memberId) => {
|
|
85
|
+
if (g.hasNode(memberId)) {
|
|
86
|
+
g.setParent(memberId, domain.id);
|
|
76
87
|
}
|
|
77
88
|
});
|
|
78
|
-
domainTableMap.set(domain.id, domain.tables);
|
|
79
89
|
});
|
|
80
90
|
}
|
|
81
91
|
|
|
82
92
|
// Execute Layout Calculation
|
|
83
93
|
dagre.layout(g);
|
|
84
94
|
|
|
85
|
-
//
|
|
95
|
+
// 6. Post-process: Convert Dagre results to Modscape Layout format
|
|
86
96
|
const newLayout = {};
|
|
87
97
|
const PAD = 48;
|
|
88
98
|
const TABLE_W = 280;
|
|
89
|
-
const TABLE_H = 160;
|
|
99
|
+
const TABLE_H = 160;
|
|
90
100
|
const GAP = 40;
|
|
91
101
|
|
|
92
102
|
// Process Domains (Grid packing)
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
const members = domain.
|
|
103
|
+
if (domains.length > 0) {
|
|
104
|
+
domains.forEach(domain => {
|
|
105
|
+
const members = domain.members.filter(mid => g.hasNode(mid));
|
|
96
106
|
if (members.length === 0) return;
|
|
97
107
|
|
|
98
108
|
// Sort by dagre rank (left -> right)
|
|
@@ -104,18 +114,17 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
104
114
|
const gridW = cols * (TABLE_W + GAP) - GAP;
|
|
105
115
|
const gridH = rowCount * (TABLE_H + GAP) - GAP;
|
|
106
116
|
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const cy = members.reduce((s, tid) => s + g.node(tid).y, 0) / members.length;
|
|
117
|
+
const cx = members.reduce((s, mid) => s + g.node(mid).x, 0) / members.length;
|
|
118
|
+
const cy = members.reduce((s, mid) => s + g.node(mid).y, 0) / members.length;
|
|
110
119
|
const originX = cx - gridW / 2;
|
|
111
120
|
const originY = cy - gridH / 2;
|
|
112
121
|
|
|
113
|
-
members.forEach((
|
|
122
|
+
members.forEach((mid, idx) => {
|
|
114
123
|
const col = idx % cols;
|
|
115
124
|
const row = Math.floor(idx / cols);
|
|
116
125
|
const xOff = col * (TABLE_W + GAP) + TABLE_W / 2;
|
|
117
126
|
const yOff = row * (TABLE_H + GAP) + TABLE_H / 2;
|
|
118
|
-
newLayout[
|
|
127
|
+
newLayout[mid] = {
|
|
119
128
|
x: Math.round(originX + xOff),
|
|
120
129
|
y: Math.round(originY + yOff),
|
|
121
130
|
parentId: domain.id
|
|
@@ -134,18 +143,30 @@ export function applyLayout(inputPath, options = {}) {
|
|
|
134
143
|
}
|
|
135
144
|
|
|
136
145
|
// Standalone Tables
|
|
137
|
-
const
|
|
146
|
+
const domainMemberIds = new Set(domains.flatMap(d => d.members));
|
|
138
147
|
schema.tables.forEach(t => {
|
|
139
|
-
if (!
|
|
148
|
+
if (!domainMemberIds.has(t.id)) {
|
|
140
149
|
const pos = g.node(t.id);
|
|
141
150
|
newLayout[t.id] = { x: Math.round(pos.x), y: Math.round(pos.y) };
|
|
142
151
|
}
|
|
143
152
|
});
|
|
144
153
|
|
|
145
|
-
//
|
|
154
|
+
// Standalone Usecases
|
|
155
|
+
consumers.forEach(uc => {
|
|
156
|
+
if (!domainMemberIds.has(uc.id)) {
|
|
157
|
+
const pos = g.node(uc.id);
|
|
158
|
+
if (pos) newLayout[uc.id] = { x: Math.round(pos.x), y: Math.round(pos.y) };
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// 7. Save back to YAML (write members, not tables, for domains)
|
|
163
|
+
schema.domains = domains.map(d => {
|
|
164
|
+
const { members, ...rest } = d;
|
|
165
|
+
return { ...rest, members };
|
|
166
|
+
});
|
|
146
167
|
schema.layout = newLayout;
|
|
147
168
|
const outputPath = options.output ? path.resolve(process.cwd(), options.output) : absolutePath;
|
|
148
|
-
|
|
169
|
+
|
|
149
170
|
fs.writeFileSync(outputPath, yaml.dump(schema, { indent: 2, lineWidth: -1, noRefs: true }), 'utf8');
|
|
150
171
|
console.log(` ✅ Layout complete! Saved to: ${path.relative(process.cwd(), outputPath)}`);
|
|
151
172
|
}
|