- *.archify-decision files: id (unique, words), title, status, date, elements, diagrams, supersedes, and prose; parsed, merged and link-checked in the model index; diagnostics on the file - diagrams point at decisions with decisions: [...] on the diagram, nodes and edges; stripped before Archify, unknown ids reported, completion, hover and go-to-definition for the ids and for elements/diagrams/ supersedes in decision files - Decisions panel on the main.archify page; Decisions rail panel under a diagram (hover lights up the node or connection); decisions on element cards and in the element dialog; decision view for the file, with a lost-model note outside a model - Archify | New Decision… command and template; editor schemas; the three New Model examples carry decisions; docs, changelog, 0.8.0
118 lines
5.9 KiB
JavaScript
118 lines
5.9 KiB
JavaScript
#!/usr/bin/env node
|
||
// Adds the model keys to the editor schemas in schemas/ (the CLI keeps the
|
||
// strict originals): `model` on main.archify, `ref` and `diagram` on
|
||
// architecture components, and a schema for *.archify-registry files.
|
||
// Run after sync-archify.mjs, or on its own after editing this file.
|
||
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||
const schemas = path.join(root, 'schemas');
|
||
const read = (file) => JSON.parse(fs.readFileSync(path.join(schemas, file), 'utf8'));
|
||
const write = (file, value) => fs.writeFileSync(path.join(schemas, file), `${JSON.stringify(value, null, 2)}\n`);
|
||
|
||
const MODEL = {
|
||
type: 'object',
|
||
description: 'Only in main.archify: the entry point of a model (a folder of diagrams that belong together).',
|
||
additionalProperties: false,
|
||
required: ['id', 'name'],
|
||
properties: {
|
||
id: { type: 'string', pattern: '^[a-zA-Z][a-zA-Z0-9_-]*$', description: 'Stable id of the model.' },
|
||
name: { type: 'string', minLength: 1, description: 'Name shown on the model page.' },
|
||
description: { type: 'string', description: 'What the model covers; shown on the model page.' },
|
||
},
|
||
};
|
||
|
||
const DIAGRAM_LINK = { type: 'string', description: 'Diagram that explains this element, relative to the model root (main.archify). Double-click the node to open it.' };
|
||
const DECISIONS = { type: 'array', items: { type: 'string', minLength: 1 }, description: 'Ids of architecture decisions (*.archify-decision files of the model) this is about. Shown in the Decisions panel under the diagram.' };
|
||
const NODE_COLLECTIONS = ['components', 'nodes', 'participants', 'states'];
|
||
const EDGE_COLLECTIONS = ['connections', 'edges', 'flows', 'transitions', 'messages'];
|
||
|
||
for (const file of fs.readdirSync(schemas)) {
|
||
if (!/^(architecture|workflow|sequence|dataflow|lifecycle)\.schema\.json$/.test(file)) continue;
|
||
const schema = read(file);
|
||
schema.properties.model = MODEL;
|
||
schema.properties.decisions = DECISIONS;
|
||
for (const key of [...NODE_COLLECTIONS, ...EDGE_COLLECTIONS]) {
|
||
const items = schema.properties[key]?.items;
|
||
if (items?.properties) items.properties.decisions = DECISIONS;
|
||
}
|
||
if (file === 'architecture.schema.json') {
|
||
const item = schema.properties.components.items;
|
||
item.properties.ref = { type: 'string', pattern: '^[a-zA-Z][a-zA-Z0-9_-]*$', description: 'A shared element from the model’s registry (*.archify-registry). Its type, label, sublabel, brand and sources come from there; the diagram adds pos/size and may override label, sublabel and tag.' };
|
||
item.properties.diagram = DIAGRAM_LINK;
|
||
// A ref brings type and label with it; an inline component must state them.
|
||
item.anyOf = [{ required: ['ref'] }, { required: ['id', 'type', 'label'] }];
|
||
delete item.required;
|
||
}
|
||
write(file, schema);
|
||
}
|
||
|
||
const common = read('common.schema.json');
|
||
const componentType = common.$defs.componentType;
|
||
const registry = {
|
||
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
||
title: 'Archify model registry',
|
||
description: 'Shared elements of a model: one name for one thing. Diagrams refer to an element with `ref: <id>`.',
|
||
type: 'object',
|
||
additionalProperties: false,
|
||
required: ['elements'],
|
||
properties: {
|
||
schema_version: { const: 1 },
|
||
elements: {
|
||
type: 'object',
|
||
description: 'Element id → element. Ids are unique across all registry files of the model.',
|
||
propertyNames: { pattern: '^[a-zA-Z][a-zA-Z0-9_-]*$' },
|
||
additionalProperties: {
|
||
type: 'object',
|
||
additionalProperties: false,
|
||
required: ['type', 'label'],
|
||
properties: {
|
||
type: componentType,
|
||
label: { type: 'string', minLength: 1 },
|
||
sublabel: { type: 'string' },
|
||
tag: { type: 'string' },
|
||
icon: common.$defs.nodeIcon,
|
||
brand: common.$defs.brandMark,
|
||
sources: common.$defs.sourceReferences,
|
||
description: { type: 'string', description: 'The one description of this thing; shown on hover and on the model page.' },
|
||
diagram: DIAGRAM_LINK,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
};
|
||
write('archify-registry.schema.json', registry);
|
||
|
||
const decision = {
|
||
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
||
title: 'Archify architecture decision',
|
||
description: 'One architecture decision of a model. Diagrams point at it with `decisions: [<id>]`.',
|
||
type: 'object',
|
||
additionalProperties: false,
|
||
required: ['decision'],
|
||
properties: {
|
||
schema_version: { const: 1 },
|
||
decision: {
|
||
type: 'object',
|
||
additionalProperties: false,
|
||
required: ['id', 'title'],
|
||
properties: {
|
||
id: { type: 'string', minLength: 1, description: 'Unique in the model. Any short string.' },
|
||
title: { type: 'string', minLength: 1 },
|
||
status: { enum: ['proposed', 'accepted', 'superseded', 'rejected'], description: 'A decision another one supersedes shows as superseded whatever this says.' },
|
||
date: { type: 'string', description: 'YYYY-MM-DD.' },
|
||
elements: { type: 'array', items: { type: 'string', minLength: 1 }, description: 'Registry element ids the decision is about.' },
|
||
diagrams: { type: 'array', items: { type: 'string', minLength: 1 }, description: 'Diagram paths (from the model root) the decision names.' },
|
||
supersedes: { type: 'array', items: { type: 'string', minLength: 1 }, description: 'Ids of older decisions this one replaces.' },
|
||
},
|
||
},
|
||
context: { type: 'string', description: 'The situation and what forced a decision.' },
|
||
decision: { type: 'string', description: 'What was decided.' },
|
||
consequences: { type: 'string', description: 'What becomes easier, harder, or must change.' },
|
||
},
|
||
};
|
||
write('archify-decision.schema.json', decision);
|
||
console.log('Patched editor schemas: model, ref, diagram, decisions; wrote archify-registry.schema.json and archify-decision.schema.json');
|