Перейти до основного вмісту

Схема

.erd.json — це формат документів редактора. Прочитайте цю сторінку, якщо вам потрібно генерувати, перевіряти або мігрувати документ поза редактором.

Ця сторінка описує форму зберігання — документ у тому вигляді, в якому його містить файл, — яку записує кожен випуск, починаючи з версії 4.0.0. Це те, що повертає editor.value, що записує експорт у JSON, і єдина форма, яку приймає опублікована JSON Schema. Її поле version має значення '3.0.0', як у кожному файлі v3, збереженому попереднім випуском, тож за version старий файл не відрізнити від новішого. setInitialValue(), сетер value і setSchemaJSON() читають її, будь-який файл, збережений попереднім випуском, і документ v2 у форматі .vuerd. До версії 4.0.0 властивість value повертала весь документ у тому вигляді, в якому його тримав редактор, тож збережений тоді файл містить більше (див. форма зберігання).
runtimeValue редактора — це інша серіалізація, яку ця сторінка не описує. Нею ініціалізують інший редактор або репліку, а кімната Yjs зберігає її у своєму знімку; вона ніколи не має потрапляти до файлу, а JSON Schema відхиляє будь-яке таке значення, що містить сутність.

Форма зберігання​

Форма зберігання містить лише наявні сутності: кожну таблицю, перелічену в doc.tableIds, стовпці, перелічені в цих таблицях, зв’язки між двома наявними таблицями, індекси наявних таблиць разом зі стовпцями індексів, стовпець яких досі є в переліку таблиці, перелічені нотатки й групи таблиць, а також, починаючи з версії 4.2.0, слова, терміни й домени аркушів словника, які містять хоч якесь значення, крім ідентифікатора. Вона не містить нічого, що редактор обчислює під час завантаження або тримає, лише поки він відкритий: ні порядку накладання, ні виміряних ширин, ні позначок ключів, ні того, чи є зв’язок ідентифікувальним, як намальовано його батьківський кінець і де він сходиться зі своїми таблицями. Кожне завантаження обчислює це знову з того, що містить файл.
Записи кожної колекції, крім словників, ідуть за ключами-ідентифікаторами в порядку кодових одиниць, причому стовпці спершу групуються за tableId, а стовпці індексів — за indexId. Починаючи з версії 4.2.0, wordEntities, termEntities і domainEntities ідуть після всіх інших колекцій, саме в цьому порядку, і кожну з них записано як заголовок і рядки, по одному рядку на рядок тексту (див. рядки словників). Кожна сутність записує свої поля у фіксованому порядку, а текст має відступ у два пробіли й завершується символом нового рядка. Тому той самий документ завжди дає ті самі байти, зміна впливає лише на те, чого вона стосується, а файл, збережений на комп’ютері з іншими шрифтами, знову відкривається з тим самим значенням value.
VS Code, Obsidian і плагін для JetBrains записують файл лише тоді, коли зміна робить значення value іншим, тож файл, збережений попереднім випуском, зберігає стару форму до першої зміни.

Починаючи з версії 4.2.0, форма зберігання також пропускає те, що нічого не містить: кожне текстове поле сутності або її ui, що має значення '' (name, comment і ui.color таблиці, name, comment, dataType і default стовпця, name індексу, value і ui.color нотатки, name і color групи таблиць), кожен список doc і кожну колекцію без жодного запису, а також саме поле collections, щойно всі колекції порожні. Текст лише з пробілів записується так, як він є, за винятком логічної назви, яка тоді пропускається, і слова, терміна чи домену, усі значення яких, крім ідентифікатора, порожні або складаються лише з пробілів: такий запис пропускається повністю. Кожен ідентифікатор, кожен список усередині сутності й кожне число, включно з 0, і далі записуються. Поле doc записується завжди, тож файл нового документа містить $schema, version, його налаштування й "doc": {}.
Під час розбору відсутнє текстове поле читається як '', а відсутня колекція — як порожня, як це робить кожен випуск v3, і форма зберігання пропускає список doc лише тоді, коли його колекція нічого не містить (див. Doc). Тож редактор до версії 4.2.0 теж відкриває такі файли, а програма, яка сама читає файл, має так само тлумачити те, чого в ньому бракує. Файл, збережений попереднім випуском, після наступного збереження повертається без порожнього тексту, списків і колекцій — ця зміна відбувається один раз, — а відтоді зберігається байт у байт.

Форма зберігання записує кожне заблоковане налаштування з його заблокованим значенням (див. lockSettings), settings.ddlScripts — лише поки скрипт містить текст, а поле groupId таблиці — лише поки воно вказує на групу таблиць, яку записує файл. Починаючи з версії 4.2.0, вона також записує settings.nameViewMode лише тоді, коли записуване значення не дорівнює 'physical', settings.columnOrder — сімома елементами там, де це можливо (див. Settings), а поле nameDerived таблиці — лише поки воно дорівнює true.
Вона записує doc.tableGroupIds і tableGroupEntities лише тоді, коли існує група таблиць, поле logicalName — лише поки воно містить щось, крім пробілів, поле domainId стовпця — лише поки воно вказує на домен, який записує файл, а кожен словник — лише поки він містить рядок, який записує файл.

Файл, збережений до версії 4.0.0, містить весь документ у тому вигляді, в якому його тоді тримав редактор, разом із видаленими сутностями й усім, що обчислює кожне завантаження. Завантаження читає його, зберігає місце кожної таблиці й нотатки в порядку накладання (див. Doc), решту обчислює заново, а наступне збереження записує лише форму зберігання.

TypeScript​

Типи описують форму зберігання, і кожен перелічує свої поля в тому порядку, в якому їх записує файл. Поле, позначене ?, — це поле, яке форма зберігання може пропустити, як докладно описано в розділі форма зберігання, а під час розбору відсутнє поле читається як таке, що нічого не містить: '', false, відсутня група таблиць чи домен, порожня колекція, 'physical' для nameViewMode і два порожні скрипти для ddlScripts. Виняток — відсутній список doc, який розбір заповнює з його колекції (див. Doc). У рядку словника ? позначає значення, перед якими рядок може закінчитися (див. рядки словників). Кожне інше поле записується завжди.

ERDEditorSchemaV3​

type ERDEditorSchemaV3 = {
$schema: 'https://raw.githubusercontent.com/dineug/erd-editor/main/json-schema/schema.json';
version: '3.0.0';
settings: Settings;
doc: Doc;
collections?: {
tableEntities?: Record<string, Table>;
tableColumnEntities?: Record<string, Column>;
relationshipEntities?: Record<string, Relationship>;
indexEntities?: Record<string, Index>;
indexColumnEntities?: Record<string, IndexColumn>;
memoEntities?: Record<string, Memo>;
tableGroupEntities?: Record<string, TableGroup>;
wordEntities?: DictionaryRows; // rows of Word
termEntities?: DictionaryRows; // rows of Term
domainEntities?: DictionaryRows; // rows of Domain
};
};

Кожна сутність має id — непрозорий рядок, що є її ключем у колекції, крім трьох словників, де це перше значення рядка. Починаючи з версії 4.2.0, під час розбору сутність однієї із семи колекцій із ключами, у якої id відсутній, порожній або не є рядком, отримує як ідентифікатор ключ, під яким її збережено, а запис, що не є об’єктом, або запис без ідентифікатора з ключем '' відкидається. Рядок словника відкидається, якщо він не є масивом або його id не є рядком, відмінним від ''. Редактор до версії 4.2.0 відкидає сутність, у якої id відсутній або порожній, тож форма зберігання й далі записує кожен ідентифікатор.
Починаючи з версії 4.0.0, кожен новий ідентифікатор, зокрема ідентифікатор рядка словника, — це UUIDv7, записаний у формі Uuid25: 25 цифр системи числення з основою 36 у нижньому регістрі, як-от 03gzrxjv20bdlcak89q25d2m7, тож ідентифікатори, які створює один редактор, сортуються в порядку їх створення. Документ зберігає ті ідентифікатори, які в ньому є, як-от ідентифікатори nanoid попереднього випуску або ідентифікатори файлу v2, тож ніщо не повинно покладатися на вигляд ідентифікатора.

Починаючи з версії 4.0.0, жодна сутність не містить об’єкта meta (createAt, updateAt), а типи EntityMeta і EntityType видалено. Ніщо його не записує, під час розбору об’єкт meta зі старішого файлу відкидається, а JSON Schema його відхиляє. Він лише задавав час збирання сміття для видалених сутностей, якого теж більше немає.

Settings​

type Settings = {
originX: number;
originY: number;
zoomLevel: number;
show: number; // Constants: Show
database: number; // Constants: Database
databaseName: string;
canvasType: string; // Constants: CanvasType
language: number; // Constants: Language
tableNameCase: number; // Constants: NameCase
columnNameCase: number; // Constants: NameCase
bracketType: number; // Constants: BracketType
relationshipDataTypeSync: boolean;
columnOrder: number[]; // Constants: ColumnType
maxWidthComment: number;
lockSettings: number; // Constants: LockSettingType
ddlScripts?: DDLScripts;
nameViewMode?: string; // Constants: NameViewMode
};

type DDLScripts = {
before: string;
after: string;
};

// Constants
const CanvasType = {
ERD: 'ERD',
visualization: '@dineug/erd-editor/builtin-visualization',
schemaSQL: '@dineug/erd-editor/builtin-schema-sql',
generatorCode: '@dineug/erd-editor/builtin-generator-code',
settings: 'settings',
dictionary: '@dineug/erd-editor/builtin-dictionary',
} as const;

const Show = {
tableComment: 1,
columnComment: 2,
columnDataType: 4,
columnDefault: 8,
columnAutoIncrement: 16,
columnPrimaryKey: 32,
columnUnique: 64,
columnNotNull: 128,
relationship: 256,
columnAlternateKey: 512,
hideReferentialAction: 1024,
hideTableGroup: 2048,
hideMemo: 4096,
columnDomain: 8192,
} as const;

const ColumnType = {
columnName: 1,
columnDataType: 2,
columnNotNull: 4,
columnUnique: 8,
columnAutoIncrement: 16,
columnDefault: 32,
columnComment: 64,
columnLogicalName: 128,
columnDomain: 256,
} as const;

const Database = {
MariaDB: 1,
MSSQL: 2,
MySQL: 4,
Oracle: 8,
PostgreSQL: 16,
SQLite: 32,
Databricks: 64,
Snowflake: 128,
} as const;

const Language = {
GraphQL: 1,
csharp: 2,
Java: 4,
Kotlin: 8,
TypeScript: 16,
JPA: 32,
Scala: 64,
Go: 128,
SQLAlchemy: 256,
TypeORM: 512,
Sequelize: 1024,
Drizzle: 2048,
DBML: 4096,
AML: 8192,
Mermaid: 16384,
PHP: 32768,
Doctrine: 65536,
Rust: 131072,
SeaORM: 262144,
Swift: 524288,
Zod: 1048576,
JSONSchema: 2097152,
} as const;

const NameCase = {
none: 1,
camelCase: 2,
pascalCase: 4,
snakeCase: 8,
} as const;

const BracketType = {
none: 1,
doubleQuote: 2,
singleQuote: 4,
backtick: 8,
} as const;

const LockSettingType = {
viewport: 1, // originX, originY and zoomLevel
canvasType: 2,
language: 4,
tableNameCase: 8,
columnNameCase: 16,
bracketType: 32,
unlockNameViewMode: 64, // nameViewMode; set while unlocked
} as const;

const NameViewMode = {
physical: 'physical',
logical: 'logical',
both: 'both',
} as const;

originX і originY — це область перегляду: точка екрана, на яку припадає точка сцени (0, 0).
Поле zoomLevel набуває значень від 0.1 до 1.5. Документ, збережений зі значенням понад 1, у редакторі до версії 3.5.0 відкривається зі значенням 1.
Починаючи з версії 4.0.0, налаштування не містять полів width, height, scrollTop, scrollLeft, relationshipOptimization чи ignoreSaveSettings. Ніщо їх не записує, парсер жодного з них не зберігає, а JSON Schema відхиляє файл, у якому вони ще є.

Поле canvasType — це вкладка, на якій відкривається документ. Документ, збережений на вкладці візуалізації, містить '@dineug/erd-editor/builtin-visualization', але не те, який режим було показано, Graph чи Flow, а щойно створений редактор показує цю вкладку в режимі Graph.
Починаючи з версії 4.0.0, поки блокування Canvas Type увімкнено, як у новому документі, документ зберігає вкладку, яку було показано в момент блокування, а перемикання вкладок залишає значення value без змін. Блокування, встановлене на вкладці налаштувань, зберігає вкладку, з якої ви на неї перейшли, а заблоковане значення 'settings', яке можна записати лише ручним редагуванням, відкривається на вкладці 'ERD'.
Починаючи з версії 4.2.0, поле canvasType також може мати значення '@dineug/erd-editor/builtin-dictionary', тобто вкладку словника, яку блокування Canvas Type утримує так само, як і інші. Редактор до версії 4.2.0 зберігає це значення, але не показує під своєю панеллю інструментів жодної вкладки, доки ви не виберете якусь.
Жодна частина власного стану режиму Flow не серіалізується: його розміщення, масштаб, панорамування, відображення рядків і таблиці у фокусі залишаються в редакторі, а починаючи з версії 4.2.0, там залишаються й подання, між якими переходять кнопки Previous table і Next table. Тож originX, originY і zoomLevel завжди описують полотно ERD.

Поля show і lockSettings є бітовими масками — об’єднуйте прапорці операцією OR.
Поле columnOrder — це масив, що містить кожне значення ColumnType по одному разу в порядку відображення; починаючи з версії 4.2.0, документ може записувати його сімома з дев’яти значень, як описано в абзаці про порядок стовпців нижче.
Кожне з полів database, language, tableNameCase, columnNameCase і bracketType містить рівно одне значення, а JSON Schema відхиляє комбіноване значення.

У новому документі поле show дорівнює 431: tableComment, columnComment, columnDataType, columnDefault, columnPrimaryKey, columnNotNull і relationship.
Починаючи з версії 3.10.0, поле show має ще два прапорці, обидва вимкнені в новому документі й у кожному документі, збереженому до версії 3.10.0.
Прапорець columnAlternateKey (512) відповідає параметру відображення Alternate Key. Коли його встановлено, він позначає стовпці кожного унікального індексу з двох або більше стовпців як AK1.1, AK1.2 і так далі. Спочатку він вимкнений, тож ширина жодної наявної діаграми не змінюється.
Прапорець hideReferentialAction (1024) працює навпаки: коли його встановлено, він ховає мітки ON DELETE і ON UPDATE на лініях зв’язку, тож за замовчуванням вони показуються. Параметр відображення Referential Actions позначено, поки прапорець вимкнений. Він не змінює розміру жодної таблиці.
Редактор до версії 3.10.0 тримає поле show як ціле число, тож обидва прапорці не втрачаються, коли документ зберігають у ньому.

Починаючи з версії 4.0.0, поле show має ще два прапорці, обидва вимкнені в новому документі й у кожному документі, збереженому до версії 4.0.0. Як і hideReferentialAction, кожен із них ховає те, що вказано в його назві, тож групи таблиць і нотатки за замовчуванням показуються.
Прапорець hideTableGroup (2048) ховає групи таблиць, а hideMemo (4096) ховає всі нотатки. Це параметри відображення Table Groups і Memos, кожен з яких позначено, поки його прапорець вимкнений. Жоден із них не змінює розміру жодної таблиці.
Редактор до версії 4.0.0 тримає поле show як ціле число, тож обидва прапорці не втрачаються, коли документ зберігають у ньому.

Починаючи з версії 4.2.0, поле show має ще один прапорець, columnDomain (8192), вимкнений у новому документі й у кожному документі, збереженому до версії 4.2.0, тож у новому документі поле show і далі дорівнює 431. Це параметр відображення Domain: коли прапорець встановлено, кожен рядок стовпця показує клітинку домену, і таблиці стають ширшими. Спочатку він вимкнений, тож ширина жодної наявної діаграми не змінюється.
Редактор до версії 4.2.0 тримає поле show як ціле число, тож прапорець не втрачається, коли документ зберігають у ньому.

Починаючи з версії 4.2.0, константа ColumnType має дев’ять значень. Значення columnLogicalName (128) задає місце клітинки логічної назви, яку рядок стовпця показує лише у варіанті Both Names, а columnDomain (256) — місце клітинки домену. Це рядки Logical Name і Domain порядку стовпців на вкладці налаштувань. Редактор тримає порядок з усіх дев’яти значень, а в новому документі цей порядок такий: 128, 1, 2, 256, 4, 8, 16, 32, 64.
Форма зберігання записує порядок сімома елементами, пропускаючи 128 і 256, поки 128 стоїть безпосередньо перед columnName, а 256 — одразу після columnDataType. Тож новий документ і далі записує 1, 2, 4, 8, 16, 32, 64, а файл, у порядку якого їх ніколи не переміщували, зберігає свої байти. В іншому разі вона записує усі дев’ять.
Під час розбору ці два значення вставляються в порядок із семи елементів саме на ці місця, хоч би в якому порядку стояли сім інших, порядок із дев’яти елементів, що містить кожен тип, береться як є.
Починаючи з версії 5.0.0, розбір приймає й будь-який інший порядок. Він зберігає кожен відомий йому ColumnType один раз, у заданому порядку, і відкидає кожен інший елемент: значення, якого не називає жоден ColumnType, як-от тип, що додасть пізніший випуск, і другий елемент того самого типу. Кожен тип, якого в порядку бракує, ставиться одразу після найближчого типу перед ним у типовому порядку, який цей порядок містить, а якщо перед ним нічого немає — одразу перед найближчим наступним, саме так 128 і 256 знаходять свої місця в порядку із семи елементів. Як типовий порядок читається лише порядок, що не називає жодного відомого типу, або значення, яке не є масивом.
Тож файл, збережений пізнішим випуском, відкривається з порядком типів, відомих цьому випуску, а збереження тут записує цей порядок без інших. До версії 5.0.0 розбір читав будь-який інший порядок як типовий, тож такий файл відкривався зі скинутим порядком стовпців. JSON Schema приймає сім або дев’ять цілих чисел.

Починаючи з версії 4.0.0, константа Language має ще вісім цільових форматів генератора коду, від Mermaid до JSONSchema. Значення language, якого парсер не знає, читається як GraphQL, тож редактор до версії 4.0.0 відкриває документ, збережений з одним із них, з мовою GraphQL, а збереження в ньому записує GraphQL.

Починаючи з версії 4.0.0, поле lockSettings замінює ignoreSaveSettings, а LockSettingType — тип SaveSettingType, який видалено. Поле lockSettings визначає налаштування, які документ зберігає такими, якими вони були в момент блокування. Біт viewport охоплює originX, originY і zoomLevel разом, оскільки початок координат, збережений без свого масштабу, відкривається в іншому місці, а кожен інший біт охоплює однойменне поле. Це рядки Lock вкладки налаштувань.
Файл містить кожне заблоковане налаштування з його заблокованим значенням, хоч би що показував редактор, а кожне інше налаштування — таким, яким воно є зараз. Тож поки відповідне блокування увімкнено, прокручування й масштабування, перемикання вкладок, зміна мови генератора коду, стилю назв або типу лапок чи перемикання відображення назв залишають файл без змін. Блокування бере значення, яке налаштування має в цей момент, а під час розбору заблоковане значення береться зі збереженого поля.
Документ, створений з нуля, — у новому редакторі чи сховищі реплікації або з порожнього значення, переданого у value чи setInitialValue(), — має поле lockSettings зі значенням 63: усі блокування ввімкнено. Документ, поле settings якого не містить lockSettings, — кожен файл, збережений до версії 4.0.0, незалежно від його ignoreSaveSettings, і кожен документ v2, — так само відкривається з усіма ввімкненими блокуваннями: його область перегляду й вкладка такі самі, як у нового документа (originX і originY дорівнюють 0, zoomLevel — 1, вкладка ERD), а його мову, стилі назв і тип лапок заблоковано на збережених значеннях. Документ, який указує свої блокування, зберігає їх, включно з 0, і всі збережені значення.
Під час розбору зберігаються всі біти lockSettings, зокрема невідомі, тож блокування, яке додасть пізніший випуск, не втрачається під час збереження тут; JSON Schema перевіряє лише те, що це ціле число. Редактор до версії 4.0.0 під час збереження відкидає lockSettings і записує область перегляду й вкладку такими, якими вони є, тож тут файл знову відкривається як збережений до версії 4.0.0.

Починаючи з версії 4.2.0, є сьоме блокування, unlockNameViewMode (64), — рядок Name View, що охоплює nameViewMode. Його біт читається навпаки порівняно з іншими бітами блокувань, як і біти приховування в Show: коли його встановлено, відображення назв розблоковано, а коли скинуто — заблоковано. Тож 63 і далі означає, що всі блокування ввімкнено, і новий документ містить це значення з відображенням назв, заблокованим на 'physical'. Файл, збережений до версії 4.2.0, містить цей біт скинутим незалежно від інших блокувань, включно з 0, тож відкривається з відображенням назв, заблокованим на 'physical'. Документ з усіма вимкненими блокуваннями містить 64, а документ з усіма іншими ввімкненими блокуваннями й розблокованим відображенням назв — 127. Перевіряйте це блокування з інверсією: воно ввімкнене, поки (lockSettings & 64) === 0.
Поки біт скинуто, файл містить у полі nameViewMode заблокований варіант, хоч би що показував будь-який редактор, а поки його встановлено — поточний варіант. Що показує кожен редактор, коли блокування ввімкнено чи вимкнено, описано в розділі Lock.
Редактор версії 4.0.0 або 4.1.0 під час збереження зберігає цей біт, але відкидає nameViewMode, тож тут документ знову відкривається зі значенням 'physical', а його блокування залишається таким, яким було.

Починаючи з версії 4.0.0, поле ddlScripts містить два скрипти вкладки схеми SQL, які записує DDL для кожної бази даних: before — після заголовка й перед таблицями, after — після згенерованого DDL. DDL записує кожен скрипт з розривами рядків LF, без початкових порожніх рядків і кінцевих пробільних символів, а для MSSQL — із GO наприкінці, як описано в розділі скрипти перед таблицями й після них; саме поле ddlScripts зберігає кожен скрипт у тому вигляді, в якому його введено. Ключ записується лише тоді, коли хоча б один скрипт містить текст, і тоді — з обома скриптами; відсутній ключ або скрипт, що не є рядком, читається як ''. Вибір у параметрах вкладки Statements, Header і Tables, а також, починаючи з версії 4.2.0, її перемикач Write logical names into empty comments не зберігаються в документі.
Редактор до версії 4.0.0 відкидає скрипти під час завантаження документа, тож під час збереження в ньому вони втрачаються.

Починаючи з версії 4.2.0, поле nameViewMode — це відображення назв, тобто назви, які показує діаграма: 'physical' — назву кожної таблиці й кожного стовпця, 'logical' — їхню логічну назву на місці назви, а 'both' — логічну назву й назву. У меню ці значення називаються Physical Names, Logical Names і Both Names. Форма зберігання записує це поле останнім, після ddlScripts, і лише тоді, коли записуване значення не дорівнює 'physical'. Відсутнє значення або таке, якого парсер не знає, читається як 'physical', а JSON Schema приймає лише "logical" і "both".
Редактор до версії 4.2.0 відкидає nameViewMode під час завантаження документа.

Константи Database, Language, Show, ColumnType, ColumnOption, LockSettingType і ReferentialAction розширюються лише додаванням у кінець. У збереженому документі записано число, тож нова СУБД, цільовий формат генерації коду, прапорець, тип стовпця, параметр стовпця, блокування чи дія займає наступний біт, а не місце посередині.

Doc​

type Doc = {
tableIds?: string[];
relationshipIds?: string[];
indexIds?: string[];
memoIds?: string[];
tableGroupIds?: string[];
};

Кожен список задає порядок сутностей одного виду, який зчитують DDL, сортування таблиць і порядок малювання. Кожен містить лише ідентифікатори сутностей, які записує файл, кожен по одному разу, у тому порядку, в якому їх перелічено, а починаючи з версії 4.2.0, список, що не містить жодного ідентифікатора, пропускається. Починаючи з версії 4.0.0, поле tableGroupIds задає порядок груп таблиць. Словники не мають тут списку, бо їхні рядки стоять у порядку аркуша (див. рядки словників), а JSON Schema не приймає в doc жодного іншого ключа.

Починаючи з версії 4.2.0, під час розбору список, який документ пропускає або містить не як масив, читається як усі записи його колекції, кожен ідентифікатор по одному разу, у збереженому порядку; порожній масив залишається порожнім, і тоді кожен запис його колекції читається як видалений. Редактор до версії 4.2.0 читає відсутній список як порожній, тож форма зберігання й далі записує кожен список, щойно його колекція містить сутність.

Починаючи з версії 4.0.0, файл не містить порядку накладання. Завантаження розміщує перелічені таблиці й нотатки відповідно до їхніх позицій у списках, пізніші — попереду, а кожну нотатку — над усіма таблицями, і так само групи таблиць між собою, позаду таблиць. Таблиця чи нотатка у файлі, збереженому до версії 4.0.0, має в ui поле zIndex, яке завантаження зберігає як її місце в порядку накладання, а наступне збереження пропускає. Тому виведення таблиці чи нотатки на передній план утрачається, коли файл відкривають знову.

Колекція: tableEntities​

type Table = {
id: string;
name?: string;
logicalName?: string;
comment?: string;
columnIds: string[];
groupId?: string;
nameDerived?: true;
ui: TableUI;
};

type TableUI = {
x: number;
y: number;
color?: string;
};

Починаючи з версії 4.0.0, таблиця містить поле groupId — ідентифікатор групи таблиць, до якої вона належить. Відсутнє чи порожнє поле groupId або таке, що не вказує на жодну групу в doc.tableGroupIds, означає відсутність групи, і форма зберігання записує його лише тоді, коли воно вказує на наявну групу.

Починаючи з версії 4.2.0, таблиця містить поле logicalName — назву, яку показують варіанти Logical Names і Both Names, або '', якщо логічної назви немає, — і поле nameDerived, яке має значення true, поки name сформовано словниками з логічної назви. Поки це поле встановлено, нова логічна назва знову формує назву; поки ні, назву задано вручну, і нова логічна назва її не чіпає, якщо тільки назва не порожня й не складається лише з пробілів. Перейменування вручну вимикає це поле, а пункт Derive Physical Names Again переписує навіть назву, задану вручну, і вмикає його скрізь, де є логічна назва.
Форма зберігання записує logicalName після name так, як його введено, і лише поки воно містить щось, крім пробілів, а nameDerived — після groupId і лише поки воно дорівнює true, єдиному значенню, яке приймає JSON Schema. Відсутнє поле logicalName читається як '', а відсутнє поле nameDerived — як false, тож кожна назва у файлі, збереженому до версії 4.2.0, вважається заданою вручну.
Редактор до версії 4.2.0 відкидає обидва поля під час завантаження документа, тож під час збереження в ньому втрачається логічна назва кожної таблиці.

Колекція: tableColumnEntities​

type Column = {
id: string;
tableId: string;
name?: string;
comment?: string;
dataType?: string;
default?: string;
options: number; // Constants: ColumnOption
logicalName?: string;
domainId?: string;
};

// Constants
const ColumnOption = {
autoIncrement: 1,
primaryKey: 2,
unique: 4,
notNull: 8,
nameDerived: 16,
} as const;

Починаючи з версії 4.0.0, файл не містить позначок ключів: кожне завантаження позначає стовпець як первинний ключ за бітом primaryKey поля options, а як зовнішній ключ — поки на ньому в його таблиці закінчується зв’язок.
Редактор до версії 4.0.0 показує стовпці файлу, збереженого тут, без значків первинного ключа.

Починаючи з версії 4.2.0, стовпець містить поле logicalName, як і таблиця, і поле domainId — ідентифікатор домену, до якого його прив’язано, або '', якщо прив’язки немає. Зміни в одному редакторі тримають прив’язаний стовпець на типі даних його домену, як описано в розділі домени й типи даних. Проте під час розбору тип жодного стовпця не змінюється, тож файл, записаний деінде, може залишити прив’язаний стовпець з іншим типом, як і перехресні зміни двох співавторів; аркуш перевірок показує кожен такий випадок як Domain type mismatch. Під час розбору очищається поле domainId, що не вказує на жоден домен, який містить файл. Після options форма зберігання записує logicalName за тим самим правилом, що й для таблиці, а domainId — лише поки воно вказує на домен, який записує файл, тож стовпець, прив’язаний до видаленого домену або до такого, який файл пропускає, записується без прив’язки й з типом даних, який він має.
Біт ColumnOption.nameDerived (16) — це прапорець сформованої назви стовпця, який зберігається в options разом з іншими бітами й означає те саме, що поле nameDerived таблиці. Наприклад, первинний ключ NOT NULL, назву якого сформували словники, має значення options, що дорівнює 26.
Редактор до версії 4.2.0 відкидає logicalName і domainId під час завантаження документа, тож під час збереження в ньому вони втрачаються. Він тримає options як ціле число, тож біт nameDerived не втрачається, коли документ зберігають у ньому.

Колекція: relationshipEntities​

type Relationship = {
id: string;
relationshipType: number; // Constants: RelationshipType
onDelete: number; // Constants: ReferentialAction
onUpdate: number; // Constants: ReferentialAction
start: RelationshipPoint;
end: RelationshipPoint;
};

type RelationshipPoint = {
tableId: string;
columnIds: string[];
};

// Constants
const RelationshipType = {
ZeroOne: 2,
ZeroN: 4,
OneOnly: 8,
OneN: 16,
} as const;

const ReferentialAction = {
none: 1,
noAction: 2,
cascade: 4,
setNull: 8,
setDefault: 16,
restrict: 32,
} as const;

Починаючи з версії 3.10.0, зв’язок містить поля onDelete і onUpdate — свої дії ON DELETE і ON UPDATE. Кожне з них містить рівно одне значення ReferentialAction. Типове значення none не записує жодної конструкції, тож застосовується власне типове значення бази даних. Інші п’ять відповідають NO ACTION, CASCADE, SET NULL, SET DEFAULT і RESTRICT. Схема SQL записує конструкцію лише для бази даних, яка підтримує відповідну дію.
Форма зберігання завжди записує обидва поля, і, починаючи з версії 4.0.0, JSON Schema вимагає їх, перевіряючи лише те, що кожне з них є цілим числом. Під час розбору відсутнє поле, як-от у кожному документі, збереженому до версії 3.10.0, і далі читається як none, і так само читається значення поза списком.
Редактор до версії 3.10.0 не знає цих полів і відкидає їх під час завантаження документа, тож під час збереження в ньому всі дії втрачаються.

Починаючи з версії 4.0.0, кожне завантаження зчитує, чи є зв’язок ідентифікувальним і як намальовано його батьківський кінець, зі стовпців, на яких він закінчується і які дочірня таблиця досі перелічує. Він ідентифікувальний, тобто має суцільну лінію, поки кожен такий стовпець є первинним ключем, а його батьківський кінець — це дві риски, поки кожен такий стовпець є NOT NULL, а в іншому разі — кільце й риска. Зв’язок, у якого не залишилося жодного такого стовпця, є неідентифікувальним з двома рисками, як і щойно створений зв’язок. Див. як читати лінію зв’язку.

Колекція: indexEntities​

type Index = {
id: string;
name?: string;
tableId: string;
indexColumnIds: string[];
unique: boolean;
};

Колекція: indexColumnEntities​

type IndexColumn = {
id: string;
indexId: string;
columnId: string;
orderType: number; // Constants: OrderType
};

// Constants
const OrderType = {
ASC: 1,
DESC: 2,
} as const;

Колекція: memoEntities​

type Memo = {
id: string;
value?: string;
ui: MemoUI;
};

type MemoUI = {
x: number;
y: number;
width: number;
height: number;
color?: string;
};

Починаючи з версії 4.0.0, таблиці й нотатки мають спільний стек, тож виведення таблиці на передній план може розмістити її поверх нотатки. Завантаження розміщує кожну нотатку над усіма таблицями, як описано в розділі Doc. Нотатка у файлі, збереженому до версії 4.0.0, зберігає записаний для неї zIndex, який брався з того самого лічильника, що й значення таблиць, тож нотатка, яку попередній випуск малював поверх усіх таблиць, може відкритися під таблицею, виведеною на передній план після неї.

Колекція: tableGroupEntities​

type TableGroup = {
id: string;
name?: string;
color?: string;
ui: TableGroupUI;
};

type TableGroupUI = {
x: number;
y: number;
width: number;
height: number;
};

Починаючи з версії 4.0.0, документ може містити групи таблиць: іменовані кольорові прямокутники, що малюються позаду таблиць. Таблиця приєднується до групи через власне поле groupId, тож група не містить списку членів. Форма зберігання записує цю колекцію, як і doc.tableGroupIds, лише тоді, коли існує група, а документ без неї, як-от збережений до версії 4.0.0, не має груп. Прапорець Show.hideTableGroup ховає їх.
Редактор до версії 4.0.0 під час завантаження документа відкидає групи й поле groupId кожної таблиці, тож під час збереження в ньому всі групи втрачаються.

Рядки словників​

Починаючи з версії 4.2.0, документ містить три словники — аркуші Words, Terms і Domains вкладки словника — у wordEntities, termEntities і domainEntities. Кожне поле слова, терміна чи домену є рядком, і жоден із них не має ui. Аркуш Checks обчислюється зі словників і діаграми й не зберігається.

type DictionaryRows = {
fields: string[];
rows: string[][];
};

Жоден із трьох не має ключів-ідентифікаторів. Кожен записано як заголовок і рядки: fields один раз називає поля сутності, починаючи з ідентифікатора, а кожен рядок містить значення однієї сутності в цьому порядку. Типи нижче перелічують поля кожної сутності в цьому порядку. Рядок завжди зберігає свій ідентифікатор і назву, але його кінцеві порожні значення після назви пропускаються, тож порожнє значення перед збереженим залишається '', а під час розбору значення, якого бракує в рядку, читається як ''. Кожен рядок записано в окремому рядку тексту як компактний JSON, як у цих двох словах із файлу:

"wordEntities": {
"fields": [
"id",
"name",
"abbreviation",
"englishName",
"description",
"synonyms",
"forbiddenWords"
],
"rows": [
["w1","Customer","CUST","customer"],
["w2","Number","NO","number","","No, Num","Nbr"]
]
}

Форма зберігання записує рядки кожного аркуша в порядку аркуша, без поля порядку, тож рядки файлу самі задають склад словника й порядок у ньому. Вона пропускає рядок, усі значення якого, крім ідентифікатора, порожні або лише з пробілів, як-от доданий і так і не заповнений або спорожнений, а також кожну прив’язку до такого домену чи до видаленого: domainId стовпця пропускається, а domainId терміна записується як '', причому сам термін пропускається, якщо більше нічого не містить.
Під час розбору рядки читаються в тому порядку, в якому стоять, тож кожен аркуш перелічує їх у тому порядку, в якому їх записав файл.
Документ без словників, як-от збережений до версії 4.2.0, не має жодного з них. Редактор до версії 4.2.0 відкидає всі три під час завантаження документа, тож під час збереження в ньому втрачаються всі слова, терміни й домени.

Колекція: wordEntities​

type Word = {
id: string;
name: string;
abbreviation?: string;
englishName?: string;
description?: string;
synonyms?: string;
forbiddenWords?: string;
};

Слово — це одне зі слів, з яких складається логічна назва, а abbreviation — те, що позначає його у фізичній назві. Поле synonyms перелічує інші написання слова, а forbiddenWords — слова, які не можна вживати замість нього; обидва списки розділено комами.

Колекція: termEntities​

type Term = {
id: string;
name: string;
physicalName?: string;
domainId?: string;
description?: string;
synonyms?: string;
};

Термін — це ціла логічна назва. Таблиця чи стовпець, у яких logicalName збігається з name, формують свою назву з physicalName або, поки воно порожнє, складають її зі скорочень слів, а стовпець також отримує домен, на який вказує domainId, або '', якщо домену немає (див. формування фізичних назв). Поле synonyms розділено комами. Під час розбору domainId, що не вказує на жоден домен, який містить файл, очищається, як і в стовпця.

Колекція: domainEntities​

type Domain = {
id: string;
name: string;
category?: string;
dataType?: string;
description?: string;
};

Домен — це іменований тип даних: dataType містить його так само, як у стовпці, разом із довжиною, як-от NUMERIC(10). Стовпець або термін прив’язується до домену за його ідентифікатором, а редактор задає кожному прив’язаному стовпцю тип даних домену (див. домени й типи даних).

JSON Schema​

Канонічний файл схеми опубліковано за адресою json-schema/schema.json, і на нього можна посилатися безпосередньо через $schema.

{
"$schema": "https://raw.githubusercontent.com/dineug/erd-editor/main/json-schema/schema.json",
"version": "3.0.0"
}

Розмістіть поле $schema на початку файлу .erd.json, і редактори, які розуміють JSON Schema, як-от VS Code, перевірятимуть документ і автодоповнюватимуть його поля під час редагування.

Починаючи з версії 4.0.0, схема описує лише форму зберігання. Кожен об’єкт з іменованими полями закритий (additionalProperties: false), кожне поле, яке форма зберігання завжди записує, є обов’язковим, кожне поле, позначене ? у наведених вище типах, названо, але не позначено обов’язковим, а жодного іншого поля не названо. Тому вона відхиляє файл, збережений до версії 4.0.0, з його meta, ширинами, zIndex, ignoreSaveSettings тощо, доки редактор версії 4.0.0 або новішої не збереже цей файл знову. Файл, збережений версією 3.2.0 або новішою, уже вказує URL цієї схеми, що веде на гілку main, тож редактор, який перевіряє файл за нею, тепер позначає його як помилковий. Файл, збережений версією 3.0.0 чи 3.1.0, або документ v2 не вказує поля $schema.

Починаючи з версії 4.2.0, схема більше не вимагає текстових полів сутності, жодного списку doc, жодної колекції чи самого поля collections, які форма зберігання пропускає, поки вони нічого не містять. Вона й далі приймає файл, збережений версією 4.0.0 або 4.1.0. Вона називає нові поля: nameViewMode, яке приймає лише "logical" і "both"; logicalName і nameDerived таблиці, причому nameDerived приймає лише true; logicalName і domainId стовпця; а також три словники, кожен із заголовком і рядками з рядкових значень. Поле canvasType перелічує вкладку словника, columnOrder приймає сім або дев’ять цілих чисел, а описи show, columnOrder, lockSettings і options називають нові біти.

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://raw.githubusercontent.com/dineug/erd-editor/main/json-schema/schema.json",
"title": "ERD Editor document",
"description": "The storage form an ERD Editor file holds (.erd, .erd.json, .vuerd, .vuerd.json): the live entities alone, each collection keyed in id order but the dictionaries, a header and one row per entry in document order, without what an editor derives on load or keeps while it is open, without an entity's text field while it is empty, which reads as the empty string, and without an empty list or collection, which reads as none",
"type": "object",
"properties": {
"$schema": {
"type": "string"
},
"version": {
"description": "Version of the schema",
"const": "3.0.0"
},
"settings": {
"$ref": "#/$defs/settings"
},
"doc": {
"$ref": "#/$defs/doc"
},
"collections": {
"description": "The entities of each kind, a kind with none left out",
"type": "object",
"properties": {
"tableEntities": {
"$ref": "#/$defs/tableEntities"
},
"tableColumnEntities": {
"$ref": "#/$defs/tableColumnEntities"
},
"relationshipEntities": {
"$ref": "#/$defs/relationshipEntities"
},
"indexEntities": {
"$ref": "#/$defs/indexEntities"
},
"indexColumnEntities": {
"$ref": "#/$defs/indexColumnEntities"
},
"memoEntities": {
"$ref": "#/$defs/memoEntities"
},
"tableGroupEntities": {
"$ref": "#/$defs/tableGroupEntities"
},
"wordEntities": {
"$ref": "#/$defs/wordEntities"
},
"termEntities": {
"$ref": "#/$defs/termEntities"
},
"domainEntities": {
"$ref": "#/$defs/domainEntities"
}
},
"additionalProperties": false
}
},
"required": ["$schema", "version", "settings", "doc"],
"additionalProperties": false,
"$defs": {
"settings": {
"type": "object",
"properties": {
"originX": {
"type": "number"
},
"originY": {
"type": "number"
},
"zoomLevel": {
"type": "number",
"minimum": 0.1,
"maximum": 1.5
},
"show": {
"description": "bit value (tableComment: 1) | (columnComment: 2) | (columnDataType: 4) | (columnDefault: 8) | (columnAutoIncrement: 16) | (columnPrimaryKey: 32) | (columnUnique: 64) | (columnNotNull: 128) | (relationship: 256) | (columnAlternateKey: 512) | (hideReferentialAction: 1024) | (hideTableGroup: 2048) | (hideMemo: 4096) | (columnDomain: 8192); the three hide bits hide what they name while set",
"type": "integer"
},
"database": {
"description": "bit value (MariaDB: 1) | (MSSQL: 2) | (MySQL: 4) | (Oracle: 8) | (PostgreSQL: 16) | (SQLite: 32) | (Databricks: 64) | (Snowflake: 128)",
"type": "integer",
"enum": [1, 2, 4, 8, 16, 32, 64, 128]
},
"databaseName": {
"type": "string"
},
"canvasType": {
"enum": [
"ERD",
"@dineug/erd-editor/builtin-visualization",
"@dineug/erd-editor/builtin-schema-sql",
"@dineug/erd-editor/builtin-generator-code",
"settings",
"@dineug/erd-editor/builtin-dictionary"
]
},
"language": {
"description": "bit value (GraphQL: 1) | (csharp: 2) | (Java: 4) | (Kotlin: 8) | (TypeScript: 16) | (JPA: 32) | (Scala: 64) | (Go: 128) | (SQLAlchemy: 256) | (TypeORM: 512) | (Sequelize: 1024) | (Drizzle: 2048) | (DBML: 4096) | (AML: 8192) | (Mermaid: 16384) | (PHP: 32768) | (Doctrine: 65536) | (Rust: 131072) | (SeaORM: 262144) | (Swift: 524288) | (Zod: 1048576) | (JSONSchema: 2097152)",
"type": "integer",
"enum": [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152]
},
"tableNameCase": {
"description": "bit value (none: 1) | (camelCase: 2) | (pascalCase: 4) | (snakeCase: 8)",
"type": "integer",
"enum": [1, 2, 4, 8]
},
"columnNameCase": {
"description": "bit value (none: 1) | (camelCase: 2) | (pascalCase: 4) | (snakeCase: 8)",
"type": "integer",
"enum": [1, 2, 4, 8]
},
"bracketType": {
"description": "bit value (none: 1) | (doubleQuote: 2) | (singleQuote: 4) | (backtick: 8)",
"type": "integer",
"enum": [1, 2, 4, 8]
},
"relationshipDataTypeSync": {
"type": "boolean"
},
"columnOrder": {
"description": "bit value (columnName: 1) | (columnDataType: 2) | (columnNotNull: 4) | (columnUnique: 8) | (columnAutoIncrement: 16) | (columnDefault: 32) | (columnComment: 64) | (columnLogicalName: 128) | (columnDomain: 256); seven entries without the last two while the logical name stands just before the name and the domain just after the data type, all nine otherwise",
"type": "array",
"prefixItems": [
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" },
{ "type": "integer" }
],
"anyOf": [
{ "minItems": 7, "maxItems": 7 },
{ "minItems": 9, "maxItems": 9 }
]
},
"maxWidthComment": {
"type": "integer"
},
"lockSettings": {
"description": "bit value (viewport: 1) | (canvasType: 2) | (language: 4) | (tableNameCase: 8) | (columnNameCase: 16) | (bracketType: 32) | (unlockNameViewMode: 64); a locked setting is saved as it stood when locked, the viewport being originX, originY and zoomLevel. The last bit reads the other way round: set, the name view mode is unlocked, and clear, as in a file saved before it, locked",
"type": "integer"
},
"ddlScripts": {
"description": "SQL the Schema SQL writes as is for every database: before after the header and ahead of the tables, after past the generated DDL. Absent while both are empty",
"type": "object",
"properties": {
"before": { "type": "string" },
"after": { "type": "string" }
},
"required": ["before", "after"],
"additionalProperties": false
},
"nameViewMode": {
"description": "The names the diagram shows: the logical ones, or both the physical and the logical. Absent while it shows the physical names, the default",
"enum": ["logical", "both"]
}
},
"required": [
"originX",
"originY",
"zoomLevel",
"show",
"database",
"databaseName",
"canvasType",
"language",
"tableNameCase",
"columnNameCase",
"bracketType",
"relationshipDataTypeSync",
"columnOrder",
"maxWidthComment",
"lockSettings"
],
"additionalProperties": false
},
"doc": {
"description": "The order of each kind of entity, which the DDL, the sort and the draw order read, a kind with none left out. A list goes out whenever its collection holds an entity, since a reader that finds none lists none",
"type": "object",
"properties": {
"tableIds": {
"type": "array",
"items": {
"type": "string"
}
},
"relationshipIds": {
"type": "array",
"items": {
"type": "string"
}
},
"indexIds": {
"type": "array",
"items": {
"type": "string"
}
},
"memoIds": {
"type": "array",
"items": {
"type": "string"
}
},
"tableGroupIds": {
"description": "Order of the table groups. Written while a group exists; absent means none",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
},
"tableEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"logicalName": {
"description": "The name the logical view shows; absent means none",
"type": "string"
},
"comment": {
"type": "string"
},
"columnIds": {
"type": "array",
"items": {
"type": "string"
}
},
"groupId": {
"description": "Id of the table group in doc.tableGroupIds the table belongs to; absent means none",
"type": "string"
},
"nameDerived": {
"description": "The dictionaries filled the name, so a new logical name derives it again; absent means a name someone wrote",
"type": "boolean",
"const": true
},
"ui": {
"type": "object",
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"color": {
"type": "string"
}
},
"required": ["x", "y"],
"additionalProperties": false
}
},
"required": ["id", "columnIds", "ui"],
"additionalProperties": false
}
},
"tableColumnEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"tableId": {
"type": "string"
},
"name": {
"type": "string"
},
"comment": {
"type": "string"
},
"dataType": {
"type": "string"
},
"default": {
"type": "string"
},
"options": {
"description": "bit value (autoIncrement: 1) | (primaryKey: 2) | (unique: 4) | (notNull: 8) | (nameDerived: 16); nameDerived is set while the dictionaries filled the name",
"type": "integer"
},
"logicalName": {
"description": "The name the logical view shows; absent means none",
"type": "string"
},
"domainId": {
"description": "Id of the domain in domainEntities that governs the data type; absent means none",
"type": "string"
}
},
"required": ["id", "tableId", "options"],
"additionalProperties": false
}
},
"relationshipEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"relationshipType": {
"description": "bit value (ZeroOne: 2) | (ZeroN: 4) | (OneOnly: 8) | (OneN: 16)",
"type": "integer"
},
"onDelete": {
"description": "bit value (none: 1) | (noAction: 2) | (cascade: 4) | (setNull: 8) | (setDefault: 16) | (restrict: 32); none, the default, writes no ON DELETE clause",
"type": "integer"
},
"onUpdate": {
"description": "bit value (none: 1) | (noAction: 2) | (cascade: 4) | (setNull: 8) | (setDefault: 16) | (restrict: 32); none, the default, writes no ON UPDATE clause",
"type": "integer"
},
"start": {
"$ref": "#/$defs/RelationshipPoint"
},
"end": {
"$ref": "#/$defs/RelationshipPoint"
}
},
"required": [
"id",
"relationshipType",
"onDelete",
"onUpdate",
"start",
"end"
],
"additionalProperties": false
}
},
"indexEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"tableId": {
"type": "string"
},
"indexColumnIds": {
"type": "array",
"items": {
"type": "string"
}
},
"unique": {
"type": "boolean"
}
},
"required": ["id", "tableId", "indexColumnIds", "unique"],
"additionalProperties": false
}
},
"indexColumnEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"indexId": {
"type": "string"
},
"columnId": {
"type": "string"
},
"orderType": {
"description": "bit value (ASC: 1) | (DESC: 2)",
"type": "integer"
}
},
"required": ["id", "indexId", "columnId", "orderType"],
"additionalProperties": false
}
},
"memoEntities": {
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"value": {
"type": "string"
},
"ui": {
"type": "object",
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
},
"color": {
"type": "string"
}
},
"required": ["x", "y", "width", "height"],
"additionalProperties": false
}
},
"required": ["id", "ui"],
"additionalProperties": false
}
},
"tableGroupEntities": {
"description": "Named, coloured rectangles drawn behind tables; a table joins one through its groupId. Written while a group exists; absent means none",
"type": "object",
"additionalProperties": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"color": {
"type": "string"
},
"ui": {
"type": "object",
"properties": {
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
}
},
"required": ["x", "y", "width", "height"],
"additionalProperties": false
}
},
"required": ["id", "ui"],
"additionalProperties": false
}
},
"wordEntities": {
"description": "The words of the dictionary, the parts a logical name is composed of, each with its abbreviation in a physical name. Written while a word exists; absent means none",
"type": "object",
"properties": {
"fields": {
"const": ["id", "name", "abbreviation", "englishName", "description", "synonyms", "forbiddenWords"]
},
"rows": {
"description": "One row per entry in document order, its values in the order fields names them; values past the name are left out from the last empty one back",
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" }
],
"items": false,
"minItems": 2
}
}
},
"required": ["fields", "rows"],
"additionalProperties": false
},
"termEntities": {
"description": "The terms of the dictionary, logical names a column matches by its text, each with its physical name and domain id. Written while a term exists; absent means none",
"type": "object",
"properties": {
"fields": {
"const": ["id", "name", "physicalName", "domainId", "description", "synonyms"]
},
"rows": {
"description": "One row per entry in document order, its values in the order fields names them; values past the name are left out from the last empty one back",
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" }
],
"items": false,
"minItems": 2
}
}
},
"required": ["fields", "rows"],
"additionalProperties": false
},
"domainEntities": {
"description": "The domains of the dictionary, named data types a column or a term links to by id. Written while a domain exists; absent means none",
"type": "object",
"properties": {
"fields": {
"const": ["id", "name", "category", "dataType", "description"]
},
"rows": {
"description": "One row per entry in document order, its values in the order fields names them; values past the name are left out from the last empty one back",
"type": "array",
"items": {
"type": "array",
"prefixItems": [
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" },
{ "type": "string" }
],
"items": false,
"minItems": 2
}
}
},
"required": ["fields", "rows"],
"additionalProperties": false
},
"RelationshipPoint": {
"type": "object",
"properties": {
"tableId": {
"type": "string"
},
"columnIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": ["tableId", "columnIds"],
"additionalProperties": false
}
}
}