@glyphlab/diagram-core 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +63 -2
- package/chunk-FFPBFKRE.js +160 -0
- package/chunk-FFPBFKRE.js.map +1 -0
- package/chunk-XCRPIPPL.js +235 -0
- package/chunk-XCRPIPPL.js.map +1 -0
- package/index.d.ts +166 -0
- package/index.js +1303 -0
- package/index.js.map +1 -0
- package/package.json +54 -4
- package/repository-C4DyvGsM.d.ts +1359 -0
- package/testing.d.ts +10 -0
- package/testing.js +158 -0
- package/testing.js.map +1 -0
- package/versioning.d.ts +78 -0
- package/versioning.js +25 -0
- package/versioning.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Subtubes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @glyphlab/diagram-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The diagram model and the one interface to change it. No UI, no storage, no history. Peer dependencies: `zod`, `zustand` (the vanilla store).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createDiagramController } from '@glyphlab/diagram-core'
|
|
7
|
+
|
|
8
|
+
const controller = createDiagramController()
|
|
9
|
+
|
|
10
|
+
const result = controller.apply({
|
|
11
|
+
ops: [
|
|
12
|
+
{ op: 'createNode', node: { title: 'Orders API', body: 'Takes the order.', color: 'blue' } },
|
|
13
|
+
{ op: 'createNode', node: { title: 'Payments', body: '' } },
|
|
14
|
+
{ op: 'createEdge', edge: { from: 'Orders API', to: 'Payments', label: 'charge' } },
|
|
15
|
+
],
|
|
16
|
+
summary: 'Orders call payments',
|
|
17
|
+
source: 'user', // anything the host tells apart: 'user', 'agent', ...
|
|
18
|
+
})
|
|
19
|
+
// { ok: true, revision: 1, applied: 3, changed: true, patch, inverse } or { ok: false, error: 'invalid' | 'stale-revision', message, currentRevision }
|
|
20
|
+
|
|
21
|
+
controller.getSnapshot() // plain data, the same object until something changes
|
|
22
|
+
controller.subscribe(render) // once per change; works with React's useSyncExternalStore
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## The controller
|
|
26
|
+
|
|
27
|
+
| | |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `apply({ ops, summary, source, expectedRevision?, id? })` | The one way to change a diagram. All or nothing; moves the revision once; refuses (and changes nothing) if `expectedRevision` is not the current one. |
|
|
30
|
+
| `applyPatch(patch, { summary, source })` | Applies a patch the host kept, normally an `inverse`, as a change of its own. This is how a host undoes. |
|
|
31
|
+
| `onChange(listener)` | Each accepted change that changed something: `{ id, revision, summary, source, at, patch, inverse }`. A history, an autosave or an approval list hangs here. |
|
|
32
|
+
| `getSnapshot()` / `subscribe()` | Read the diagram as plain data, and hear about changes. |
|
|
33
|
+
| `load(diagram, revision?)` / `onLoad()` | Put a whole diagram in. Not a change: no event, no inverse. |
|
|
34
|
+
| `export()` | The diagram as a file; `serializeDiagram(file)` writes the versioned JSON. |
|
|
35
|
+
| `stores` | The per-kind stores a renderer subscribes to. Writing to them directly is only for a live preview that is rolled back (a drag in progress). |
|
|
36
|
+
|
|
37
|
+
## The pieces
|
|
38
|
+
|
|
39
|
+
- **Format** (`format.ts`): the versioned file (`schemaVersion` 3: nodes, groups, edges, optional `color`, optional `layer`, optional diagram `type` and group `lane` metadata), `readDiagram` (upgrades v1/v2 without changing layout, says why it cannot read one), `serializeDiagram`. Released versions are frozen; fixtures and JSON Schemas are checked in `src/fixtures`.
|
|
40
|
+
- **Operations** (`ops.ts`) and the **planner** (`plan.ts`): the rules for each operation, applied to a copy, answering with the new diagram and the exact inverse. Pure functions.
|
|
41
|
+
- **Repository** (`repository.ts`): `DiagramRepository`, `memoryRepository`, `storageRepository`, `httpRepository`, and `bindRepository(controller, repository, { id })` which loads and autosaves (a burst of changes is one save; a save over somebody else's work stops with `onConflict`).
|
|
42
|
+
- **Geometry, colours and highlights**: edge routing, bounds, resize rules, the colour presets, and the kinds of highlight a canvas can show (`NODE_HIGHLIGHTS`, `Highlights`), which a producer such as a scenario player and a canvas share without importing each other.
|
|
43
|
+
|
|
44
|
+
## Swimlanes
|
|
45
|
+
|
|
46
|
+
A lane is a group with `lane: { body: string, orientation: 'horizontal' | 'vertical' }`.
|
|
47
|
+
Set diagram `type: 'swimlane'` when constructing a new controller to retain that type even with no lanes.
|
|
48
|
+
`createLane`, `updateLane`, `deleteLane` and `arrangeLanes` share the normal atomic planner, validation and inverse patches.
|
|
49
|
+
`arrangeLanes` accepts an orientation and optional `laneIds` containing every lane exactly once.
|
|
50
|
+
All lanes use one orientation; changing it reflows their bounds and moves contained nodes with them.
|
|
51
|
+
`createNode.node.laneId` places a node inside a lane and expands the lane if needed.
|
|
52
|
+
Membership is geometric (node centre inside lane), not a persisted node field.
|
|
53
|
+
Moving a lane carries its member nodes; resizing changes only its bounds; deletion keeps nodes and edges.
|
|
54
|
+
|
|
55
|
+
## Testing your own controller or repository
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { defineControllerConformance, defineRepositoryConformance } from '@glyphlab/diagram-core/testing'
|
|
59
|
+
|
|
60
|
+
defineControllerConformance('mine', () => makeMyController())
|
|
61
|
+
defineRepositoryConformance('mine', () => makeMyRepository())
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Both are vitest suites. They are the contract: what `apply` must refuse, what an inverse must restore exactly, how a repository must treat a stale save.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// packages/diagram-core/src/versioning.ts
|
|
2
|
+
var NotADocumentError = class extends Error {
|
|
3
|
+
kind = "not-a-document";
|
|
4
|
+
format;
|
|
5
|
+
constructor(format, detail) {
|
|
6
|
+
super(`Not a ${format} document: ${detail}`);
|
|
7
|
+
this.format = format;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var UnsupportedVersionError = class extends Error {
|
|
11
|
+
kind = "unsupported-version";
|
|
12
|
+
format;
|
|
13
|
+
found;
|
|
14
|
+
minSupported;
|
|
15
|
+
current;
|
|
16
|
+
reason;
|
|
17
|
+
constructor(format, found, minSupported, current, reason) {
|
|
18
|
+
super(`${format} version ${found} is not supported (supported: ${minSupported}-${current})`);
|
|
19
|
+
this.format = format;
|
|
20
|
+
this.found = found;
|
|
21
|
+
this.minSupported = minSupported;
|
|
22
|
+
this.current = current;
|
|
23
|
+
this.reason = reason;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var ValidationError = class extends Error {
|
|
27
|
+
kind = "validation";
|
|
28
|
+
format;
|
|
29
|
+
version;
|
|
30
|
+
issues;
|
|
31
|
+
constructor(format, version, issues) {
|
|
32
|
+
super(`Invalid ${format} (v${version}): ${issues[0]?.message ?? "unknown problem"}`);
|
|
33
|
+
this.format = format;
|
|
34
|
+
this.version = version;
|
|
35
|
+
this.issues = issues;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var MigrationError = class extends Error {
|
|
39
|
+
kind = "migration";
|
|
40
|
+
format;
|
|
41
|
+
from;
|
|
42
|
+
to;
|
|
43
|
+
constructor(format, from, to, cause) {
|
|
44
|
+
super(`Migrating ${format} from v${from} to v${to} failed`, { cause });
|
|
45
|
+
this.format = format;
|
|
46
|
+
this.from = from;
|
|
47
|
+
this.to = to;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
function defineFormat(def) {
|
|
51
|
+
const history = def.history;
|
|
52
|
+
if (history.length === 0) throw new Error(`${def.name}: a format needs at least one version`);
|
|
53
|
+
history.forEach((entry, i) => {
|
|
54
|
+
if (i > 0 && entry.version <= history[i - 1].version) {
|
|
55
|
+
throw new Error(`${def.name}: versions must be strictly ascending`);
|
|
56
|
+
}
|
|
57
|
+
const last2 = i === history.length - 1;
|
|
58
|
+
if (!last2 && !entry.migrate) throw new Error(`${def.name}: v${entry.version} needs a migrate function`);
|
|
59
|
+
});
|
|
60
|
+
const last = history[history.length - 1];
|
|
61
|
+
return {
|
|
62
|
+
name: def.name,
|
|
63
|
+
versionField: def.versionField ?? "schemaVersion",
|
|
64
|
+
history,
|
|
65
|
+
current: last.version,
|
|
66
|
+
minSupported: history[0].version,
|
|
67
|
+
currentSchema: last.schema
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function readVersioned(format, input, options = {}) {
|
|
71
|
+
const { validateSteps = true } = options;
|
|
72
|
+
const fail = (error) => ({ ok: false, error });
|
|
73
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
74
|
+
return fail(new NotADocumentError(format.name, "expected an object"));
|
|
75
|
+
}
|
|
76
|
+
const found = input[format.versionField];
|
|
77
|
+
if (typeof found !== "number" || !Number.isInteger(found) || found < 1) {
|
|
78
|
+
return fail(new NotADocumentError(format.name, `missing a valid ${format.versionField}`));
|
|
79
|
+
}
|
|
80
|
+
if (found > format.current) {
|
|
81
|
+
return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, "too-new"));
|
|
82
|
+
}
|
|
83
|
+
if (found < format.minSupported) {
|
|
84
|
+
return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, "too-old"));
|
|
85
|
+
}
|
|
86
|
+
const start = format.history.findIndex((h) => h.version === found);
|
|
87
|
+
if (start < 0) {
|
|
88
|
+
return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, "unknown"));
|
|
89
|
+
}
|
|
90
|
+
const first = format.history[start].schema.safeParse(input);
|
|
91
|
+
if (!first.success) return fail(new ValidationError(format.name, found, first.error.issues));
|
|
92
|
+
let data = first.data;
|
|
93
|
+
const warnings = [];
|
|
94
|
+
for (let i = start; i < format.history.length - 1; i++) {
|
|
95
|
+
const from = format.history[i];
|
|
96
|
+
const to = format.history[i + 1];
|
|
97
|
+
let result;
|
|
98
|
+
try {
|
|
99
|
+
result = from.migrate(data);
|
|
100
|
+
} catch (cause) {
|
|
101
|
+
return fail(new MigrationError(format.name, from.version, to.version, cause));
|
|
102
|
+
}
|
|
103
|
+
warnings.push(...result.warnings ?? []);
|
|
104
|
+
const check = to.schema.safeParse(result.data);
|
|
105
|
+
if (!check.success) {
|
|
106
|
+
if (validateSteps || i === format.history.length - 2) {
|
|
107
|
+
return fail(
|
|
108
|
+
new MigrationError(format.name, from.version, to.version, new ValidationError(format.name, to.version, check.error.issues))
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
result = { ...result, data: check.data };
|
|
113
|
+
}
|
|
114
|
+
data = result.data;
|
|
115
|
+
}
|
|
116
|
+
return { ok: true, value: data, from: found, upgraded: start < format.history.length - 1, warnings };
|
|
117
|
+
}
|
|
118
|
+
function readVersionedOrThrow(format, input, options) {
|
|
119
|
+
const result = readVersioned(format, input, options);
|
|
120
|
+
if (!result.ok) throw result.error;
|
|
121
|
+
return result.value;
|
|
122
|
+
}
|
|
123
|
+
function writeVersioned(format, value) {
|
|
124
|
+
return format.currentSchema.parse(value);
|
|
125
|
+
}
|
|
126
|
+
function serializeVersioned(format, value, space = 2) {
|
|
127
|
+
return JSON.stringify(writeVersioned(format, value), null, space) + "\n";
|
|
128
|
+
}
|
|
129
|
+
function describeVersionError(error) {
|
|
130
|
+
switch (error.kind) {
|
|
131
|
+
case "not-a-document":
|
|
132
|
+
return `This is not a valid ${error.format} file. ${error.message.split(": ")[1] ?? ""}`.trim();
|
|
133
|
+
case "unsupported-version":
|
|
134
|
+
if (error.reason === "too-new") {
|
|
135
|
+
return `This ${error.format} was made by a newer version of the app (file version ${error.found}, this app supports up to ${error.current}). Update the app to open it.`;
|
|
136
|
+
}
|
|
137
|
+
return `This ${error.format} is too old to open directly (file version ${error.found}, this app supports ${error.minSupported} and up). Convert it with an older release first.`;
|
|
138
|
+
case "validation": {
|
|
139
|
+
const issue = error.issues[0];
|
|
140
|
+
const where = issue?.path.length ? ` at ${issue.path.join(".")}` : "";
|
|
141
|
+
return `This ${error.format} file is invalid${where}: ${issue?.message ?? "unknown problem"}.`;
|
|
142
|
+
}
|
|
143
|
+
case "migration":
|
|
144
|
+
return `Upgrading this ${error.format} from version ${error.from} to ${error.to} failed. This is a bug in the app; please report it.`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
NotADocumentError,
|
|
150
|
+
UnsupportedVersionError,
|
|
151
|
+
ValidationError,
|
|
152
|
+
MigrationError,
|
|
153
|
+
defineFormat,
|
|
154
|
+
readVersioned,
|
|
155
|
+
readVersionedOrThrow,
|
|
156
|
+
writeVersioned,
|
|
157
|
+
serializeVersioned,
|
|
158
|
+
describeVersionError
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=chunk-FFPBFKRE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/versioning.ts"],"sourcesContent":["import type { z } from 'zod'\n\n// Upgrade on read, current-only in memory.\n//\n// A persisted document carries a version number. Each released version keeps a frozen schema\n// that is never edited. Reading validates the document against the schema of the version it\n// was written with, then runs pure migrations (vN -> vN+1, chained) until it matches the\n// current schema. The rest of the app only ever sees the current shape.\n\n// ---- Typed errors: failures are data, so the UI can say what to do next ----\n\nexport class NotADocumentError extends Error {\n readonly kind = 'not-a-document' as const\n readonly format: string\n constructor(format: string, detail: string) {\n super(`Not a ${format} document: ${detail}`)\n this.format = format\n }\n}\n\nexport class UnsupportedVersionError extends Error {\n readonly kind = 'unsupported-version' as const\n readonly format: string\n readonly found: number\n readonly minSupported: number\n readonly current: number\n readonly reason: 'too-old' | 'too-new' | 'unknown'\n constructor(format: string, found: number, minSupported: number, current: number, reason: 'too-old' | 'too-new' | 'unknown') {\n super(`${format} version ${found} is not supported (supported: ${minSupported}-${current})`)\n this.format = format\n this.found = found\n this.minSupported = minSupported\n this.current = current\n this.reason = reason\n }\n}\n\nexport class ValidationError extends Error {\n readonly kind = 'validation' as const\n readonly format: string\n readonly version: number\n readonly issues: z.core.$ZodIssue[]\n constructor(format: string, version: number, issues: z.core.$ZodIssue[]) {\n super(`Invalid ${format} (v${version}): ${issues[0]?.message ?? 'unknown problem'}`)\n this.format = format\n this.version = version\n this.issues = issues\n }\n}\n\n// A bug in a migration, not the user's fault: log it.\nexport class MigrationError extends Error {\n readonly kind = 'migration' as const\n readonly format: string\n readonly from: number\n readonly to: number\n constructor(format: string, from: number, to: number, cause: unknown) {\n super(`Migrating ${format} from v${from} to v${to} failed`, { cause })\n this.format = format\n this.from = from\n this.to = to\n }\n}\n\nexport type VersionError = NotADocumentError | UnsupportedVersionError | ValidationError | MigrationError\n\n// ---- Formats ----\n\nexport interface MigrationResult {\n data: unknown\n // Anything lossy or assumed; shown to the user once after opening.\n warnings?: string[]\n}\n\nexport interface VersionDef {\n version: number\n // Frozen once released.\n schema: z.ZodType\n // Turns data of this version into the next version's shape. Pure: no I/O, no randomness.\n // Required on every version except the last.\n migrate?: (data: any) => MigrationResult\n}\n\nexport interface CurrentVersionDef<T> {\n version: number\n schema: z.ZodType<T>\n}\n\nexport interface Format<T> {\n name: string\n versionField: string\n history: VersionDef[]\n readonly current: number\n readonly minSupported: number\n readonly currentSchema: z.ZodType<T>\n}\n\n// `history` is ordered oldest first and ends with the current version. Dropping an old\n// version from the front drops support for it (such files read as 'too-old').\nexport function defineFormat<T>(def: {\n name: string\n versionField?: string\n history: [...VersionDef[], CurrentVersionDef<T>]\n}): Format<T> {\n const history = def.history as VersionDef[]\n if (history.length === 0) throw new Error(`${def.name}: a format needs at least one version`)\n\n history.forEach((entry, i) => {\n if (i > 0 && entry.version <= history[i - 1].version) {\n throw new Error(`${def.name}: versions must be strictly ascending`)\n }\n const last = i === history.length - 1\n if (!last && !entry.migrate) throw new Error(`${def.name}: v${entry.version} needs a migrate function`)\n })\n\n const last = history[history.length - 1]\n return {\n name: def.name,\n versionField: def.versionField ?? 'schemaVersion',\n history,\n current: last.version,\n minSupported: history[0].version,\n currentSchema: last.schema as z.ZodType<T>,\n }\n}\n\nexport type ReadResult<T> =\n | { ok: true; value: T; from: number; upgraded: boolean; warnings: string[] }\n | { ok: false; error: VersionError }\n\nexport interface ReadOptions {\n // Check every migration step against the next version's schema, not only the final result.\n validateSteps?: boolean\n}\n\nexport function readVersioned<T>(format: Format<T>, input: unknown, options: ReadOptions = {}): ReadResult<T> {\n const { validateSteps = true } = options\n const fail = (error: VersionError): ReadResult<T> => ({ ok: false, error })\n\n if (typeof input !== 'object' || input === null || Array.isArray(input)) {\n return fail(new NotADocumentError(format.name, 'expected an object'))\n }\n const found = (input as Record<string, unknown>)[format.versionField]\n if (typeof found !== 'number' || !Number.isInteger(found) || found < 1) {\n return fail(new NotADocumentError(format.name, `missing a valid ${format.versionField}`))\n }\n\n if (found > format.current) {\n return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, 'too-new'))\n }\n if (found < format.minSupported) {\n return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, 'too-old'))\n }\n const start = format.history.findIndex((h) => h.version === found)\n if (start < 0) {\n return fail(new UnsupportedVersionError(format.name, found, format.minSupported, format.current, 'unknown'))\n }\n\n // The file is judged by the rules of the version it was written under.\n const first = format.history[start].schema.safeParse(input)\n if (!first.success) return fail(new ValidationError(format.name, found, first.error.issues))\n\n let data: unknown = first.data\n const warnings: string[] = []\n\n for (let i = start; i < format.history.length - 1; i++) {\n const from = format.history[i]\n const to = format.history[i + 1]\n let result: MigrationResult\n try {\n result = from.migrate!(data)\n } catch (cause) {\n return fail(new MigrationError(format.name, from.version, to.version, cause))\n }\n warnings.push(...(result.warnings ?? []))\n\n // A failure here means the migration produced something its target version rejects.\n const check = to.schema.safeParse(result.data)\n if (!check.success) {\n if (validateSteps || i === format.history.length - 2) {\n return fail(\n new MigrationError(format.name, from.version, to.version, new ValidationError(format.name, to.version, check.error.issues)),\n )\n }\n } else {\n result = { ...result, data: check.data }\n }\n data = result.data\n }\n\n return { ok: true, value: data as T, from: found, upgraded: start < format.history.length - 1, warnings }\n}\n\n// For trusted input such as bundled samples: throws the typed error.\nexport function readVersionedOrThrow<T>(format: Format<T>, input: unknown, options?: ReadOptions): T {\n const result = readVersioned(format, input, options)\n if (!result.ok) throw result.error\n return result.value\n}\n\n// Writing always produces the current version, and refuses anything invalid.\nexport function writeVersioned<T>(format: Format<T>, value: T): T {\n return format.currentSchema.parse(value)\n}\n\nexport function serializeVersioned<T>(format: Format<T>, value: T, space = 2): string {\n return JSON.stringify(writeVersioned(format, value), null, space) + '\\n'\n}\n\n// A message for the user, with a next step.\nexport function describeVersionError(error: VersionError): string {\n switch (error.kind) {\n case 'not-a-document':\n return `This is not a valid ${error.format} file. ${error.message.split(': ')[1] ?? ''}`.trim()\n case 'unsupported-version':\n if (error.reason === 'too-new') {\n return `This ${error.format} was made by a newer version of the app (file version ${error.found}, this app supports up to ${error.current}). Update the app to open it.`\n }\n return `This ${error.format} is too old to open directly (file version ${error.found}, this app supports ${error.minSupported} and up). Convert it with an older release first.`\n case 'validation': {\n const issue = error.issues[0]\n const where = issue?.path.length ? ` at ${issue.path.join('.')}` : ''\n return `This ${error.format} file is invalid${where}: ${issue?.message ?? 'unknown problem'}.`\n }\n case 'migration':\n return `Upgrading this ${error.format} from version ${error.from} to ${error.to} failed. This is a bug in the app; please report it.`\n }\n}\n"],"mappings":";AAWO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAClC,OAAO;AAAA,EACP;AAAA,EACT,YAAY,QAAgB,QAAgB;AAC1C,UAAM,SAAS,MAAM,cAAc,MAAM,EAAE;AAC3C,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACxC,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAY,QAAgB,OAAe,cAAsB,SAAiB,QAA2C;AAC3H,UAAM,GAAG,MAAM,YAAY,KAAK,iCAAiC,YAAY,IAAI,OAAO,GAAG;AAC3F,SAAK,SAAS;AACd,SAAK,QAAQ;AACb,SAAK,eAAe;AACpB,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AACF;AAEO,IAAM,kBAAN,cAA8B,MAAM;AAAA,EAChC,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAY,QAAgB,SAAiB,QAA4B;AACvE,UAAM,WAAW,MAAM,MAAM,OAAO,MAAM,OAAO,CAAC,GAAG,WAAW,iBAAiB,EAAE;AACnF,SAAK,SAAS;AACd,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,EAChB;AACF;AAGO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EAC/B,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EACT,YAAY,QAAgB,MAAc,IAAY,OAAgB;AACpE,UAAM,aAAa,MAAM,UAAU,IAAI,QAAQ,EAAE,WAAW,EAAE,MAAM,CAAC;AACrE,SAAK,SAAS;AACd,SAAK,OAAO;AACZ,SAAK,KAAK;AAAA,EACZ;AACF;AAqCO,SAAS,aAAgB,KAIlB;AACZ,QAAM,UAAU,IAAI;AACpB,MAAI,QAAQ,WAAW,EAAG,OAAM,IAAI,MAAM,GAAG,IAAI,IAAI,uCAAuC;AAE5F,UAAQ,QAAQ,CAAC,OAAO,MAAM;AAC5B,QAAI,IAAI,KAAK,MAAM,WAAW,QAAQ,IAAI,CAAC,EAAE,SAAS;AACpD,YAAM,IAAI,MAAM,GAAG,IAAI,IAAI,uCAAuC;AAAA,IACpE;AACA,UAAMA,QAAO,MAAM,QAAQ,SAAS;AACpC,QAAI,CAACA,SAAQ,CAAC,MAAM,QAAS,OAAM,IAAI,MAAM,GAAG,IAAI,IAAI,MAAM,MAAM,OAAO,2BAA2B;AAAA,EACxG,CAAC;AAED,QAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,SAAO;AAAA,IACL,MAAM,IAAI;AAAA,IACV,cAAc,IAAI,gBAAgB;AAAA,IAClC;AAAA,IACA,SAAS,KAAK;AAAA,IACd,cAAc,QAAQ,CAAC,EAAE;AAAA,IACzB,eAAe,KAAK;AAAA,EACtB;AACF;AAWO,SAAS,cAAiB,QAAmB,OAAgB,UAAuB,CAAC,GAAkB;AAC5G,QAAM,EAAE,gBAAgB,KAAK,IAAI;AACjC,QAAM,OAAO,CAAC,WAAwC,EAAE,IAAI,OAAO,MAAM;AAEzE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO,KAAK,IAAI,kBAAkB,OAAO,MAAM,oBAAoB,CAAC;AAAA,EACtE;AACA,QAAM,QAAS,MAAkC,OAAO,YAAY;AACpE,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACtE,WAAO,KAAK,IAAI,kBAAkB,OAAO,MAAM,mBAAmB,OAAO,YAAY,EAAE,CAAC;AAAA,EAC1F;AAEA,MAAI,QAAQ,OAAO,SAAS;AAC1B,WAAO,KAAK,IAAI,wBAAwB,OAAO,MAAM,OAAO,OAAO,cAAc,OAAO,SAAS,SAAS,CAAC;AAAA,EAC7G;AACA,MAAI,QAAQ,OAAO,cAAc;AAC/B,WAAO,KAAK,IAAI,wBAAwB,OAAO,MAAM,OAAO,OAAO,cAAc,OAAO,SAAS,SAAS,CAAC;AAAA,EAC7G;AACA,QAAM,QAAQ,OAAO,QAAQ,UAAU,CAAC,MAAM,EAAE,YAAY,KAAK;AACjE,MAAI,QAAQ,GAAG;AACb,WAAO,KAAK,IAAI,wBAAwB,OAAO,MAAM,OAAO,OAAO,cAAc,OAAO,SAAS,SAAS,CAAC;AAAA,EAC7G;AAGA,QAAM,QAAQ,OAAO,QAAQ,KAAK,EAAE,OAAO,UAAU,KAAK;AAC1D,MAAI,CAAC,MAAM,QAAS,QAAO,KAAK,IAAI,gBAAgB,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,CAAC;AAE3F,MAAI,OAAgB,MAAM;AAC1B,QAAM,WAAqB,CAAC;AAE5B,WAAS,IAAI,OAAO,IAAI,OAAO,QAAQ,SAAS,GAAG,KAAK;AACtD,UAAM,OAAO,OAAO,QAAQ,CAAC;AAC7B,UAAM,KAAK,OAAO,QAAQ,IAAI,CAAC;AAC/B,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,QAAS,IAAI;AAAA,IAC7B,SAAS,OAAO;AACd,aAAO,KAAK,IAAI,eAAe,OAAO,MAAM,KAAK,SAAS,GAAG,SAAS,KAAK,CAAC;AAAA,IAC9E;AACA,aAAS,KAAK,GAAI,OAAO,YAAY,CAAC,CAAE;AAGxC,UAAM,QAAQ,GAAG,OAAO,UAAU,OAAO,IAAI;AAC7C,QAAI,CAAC,MAAM,SAAS;AAClB,UAAI,iBAAiB,MAAM,OAAO,QAAQ,SAAS,GAAG;AACpD,eAAO;AAAA,UACL,IAAI,eAAe,OAAO,MAAM,KAAK,SAAS,GAAG,SAAS,IAAI,gBAAgB,OAAO,MAAM,GAAG,SAAS,MAAM,MAAM,MAAM,CAAC;AAAA,QAC5H;AAAA,MACF;AAAA,IACF,OAAO;AACL,eAAS,EAAE,GAAG,QAAQ,MAAM,MAAM,KAAK;AAAA,IACzC;AACA,WAAO,OAAO;AAAA,EAChB;AAEA,SAAO,EAAE,IAAI,MAAM,OAAO,MAAW,MAAM,OAAO,UAAU,QAAQ,OAAO,QAAQ,SAAS,GAAG,SAAS;AAC1G;AAGO,SAAS,qBAAwB,QAAmB,OAAgB,SAA0B;AACnG,QAAM,SAAS,cAAc,QAAQ,OAAO,OAAO;AACnD,MAAI,CAAC,OAAO,GAAI,OAAM,OAAO;AAC7B,SAAO,OAAO;AAChB;AAGO,SAAS,eAAkB,QAAmB,OAAa;AAChE,SAAO,OAAO,cAAc,MAAM,KAAK;AACzC;AAEO,SAAS,mBAAsB,QAAmB,OAAU,QAAQ,GAAW;AACpF,SAAO,KAAK,UAAU,eAAe,QAAQ,KAAK,GAAG,MAAM,KAAK,IAAI;AACtE;AAGO,SAAS,qBAAqB,OAA6B;AAChE,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aAAO,uBAAuB,MAAM,MAAM,UAAU,MAAM,QAAQ,MAAM,IAAI,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK;AAAA,IAChG,KAAK;AACH,UAAI,MAAM,WAAW,WAAW;AAC9B,eAAO,QAAQ,MAAM,MAAM,yDAAyD,MAAM,KAAK,6BAA6B,MAAM,OAAO;AAAA,MAC3I;AACA,aAAO,QAAQ,MAAM,MAAM,8CAA8C,MAAM,KAAK,uBAAuB,MAAM,YAAY;AAAA,IAC/H,KAAK,cAAc;AACjB,YAAM,QAAQ,MAAM,OAAO,CAAC;AAC5B,YAAM,QAAQ,OAAO,KAAK,SAAS,OAAO,MAAM,KAAK,KAAK,GAAG,CAAC,KAAK;AACnE,aAAO,QAAQ,MAAM,MAAM,mBAAmB,KAAK,KAAK,OAAO,WAAW,iBAAiB;AAAA,IAC7F;AAAA,IACA,KAAK;AACH,aAAO,kBAAkB,MAAM,MAAM,iBAAiB,MAAM,IAAI,OAAO,MAAM,EAAE;AAAA,EACnF;AACF;","names":["last"]}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineFormat,
|
|
3
|
+
readVersioned,
|
|
4
|
+
readVersionedOrThrow,
|
|
5
|
+
serializeVersioned
|
|
6
|
+
} from "./chunk-FFPBFKRE.js";
|
|
7
|
+
|
|
8
|
+
// packages/diagram-core/src/colors.ts
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
var COLOR_PRESETS = ["red", "orange", "yellow", "green", "teal", "blue", "purple", "pink", "gray"];
|
|
11
|
+
var HEX_COLOR = /^#[0-9a-fA-F]{6}$/;
|
|
12
|
+
var colorSchema = z.union([z.enum(COLOR_PRESETS), z.string().regex(HEX_COLOR)]);
|
|
13
|
+
var isPreset = (color) => COLOR_PRESETS.includes(color);
|
|
14
|
+
function colorCss(color) {
|
|
15
|
+
if (color === void 0) return void 0;
|
|
16
|
+
if (isPreset(color)) return `var(--c-${color})`;
|
|
17
|
+
return HEX_COLOR.test(color) ? color.toLowerCase() : void 0;
|
|
18
|
+
}
|
|
19
|
+
var colorKey = (color) => isPreset(color) ? color : color.slice(1).toLowerCase();
|
|
20
|
+
var arrowId = (base, color) => colorCss(color) && color ? `${base}-${colorKey(color)}` : base;
|
|
21
|
+
var colorLabel = (color) => isPreset(color) ? color[0].toUpperCase() + color.slice(1) : color.toLowerCase();
|
|
22
|
+
|
|
23
|
+
// packages/diagram-core/src/format.ts
|
|
24
|
+
import { z as z2 } from "zod";
|
|
25
|
+
var DIAGRAM_SCHEMA_VERSION = 5;
|
|
26
|
+
var LAYERS = ["ui", "runtime", "tools", "domain"];
|
|
27
|
+
var layerSchema = z2.enum(LAYERS);
|
|
28
|
+
var idSchema = z2.string().min(1);
|
|
29
|
+
var coordinate = z2.number().finite();
|
|
30
|
+
var dimension = z2.number().finite().positive();
|
|
31
|
+
var groupV1Schema = z2.strictObject({
|
|
32
|
+
id: idSchema,
|
|
33
|
+
title: z2.string().min(1),
|
|
34
|
+
layer: layerSchema,
|
|
35
|
+
x: coordinate,
|
|
36
|
+
y: coordinate,
|
|
37
|
+
w: dimension,
|
|
38
|
+
h: dimension
|
|
39
|
+
});
|
|
40
|
+
var nodeV1Schema = z2.strictObject({
|
|
41
|
+
id: idSchema,
|
|
42
|
+
title: z2.string().min(1),
|
|
43
|
+
layer: layerSchema,
|
|
44
|
+
x: coordinate,
|
|
45
|
+
y: coordinate,
|
|
46
|
+
w: dimension,
|
|
47
|
+
h: dimension,
|
|
48
|
+
// Markdown (GFM) with fenced code blocks.
|
|
49
|
+
body: z2.string()
|
|
50
|
+
});
|
|
51
|
+
var edgeV1Schema = z2.strictObject({
|
|
52
|
+
id: idSchema,
|
|
53
|
+
from: idSchema,
|
|
54
|
+
to: idSchema,
|
|
55
|
+
label: z2.string(),
|
|
56
|
+
// Asynchronous event rather than a direct call.
|
|
57
|
+
dashed: z2.boolean().optional(),
|
|
58
|
+
// Optional AI assistance rather than a required step.
|
|
59
|
+
optional: z2.boolean().optional()
|
|
60
|
+
});
|
|
61
|
+
function findDuplicates(ids) {
|
|
62
|
+
const seen = /* @__PURE__ */ new Set();
|
|
63
|
+
const dupes = /* @__PURE__ */ new Set();
|
|
64
|
+
for (const id of ids) {
|
|
65
|
+
if (seen.has(id)) dupes.add(id);
|
|
66
|
+
seen.add(id);
|
|
67
|
+
}
|
|
68
|
+
return [...dupes];
|
|
69
|
+
}
|
|
70
|
+
function checkDiagram(diagram, ctx) {
|
|
71
|
+
const collections = [
|
|
72
|
+
["groups", diagram.groups],
|
|
73
|
+
["nodes", diagram.nodes],
|
|
74
|
+
["edges", diagram.edges]
|
|
75
|
+
];
|
|
76
|
+
for (const [key, items] of collections) {
|
|
77
|
+
for (const id of findDuplicates(items.map((item) => item.id))) {
|
|
78
|
+
ctx.addIssue({ code: "custom", path: [key], message: `Duplicate ${key} id "${id}"` });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const nodeIds = new Set(diagram.nodes.map((n) => n.id));
|
|
82
|
+
diagram.edges.forEach((edge, i) => {
|
|
83
|
+
for (const end of ["from", "to"]) {
|
|
84
|
+
if (!nodeIds.has(edge[end])) {
|
|
85
|
+
ctx.addIssue({
|
|
86
|
+
code: "custom",
|
|
87
|
+
path: ["edges", i, end],
|
|
88
|
+
message: `Edge "${edge.id}" references unknown node "${edge[end]}"`
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
var diagramV1Schema = z2.strictObject({
|
|
95
|
+
schemaVersion: z2.literal(1),
|
|
96
|
+
name: z2.string().min(1),
|
|
97
|
+
description: z2.string().optional(),
|
|
98
|
+
groups: z2.array(groupV1Schema),
|
|
99
|
+
nodes: z2.array(nodeV1Schema),
|
|
100
|
+
edges: z2.array(edgeV1Schema)
|
|
101
|
+
}).superRefine(checkDiagram);
|
|
102
|
+
var groupV2Schema = groupV1Schema.extend({ layer: layerSchema.optional(), color: colorSchema.optional() });
|
|
103
|
+
var nodeV2Schema = nodeV1Schema.extend({ layer: layerSchema.optional(), color: colorSchema.optional() });
|
|
104
|
+
var edgeSchema = edgeV1Schema.extend({ color: colorSchema.optional() });
|
|
105
|
+
var diagramV2Schema = z2.strictObject({
|
|
106
|
+
schemaVersion: z2.literal(2),
|
|
107
|
+
name: z2.string().min(1),
|
|
108
|
+
description: z2.string().optional(),
|
|
109
|
+
groups: z2.array(groupV2Schema),
|
|
110
|
+
nodes: z2.array(nodeV2Schema),
|
|
111
|
+
edges: z2.array(edgeSchema)
|
|
112
|
+
}).superRefine(checkDiagram);
|
|
113
|
+
var nodeSchema = nodeV2Schema.extend({ decisions: z2.string().optional(), systemsDesign: z2.record(z2.string(), z2.string()).optional() });
|
|
114
|
+
var linkV4Schema = z2.strictObject({
|
|
115
|
+
path: z2.string().min(1),
|
|
116
|
+
symbol: z2.string().min(1).optional(),
|
|
117
|
+
lines: z2.tuple([z2.number().int().positive(), z2.number().int().positive()]).refine(([from, to]) => from <= to, "lines must run from a line to the same or a later one").optional(),
|
|
118
|
+
hash: z2.string().min(1).optional()
|
|
119
|
+
});
|
|
120
|
+
var nodeV3Schema = nodeSchema;
|
|
121
|
+
var nodeV4Schema = nodeV3Schema.extend({ links: z2.array(linkV4Schema).optional() });
|
|
122
|
+
var repoIdSchema = z2.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,39}$/, 'a repo id is letters, digits, "-" or "_"');
|
|
123
|
+
var linkSchema = linkV4Schema.extend({ repo: repoIdSchema.optional() });
|
|
124
|
+
var repoRefSchema = z2.strictObject({
|
|
125
|
+
// e.g. "github.com/you/glyph"
|
|
126
|
+
remote: z2.string().min(1).optional(),
|
|
127
|
+
branch: z2.string().min(1).optional(),
|
|
128
|
+
// The commit the links were written against (a full or abbreviated hex SHA).
|
|
129
|
+
sha: z2.string().regex(/^[0-9a-f]{7,64}$/, "a commit SHA is 7 to 64 lowercase hex characters").optional()
|
|
130
|
+
});
|
|
131
|
+
var nodeV5Schema = nodeV3Schema.extend({ links: z2.array(linkSchema).optional() });
|
|
132
|
+
var orientationSchema = z2.enum(["horizontal", "vertical"]);
|
|
133
|
+
var groupSchema = groupV2Schema.extend({
|
|
134
|
+
lane: z2.object({ body: z2.string(), orientation: orientationSchema }).optional()
|
|
135
|
+
});
|
|
136
|
+
var diagramV3Schema = z2.strictObject({
|
|
137
|
+
schemaVersion: z2.literal(3),
|
|
138
|
+
name: z2.string().min(1),
|
|
139
|
+
description: z2.string().optional(),
|
|
140
|
+
type: z2.enum(["system", "swimlane"]).optional(),
|
|
141
|
+
groups: z2.array(groupSchema),
|
|
142
|
+
nodes: z2.array(nodeV3Schema),
|
|
143
|
+
edges: z2.array(edgeSchema)
|
|
144
|
+
}).superRefine(checkDiagram);
|
|
145
|
+
var diagramV4Schema = z2.strictObject({
|
|
146
|
+
schemaVersion: z2.literal(4),
|
|
147
|
+
name: z2.string().min(1),
|
|
148
|
+
description: z2.string().optional(),
|
|
149
|
+
type: z2.enum(["system", "swimlane"]).optional(),
|
|
150
|
+
groups: z2.array(groupSchema),
|
|
151
|
+
nodes: z2.array(nodeV4Schema),
|
|
152
|
+
edges: z2.array(edgeSchema)
|
|
153
|
+
}).superRefine(checkDiagram);
|
|
154
|
+
var diagramV5Schema = z2.strictObject({
|
|
155
|
+
schemaVersion: z2.literal(DIAGRAM_SCHEMA_VERSION),
|
|
156
|
+
name: z2.string().min(1),
|
|
157
|
+
description: z2.string().optional(),
|
|
158
|
+
type: z2.enum(["system", "swimlane"]).optional(),
|
|
159
|
+
repos: z2.record(repoIdSchema, repoRefSchema).optional(),
|
|
160
|
+
groups: z2.array(groupSchema),
|
|
161
|
+
nodes: z2.array(nodeV5Schema),
|
|
162
|
+
edges: z2.array(edgeSchema)
|
|
163
|
+
}).superRefine(checkDiagram);
|
|
164
|
+
var diagramSchema = diagramV5Schema;
|
|
165
|
+
var LAYER_COLOR = { ui: "blue", runtime: "purple", tools: "orange", domain: "green" };
|
|
166
|
+
function upgradeV1(data) {
|
|
167
|
+
const colored = (item) => ({ ...item, color: LAYER_COLOR[item.layer] });
|
|
168
|
+
return { data: { ...data, schemaVersion: 2, groups: data.groups.map(colored), nodes: data.nodes.map(colored) } };
|
|
169
|
+
}
|
|
170
|
+
var diagramFormat = defineFormat({
|
|
171
|
+
name: "diagram",
|
|
172
|
+
history: [
|
|
173
|
+
// Nodes and groups were coloured by their layer. Each is given that layer's colour, so a v1 diagram looks the same and a
|
|
174
|
+
// colour is from then on its own setting.
|
|
175
|
+
{ version: 1, schema: diagramV1Schema, migrate: upgradeV1 },
|
|
176
|
+
{ version: 2, schema: diagramV2Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 3 } }) },
|
|
177
|
+
{ version: 3, schema: diagramV3Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 4 } }) },
|
|
178
|
+
{ version: 4, schema: diagramV4Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 5 } }) },
|
|
179
|
+
{ version: 5, schema: diagramV5Schema }
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
function parseDiagram(input) {
|
|
183
|
+
return readVersionedOrThrow(diagramFormat, input);
|
|
184
|
+
}
|
|
185
|
+
function readDiagram(input) {
|
|
186
|
+
return readVersioned(diagramFormat, input);
|
|
187
|
+
}
|
|
188
|
+
function parseDiagramJson(text) {
|
|
189
|
+
return parseDiagram(JSON.parse(text));
|
|
190
|
+
}
|
|
191
|
+
function serializeDiagram(diagram) {
|
|
192
|
+
return serializeVersioned(diagramFormat, diagram);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export {
|
|
196
|
+
COLOR_PRESETS,
|
|
197
|
+
HEX_COLOR,
|
|
198
|
+
colorSchema,
|
|
199
|
+
isPreset,
|
|
200
|
+
colorCss,
|
|
201
|
+
colorKey,
|
|
202
|
+
arrowId,
|
|
203
|
+
colorLabel,
|
|
204
|
+
DIAGRAM_SCHEMA_VERSION,
|
|
205
|
+
LAYERS,
|
|
206
|
+
layerSchema,
|
|
207
|
+
groupV1Schema,
|
|
208
|
+
nodeV1Schema,
|
|
209
|
+
edgeV1Schema,
|
|
210
|
+
diagramV1Schema,
|
|
211
|
+
groupV2Schema,
|
|
212
|
+
nodeV2Schema,
|
|
213
|
+
edgeSchema,
|
|
214
|
+
diagramV2Schema,
|
|
215
|
+
nodeSchema,
|
|
216
|
+
linkV4Schema,
|
|
217
|
+
nodeV3Schema,
|
|
218
|
+
nodeV4Schema,
|
|
219
|
+
repoIdSchema,
|
|
220
|
+
linkSchema,
|
|
221
|
+
repoRefSchema,
|
|
222
|
+
nodeV5Schema,
|
|
223
|
+
orientationSchema,
|
|
224
|
+
groupSchema,
|
|
225
|
+
diagramV3Schema,
|
|
226
|
+
diagramV4Schema,
|
|
227
|
+
diagramV5Schema,
|
|
228
|
+
diagramSchema,
|
|
229
|
+
diagramFormat,
|
|
230
|
+
parseDiagram,
|
|
231
|
+
readDiagram,
|
|
232
|
+
parseDiagramJson,
|
|
233
|
+
serializeDiagram
|
|
234
|
+
};
|
|
235
|
+
//# sourceMappingURL=chunk-XCRPIPPL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/colors.ts","../src/format.ts"],"sourcesContent":["import { z } from 'zod'\n\n// The colours a node, group or edge can be given. A colour is a preset name, which follows the theme (see --c-* in theme.css of\n// @glyphlab/diagram-react), or a fixed #rrggbb. The website checks the same two forms (glyph-remote lib/pull-request-reports/schema.ts).\nexport const COLOR_PRESETS = ['red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'pink', 'gray'] as const\nexport type ColorPreset = (typeof COLOR_PRESETS)[number]\n\nexport const HEX_COLOR = /^#[0-9a-fA-F]{6}$/\n\nexport const colorSchema = z.union([z.enum(COLOR_PRESETS), z.string().regex(HEX_COLOR)])\n\nexport const isPreset = (color: string): color is ColorPreset => (COLOR_PRESETS as readonly string[]).includes(color)\n\n// What CSS draws for a stored colour. Anything that is neither a preset nor a hex gives nothing, so a bad value in a file\n// can never reach a style attribute.\nexport function colorCss(color: string | undefined): string | undefined {\n if (color === undefined) return undefined\n if (isPreset(color)) return `var(--c-${color})`\n return HEX_COLOR.test(color) ? color.toLowerCase() : undefined\n}\n\n// One arrow head is drawn for each colour in use, since a marker cannot take the colour of the line that points at it.\nexport const colorKey = (color: string) => (isPreset(color) ? color : color.slice(1).toLowerCase())\nexport const arrowId = (base: string, color: string | undefined) => (colorCss(color) && color ? `${base}-${colorKey(color)}` : base)\n\nexport const colorLabel = (color: string) => (isPreset(color) ? color[0].toUpperCase() + color.slice(1) : color.toLowerCase())\n","import { z } from 'zod'\nimport { colorSchema } from './colors'\nimport { defineFormat, readVersioned, readVersionedOrThrow, serializeVersioned } from './versioning'\n\n// Bump when the serialized shape changes in a way old files can't satisfy.\nexport const DIAGRAM_SCHEMA_VERSION = 5\n\n// Layers were the only way to colour a node in v1. From v2 a layer is an optional label and a colour is its own setting.\nexport const LAYERS = ['ui', 'runtime', 'tools', 'domain'] as const\nexport const layerSchema = z.enum(LAYERS)\n\nexport { colorSchema }\n\nconst idSchema = z.string().min(1)\nconst coordinate = z.number().finite()\nconst dimension = z.number().finite().positive()\n\nexport const groupV1Schema = z.strictObject({\n id: idSchema,\n title: z.string().min(1),\n layer: layerSchema,\n x: coordinate,\n y: coordinate,\n w: dimension,\n h: dimension,\n})\n\nexport const nodeV1Schema = z.strictObject({\n id: idSchema,\n title: z.string().min(1),\n layer: layerSchema,\n x: coordinate,\n y: coordinate,\n w: dimension,\n h: dimension,\n // Markdown (GFM) with fenced code blocks.\n body: z.string(),\n})\n\nexport const edgeV1Schema = z.strictObject({\n id: idSchema,\n from: idSchema,\n to: idSchema,\n label: z.string(),\n // Asynchronous event rather than a direct call.\n dashed: z.boolean().optional(),\n // Optional AI assistance rather than a required step.\n optional: z.boolean().optional(),\n})\n\nfunction findDuplicates(ids: string[]) {\n const seen = new Set<string>()\n const dupes = new Set<string>()\n for (const id of ids) {\n if (seen.has(id)) dupes.add(id)\n seen.add(id)\n }\n return [...dupes]\n}\n\n// The checks both versions share: ids are unique and an edge joins nodes that exist.\nfunction checkDiagram(\n diagram: { groups: { id: string }[]; nodes: { id: string }[]; edges: { id: string; from: string; to: string }[] },\n ctx: z.RefinementCtx,\n) {\n const collections = [\n ['groups', diagram.groups],\n ['nodes', diagram.nodes],\n ['edges', diagram.edges],\n ] as const\n\n for (const [key, items] of collections) {\n for (const id of findDuplicates(items.map((item) => item.id))) {\n ctx.addIssue({ code: 'custom', path: [key], message: `Duplicate ${key} id \"${id}\"` })\n }\n }\n\n const nodeIds = new Set(diagram.nodes.map((n) => n.id))\n diagram.edges.forEach((edge, i) => {\n for (const end of ['from', 'to'] as const) {\n if (!nodeIds.has(edge[end])) {\n ctx.addIssue({\n code: 'custom',\n path: ['edges', i, end],\n message: `Edge \"${edge.id}\" references unknown node \"${edge[end]}\"`,\n })\n }\n }\n })\n}\n\n// Version 1 of the file format. Frozen: a released schema is never edited. A breaking change adds\n// a new version (and a migration) in `diagramFormat` below instead.\nexport const diagramV1Schema = z\n .strictObject({\n schemaVersion: z.literal(1),\n name: z.string().min(1),\n description: z.string().optional(),\n groups: z.array(groupV1Schema),\n nodes: z.array(nodeV1Schema),\n edges: z.array(edgeV1Schema),\n })\n .superRefine(checkDiagram)\n\n// Version 2: a node, group or edge may have its own colour, and a layer is optional (a node needs none).\nexport const groupV2Schema = groupV1Schema.extend({ layer: layerSchema.optional(), color: colorSchema.optional() })\nexport const nodeV2Schema = nodeV1Schema.extend({ layer: layerSchema.optional(), color: colorSchema.optional() })\nexport const edgeSchema = edgeV1Schema.extend({ color: colorSchema.optional() })\n\nexport const diagramV2Schema = z\n .strictObject({\n schemaVersion: z.literal(2),\n name: z.string().min(1),\n description: z.string().optional(),\n groups: z.array(groupV2Schema),\n nodes: z.array(nodeV2Schema),\n edges: z.array(edgeSchema),\n })\n .superRefine(checkDiagram)\n\n// Version 3 additions: a node may carry `decisions`, optional Markdown on why it was chosen and what else was considered.\n// `systemsDesign` holds the node's answers to the Systems design questions, by question id (see checklist.ts).\nexport const nodeSchema = nodeV2Schema.extend({ decisions: z.string().optional(), systemsDesign: z.record(z.string(), z.string()).optional() })\n\n// Version 4 addition: a node may list the code it describes. `hash` is taken from the linked lines when the link is made, so a later\n// check can tell that the code changed; a link without lines or a hash can still be checked for the file and symbol existing.\nexport const linkV4Schema = z.strictObject({\n path: z.string().min(1),\n symbol: z.string().min(1).optional(),\n lines: z.tuple([z.number().int().positive(), z.number().int().positive()]).refine(([from, to]) => from <= to, 'lines must run from a line to the same or a later one').optional(),\n hash: z.string().min(1).optional(),\n})\nexport const nodeV3Schema = nodeSchema\nexport const nodeV4Schema = nodeV3Schema.extend({ links: z.array(linkV4Schema).optional() })\n\n// Version 5 additions: a link names the repo its path is in (`repo`, an id from the project's workspace file), and the path is then\n// relative to that repo. A link with no `repo` belongs to the project's default repo, so v4 files read unchanged. The diagram may\n// carry `repos`: for each repo id, where it is on GitHub and which commit the design was written against (filled in when publishing),\n// so a viewer with no access to the files can still link to them. Nothing about where a repo is on a person's disk is ever stored here.\nexport const repoIdSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,39}$/, 'a repo id is letters, digits, \"-\" or \"_\"')\nexport const linkSchema = linkV4Schema.extend({ repo: repoIdSchema.optional() })\nexport const repoRefSchema = z.strictObject({\n // e.g. \"github.com/you/glyph\"\n remote: z.string().min(1).optional(),\n branch: z.string().min(1).optional(),\n // The commit the links were written against (a full or abbreviated hex SHA).\n sha: z.string().regex(/^[0-9a-f]{7,64}$/, 'a commit SHA is 7 to 64 lowercase hex characters').optional(),\n})\nexport const nodeV5Schema = nodeV3Schema.extend({ links: z.array(linkSchema).optional() })\n\nexport const orientationSchema = z.enum(['horizontal', 'vertical'])\nexport const groupSchema = groupV2Schema.extend({\n lane: z.object({ body: z.string(), orientation: orientationSchema }).optional(),\n})\nexport const diagramV3Schema = z.strictObject({\n schemaVersion: z.literal(3),\n name: z.string().min(1),\n description: z.string().optional(),\n type: z.enum(['system', 'swimlane']).optional(),\n groups: z.array(groupSchema),\n nodes: z.array(nodeV3Schema),\n edges: z.array(edgeSchema),\n}).superRefine(checkDiagram)\nexport const diagramV4Schema = z.strictObject({\n schemaVersion: z.literal(4),\n name: z.string().min(1),\n description: z.string().optional(),\n type: z.enum(['system', 'swimlane']).optional(),\n groups: z.array(groupSchema),\n nodes: z.array(nodeV4Schema),\n edges: z.array(edgeSchema),\n}).superRefine(checkDiagram)\n\nexport const diagramV5Schema = z.strictObject({\n schemaVersion: z.literal(DIAGRAM_SCHEMA_VERSION),\n name: z.string().min(1),\n description: z.string().optional(),\n type: z.enum(['system', 'swimlane']).optional(),\n repos: z.record(repoIdSchema, repoRefSchema).optional(),\n groups: z.array(groupSchema),\n nodes: z.array(nodeV5Schema),\n edges: z.array(edgeSchema),\n}).superRefine(checkDiagram)\n\nexport type LaneOrientation = z.infer<typeof orientationSchema>\nexport type DiagramType = 'system' | 'swimlane'\nexport type Layer = z.infer<typeof layerSchema>\nexport type Color = z.infer<typeof colorSchema>\nexport type GroupDef = z.infer<typeof groupSchema>\nexport type LinkDef = z.infer<typeof linkSchema>\nexport type NodeDef = z.infer<typeof nodeV5Schema>\nexport type RepoRef = z.infer<typeof repoRefSchema>\nexport type EdgeDef = z.infer<typeof edgeSchema>\nexport type DiagramFile = z.infer<typeof diagramSchema>\n\n// The part of a diagram the stores hold: everything except file metadata.\nexport type DiagramData = Pick<DiagramFile, 'groups' | 'nodes' | 'edges' | 'type' | 'repos'>\n\n// The schema of the current version.\nexport const diagramSchema = diagramV5Schema\n\nconst LAYER_COLOR = { ui: 'blue', runtime: 'purple', tools: 'orange', domain: 'green' } as const\n\nfunction upgradeV1(data: z.infer<typeof diagramV1Schema>) {\n const colored = <T extends { layer: Layer }>(item: T) => ({ ...item, color: LAYER_COLOR[item.layer] })\n return { data: { ...data, schemaVersion: 2, groups: data.groups.map(colored), nodes: data.nodes.map(colored) } }\n}\n\n// Every version the app can read, oldest first, ending with the current one.\nexport const diagramFormat = defineFormat<DiagramFile>({\n name: 'diagram',\n history: [\n // Nodes and groups were coloured by their layer. Each is given that layer's colour, so a v1 diagram looks the same and a\n // colour is from then on its own setting.\n { version: 1, schema: diagramV1Schema, migrate: upgradeV1 },\n { version: 2, schema: diagramV2Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 3 } }) },\n { version: 3, schema: diagramV3Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 4 } }) },\n { version: 4, schema: diagramV4Schema, migrate: (data) => ({ data: { ...data, schemaVersion: 5 } }) },\n { version: 5, schema: diagramV5Schema },\n ],\n})\n\n// Throws a typed error (see shared/versioning); use for trusted input like bundled samples.\nexport function parseDiagram(input: unknown): DiagramFile {\n return readVersionedOrThrow(diagramFormat, input)\n}\n\n// Returns a result instead of throwing; use for user-supplied files. Older versions are\n// upgraded on the way in, with any warnings reported.\nexport function readDiagram(input: unknown) {\n return readVersioned(diagramFormat, input)\n}\n\nexport function parseDiagramJson(text: string): DiagramFile {\n return parseDiagram(JSON.parse(text))\n}\n\n// Always writes the current version.\nexport function serializeDiagram(diagram: DiagramFile): string {\n return serializeVersioned(diagramFormat, diagram)\n}\n"],"mappings":"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","names":["z","z","z"]}
|