@mica-js/document 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +12 -2
- package/dist/canvas/index.d.ts +223 -0
- package/dist/canvas/index.js +570 -0
- package/dist/canvas/index.js.map +1 -0
- package/dist/errors-7cnIjhnV.js +69 -0
- package/dist/errors-7cnIjhnV.js.map +1 -0
- package/dist/index.d.ts +182 -0
- package/dist/index.js +3 -0
- package/dist/schematic/index.d.ts +931 -0
- package/dist/schematic/index.js +2845 -0
- package/dist/schematic/index.js.map +1 -0
- package/dist/type-name-B6Vkwj77.js +154 -0
- package/dist/type-name-B6Vkwj77.js.map +1 -0
- package/dist/validate-Cz-ZbbEr.js +232 -0
- package/dist/validate-Cz-ZbbEr.js.map +1 -0
- package/package.json +45 -3
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import { a as DELIVERY_KINDS, c as parseDeliveryKind, l as resolveDelivery, n as isAssignable, r as parsePayloadType, s as defaultDelivery, t as PAYLOAD_TYPES } from "../type-name-B6Vkwj77.js";
|
|
2
|
+
import { n as throwIfErrors, t as SchematicError } from "../errors-7cnIjhnV.js";
|
|
3
|
+
import { a as readArrayField, d as readStringField, f as report, h as parseInput, i as isRecord, m as isUuidV4, n as forbidUnknownFields, o as readIntegerField, r as hasErrors, s as readNumberField } from "../validate-Cz-ZbbEr.js";
|
|
4
|
+
//#region src/canvas/format/types.ts
|
|
5
|
+
/**
|
|
6
|
+
* Schema version of the `.cnv` canvas contract (see x-botlab/mica-js#67).
|
|
7
|
+
*
|
|
8
|
+
* A canvas is an independent 2D plane: it records widget instances with their placement, property
|
|
9
|
+
* overrides and port-to-symbol bindings. Electrical topology stays in the `.sch` the canvas binds
|
|
10
|
+
* (see x-botlab/mica-js#6); the two coordinate spaces are unrelated.
|
|
11
|
+
*/
|
|
12
|
+
var CANVAS_VERSION = 1;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/canvas/format/parse.ts
|
|
15
|
+
/**
|
|
16
|
+
* `"<package>:<widget>"`; `builtin` matches the package pattern, so the same expression also
|
|
17
|
+
* accepts the reserved `builtin:<widget>` form (#67).
|
|
18
|
+
*/
|
|
19
|
+
var WIDGET_REFERENCE = /^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*$/;
|
|
20
|
+
var WIDGET_FIELDS = [
|
|
21
|
+
"id",
|
|
22
|
+
"widget",
|
|
23
|
+
"x",
|
|
24
|
+
"y",
|
|
25
|
+
"width",
|
|
26
|
+
"height",
|
|
27
|
+
"rotation",
|
|
28
|
+
"properties",
|
|
29
|
+
"bindings"
|
|
30
|
+
];
|
|
31
|
+
/** @throws {SchematicError} when the input does not match the `.cnv` contract. */
|
|
32
|
+
function parseCanvas(input) {
|
|
33
|
+
const diagnostics = [];
|
|
34
|
+
const parsed = parseInput(input, diagnostics);
|
|
35
|
+
if (!parsed.ok) throw new SchematicError(diagnostics);
|
|
36
|
+
const value = readDocument(parsed.value, diagnostics);
|
|
37
|
+
if (value === void 0 || hasErrors(diagnostics)) throw new SchematicError(diagnostics);
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function readDocument(value, diagnostics) {
|
|
41
|
+
if (!isRecord(value)) {
|
|
42
|
+
report(diagnostics, "json/not-object", "Canvas must be a JSON object.", []);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
forbidUnknownFields(value, [
|
|
46
|
+
"version",
|
|
47
|
+
"schematic",
|
|
48
|
+
"widgets"
|
|
49
|
+
], [], diagnostics);
|
|
50
|
+
const version = readVersion(value, diagnostics);
|
|
51
|
+
const schematic = readSchematic(value, diagnostics);
|
|
52
|
+
const widgets = readWidgets(readArrayField(value, "widgets", [], diagnostics), diagnostics);
|
|
53
|
+
if (version === void 0 || widgets === void 0) return;
|
|
54
|
+
return schematic === void 0 ? {
|
|
55
|
+
version,
|
|
56
|
+
widgets
|
|
57
|
+
} : {
|
|
58
|
+
version,
|
|
59
|
+
schematic,
|
|
60
|
+
widgets
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function readVersion(value, diagnostics) {
|
|
64
|
+
const version = readIntegerField(value, "version", [], diagnostics);
|
|
65
|
+
if (version === void 0) return;
|
|
66
|
+
if (version !== 1) {
|
|
67
|
+
report(diagnostics, "schema/unsupported-version", `Unsupported version ${version}; expected 1.`, ["version"]);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
function readSchematic(value, diagnostics) {
|
|
73
|
+
if (!hasField(value, "schematic")) return;
|
|
74
|
+
return readStringField(value, "schematic", [], diagnostics, { nonEmpty: true });
|
|
75
|
+
}
|
|
76
|
+
function readWidgets(raw, diagnostics) {
|
|
77
|
+
if (raw === void 0) return;
|
|
78
|
+
const widgets = [];
|
|
79
|
+
const seen = /* @__PURE__ */ new Set();
|
|
80
|
+
raw.forEach((item, index) => {
|
|
81
|
+
const path = ["widgets", String(index)];
|
|
82
|
+
const widget = readWidget(item, path, diagnostics);
|
|
83
|
+
if (widget === void 0) return;
|
|
84
|
+
if (seen.has(widget.id)) {
|
|
85
|
+
report(diagnostics, "id/duplicate", `Duplicate widget id "${widget.id}".`, [...path, "id"]);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
seen.add(widget.id);
|
|
89
|
+
widgets.push(widget);
|
|
90
|
+
});
|
|
91
|
+
return widgets;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* @internal Shared with the canvas edit commands so added instances obey the same rules as parsed
|
|
95
|
+
* ones; not re-exported from `canvas/index.ts`.
|
|
96
|
+
*/
|
|
97
|
+
function readWidget(value, path, diagnostics) {
|
|
98
|
+
if (!isRecord(value)) {
|
|
99
|
+
report(diagnostics, "schema/invalid-type", "Widget instance must be an object.", path);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
forbidUnknownFields(value, WIDGET_FIELDS, path, diagnostics);
|
|
103
|
+
const id = readId(value, path, diagnostics);
|
|
104
|
+
const widget = readWidgetReference(value, path, diagnostics);
|
|
105
|
+
const placement = readPlacement(value, path, diagnostics);
|
|
106
|
+
const hasProperties = hasField(value, "properties");
|
|
107
|
+
const properties = hasProperties ? readProperties(value["properties"], [...path, "properties"], diagnostics) : void 0;
|
|
108
|
+
const hasBindings = hasField(value, "bindings");
|
|
109
|
+
const bindings = hasBindings ? readBindings(value["bindings"], [...path, "bindings"], diagnostics) : void 0;
|
|
110
|
+
if (id === void 0 || widget === void 0 || placement === void 0 || hasProperties && properties === void 0 || hasBindings && bindings === void 0) return;
|
|
111
|
+
return {
|
|
112
|
+
id,
|
|
113
|
+
widget,
|
|
114
|
+
...placement,
|
|
115
|
+
...properties === void 0 ? {} : { properties },
|
|
116
|
+
...bindings === void 0 ? {} : { bindings }
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function readId(value, path, diagnostics) {
|
|
120
|
+
const id = readStringField(value, "id", path, diagnostics, { nonEmpty: true });
|
|
121
|
+
if (id === void 0) return;
|
|
122
|
+
if (!isUuidV4(id)) {
|
|
123
|
+
report(diagnostics, "schema/invalid-value", "Field \"id\" must be a UUID v4.", [...path, "id"]);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
return id;
|
|
127
|
+
}
|
|
128
|
+
function readWidgetReference(value, path, diagnostics) {
|
|
129
|
+
const widget = readStringField(value, "widget", path, diagnostics, { nonEmpty: true });
|
|
130
|
+
if (widget === void 0) return;
|
|
131
|
+
if (!WIDGET_REFERENCE.test(widget)) {
|
|
132
|
+
report(diagnostics, "schema/invalid-value", "Field \"widget\" must be \"<package>:<widget>\" or \"builtin:<widget>\".", [...path, "widget"]);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
return widget;
|
|
136
|
+
}
|
|
137
|
+
function readPlacement(value, path, diagnostics) {
|
|
138
|
+
const x = readNumberField(value, "x", path, diagnostics);
|
|
139
|
+
const y = readNumberField(value, "y", path, diagnostics);
|
|
140
|
+
const width = readPositiveNumber(value, "width", path, diagnostics);
|
|
141
|
+
const height = readPositiveNumber(value, "height", path, diagnostics);
|
|
142
|
+
const rotation = readNumberField(value, "rotation", path, diagnostics);
|
|
143
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0 || rotation === void 0) return;
|
|
144
|
+
return {
|
|
145
|
+
x,
|
|
146
|
+
y,
|
|
147
|
+
width,
|
|
148
|
+
height,
|
|
149
|
+
rotation
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* @internal Shared with the canvas edit commands so a replacement placement is validated like a
|
|
154
|
+
* parsed one.
|
|
155
|
+
*/
|
|
156
|
+
function readWidgetPlacement(value, path, diagnostics) {
|
|
157
|
+
if (!isRecord(value)) {
|
|
158
|
+
report(diagnostics, "schema/invalid-type", "Placement must be an object.", path);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
return readPlacement(value, path, diagnostics);
|
|
162
|
+
}
|
|
163
|
+
function readPositiveNumber(value, key, path, diagnostics) {
|
|
164
|
+
const field = readNumberField(value, key, path, diagnostics);
|
|
165
|
+
if (field === void 0) return;
|
|
166
|
+
if (field <= 0) {
|
|
167
|
+
report(diagnostics, "schema/invalid-value", `Field "${key}" must be a positive number.`, [...path, key]);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
return field;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Property values are opaque to the format: their types come from the widget definition (#7), so
|
|
174
|
+
* only the map shape and key spelling are checked here.
|
|
175
|
+
*/
|
|
176
|
+
function readProperties(value, path, diagnostics) {
|
|
177
|
+
if (!isRecord(value)) {
|
|
178
|
+
report(diagnostics, "schema/invalid-type", "Field \"properties\" must be an object.", path);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const properties = Object.create(null);
|
|
182
|
+
let failed = false;
|
|
183
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
184
|
+
if (key.length === 0) {
|
|
185
|
+
report(diagnostics, "schema/invalid-value", "Property keys must not be empty.", path);
|
|
186
|
+
failed = true;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
properties[key] = clonePropertyValue(entry);
|
|
190
|
+
}
|
|
191
|
+
return failed ? void 0 : properties;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Values are copied so the parsed document never aliases the caller's input object.
|
|
195
|
+
*
|
|
196
|
+
* @internal Shared with the canvas edit commands so a written property value has the same
|
|
197
|
+
* ownership semantics as a parsed one; not re-exported from `canvas/index.ts`.
|
|
198
|
+
*/
|
|
199
|
+
function clonePropertyValue(value) {
|
|
200
|
+
if (Array.isArray(value)) return value.map(clonePropertyValue);
|
|
201
|
+
const prototype = isRecord(value) ? Object.getPrototypeOf(value) : void 0;
|
|
202
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
203
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, clonePropertyValue(entry)]));
|
|
204
|
+
}
|
|
205
|
+
function readBindings(value, path, diagnostics) {
|
|
206
|
+
if (!isRecord(value)) {
|
|
207
|
+
report(diagnostics, "schema/invalid-type", "Field \"bindings\" must be an object.", path);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const bindings = Object.create(null);
|
|
211
|
+
let failed = false;
|
|
212
|
+
for (const [port, entry] of Object.entries(value)) {
|
|
213
|
+
if (port.length === 0) {
|
|
214
|
+
report(diagnostics, "schema/invalid-value", "Binding port keys must not be empty.", path);
|
|
215
|
+
failed = true;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const target = readBindingTarget(entry, [...path, port], diagnostics);
|
|
219
|
+
if (target === void 0) {
|
|
220
|
+
failed = true;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
bindings[port] = target;
|
|
224
|
+
}
|
|
225
|
+
return failed ? void 0 : bindings;
|
|
226
|
+
}
|
|
227
|
+
/** @internal Shared with the canvas edit commands for structural binding checks. */
|
|
228
|
+
function readBindingTarget(value, path, diagnostics) {
|
|
229
|
+
if (!isRecord(value)) {
|
|
230
|
+
report(diagnostics, "schema/invalid-type", "Binding target must be an object.", path);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
forbidUnknownFields(value, ["symbol", "port"], path, diagnostics);
|
|
234
|
+
const symbol = readStringField(value, "symbol", path, diagnostics, { nonEmpty: true });
|
|
235
|
+
const port = readStringField(value, "port", path, diagnostics, { nonEmpty: true });
|
|
236
|
+
if (symbol === void 0 || port === void 0) return;
|
|
237
|
+
return {
|
|
238
|
+
symbol,
|
|
239
|
+
port
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function hasField(value, key) {
|
|
243
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
244
|
+
}
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/canvas/format/serialize.ts
|
|
247
|
+
/**
|
|
248
|
+
* Serializes with a fixed field order and 2-space indentation, matching the example in #67.
|
|
249
|
+
* `properties` and `bindings` are written whenever the document carries them, even when empty, so
|
|
250
|
+
* a parse / serialize round trip keeps the original shape.
|
|
251
|
+
*/
|
|
252
|
+
function serializeCanvas(document) {
|
|
253
|
+
const serializable = { version: document.version };
|
|
254
|
+
if (document.schematic !== void 0) serializable["schematic"] = document.schematic;
|
|
255
|
+
serializable["widgets"] = document.widgets.map(serializeWidget);
|
|
256
|
+
return `${JSON.stringify(serializable, null, 2)}\n`;
|
|
257
|
+
}
|
|
258
|
+
function serializeWidget(widget) {
|
|
259
|
+
const serialized = {
|
|
260
|
+
id: widget.id,
|
|
261
|
+
widget: widget.widget,
|
|
262
|
+
x: widget.x,
|
|
263
|
+
y: widget.y,
|
|
264
|
+
width: widget.width,
|
|
265
|
+
height: widget.height,
|
|
266
|
+
rotation: widget.rotation
|
|
267
|
+
};
|
|
268
|
+
if (widget.properties !== void 0) serialized["properties"] = { ...widget.properties };
|
|
269
|
+
if (widget.bindings !== void 0) {
|
|
270
|
+
const bindings = Object.create(null);
|
|
271
|
+
for (const [port, target] of Object.entries(widget.bindings)) bindings[port] = {
|
|
272
|
+
symbol: target.symbol,
|
|
273
|
+
port: target.port
|
|
274
|
+
};
|
|
275
|
+
serialized["bindings"] = bindings;
|
|
276
|
+
}
|
|
277
|
+
return serialized;
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
//#region src/canvas/edit/instances.ts
|
|
281
|
+
/** Position of `instanceId` in drawing order, or `-1` when the canvas has no such instance. */
|
|
282
|
+
function findInstanceIndex(document, instanceId) {
|
|
283
|
+
return document.widgets.findIndex((widget) => widget.id === instanceId);
|
|
284
|
+
}
|
|
285
|
+
/** Replaces one instance, keeping the rest of the document and the array order untouched. */
|
|
286
|
+
function replaceInstance(document, index, instance) {
|
|
287
|
+
const widgets = [...document.widgets];
|
|
288
|
+
widgets[index] = instance;
|
|
289
|
+
return {
|
|
290
|
+
...document,
|
|
291
|
+
widgets
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function addInstance(document, instance, z, diagnostics) {
|
|
295
|
+
const index = isIndexWithin(z, document.widgets.length) ? z : void 0;
|
|
296
|
+
if (index === void 0) report(diagnostics, "edit/invalid-command", `Insertion index ${z} is out of range 0..${document.widgets.length}.`, ["widgets"]);
|
|
297
|
+
const widgetPath = ["widgets", String(index ?? document.widgets.length)];
|
|
298
|
+
const widget = readWidget(instance, widgetPath, diagnostics);
|
|
299
|
+
if (widget !== void 0 && findInstanceIndex(document, widget.id) !== -1) {
|
|
300
|
+
report(diagnostics, "id/duplicate", `Duplicate widget id "${widget.id}".`, [...widgetPath, "id"]);
|
|
301
|
+
return document;
|
|
302
|
+
}
|
|
303
|
+
if (index === void 0 || widget === void 0) return document;
|
|
304
|
+
const widgets = [...document.widgets];
|
|
305
|
+
widgets.splice(index, 0, widget);
|
|
306
|
+
return {
|
|
307
|
+
...document,
|
|
308
|
+
widgets
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function removeInstance(document, instanceId, diagnostics) {
|
|
312
|
+
const index = findInstanceIndex(document, instanceId);
|
|
313
|
+
if (index === -1) {
|
|
314
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
315
|
+
return document;
|
|
316
|
+
}
|
|
317
|
+
const widgets = [...document.widgets];
|
|
318
|
+
widgets.splice(index, 1);
|
|
319
|
+
return {
|
|
320
|
+
...document,
|
|
321
|
+
widgets
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function setPlacement(document, instanceId, placement, diagnostics) {
|
|
325
|
+
const index = findInstanceIndex(document, instanceId);
|
|
326
|
+
const current = index === -1 ? void 0 : document.widgets[index];
|
|
327
|
+
if (current === void 0) {
|
|
328
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
329
|
+
return document;
|
|
330
|
+
}
|
|
331
|
+
const parsed = readWidgetPlacement(placement, ["widgets", String(index)], diagnostics);
|
|
332
|
+
if (parsed === void 0) return document;
|
|
333
|
+
return replaceInstance(document, index, {
|
|
334
|
+
...current,
|
|
335
|
+
...parsed
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function setZ(document, instanceId, z, diagnostics) {
|
|
339
|
+
const index = findInstanceIndex(document, instanceId);
|
|
340
|
+
const current = index === -1 ? void 0 : document.widgets[index];
|
|
341
|
+
if (current === void 0) {
|
|
342
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
343
|
+
return document;
|
|
344
|
+
}
|
|
345
|
+
const widgets = [...document.widgets];
|
|
346
|
+
widgets.splice(index, 1);
|
|
347
|
+
if (!isIndexWithin(z, widgets.length)) {
|
|
348
|
+
report(diagnostics, "edit/invalid-command", `Index ${z} is out of range 0..${widgets.length}.`, ["widgets"]);
|
|
349
|
+
return document;
|
|
350
|
+
}
|
|
351
|
+
widgets.splice(z, 0, current);
|
|
352
|
+
return {
|
|
353
|
+
...document,
|
|
354
|
+
widgets
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function isIndexWithin(index, maximum) {
|
|
358
|
+
return Number.isInteger(index) && index >= 0 && index <= maximum;
|
|
359
|
+
}
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/canvas/edit/bindings.ts
|
|
362
|
+
/**
|
|
363
|
+
* Binds, rebinds or unbinds one widget port. Only the binding shape is checked here; whether the
|
|
364
|
+
* target symbol and port exist, and whether they match in direction and payload, belongs to the
|
|
365
|
+
* binding loader.
|
|
366
|
+
*/
|
|
367
|
+
function setBinding(document, instanceId, port, target, diagnostics) {
|
|
368
|
+
const index = findInstanceIndex(document, instanceId);
|
|
369
|
+
const current = index === -1 ? void 0 : document.widgets[index];
|
|
370
|
+
if (current === void 0) {
|
|
371
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
372
|
+
return document;
|
|
373
|
+
}
|
|
374
|
+
const path = [
|
|
375
|
+
"widgets",
|
|
376
|
+
String(index),
|
|
377
|
+
"bindings"
|
|
378
|
+
];
|
|
379
|
+
if (port.length === 0) {
|
|
380
|
+
report(diagnostics, "schema/invalid-value", "Binding port keys must not be empty.", path);
|
|
381
|
+
return document;
|
|
382
|
+
}
|
|
383
|
+
const bindings = { ...current.bindings };
|
|
384
|
+
if (target === null) delete bindings[port];
|
|
385
|
+
else {
|
|
386
|
+
const parsed = readBindingTarget(target, [...path, port], diagnostics);
|
|
387
|
+
if (parsed === void 0) return document;
|
|
388
|
+
bindings[port] = {
|
|
389
|
+
symbol: parsed.symbol,
|
|
390
|
+
port: parsed.port
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return replaceInstance(document, index, {
|
|
394
|
+
...current,
|
|
395
|
+
bindings
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
/** Sets the bound `.sch` path, or drops the field entirely when `schematic` is `null`. */
|
|
399
|
+
function setSchematic(document, schematic, diagnostics) {
|
|
400
|
+
if (schematic === null) return {
|
|
401
|
+
version: document.version,
|
|
402
|
+
widgets: document.widgets
|
|
403
|
+
};
|
|
404
|
+
if (schematic.length === 0) {
|
|
405
|
+
report(diagnostics, "schema/invalid-value", "Field \"schematic\" must not be empty.", ["schematic"]);
|
|
406
|
+
return document;
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
...document,
|
|
410
|
+
schematic
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
//#endregion
|
|
414
|
+
//#region src/canvas/edit/properties.ts
|
|
415
|
+
function setProperty(document, instanceId, key, value, diagnostics) {
|
|
416
|
+
const index = findInstanceIndex(document, instanceId);
|
|
417
|
+
const current = index === -1 ? void 0 : document.widgets[index];
|
|
418
|
+
if (current === void 0) {
|
|
419
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
420
|
+
return document;
|
|
421
|
+
}
|
|
422
|
+
if (key.length === 0) {
|
|
423
|
+
report(diagnostics, "schema/invalid-value", "Property keys must not be empty.", [
|
|
424
|
+
"widgets",
|
|
425
|
+
String(index),
|
|
426
|
+
"properties"
|
|
427
|
+
]);
|
|
428
|
+
return document;
|
|
429
|
+
}
|
|
430
|
+
const properties = {
|
|
431
|
+
...current.properties,
|
|
432
|
+
[key]: clonePropertyValue(value)
|
|
433
|
+
};
|
|
434
|
+
return replaceInstance(document, index, {
|
|
435
|
+
...current,
|
|
436
|
+
properties
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
function unsetProperty(document, instanceId, key, diagnostics) {
|
|
440
|
+
const index = findInstanceIndex(document, instanceId);
|
|
441
|
+
const current = index === -1 ? void 0 : document.widgets[index];
|
|
442
|
+
if (current === void 0) {
|
|
443
|
+
report(diagnostics, "edit/unknown-entity", `Unknown widget instance "${instanceId}".`, ["widgets"]);
|
|
444
|
+
return document;
|
|
445
|
+
}
|
|
446
|
+
const properties = { ...current.properties };
|
|
447
|
+
if (!Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
448
|
+
report(diagnostics, "edit/unknown-entity", `Widget instance "${current.id}" has no property ${JSON.stringify(key)}.`, [
|
|
449
|
+
"widgets",
|
|
450
|
+
String(index),
|
|
451
|
+
"properties"
|
|
452
|
+
]);
|
|
453
|
+
return document;
|
|
454
|
+
}
|
|
455
|
+
delete properties[key];
|
|
456
|
+
return replaceInstance(document, index, {
|
|
457
|
+
...current,
|
|
458
|
+
properties
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/canvas/edit/commands.ts
|
|
463
|
+
/**
|
|
464
|
+
* Applies one command to a copy of `document`.
|
|
465
|
+
*
|
|
466
|
+
* @throws {SchematicError} when the command is invalid; `diagnostics` carries every problem found
|
|
467
|
+
* and the input document is never mutated.
|
|
468
|
+
*/
|
|
469
|
+
function applyCanvasCommand(document, command) {
|
|
470
|
+
const diagnostics = [];
|
|
471
|
+
const result = dispatch(document, command, diagnostics);
|
|
472
|
+
throwIfErrors(diagnostics);
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
function dispatch(document, command, diagnostics) {
|
|
476
|
+
switch (command.type) {
|
|
477
|
+
case "add-instance": return addInstance(document, command.instance, command.z, diagnostics);
|
|
478
|
+
case "remove-instance": return removeInstance(document, command.instanceId, diagnostics);
|
|
479
|
+
case "set-placement": return setPlacement(document, command.instanceId, command.placement, diagnostics);
|
|
480
|
+
case "set-z": return setZ(document, command.instanceId, command.z, diagnostics);
|
|
481
|
+
case "set-property": return setProperty(document, command.instanceId, command.key, command.value, diagnostics);
|
|
482
|
+
case "unset-property": return unsetProperty(document, command.instanceId, command.key, diagnostics);
|
|
483
|
+
case "set-binding": return setBinding(document, command.instanceId, command.port, command.target, diagnostics);
|
|
484
|
+
case "set-schematic": return setSchematic(document, command.schematic, diagnostics);
|
|
485
|
+
default:
|
|
486
|
+
diagnostics.push({
|
|
487
|
+
severity: "error",
|
|
488
|
+
code: "edit/invalid-command",
|
|
489
|
+
message: `Unsupported canvas command ${JSON.stringify(command.type)}.`
|
|
490
|
+
});
|
|
491
|
+
return document;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/canvas/bindings/validate.ts
|
|
496
|
+
/**
|
|
497
|
+
* Collects binding diagnostics; never throws.
|
|
498
|
+
*
|
|
499
|
+
* Without a `catalog` the checks that need the widget registry and the schematic are skipped, so a
|
|
500
|
+
* host that has not loaded them can call this safely. `schematic` is optional on the document: when
|
|
501
|
+
* absent, only widget-side facts are checked.
|
|
502
|
+
*/
|
|
503
|
+
function validateCanvasBindings(document, options = {}) {
|
|
504
|
+
const { catalog } = options;
|
|
505
|
+
if (catalog === void 0) return [];
|
|
506
|
+
const diagnostics = [];
|
|
507
|
+
const schematic = document.schematic;
|
|
508
|
+
const schematicLoaded = schematic !== void 0 && catalog.hasSchematic(schematic);
|
|
509
|
+
if (schematic !== void 0 && !schematicLoaded) report(diagnostics, "canvas/unknown-schematic", `Schematic "${schematic}" is not loaded.`, ["schematic"]);
|
|
510
|
+
const context = {
|
|
511
|
+
catalog,
|
|
512
|
+
checkWidgets: options.skipWidgets !== true,
|
|
513
|
+
checkSymbols: schematicLoaded
|
|
514
|
+
};
|
|
515
|
+
document.widgets.forEach((widget, index) => {
|
|
516
|
+
validateInstance(widget, index, context, diagnostics);
|
|
517
|
+
});
|
|
518
|
+
return diagnostics;
|
|
519
|
+
}
|
|
520
|
+
/** @throws {SchematicError} when any diagnostic is an error. */
|
|
521
|
+
function assertCanvasBindings(document, options = {}) {
|
|
522
|
+
throwIfErrors(validateCanvasBindings(document, options));
|
|
523
|
+
}
|
|
524
|
+
function validateInstance(widget, index, context, diagnostics) {
|
|
525
|
+
const path = ["widgets", String(index)];
|
|
526
|
+
const definition = context.checkWidgets ? context.catalog.widgetDefinition(widget.widget) : void 0;
|
|
527
|
+
if (context.checkWidgets && definition === void 0) report(diagnostics, "canvas/unknown-widget", `Unknown widget "${widget.widget}".`, [...path, "widget"]);
|
|
528
|
+
if (definition !== void 0) validateProperties(widget, path, definition.propertyKeys, diagnostics);
|
|
529
|
+
for (const [port, target] of Object.entries(widget.bindings ?? {})) validateBinding(widget, port, target, definition, context, [
|
|
530
|
+
...path,
|
|
531
|
+
"bindings",
|
|
532
|
+
port
|
|
533
|
+
], diagnostics);
|
|
534
|
+
}
|
|
535
|
+
function validateProperties(widget, path, propertyKeys, diagnostics) {
|
|
536
|
+
for (const key of Object.keys(widget.properties ?? {})) if (!propertyKeys.includes(key)) report(diagnostics, "canvas/unknown-property", `Widget "${widget.widget}" has no property "${key}".`, [
|
|
537
|
+
...path,
|
|
538
|
+
"properties",
|
|
539
|
+
key
|
|
540
|
+
]);
|
|
541
|
+
}
|
|
542
|
+
function validateBinding(widget, port, target, definition, context, path, diagnostics) {
|
|
543
|
+
const widgetPort = definition?.ports[port];
|
|
544
|
+
if (definition !== void 0 && widgetPort === void 0) report(diagnostics, "canvas/unknown-port", `Widget "${widget.widget}" has no port "${port}".`, path);
|
|
545
|
+
if (!context.checkSymbols) return;
|
|
546
|
+
if (!context.catalog.hasSymbol(target.symbol)) {
|
|
547
|
+
report(diagnostics, "canvas/unknown-symbol", `Unknown symbol "${target.symbol}".`, [...path, "symbol"]);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const devicePort = context.catalog.symbolPort(target.symbol, target.port);
|
|
551
|
+
if (devicePort === void 0) {
|
|
552
|
+
report(diagnostics, "canvas/unknown-target-port", `Symbol "${target.symbol}" has no port "${target.port}".`, [...path, "port"]);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (widgetPort === void 0) return;
|
|
556
|
+
validatePortPair(port, widgetPort, target.port, devicePort, path, diagnostics);
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* A widget input receives what a device output sends and a widget output feeds a device input, so
|
|
560
|
+
* the directions must be complementary and the payload types identical.
|
|
561
|
+
*/
|
|
562
|
+
function validatePortPair(widgetPortName, widgetPort, devicePortName, devicePort, path, diagnostics) {
|
|
563
|
+
const sides = `Widget port "${widgetPortName}" (${widgetPort.direction}, ${widgetPort.type}) cannot bind to device port "${devicePortName}" (${devicePort.direction}, ${devicePort.type})`;
|
|
564
|
+
if (widgetPort.direction === devicePort.direction) report(diagnostics, "topic/type-mismatch", `${sides}: directions must be complementary.`, path);
|
|
565
|
+
if (widgetPort.type !== devicePort.type) report(diagnostics, "topic/type-mismatch", `${sides}: payload types must match.`, path);
|
|
566
|
+
}
|
|
567
|
+
//#endregion
|
|
568
|
+
export { CANVAS_VERSION, DELIVERY_KINDS, PAYLOAD_TYPES, SchematicError, applyCanvasCommand, assertCanvasBindings, defaultDelivery, isAssignable, parseCanvas, parseDeliveryKind, parsePayloadType, resolveDelivery, serializeCanvas, validateCanvasBindings };
|
|
569
|
+
|
|
570
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/canvas/format/types.ts","../../src/canvas/format/parse.ts","../../src/canvas/format/serialize.ts","../../src/canvas/edit/instances.ts","../../src/canvas/edit/bindings.ts","../../src/canvas/edit/properties.ts","../../src/canvas/edit/commands.ts","../../src/canvas/bindings/validate.ts"],"sourcesContent":["/**\n * Schema version of the `.cnv` canvas contract (see x-botlab/mica-js#67).\n *\n * A canvas is an independent 2D plane: it records widget instances with their placement, property\n * overrides and port-to-symbol bindings. Electrical topology stays in the `.sch` the canvas binds\n * (see x-botlab/mica-js#6); the two coordinate spaces are unrelated.\n */\nexport const CANVAS_VERSION = 1;\n\n/** Placement and size of one widget instance, in logical units. */\nexport interface WidgetPlacement {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n /** Clockwise rotation in degrees around the instance center. */\n readonly rotation: number;\n}\n\n/** Endpoint of a widget port: a port of one symbol instance in the bound schematic. */\nexport interface CanvasBindingTarget {\n readonly symbol: string;\n readonly port: string;\n}\n\n/** One widget placed on a canvas; the order in `widgets` is the drawing order (#67). */\nexport interface CanvasWidgetInstance extends WidgetPlacement {\n /** Stable identity: UUID v4, unique within the document. */\n readonly id: string;\n /** Widget reference `\"<package>:<widget>\"`; built-ins use the reserved `builtin:` namespace. */\n readonly widget: string;\n /** Property overrides keyed by the widget definition; values follow that definition (#7). */\n readonly properties?: Readonly<Record<string, unknown>>;\n /** Port key → endpoint; only bound ports take part in messaging (#10). */\n readonly bindings?: Readonly<Record<string, CanvasBindingTarget>>;\n}\n\n/** Root of a `.cnv` file; the project-root relative path is the canvas identity. */\nexport interface CanvasDocument {\n readonly version: typeof CANVAS_VERSION;\n /** Project-root relative POSIX path of the `.sch` the canvas draws and simulates against. */\n readonly schematic?: string;\n /** Widget instances in drawing order; later instances paint above earlier ones. */\n readonly widgets: readonly CanvasWidgetInstance[];\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { SchematicError } from '../../format/errors';\nimport { parseInput } from '../../format/input';\nimport { isUuidV4 } from '../../format/uuid';\nimport {\n forbidUnknownFields,\n hasErrors,\n isRecord,\n readArrayField,\n readIntegerField,\n readNumberField,\n readStringField,\n report,\n} from '../../format/validate';\nimport type {\n CanvasBindingTarget,\n CanvasDocument,\n CanvasWidgetInstance,\n WidgetPlacement,\n} from './types';\nimport { CANVAS_VERSION } from './types';\n\n/**\n * `\"<package>:<widget>\"`; `builtin` matches the package pattern, so the same expression also\n * accepts the reserved `builtin:<widget>` form (#67).\n */\nconst WIDGET_REFERENCE = /^[a-z0-9][a-z0-9-]*:[a-z0-9][a-z0-9-]*$/;\n\nconst WIDGET_FIELDS = [\n 'id',\n 'widget',\n 'x',\n 'y',\n 'width',\n 'height',\n 'rotation',\n 'properties',\n 'bindings',\n] as const;\n\n/** @throws {SchematicError} when the input does not match the `.cnv` contract. */\nexport function parseCanvas(input: string | unknown): CanvasDocument {\n const diagnostics: Diagnostic[] = [];\n const parsed = parseInput(input, diagnostics);\n if (!parsed.ok) {\n throw new SchematicError(diagnostics);\n }\n const value = readDocument(parsed.value, diagnostics);\n if (value === undefined || hasErrors(diagnostics)) {\n throw new SchematicError(diagnostics);\n }\n return value;\n}\n\nfunction readDocument(value: unknown, diagnostics: Diagnostic[]): CanvasDocument | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'json/not-object', 'Canvas must be a JSON object.', []);\n return undefined;\n }\n forbidUnknownFields(value, ['version', 'schematic', 'widgets'], [], diagnostics);\n const version = readVersion(value, diagnostics);\n const schematic = readSchematic(value, diagnostics);\n const widgets = readWidgets(readArrayField(value, 'widgets', [], diagnostics), diagnostics);\n if (version === undefined || widgets === undefined) {\n return undefined;\n }\n return schematic === undefined ? { version, widgets } : { version, schematic, widgets };\n}\n\nfunction readVersion(\n value: Record<string, unknown>,\n diagnostics: Diagnostic[],\n): typeof CANVAS_VERSION | undefined {\n const version = readIntegerField(value, 'version', [], diagnostics);\n if (version === undefined) {\n return undefined;\n }\n if (version !== CANVAS_VERSION) {\n report(\n diagnostics,\n 'schema/unsupported-version',\n `Unsupported version ${version}; expected ${CANVAS_VERSION}.`,\n ['version'],\n );\n return undefined;\n }\n return CANVAS_VERSION;\n}\n\nfunction readSchematic(\n value: Record<string, unknown>,\n diagnostics: Diagnostic[],\n): string | undefined {\n if (!hasField(value, 'schematic')) {\n return undefined;\n }\n return readStringField(value, 'schematic', [], diagnostics, { nonEmpty: true });\n}\n\nfunction readWidgets(\n raw: readonly unknown[] | undefined,\n diagnostics: Diagnostic[],\n): CanvasWidgetInstance[] | undefined {\n if (raw === undefined) {\n return undefined;\n }\n const widgets: CanvasWidgetInstance[] = [];\n const seen = new Set<string>();\n raw.forEach((item, index) => {\n const path = ['widgets', String(index)];\n const widget = readWidget(item, path, diagnostics);\n if (widget === undefined) {\n return;\n }\n if (seen.has(widget.id)) {\n report(diagnostics, 'id/duplicate', `Duplicate widget id \"${widget.id}\".`, [...path, 'id']);\n return;\n }\n seen.add(widget.id);\n widgets.push(widget);\n });\n return widgets;\n}\n\n/**\n * @internal Shared with the canvas edit commands so added instances obey the same rules as parsed\n * ones; not re-exported from `canvas/index.ts`.\n */\nexport function readWidget(\n value: unknown,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): CanvasWidgetInstance | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'schema/invalid-type', 'Widget instance must be an object.', path);\n return undefined;\n }\n forbidUnknownFields(value, WIDGET_FIELDS, path, diagnostics);\n const id = readId(value, path, diagnostics);\n const widget = readWidgetReference(value, path, diagnostics);\n const placement = readPlacement(value, path, diagnostics);\n const hasProperties = hasField(value, 'properties');\n const properties = hasProperties\n ? readProperties(value['properties'], [...path, 'properties'], diagnostics)\n : undefined;\n const hasBindings = hasField(value, 'bindings');\n const bindings = hasBindings\n ? readBindings(value['bindings'], [...path, 'bindings'], diagnostics)\n : undefined;\n if (\n id === undefined ||\n widget === undefined ||\n placement === undefined ||\n (hasProperties && properties === undefined) ||\n (hasBindings && bindings === undefined)\n ) {\n return undefined;\n }\n return {\n id,\n widget,\n ...placement,\n ...(properties === undefined ? {} : { properties }),\n ...(bindings === undefined ? {} : { bindings }),\n };\n}\n\nfunction readId(\n value: Record<string, unknown>,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): string | undefined {\n const id = readStringField(value, 'id', path, diagnostics, { nonEmpty: true });\n if (id === undefined) {\n return undefined;\n }\n if (!isUuidV4(id)) {\n report(diagnostics, 'schema/invalid-value', 'Field \"id\" must be a UUID v4.', [...path, 'id']);\n return undefined;\n }\n return id;\n}\n\nfunction readWidgetReference(\n value: Record<string, unknown>,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): string | undefined {\n const widget = readStringField(value, 'widget', path, diagnostics, { nonEmpty: true });\n if (widget === undefined) {\n return undefined;\n }\n if (!WIDGET_REFERENCE.test(widget)) {\n report(\n diagnostics,\n 'schema/invalid-value',\n 'Field \"widget\" must be \"<package>:<widget>\" or \"builtin:<widget>\".',\n [...path, 'widget'],\n );\n return undefined;\n }\n return widget;\n}\n\nfunction readPlacement(\n value: Record<string, unknown>,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): WidgetPlacement | undefined {\n const x = readNumberField(value, 'x', path, diagnostics);\n const y = readNumberField(value, 'y', path, diagnostics);\n const width = readPositiveNumber(value, 'width', path, diagnostics);\n const height = readPositiveNumber(value, 'height', path, diagnostics);\n const rotation = readNumberField(value, 'rotation', path, diagnostics);\n if (\n x === undefined ||\n y === undefined ||\n width === undefined ||\n height === undefined ||\n rotation === undefined\n ) {\n return undefined;\n }\n return { x, y, width, height, rotation };\n}\n\n/**\n * @internal Shared with the canvas edit commands so a replacement placement is validated like a\n * parsed one.\n */\nexport function readWidgetPlacement(\n value: unknown,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): WidgetPlacement | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'schema/invalid-type', 'Placement must be an object.', path);\n return undefined;\n }\n return readPlacement(value, path, diagnostics);\n}\n\nfunction readPositiveNumber(\n value: Record<string, unknown>,\n key: string,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): number | undefined {\n const field = readNumberField(value, key, path, diagnostics);\n if (field === undefined) {\n return undefined;\n }\n if (field <= 0) {\n report(diagnostics, 'schema/invalid-value', `Field \"${key}\" must be a positive number.`, [\n ...path,\n key,\n ]);\n return undefined;\n }\n return field;\n}\n\n/**\n * Property values are opaque to the format: their types come from the widget definition (#7), so\n * only the map shape and key spelling are checked here.\n */\nfunction readProperties(\n value: unknown,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): Record<string, unknown> | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'schema/invalid-type', 'Field \"properties\" must be an object.', path);\n return undefined;\n }\n const properties: Record<string, unknown> = Object.create(null);\n let failed = false;\n for (const [key, entry] of Object.entries(value)) {\n if (key.length === 0) {\n report(diagnostics, 'schema/invalid-value', 'Property keys must not be empty.', path);\n failed = true;\n continue;\n }\n properties[key] = clonePropertyValue(entry);\n }\n return failed ? undefined : properties;\n}\n\n/**\n * Values are copied so the parsed document never aliases the caller's input object.\n *\n * @internal Shared with the canvas edit commands so a written property value has the same\n * ownership semantics as a parsed one; not re-exported from `canvas/index.ts`.\n */\nexport function clonePropertyValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(clonePropertyValue);\n }\n const prototype = isRecord(value) ? Object.getPrototypeOf(value) : undefined;\n if (prototype !== Object.prototype && prototype !== null) {\n return value;\n }\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>).map(([key, entry]) => [\n key,\n clonePropertyValue(entry),\n ]),\n );\n}\n\nfunction readBindings(\n value: unknown,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): Record<string, CanvasBindingTarget> | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'schema/invalid-type', 'Field \"bindings\" must be an object.', path);\n return undefined;\n }\n const bindings: Record<string, CanvasBindingTarget> = Object.create(null);\n let failed = false;\n for (const [port, entry] of Object.entries(value)) {\n if (port.length === 0) {\n report(diagnostics, 'schema/invalid-value', 'Binding port keys must not be empty.', path);\n failed = true;\n continue;\n }\n const target = readBindingTarget(entry, [...path, port], diagnostics);\n if (target === undefined) {\n failed = true;\n continue;\n }\n bindings[port] = target;\n }\n return failed ? undefined : bindings;\n}\n\n/** @internal Shared with the canvas edit commands for structural binding checks. */\nexport function readBindingTarget(\n value: unknown,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): CanvasBindingTarget | undefined {\n if (!isRecord(value)) {\n report(diagnostics, 'schema/invalid-type', 'Binding target must be an object.', path);\n return undefined;\n }\n forbidUnknownFields(value, ['symbol', 'port'], path, diagnostics);\n const symbol = readStringField(value, 'symbol', path, diagnostics, { nonEmpty: true });\n const port = readStringField(value, 'port', path, diagnostics, { nonEmpty: true });\n if (symbol === undefined || port === undefined) {\n return undefined;\n }\n return { symbol, port };\n}\n\nfunction hasField(value: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n","import type { CanvasBindingTarget, CanvasDocument, CanvasWidgetInstance } from './types';\n\n/**\n * Serializes with a fixed field order and 2-space indentation, matching the example in #67.\n * `properties` and `bindings` are written whenever the document carries them, even when empty, so\n * a parse / serialize round trip keeps the original shape.\n */\nexport function serializeCanvas(document: CanvasDocument): string {\n const serializable: Record<string, unknown> = { version: document.version };\n if (document.schematic !== undefined) {\n serializable['schematic'] = document.schematic;\n }\n serializable['widgets'] = document.widgets.map(serializeWidget);\n return `${JSON.stringify(serializable, null, 2)}\\n`;\n}\n\nfunction serializeWidget(widget: CanvasWidgetInstance): Record<string, unknown> {\n const serialized: Record<string, unknown> = {\n id: widget.id,\n widget: widget.widget,\n x: widget.x,\n y: widget.y,\n width: widget.width,\n height: widget.height,\n rotation: widget.rotation,\n };\n if (widget.properties !== undefined) {\n serialized['properties'] = { ...widget.properties };\n }\n if (widget.bindings !== undefined) {\n const bindings: Record<string, CanvasBindingTarget> = Object.create(null);\n for (const [port, target] of Object.entries(widget.bindings)) {\n bindings[port] = { symbol: target.symbol, port: target.port };\n }\n serialized['bindings'] = bindings;\n }\n return serialized;\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { report } from '../../format/validate';\nimport { readWidget, readWidgetPlacement } from '../format/parse';\nimport type { CanvasDocument, CanvasWidgetInstance, WidgetPlacement } from '../format/types';\n\n/** Position of `instanceId` in drawing order, or `-1` when the canvas has no such instance. */\nexport function findInstanceIndex(document: CanvasDocument, instanceId: string): number {\n return document.widgets.findIndex((widget) => widget.id === instanceId);\n}\n\n/** Replaces one instance, keeping the rest of the document and the array order untouched. */\nexport function replaceInstance(\n document: CanvasDocument,\n index: number,\n instance: CanvasWidgetInstance,\n): CanvasDocument {\n const widgets = [...document.widgets];\n widgets[index] = instance;\n return { ...document, widgets };\n}\n\nexport function addInstance(\n document: CanvasDocument,\n instance: CanvasWidgetInstance,\n z: number,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = isIndexWithin(z, document.widgets.length) ? z : undefined;\n if (index === undefined) {\n report(\n diagnostics,\n 'edit/invalid-command',\n `Insertion index ${z} is out of range 0..${document.widgets.length}.`,\n ['widgets'],\n );\n }\n // Instance problems are reported even when `z` is out of range, so one call surfaces them all.\n const widgetPath = ['widgets', String(index ?? document.widgets.length)];\n const widget = readWidget(instance, widgetPath, diagnostics);\n if (widget !== undefined && findInstanceIndex(document, widget.id) !== -1) {\n report(diagnostics, 'id/duplicate', `Duplicate widget id \"${widget.id}\".`, [\n ...widgetPath,\n 'id',\n ]);\n return document;\n }\n if (index === undefined || widget === undefined) {\n return document;\n }\n const widgets = [...document.widgets];\n widgets.splice(index, 0, widget);\n return { ...document, widgets };\n}\n\nexport function removeInstance(\n document: CanvasDocument,\n instanceId: string,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n if (index === -1) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n const widgets = [...document.widgets];\n widgets.splice(index, 1);\n return { ...document, widgets };\n}\n\nexport function setPlacement(\n document: CanvasDocument,\n instanceId: string,\n placement: WidgetPlacement,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n const current = index === -1 ? undefined : document.widgets[index];\n if (current === undefined) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n const parsed = readWidgetPlacement(placement, ['widgets', String(index)], diagnostics);\n if (parsed === undefined) {\n return document;\n }\n return replaceInstance(document, index, { ...current, ...parsed });\n}\n\nexport function setZ(\n document: CanvasDocument,\n instanceId: string,\n z: number,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n const current = index === -1 ? undefined : document.widgets[index];\n if (current === undefined) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n const widgets = [...document.widgets];\n widgets.splice(index, 1);\n // The target index addresses the array after the removal, so the top position shrinks by one.\n if (!isIndexWithin(z, widgets.length)) {\n report(\n diagnostics,\n 'edit/invalid-command',\n `Index ${z} is out of range 0..${widgets.length}.`,\n ['widgets'],\n );\n return document;\n }\n widgets.splice(z, 0, current);\n return { ...document, widgets };\n}\n\nfunction isIndexWithin(index: number, maximum: number): boolean {\n return Number.isInteger(index) && index >= 0 && index <= maximum;\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { report } from '../../format/validate';\nimport { readBindingTarget } from '../format/parse';\nimport type { CanvasBindingTarget, CanvasDocument } from '../format/types';\nimport { findInstanceIndex, replaceInstance } from './instances';\n\n/**\n * Binds, rebinds or unbinds one widget port. Only the binding shape is checked here; whether the\n * target symbol and port exist, and whether they match in direction and payload, belongs to the\n * binding loader.\n */\nexport function setBinding(\n document: CanvasDocument,\n instanceId: string,\n port: string,\n target: CanvasBindingTarget | null,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n const current = index === -1 ? undefined : document.widgets[index];\n if (current === undefined) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n const path = ['widgets', String(index), 'bindings'];\n if (port.length === 0) {\n report(diagnostics, 'schema/invalid-value', 'Binding port keys must not be empty.', path);\n return document;\n }\n const bindings = { ...current.bindings };\n if (target === null) {\n delete bindings[port];\n } else {\n const parsed = readBindingTarget(target, [...path, port], diagnostics);\n if (parsed === undefined) {\n return document;\n }\n bindings[port] = { symbol: parsed.symbol, port: parsed.port };\n }\n return replaceInstance(document, index, { ...current, bindings });\n}\n\n/** Sets the bound `.sch` path, or drops the field entirely when `schematic` is `null`. */\nexport function setSchematic(\n document: CanvasDocument,\n schematic: string | null,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n if (schematic === null) {\n return { version: document.version, widgets: document.widgets };\n }\n if (schematic.length === 0) {\n report(diagnostics, 'schema/invalid-value', 'Field \"schematic\" must not be empty.', [\n 'schematic',\n ]);\n return document;\n }\n return { ...document, schematic };\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { report } from '../../format/validate';\nimport { clonePropertyValue } from '../format/parse';\nimport type { CanvasDocument } from '../format/types';\nimport { findInstanceIndex, replaceInstance } from './instances';\n\nexport function setProperty(\n document: CanvasDocument,\n instanceId: string,\n key: string,\n value: unknown,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n const current = index === -1 ? undefined : document.widgets[index];\n if (current === undefined) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n if (key.length === 0) {\n report(diagnostics, 'schema/invalid-value', 'Property keys must not be empty.', [\n 'widgets',\n String(index),\n 'properties',\n ]);\n return document;\n }\n // A fresh map keeps the input document and its nested property objects untouched; cloning keeps\n // later mutation of `value` from leaking into the document, matching `parseCanvas`.\n const properties = { ...current.properties, [key]: clonePropertyValue(value) };\n return replaceInstance(document, index, { ...current, properties });\n}\n\nexport function unsetProperty(\n document: CanvasDocument,\n instanceId: string,\n key: string,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n const index = findInstanceIndex(document, instanceId);\n const current = index === -1 ? undefined : document.widgets[index];\n if (current === undefined) {\n report(diagnostics, 'edit/unknown-entity', `Unknown widget instance \"${instanceId}\".`, [\n 'widgets',\n ]);\n return document;\n }\n const properties = { ...current.properties };\n if (!Object.prototype.hasOwnProperty.call(properties, key)) {\n report(\n diagnostics,\n 'edit/unknown-entity',\n `Widget instance \"${current.id}\" has no property ${JSON.stringify(key)}.`,\n ['widgets', String(index), 'properties'],\n );\n return document;\n }\n delete properties[key];\n return replaceInstance(document, index, { ...current, properties });\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { throwIfErrors } from '../../format/errors';\nimport type {\n CanvasBindingTarget,\n CanvasDocument,\n CanvasWidgetInstance,\n WidgetPlacement,\n} from '../format/types';\nimport { setBinding, setSchematic } from './bindings';\nimport { addInstance, removeInstance, setPlacement, setZ } from './instances';\nimport { setProperty, unsetProperty } from './properties';\n\n/**\n * One edit to a `.cnv` document. Every command describes document content only, so hosts can\n * journal them for undo and persistence.\n */\nexport type CanvasCommand =\n | { readonly type: 'add-instance'; readonly instance: CanvasWidgetInstance; readonly z: number }\n | { readonly type: 'remove-instance'; readonly instanceId: string }\n | {\n readonly type: 'set-placement';\n readonly instanceId: string;\n readonly placement: WidgetPlacement;\n }\n | { readonly type: 'set-z'; readonly instanceId: string; readonly z: number }\n | {\n readonly type: 'set-property';\n readonly instanceId: string;\n readonly key: string;\n readonly value: unknown;\n }\n | { readonly type: 'unset-property'; readonly instanceId: string; readonly key: string }\n | {\n readonly type: 'set-binding';\n readonly instanceId: string;\n readonly port: string;\n readonly target: CanvasBindingTarget | null;\n }\n | { readonly type: 'set-schematic'; readonly schematic: string | null };\n\n/**\n * Applies one command to a copy of `document`.\n *\n * @throws {SchematicError} when the command is invalid; `diagnostics` carries every problem found\n * and the input document is never mutated.\n */\nexport function applyCanvasCommand(\n document: CanvasDocument,\n command: CanvasCommand,\n): CanvasDocument {\n const diagnostics: Diagnostic[] = [];\n const result = dispatch(document, command, diagnostics);\n throwIfErrors(diagnostics);\n return result;\n}\n\nfunction dispatch(\n document: CanvasDocument,\n command: CanvasCommand,\n diagnostics: Diagnostic[],\n): CanvasDocument {\n switch (command.type) {\n case 'add-instance':\n return addInstance(document, command.instance, command.z, diagnostics);\n case 'remove-instance':\n return removeInstance(document, command.instanceId, diagnostics);\n case 'set-placement':\n return setPlacement(document, command.instanceId, command.placement, diagnostics);\n case 'set-z':\n return setZ(document, command.instanceId, command.z, diagnostics);\n case 'set-property':\n return setProperty(document, command.instanceId, command.key, command.value, diagnostics);\n case 'unset-property':\n return unsetProperty(document, command.instanceId, command.key, diagnostics);\n case 'set-binding':\n return setBinding(document, command.instanceId, command.port, command.target, diagnostics);\n case 'set-schematic':\n return setSchematic(document, command.schematic, diagnostics);\n default: {\n diagnostics.push({\n severity: 'error',\n code: 'edit/invalid-command',\n message: `Unsupported canvas command ${JSON.stringify((command as { type?: unknown }).type)}.`,\n });\n return document;\n }\n }\n}\n","import type { Diagnostic } from '../../format/diagnostics';\nimport { throwIfErrors } from '../../format/errors';\nimport { report } from '../../format/validate';\nimport type { CanvasBindingTarget, CanvasDocument, CanvasWidgetInstance } from '../format/types';\nimport type { CanvasBindingCatalog, CanvasPortDescriptor } from './catalog';\n\nexport interface ValidateCanvasBindingsOptions {\n readonly catalog?: CanvasBindingCatalog;\n /** Skip widget/property/port checks; useful for partial validation. Default `false`. */\n readonly skipWidgets?: boolean;\n}\n\ntype WidgetDefinition = NonNullable<ReturnType<CanvasBindingCatalog['widgetDefinition']>>;\n\ninterface InstanceContext {\n readonly catalog: CanvasBindingCatalog;\n readonly checkWidgets: boolean;\n readonly checkSymbols: boolean;\n}\n\n/**\n * Collects binding diagnostics; never throws.\n *\n * Without a `catalog` the checks that need the widget registry and the schematic are skipped, so a\n * host that has not loaded them can call this safely. `schematic` is optional on the document: when\n * absent, only widget-side facts are checked.\n */\nexport function validateCanvasBindings(\n document: CanvasDocument,\n options: ValidateCanvasBindingsOptions = {},\n): readonly Diagnostic[] {\n const { catalog } = options;\n if (catalog === undefined) {\n return [];\n }\n const diagnostics: Diagnostic[] = [];\n const schematic = document.schematic;\n // An unloaded schematic makes every symbol unknown, so reporting the path once keeps the output\n // free of cascading `canvas/unknown-symbol` errors.\n const schematicLoaded = schematic !== undefined && catalog.hasSchematic(schematic);\n if (schematic !== undefined && !schematicLoaded) {\n report(diagnostics, 'canvas/unknown-schematic', `Schematic \"${schematic}\" is not loaded.`, [\n 'schematic',\n ]);\n }\n const context: InstanceContext = {\n catalog,\n checkWidgets: options.skipWidgets !== true,\n checkSymbols: schematicLoaded,\n };\n document.widgets.forEach((widget, index) => {\n validateInstance(widget, index, context, diagnostics);\n });\n return diagnostics;\n}\n\n/** @throws {SchematicError} when any diagnostic is an error. */\nexport function assertCanvasBindings(\n document: CanvasDocument,\n options: ValidateCanvasBindingsOptions = {},\n): void {\n throwIfErrors(validateCanvasBindings(document, options));\n}\n\nfunction validateInstance(\n widget: CanvasWidgetInstance,\n index: number,\n context: InstanceContext,\n diagnostics: Diagnostic[],\n): void {\n const path = ['widgets', String(index)];\n const definition = context.checkWidgets\n ? context.catalog.widgetDefinition(widget.widget)\n : undefined;\n if (context.checkWidgets && definition === undefined) {\n report(diagnostics, 'canvas/unknown-widget', `Unknown widget \"${widget.widget}\".`, [\n ...path,\n 'widget',\n ]);\n }\n if (definition !== undefined) {\n validateProperties(widget, path, definition.propertyKeys, diagnostics);\n }\n for (const [port, target] of Object.entries(widget.bindings ?? {})) {\n validateBinding(\n widget,\n port,\n target,\n definition,\n context,\n [...path, 'bindings', port],\n diagnostics,\n );\n }\n}\n\nfunction validateProperties(\n widget: CanvasWidgetInstance,\n path: readonly string[],\n propertyKeys: readonly string[],\n diagnostics: Diagnostic[],\n): void {\n for (const key of Object.keys(widget.properties ?? {})) {\n if (!propertyKeys.includes(key)) {\n report(\n diagnostics,\n 'canvas/unknown-property',\n `Widget \"${widget.widget}\" has no property \"${key}\".`,\n [...path, 'properties', key],\n );\n }\n }\n}\n\nfunction validateBinding(\n widget: CanvasWidgetInstance,\n port: string,\n target: CanvasBindingTarget,\n definition: WidgetDefinition | undefined,\n context: InstanceContext,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): void {\n const widgetPort = definition?.ports[port];\n if (definition !== undefined && widgetPort === undefined) {\n report(\n diagnostics,\n 'canvas/unknown-port',\n `Widget \"${widget.widget}\" has no port \"${port}\".`,\n path,\n );\n }\n if (!context.checkSymbols) {\n return;\n }\n if (!context.catalog.hasSymbol(target.symbol)) {\n report(diagnostics, 'canvas/unknown-symbol', `Unknown symbol \"${target.symbol}\".`, [\n ...path,\n 'symbol',\n ]);\n return;\n }\n const devicePort = context.catalog.symbolPort(target.symbol, target.port);\n if (devicePort === undefined) {\n report(\n diagnostics,\n 'canvas/unknown-target-port',\n `Symbol \"${target.symbol}\" has no port \"${target.port}\".`,\n [...path, 'port'],\n );\n return;\n }\n if (widgetPort === undefined) {\n return;\n }\n validatePortPair(port, widgetPort, target.port, devicePort, path, diagnostics);\n}\n\n/**\n * A widget input receives what a device output sends and a widget output feeds a device input, so\n * the directions must be complementary and the payload types identical.\n */\nfunction validatePortPair(\n widgetPortName: string,\n widgetPort: CanvasPortDescriptor,\n devicePortName: string,\n devicePort: CanvasPortDescriptor,\n path: readonly string[],\n diagnostics: Diagnostic[],\n): void {\n const sides =\n `Widget port \"${widgetPortName}\" (${widgetPort.direction}, ${widgetPort.type}) cannot bind ` +\n `to device port \"${devicePortName}\" (${devicePort.direction}, ${devicePort.type})`;\n if (widgetPort.direction === devicePort.direction) {\n report(diagnostics, 'topic/type-mismatch', `${sides}: directions must be complementary.`, path);\n }\n if (widgetPort.type !== devicePort.type) {\n report(diagnostics, 'topic/type-mismatch', `${sides}: payload types must match.`, path);\n }\n}\n"],"mappings":"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"}
|