@yanqirenshi/model.tm 0.0.0-stage → 0.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/src/schema.ts ADDED
@@ -0,0 +1,194 @@
1
+ // TM(d3.ter)の入力データの JSON Schema(2020-12)。コレクションごとに 1 つ。
2
+ //
3
+ // iHub の server/schemas/tm/*.schema.json を移したもの。iHub 版と違い additionalProperties を閉じていない
4
+ // (利用側の項目を落とさないため)。検査するのは、図の構造にかかわる項目(id・type・参照・position・size・
5
+ // 関係の端点・subset・mapping)。name は文字列でも { logical, physical } でもよい。
6
+ //
7
+ // x-id-start はスキーマの検証には使わない注釈(コレクションが空のときに最初に振る id)。
8
+ // Ajv で使うときは addKeyword({ keyword: 'x-id-start', schemaType: 'number' }) を足す。
9
+
10
+ import { CARDINALITIES, ENTITY_TYPES, OPTIONALITIES, SUBSET_KINDS } from './defaults.js';
11
+
12
+ const ID = { type: ['integer', 'string'] } as const;
13
+
14
+ const NAME = {
15
+ description: '文字列、または論理名と物理名の組',
16
+ oneOf: [
17
+ { type: 'string' },
18
+ {
19
+ type: 'object',
20
+ properties: {
21
+ logical: { type: 'string' },
22
+ physical: { type: 'string' },
23
+ description: { type: 'string' },
24
+ },
25
+ additionalProperties: true,
26
+ },
27
+ ],
28
+ } as const;
29
+
30
+ // マスタ(identifiers / attributes)の共通形
31
+ function masterSchema(title: string, description: string, idStart: number) {
32
+ return {
33
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
34
+ title,
35
+ description,
36
+ 'x-id-start': idStart,
37
+ type: 'array',
38
+ items: {
39
+ type: 'object',
40
+ properties: {
41
+ id: ID,
42
+ name: NAME,
43
+ description: { type: ['string', 'null'] },
44
+ },
45
+ required: ['id', 'name'],
46
+ additionalProperties: true,
47
+ },
48
+ } as const;
49
+ }
50
+
51
+ export const IDENTIFIERS_SCHEMA = masterSchema(
52
+ 'TM の識別子のマスタ',
53
+ '識別子 (個体指定子) のマスタ。entities[].identifiers[].identifier から id で参照される。',
54
+ 10000,
55
+ );
56
+
57
+ export const ATTRIBUTES_SCHEMA = masterSchema(
58
+ 'TM の属性のマスタ',
59
+ '属性のマスタ。entities[].attributes[].attribute から id で参照される。',
60
+ 20000,
61
+ );
62
+
63
+ // エンティティが持つインスタンス(識別子・属性)の共通形。ref はマスタを指す項目名
64
+ function instanceSchema(ref: 'identifier' | 'attribute') {
65
+ return {
66
+ type: 'array',
67
+ items: {
68
+ type: 'object',
69
+ properties: {
70
+ id: ID,
71
+ [ref]: ID,
72
+ name: NAME,
73
+ description: { type: ['string', 'null'] },
74
+ },
75
+ required: ['id', ref],
76
+ additionalProperties: true,
77
+ },
78
+ } as const;
79
+ }
80
+
81
+ export const ENTITIES_SCHEMA = {
82
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
83
+ title: 'TM のエンティティ',
84
+ description: 'TM (T字形ER) のエンティティ。type は 10 種の種別 (これ以外は d3.ter が描画時に例外にする)。識別子・属性はマスタの id を参照するインスタンスとして持ち、インスタンスの id は全エンティティを通して一意 (関係の details がこれを指す)。name は文字列か論理名・物理名の組。position はドラッグで書き換わる。size の w / h が 0 のときは内容に合わせる。',
85
+ 'x-id-start': 30000,
86
+ type: 'array',
87
+ items: {
88
+ type: 'object',
89
+ properties: {
90
+ id: ID,
91
+ type: { enum: [...ENTITY_TYPES] },
92
+ name: NAME,
93
+ description: { type: ['string', 'null'] },
94
+ position: {
95
+ type: 'object',
96
+ properties: {
97
+ x: { type: 'number' },
98
+ y: { type: 'number' },
99
+ z: { type: 'number' },
100
+ },
101
+ required: ['x', 'y'],
102
+ additionalProperties: false,
103
+ },
104
+ size: {
105
+ type: 'object',
106
+ properties: {
107
+ w: { type: 'number', minimum: 0 },
108
+ h: { type: 'number', minimum: 0 },
109
+ },
110
+ required: ['w', 'h'],
111
+ additionalProperties: false,
112
+ },
113
+ identifiers: instanceSchema('identifier'),
114
+ attributes: instanceSchema('attribute'),
115
+ },
116
+ required: ['id', 'type', 'name'],
117
+ additionalProperties: true,
118
+ },
119
+ } as const;
120
+
121
+ export const RELATIONSHIPS_SCHEMA = {
122
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
123
+ title: 'TM のリレーションシップ',
124
+ description: 'エンティティの間の結線。from.entity / to.entity はエンティティの id (同じエンティティを指すと自己結線 = 再帰のループ)。端点の cardinality (1 / 3) と optionality (0 / 1) は無ければ記号を描かない。position は接続位置の角度 (0=真下・時計回り)。details は線を取り付けるインスタンス (省略すると先頭の識別子)。subset はサブセット結線の区分コード (same は =、different は ×)。mapping は結線の中点から垂下する対応表・対照表・再帰表 (再帰表は自己結線にだけ)。',
125
+ 'x-id-start': 1,
126
+ type: 'array',
127
+ items: {
128
+ type: 'object',
129
+ properties: {
130
+ id: ID,
131
+ type: { type: ['string', 'null'] },
132
+ from: { $ref: '#/$defs/end' },
133
+ to: { $ref: '#/$defs/end' },
134
+ details: {
135
+ type: 'array',
136
+ items: {
137
+ type: 'object',
138
+ properties: {
139
+ from: { $ref: '#/$defs/detail_end' },
140
+ to: { $ref: '#/$defs/detail_end' },
141
+ },
142
+ required: ['from', 'to'],
143
+ },
144
+ },
145
+ subset: {
146
+ type: ['object', 'null'],
147
+ properties: {
148
+ kind: { enum: [...SUBSET_KINDS] },
149
+ code: { type: 'string' },
150
+ },
151
+ required: ['kind', 'code'],
152
+ },
153
+ mapping: {
154
+ type: ['object', 'null'],
155
+ properties: { entity: ID },
156
+ required: ['entity'],
157
+ },
158
+ label: { type: ['string', 'null'] },
159
+ description: { type: ['string', 'null'] },
160
+ },
161
+ required: ['id', 'from', 'to'],
162
+ additionalProperties: true,
163
+ },
164
+ $defs: {
165
+ end: {
166
+ type: 'object',
167
+ properties: {
168
+ entity: ID,
169
+ cardinality: { enum: [...CARDINALITIES], description: '1 か 多 (3)。無ければ記号を描かない' },
170
+ optionality: { enum: [...OPTIONALITIES], description: '0 は任意 (○)、1 は必須 (横棒)。無ければ記号を描かない' },
171
+ position: { type: 'number', description: '接続位置の角度。0=真下・時計回り (90=左 / 180=真上 / 270=右)' },
172
+ id: { ...ID, description: '旧来の項目。端点の識別子のインスタンスの id (details に置き換わった)' },
173
+ },
174
+ required: ['entity'],
175
+ additionalProperties: true,
176
+ },
177
+ detail_end: {
178
+ type: 'object',
179
+ properties: {
180
+ id: ID,
181
+ description: { type: ['string', 'null'] },
182
+ },
183
+ required: ['id'],
184
+ },
185
+ },
186
+ } as const;
187
+
188
+ /** コレクション名 → スキーマ */
189
+ export const SCHEMAS = {
190
+ identifiers: IDENTIFIERS_SCHEMA,
191
+ attributes: ATTRIBUTES_SCHEMA,
192
+ entities: ENTITIES_SCHEMA,
193
+ relationships: RELATIONSHIPS_SCHEMA,
194
+ } as const;
package/src/types.ts ADDED
@@ -0,0 +1,165 @@
1
+ // model.tm の公開型。
2
+ //
3
+ // ここで言う「レコード」は、d3.ter の data() に渡す入力そのものの 1 要素。
4
+ // { identifiers, attributes, entities, relationships }
5
+ // 形は model.er(ER 図)と同じ階層形式で、TM 固有の項目(エンティティの type、関係の subset / mapping / label)が足されている。
6
+ // d3.ter が正規化したあとの datum(_id / _core を持つ Atman)ではない。
7
+
8
+ import type { Name, Position, Size } from '@yanqirenshi/types';
9
+
10
+ /** レコードの id(コレクションの中で一意。インスタンス(identifiers / attributes の行)の id は全エンティティを通して一意) */
11
+ export type TmId = number | string;
12
+
13
+ /** 論理名・物理名の組(共有型 Name) */
14
+ export type TmName = Name;
15
+
16
+ /**
17
+ * TM(T字形ER)のエンティティ種別(10 種)。これ以外は d3.ter が描画時に例外にする。
18
+ * CLASS は「クラスで整える」の箱で、種別バッジを持たず、切り出し元(親)としか結線できない。
19
+ */
20
+ export type TmEntityType =
21
+ | 'RESOURCE' | 'RESOURCE-SUBSET'
22
+ | 'EVENT' | 'EVENT-SUBSET'
23
+ | 'COMPARATIVE' | 'CORRESPONDENCE' | 'RECURSION'
24
+ | 'MANY-VALUED-OR' | 'MANY-VALUED-AND'
25
+ | 'CLASS';
26
+
27
+ /** マスタ(identifiers / attributes)のレコード。name は文字列でも Name でもよい */
28
+ export interface TmMasterRecord {
29
+ id: TmId;
30
+ name?: string | TmName | null;
31
+ description?: string | null;
32
+ [key: string]: unknown;
33
+ }
34
+
35
+ /** エンティティが持つ識別子のインスタンス(identifier は identifiers[].id)。name は表示名の上書き(TM の (R) 表記など) */
36
+ export interface TmIdentifierRef {
37
+ id: TmId;
38
+ identifier: TmId;
39
+ name?: string | TmName | null;
40
+ description?: string | null;
41
+ [key: string]: unknown;
42
+ }
43
+
44
+ /** エンティティが持つ属性のインスタンス(attribute は attributes[].id) */
45
+ export interface TmAttributeRef {
46
+ id: TmId;
47
+ attribute: TmId;
48
+ name?: string | TmName | null;
49
+ description?: string | null;
50
+ [key: string]: unknown;
51
+ }
52
+
53
+ /** エンティティのレコード */
54
+ export interface TmEntityRecord {
55
+ id: TmId;
56
+ type?: TmEntityType | string;
57
+ name?: string | TmName | null;
58
+ description?: string | null;
59
+ position?: Position;
60
+ /** w / h が 0 のときは view が内容に合わせて決める */
61
+ size?: Size;
62
+ identifiers?: TmIdentifierRef[];
63
+ attributes?: TmAttributeRef[];
64
+ [key: string]: unknown;
65
+ }
66
+
67
+ /** 1 か 多(3) */
68
+ export type TmCardinality = 1 | 3;
69
+
70
+ /** 0 は任意(○)、1 は必須(横棒) */
71
+ export type TmOptionality = 0 | 1;
72
+
73
+ /**
74
+ * 関係の端点。position はエンティティの中心から見た角度(0=真下・時計回り。90=左 / 180=真上 / 270=右)。
75
+ * cardinality / optionality が無いときは記号を描かない。
76
+ * id は旧来の項目(端点の識別子のインスタンス。details に置き換わった)で、あれば検査だけする。
77
+ */
78
+ export interface TmRelationshipEnd {
79
+ entity: TmId;
80
+ cardinality?: TmCardinality;
81
+ optionality?: TmOptionality;
82
+ position?: number;
83
+ id?: TmId;
84
+ [key: string]: unknown;
85
+ }
86
+
87
+ /** 関係の線を取り付けるインスタンス(from.id / to.id はそれぞれの端のエンティティの識別子・属性のインスタンス) */
88
+ export interface TmRelationshipDetail {
89
+ from: { id: TmId; description?: string | null };
90
+ to: { id: TmId; description?: string | null };
91
+ }
92
+
93
+ /** サブセットへの結線に添える区分コード。same は「=区分コード」、different は「×区分コード」 */
94
+ export interface TmSubset {
95
+ kind: 'same' | 'different';
96
+ code: string;
97
+ }
98
+
99
+ /** 関係(リレーションシップ)のレコード */
100
+ export interface TmRelationshipRecord {
101
+ id: TmId;
102
+ type?: string | null;
103
+ from: TmRelationshipEnd;
104
+ to: TmRelationshipEnd;
105
+ details?: TmRelationshipDetail[];
106
+ subset?: TmSubset | null;
107
+ /** 結線の中点から垂下する対応表・対照表・再帰表(エンティティの id) */
108
+ mapping?: { entity: TmId } | null;
109
+ label?: string | null;
110
+ description?: string | null;
111
+ [key: string]: unknown;
112
+ }
113
+
114
+ export type TmRecord = TmMasterRecord | TmEntityRecord | TmRelationshipRecord;
115
+
116
+ /** 図全体(d3.ter の data() に渡すもの) */
117
+ export interface TmGraph {
118
+ identifiers?: TmMasterRecord[];
119
+ attributes?: TmMasterRecord[];
120
+ entities?: TmEntityRecord[];
121
+ relationships?: TmRelationshipRecord[];
122
+ }
123
+
124
+ /** マスタのコレクション */
125
+ export type TmMasterCollection = 'identifiers' | 'attributes';
126
+
127
+ /** レコードを置くコレクション */
128
+ export type TmCollection = TmMasterCollection | 'entities' | 'relationships';
129
+
130
+ /** レコードの種類(どの形か) */
131
+ export type TmKind = 'master' | 'entity' | 'relationship';
132
+
133
+ /** JSON Merge Patch(RFC 7386)。null は「その項目を除く」 */
134
+ export type MergePatch = { [key: string]: unknown };
135
+
136
+ /** 保存する変更。利用側は図の種類を知らずにこれを保存すればよい */
137
+ export interface TmChange {
138
+ collection: TmCollection;
139
+ id: TmId;
140
+ patch: MergePatch;
141
+ }
142
+
143
+ /** ドラッグの移動量(世界座標) */
144
+ export interface TmMove {
145
+ dx: number;
146
+ dy: number;
147
+ }
148
+
149
+ /**
150
+ * 編集できる項目。colonoscope の ColonoscopeField(path / label / type)と同じ形で、
151
+ * 取りうる値の一覧 options だけ足している(型は構造的に互換。colonoscope には依存しない)。
152
+ */
153
+ export type TmFieldType = 'text' | 'textarea' | 'number' | 'readonly';
154
+
155
+ export interface TmField {
156
+ /** レコードの項目のパス(ドット区切り。例: 'from.position')。写し先ではなく実際の項目を指す */
157
+ path: string;
158
+ label: string;
159
+ type: TmFieldType;
160
+ /** 取りうる値の一覧(種別・カーディナリティなど。文字列で持つ) */
161
+ options?: readonly string[];
162
+ }
163
+
164
+ /** インスペクタの「適用」が渡す値(path → 入力した文字列) */
165
+ export type TmValues = Record<string, string>;