@cardenelabs/dragon 0.7.0 → 0.8.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.md +111 -0
- package/dist/index.cjs +2365 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +240 -3
- package/dist/index.d.ts +240 -3
- package/dist/index.js +2366 -279
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compile.ts +2895 -258
- package/src/index.ts +4 -1
- package/src/input-size.ts +8 -1
- package/src/json-parser.ts +480 -30
- package/src/notation-lint.ts +27 -4
- package/src/schemas/diagram.json +56 -5
- package/src/types.ts +125 -0
- package/src/v05/parser.ts +341 -41
- package/src/value-syntax.ts +313 -0
package/src/notation-lint.ts
CHANGED
|
@@ -102,7 +102,6 @@ const KIND_TO_JA: Record<string, string> = {
|
|
|
102
102
|
tree: "階層ツリー",
|
|
103
103
|
userJourney: "ユーザージャーニー",
|
|
104
104
|
mindMap: "マインドマップ",
|
|
105
|
-
mindMapRadial: "放射状マインドマップ",
|
|
106
105
|
funnel: "ファネル (段階別離脱)",
|
|
107
106
|
quadrant: "四象限マトリクス",
|
|
108
107
|
gantt: "ガントチャート",
|
|
@@ -113,7 +112,7 @@ const KIND_TO_JA: Record<string, string> = {
|
|
|
113
112
|
function applyTopicAutoFix(topic: string): string {
|
|
114
113
|
// 1. 先頭の kind name を検出、 マッチしたら JA description に置換
|
|
115
114
|
const kindMatch = topic.match(
|
|
116
|
-
/^\s*(chart|flow|swimlane|sequence|topology|er|stateMachine2?|infrastructure|classDiagram|tree|userJourney|mindMap
|
|
115
|
+
/^\s*(chart|flow|swimlane|sequence|topology|er|stateMachine2?|infrastructure|classDiagram|tree|userJourney|mindMap|funnel|quadrant|gantt|flowchart|network|line chart|pie chart|bar chart)\b/i,
|
|
117
116
|
);
|
|
118
117
|
if (kindMatch) {
|
|
119
118
|
const kind = kindMatch[1]!.toLowerCase();
|
|
@@ -202,10 +201,22 @@ function ruleGanttUnknownDependsOn(d: CdlDiagram): LintIssue[] {
|
|
|
202
201
|
return out;
|
|
203
202
|
}
|
|
204
203
|
|
|
204
|
+
/**
|
|
205
|
+
* 枝の親が実在するかを見る。
|
|
206
|
+
*
|
|
207
|
+
* **記法からも届く** (`#1177`)。 一時期は組立て API (`mindMap()` builder) で組んだ図にしか
|
|
208
|
+
* 当たらなかった = 記法の `type: mind` が `card` を 3 列に並べる別実装で、 `mind-map` 種別を
|
|
209
|
+
* 作っていなかったため (`#1170` で `type: radial` が消えて唯一の作り手が無くなった)。
|
|
210
|
+
* `#1177` で `compileMind` を `mind-map` 種別に寄せたので、 前提が揃うようになった。
|
|
211
|
+
*
|
|
212
|
+
* ただし **記法から違反が出ることは無い**。 記法の `actors` は「1 つ目が中心、 残りが枝」 の
|
|
213
|
+
* 並びで親を書く場所が無く、 全ての枝が中心の直下 (必ず実在する) になるため。 違反が出るのは
|
|
214
|
+
* 組立て API で親を書き間違えた図になる。
|
|
215
|
+
*/
|
|
205
216
|
function ruleMindMapParentReference(d: CdlDiagram): LintIssue[] {
|
|
206
217
|
const out: LintIssue[] = [];
|
|
207
218
|
for (const n of d.nodes) {
|
|
208
|
-
if (
|
|
219
|
+
if (n.kind === "mind-map" && n.mindData) {
|
|
209
220
|
const known = new Set<string>([n.mindData.rootId]);
|
|
210
221
|
for (const b of n.mindData.branches) known.add(b.id);
|
|
211
222
|
for (const b of n.mindData.branches) {
|
|
@@ -278,13 +289,25 @@ function ruleQuadrantMissingItems(d: CdlDiagram): LintIssue[] {
|
|
|
278
289
|
return out;
|
|
279
290
|
}
|
|
280
291
|
|
|
292
|
+
/**
|
|
293
|
+
* 段の人数が減っていくことを見る。
|
|
294
|
+
*
|
|
295
|
+
* 人数の欄は `{名前}` を書ける (状態から取る形、 cdl の `render/payload-binding.ts` が解く)。
|
|
296
|
+
* その場合ここでは値が決まらないので、**数どうしの組だけを比べる** (#1194)。
|
|
297
|
+
*
|
|
298
|
+
* 素通しで比べると文字列の大小比較になり、`{trial}` が `{signup}` より大きいという理由で
|
|
299
|
+
* 発火する。 実際に見本帳の funnel を状態から取る形にした時、その偽発火が出た。
|
|
300
|
+
*/
|
|
281
301
|
function ruleFunnelMonotonicCount(d: CdlDiagram): LintIssue[] {
|
|
282
302
|
const out: LintIssue[] = [];
|
|
283
303
|
for (const n of d.nodes) {
|
|
284
304
|
if (n.kind === "funnel-stages" && n.funnelData) {
|
|
285
305
|
const stages = n.funnelData;
|
|
286
306
|
for (let i = 1; i < stages.length; i++) {
|
|
287
|
-
|
|
307
|
+
const 今 = stages[i]!.count;
|
|
308
|
+
const 前 = stages[i - 1]!.count;
|
|
309
|
+
if (typeof 今 !== "number" || typeof 前 !== "number") continue;
|
|
310
|
+
if (今 > 前) {
|
|
288
311
|
out.push({
|
|
289
312
|
rule: "funnel-increasing-count",
|
|
290
313
|
severity: "warn",
|
package/src/schemas/diagram.json
CHANGED
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
},
|
|
15
15
|
"type": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"enum": ["sequence", "flow", "swimlane", "er", "state", "topology", "solidity", "gantt", "class", "pie", "c4", "mind"],
|
|
18
|
-
"description": "
|
|
17
|
+
"enum": ["sequence", "flow", "swimlane", "er", "state", "topology", "solidity", "gantt", "class", "pie", "bar", "line", "funnel", "tree", "journey", "quadrant", "c4", "mind"],
|
|
18
|
+
"description": "図の種類。 型ごとに actors の書き方が違う。 [関係を描く] sequence (時系列の呼び出し) / flow (処理の流れ) / swimlane (責務ごとの流れ) / er (DB の schema) / state (状態の遷移) / topology (network) / solidity (contract) / class (UML の class) / c4 (architecture) / mind (発想の枝分かれ) は actors に名前を並べ flow に矢印を書く。 [値を描く] pie (割合) / bar (棒の高さ) / line (線の高さ、 書いた順に並ぶ) は actors に `- 名前: \"45\"` の形で数を書く。 funnel (段ごとに減る数) も同じ形。 [語を描く] journey (体験の起伏) は `- 登録: \"不満\"` の形で 最高 / 満足 / 普通 / 不満 / 怒り のどれかを書く。 quadrant (2 軸の仕分け) は `- 重複削除: \"左上\"` の形で 左上 / 右上 / 左下 / 右下 のどれかを書く。 [時期を描く] gantt (工程の並び) は `- 設計: \"1月\"` の形で始まりの時期を書き、 flow の矢印で前後関係を書く。 [親子を描く] tree (親子の入れ子) は actors に名前を並べ flow の矢印で親子を書く (矢印の先が子)"
|
|
19
|
+
},
|
|
20
|
+
"eyebrow": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "図表の箱の上に出す小見出し (省略可)。 効くのは図全体を 1 箱にする型 (pie / bar / line / funnel / tree / journey / quadrant / mind / gantt) だけ。 箱ごとに分かれる型では相手が決まらないため、 actors[].eyebrow に書く"
|
|
19
23
|
},
|
|
20
24
|
"actors": {
|
|
21
25
|
"type": "array",
|
|
@@ -67,13 +71,39 @@
|
|
|
67
71
|
"guard": {"type": "string", "description": "state 遷移の条件 (kind: state 用)"},
|
|
68
72
|
"cardinality": {"type": "string", "description": "ER の多重度 (1..N 等、 kind: er 用)"},
|
|
69
73
|
"labelOffsetX": {"type": "number", "description": "ラベル位置 x 調整"},
|
|
70
|
-
"labelOffsetY": {"type": "number", "description": "ラベル位置 y 調整"}
|
|
74
|
+
"labelOffsetY": {"type": "number", "description": "ラベル位置 y 調整"},
|
|
75
|
+
"overlay": {"type": "boolean", "description": "true で説明文を矢印の線の上に重ねる (分岐図の条件ラベル用)"}
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
},
|
|
79
|
+
"states": {
|
|
80
|
+
"type": "object",
|
|
81
|
+
"description": "状態の初期値。 名前 -> 初期値 の object。 optional。 箱の文字に `{名前}` を置くと、 ここに書いた値が描画時に置き換わる。 段で動かすには animation[].tween / animation[].set を使う。",
|
|
82
|
+
"additionalProperties": false,
|
|
83
|
+
"patternProperties": {
|
|
84
|
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
|
|
85
|
+
"type": ["number", "string"],
|
|
86
|
+
"description": "初期値。 数か文字列。 名前は英字か _ で始め、 英数字と _ だけを使う"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"examples": [{ "inflow": 0, "done": 0 }]
|
|
90
|
+
},
|
|
91
|
+
"values": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"description": "他の値から自動で決まる値。 名前 -> 式 の object。 optional。 式には四則 (+ - * /) と括弧、 比較 (> >= < <= == !=)、 min / max が書ける。 他の値は {名前} で読む。 解くのは描画側で、 参照した値が動けば追随する。",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"patternProperties": {
|
|
96
|
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"minLength": 1,
|
|
99
|
+
"description": "式。 例 `{inflow} - {done}`"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"examples": [{ "waiting": "{inflow} - {done}", "busy": "{waiting} > 80" }]
|
|
103
|
+
},
|
|
74
104
|
"animation": {
|
|
75
105
|
"type": "array",
|
|
76
|
-
"description": "アニメーション phase 配列。 各 phase で focus 対象を highlight
|
|
106
|
+
"description": "アニメーション phase 配列。 各 phase で focus 対象を highlight し、 tween / set で states の値を動かす。 optional。",
|
|
77
107
|
"items": {
|
|
78
108
|
"type": "object",
|
|
79
109
|
"required": ["step"],
|
|
@@ -83,7 +113,28 @@
|
|
|
83
113
|
"duration": {"type": "number", "minimum": 0.1, "description": "phase 時間 (秒、 default 1.4)"},
|
|
84
114
|
"focus": {"type": "array", "items": {"type": "string"}, "description": "この phase で active 化する actor 名 or edge 'A -> B' の配列"},
|
|
85
115
|
"body": {"type": "string", "description": "phase 説明文"},
|
|
86
|
-
"badge": {"type": "string", "description": "phase 中に表示する badge label"}
|
|
116
|
+
"badge": {"type": "string", "description": "phase 中に表示する badge label"},
|
|
117
|
+
"tween": {
|
|
118
|
+
"type": "object",
|
|
119
|
+
"description": "この phase で補間する状態。 状態名 -> [開始値, 終了値]。 名前は英字か _ で始め、 英数字と _ だけを使う",
|
|
120
|
+
"additionalProperties": false,
|
|
121
|
+
"patternProperties": {
|
|
122
|
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
|
|
123
|
+
"type": "array",
|
|
124
|
+
"items": {"type": "number"},
|
|
125
|
+
"minItems": 2,
|
|
126
|
+
"maxItems": 2
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"set": {
|
|
131
|
+
"type": "object",
|
|
132
|
+
"description": "この phase の切替で差し替える状態。 状態名 -> 値。 名前は英字か _ で始め、 英数字と _ だけを使う",
|
|
133
|
+
"additionalProperties": false,
|
|
134
|
+
"patternProperties": {
|
|
135
|
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {"type": ["number", "string"]}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
87
138
|
}
|
|
88
139
|
}
|
|
89
140
|
},
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,12 @@ export type PresetType =
|
|
|
17
17
|
| "gantt"
|
|
18
18
|
| "class"
|
|
19
19
|
| "pie"
|
|
20
|
+
| "bar"
|
|
21
|
+
| "line"
|
|
22
|
+
| "funnel"
|
|
23
|
+
| "tree"
|
|
24
|
+
| "journey"
|
|
25
|
+
| "quadrant"
|
|
20
26
|
| "c4"
|
|
21
27
|
| "mind";
|
|
22
28
|
|
|
@@ -50,9 +56,42 @@ export type LayoutMode = "auto" | "manual";
|
|
|
50
56
|
export type DslDocument = {
|
|
51
57
|
title: string;
|
|
52
58
|
type: PresetType;
|
|
59
|
+
/**
|
|
60
|
+
* 図全体を 1 箱にする図種 (`pie` / `bar` / `line` / `funnel` / `tree` / `journey` /
|
|
61
|
+
* `quadrant` / `mind` / `gantt`) で、 その箱の上に出す小見出し (#1247)。
|
|
62
|
+
*
|
|
63
|
+
* これらの図種は箱を 1 つしか作らないため「どの箱の小見出しか」 が決まる。 箱ごとに
|
|
64
|
+
* 分かれる図種では決まらないので、 書かれていたら組み立て側が知らせる。
|
|
65
|
+
*/
|
|
66
|
+
eyebrow?: string;
|
|
67
|
+
/**
|
|
68
|
+
* `eyebrow` を書いた行 (#1247)。 知らせの行番号に使う。
|
|
69
|
+
*
|
|
70
|
+
* 図の `pos` は常に 1 行目を指すため、 そこを使うと「10 行目に書いた `eyebrow` が効かない」
|
|
71
|
+
* を 1 行目として知らせることになり、 書いた場所に辿り着けない。
|
|
72
|
+
*/
|
|
73
|
+
eyebrowPos?: Position;
|
|
74
|
+
/**
|
|
75
|
+
* 2 軸で仕分ける図 (`type: quadrant`) の軸の名前 (#1251)。
|
|
76
|
+
*
|
|
77
|
+
* 書かないと「小さい / 大きい」 のままになり、何を判断する図か読めない。
|
|
78
|
+
* 区画の名前 (`右上` 等) は軸の名前から `{上} × {右}` の形で決まる。
|
|
79
|
+
*
|
|
80
|
+
* 他の図種には軸が無いため、書かれていたら組み立て側が知らせる。
|
|
81
|
+
*/
|
|
82
|
+
axes?: DslAxes;
|
|
83
|
+
/** `axes` を書いた行 (#1251)。 知らせの行番号に使う */
|
|
84
|
+
axesPos?: Position;
|
|
53
85
|
actors: DslActor[];
|
|
54
86
|
flow: DslStep[];
|
|
55
87
|
animate?: DslAnimate;
|
|
88
|
+
/**
|
|
89
|
+
* 他の値から自動で決まる値 (`values:`)。 時間を持たず、 参照した値が動けば常に追随する。
|
|
90
|
+
*
|
|
91
|
+
* `states` (初期値だけを持つ) と `animation` (いつ何を動かすか) の間に置く層で、
|
|
92
|
+
* 値どうしの関係を書く場所。 書かなければ今までと同じ挙動。
|
|
93
|
+
*/
|
|
94
|
+
values?: DslValue[];
|
|
56
95
|
/** v0.5+ 拡張 ... viewport / lanes / groups */
|
|
57
96
|
viewport?: DslViewport;
|
|
58
97
|
lanes?: Record<string, DslLane>;
|
|
@@ -65,6 +104,12 @@ export type DslDocument = {
|
|
|
65
104
|
pos: Position;
|
|
66
105
|
};
|
|
67
106
|
|
|
107
|
+
/** 2 軸で仕分ける図の軸の名前 (#1251) */
|
|
108
|
+
export type DslAxes = {
|
|
109
|
+
x?: { left?: string; right?: string };
|
|
110
|
+
y?: { bottom?: string; top?: string };
|
|
111
|
+
};
|
|
112
|
+
|
|
68
113
|
/** 登場人物 (v0.5+ ... inline option 拡張) */
|
|
69
114
|
export type DslActor = {
|
|
70
115
|
name: string;
|
|
@@ -83,6 +128,35 @@ export type DslActor = {
|
|
|
83
128
|
eyebrow?: string;
|
|
84
129
|
value?: string;
|
|
85
130
|
rows?: string[];
|
|
131
|
+
/**
|
|
132
|
+
* 工程の並び (`type: gantt`) で、その工程の担当 (#1251)。
|
|
133
|
+
*
|
|
134
|
+
* 他の図種では相手が無いため、書かれていたら組み立て側が知らせる。
|
|
135
|
+
*/
|
|
136
|
+
owner?: string;
|
|
137
|
+
/**
|
|
138
|
+
* 工程の並び (`type: gantt`) で、その工程が終わる時期 (#1251)。
|
|
139
|
+
*
|
|
140
|
+
* 書かなければ始まりと同じ時期に終わる (帯が 1 コマ)。 `{名前}` を書くと状態から取り、
|
|
141
|
+
* 段で帯が伸び縮みする様子を見せられる。
|
|
142
|
+
*
|
|
143
|
+
* 他の図種では相手が無いため、書かれていたら組み立て側が知らせる。
|
|
144
|
+
*/
|
|
145
|
+
end?: string;
|
|
146
|
+
/**
|
|
147
|
+
* 体験の道筋 (`type: journey`) で、その段階が起きる場所 (#1251)。
|
|
148
|
+
*
|
|
149
|
+
* 「どこで起きたか」 を段の下に出す。 他の図種では相手が無いため、書かれていたら
|
|
150
|
+
* 組み立て側が知らせる。
|
|
151
|
+
*/
|
|
152
|
+
touchpoint?: string;
|
|
153
|
+
/**
|
|
154
|
+
* 体験の道筋 (`type: journey`) で、その段階の改善の余地 (#1251)。
|
|
155
|
+
*
|
|
156
|
+
* 気持ちが落ちる段に「何を直せるか」 を添える。 他の図種では相手が無いため、
|
|
157
|
+
* 書かれていたら組み立て側が知らせる。
|
|
158
|
+
*/
|
|
159
|
+
opportunity?: string;
|
|
86
160
|
lane?: string;
|
|
87
161
|
stack?: number;
|
|
88
162
|
initial?: boolean;
|
|
@@ -189,6 +263,8 @@ export type DslStep = {
|
|
|
189
263
|
cardinality?: string;
|
|
190
264
|
labelOffsetX?: number;
|
|
191
265
|
labelOffsetY?: number;
|
|
266
|
+
/** true で説明文を矢印の線の上に重ねる。 分岐図の条件ラベル用。 */
|
|
267
|
+
overlay?: boolean;
|
|
192
268
|
/**
|
|
193
269
|
* canvas pivot (CAR-1693 Phase 1) DSL 表面 `pos: {x, y}` 由来の layout offset。 step の edge
|
|
194
270
|
* label 位置を auto layout compute から (dx, dy) shift する。 未指定は auto、 set 済は Phase 2 で適用。
|
|
@@ -259,6 +335,55 @@ export type DslState = {
|
|
|
259
335
|
pos: Position;
|
|
260
336
|
};
|
|
261
337
|
|
|
338
|
+
/**
|
|
339
|
+
* 値が動き出すきっかけ (#1161 段 2)。
|
|
340
|
+
*
|
|
341
|
+
* `step` は段が始まった時、 `reaches` は別の値が境目を越えた時。 どちらも「成り立った瞬間の
|
|
342
|
+
* 出来事」 で、 常に成り立つ関係を表す式とは別物 (spec § 2.3)。
|
|
343
|
+
*/
|
|
344
|
+
export type DslValueTrigger =
|
|
345
|
+
| { kind: "step"; step: string }
|
|
346
|
+
| {
|
|
347
|
+
kind: "reaches";
|
|
348
|
+
/** 見張る相手の値の名前 */
|
|
349
|
+
source: string;
|
|
350
|
+
op: ">=" | ">" | "<=" | "<" | "==" | "!=";
|
|
351
|
+
threshold: number;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* 記法の `values:` の 1 件。 形は 2 つある。
|
|
356
|
+
*
|
|
357
|
+
* | 形 | 持つもの | 意味 |
|
|
358
|
+
* |---|---|---|
|
|
359
|
+
* | 式 | `expression` | 常に成り立つ関係。 時間を持たない |
|
|
360
|
+
* | きっかけ | `trigger` / `to` / `durationMs` | きっかけから `to` まで動く。 時間を持つ |
|
|
361
|
+
*
|
|
362
|
+
* 両方を持つ形は無い (parser が弾く)。 きっかけ形は組み立ての時点で段の時計を読む式へ畳まれる
|
|
363
|
+
* ため、 図に載る時にはどちらも `derived` になる。
|
|
364
|
+
*/
|
|
365
|
+
export type DslValue = {
|
|
366
|
+
name: string;
|
|
367
|
+
pos: Position;
|
|
368
|
+
} & (
|
|
369
|
+
| {
|
|
370
|
+
/** 式そのもの。 評価は描画側が毎 frame 行う */
|
|
371
|
+
expression: string;
|
|
372
|
+
trigger?: never;
|
|
373
|
+
to?: never;
|
|
374
|
+
durationMs?: never;
|
|
375
|
+
}
|
|
376
|
+
| {
|
|
377
|
+
expression?: never;
|
|
378
|
+
/** 動き出すきっかけ */
|
|
379
|
+
trigger: DslValueTrigger;
|
|
380
|
+
/** 動いた先の値 */
|
|
381
|
+
to: number;
|
|
382
|
+
/** 動く長さ (ms) */
|
|
383
|
+
durationMs: number;
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
|
|
262
387
|
/** ステップ (phase) */
|
|
263
388
|
export type DslPhase = {
|
|
264
389
|
name: string;
|