Схема
.erd.json — это формат документа редактора. Прочитайте эту страницу, если нужно генерировать или проверять документ либо выполнять его миграцию вне редактора.
На этой странице описана форма хранения — документ в том виде, в каком его содержит файл, — которую записывает каждая версия начиная с 4.0.0. Ее возвращает editor.value, ее записывает экспорт в JSON, и это единственная форма, которую принимает опубликованная JSON Schema. Ее поле version равно '3.0.0', как и в любом файле v3, сохраненном более ранней версией, поэтому по полю version старый файл от нового не отличить. Метод setInitialValue(), сеттер value и метод setSchemaJSON() читают ее, любой файл, сохраненный более ранней версией, и документ .vuerd формата v2. До версии 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
}
}
}