@openmbee/opensysml 0.9.1 → 0.10.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/README.md +355 -33
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.js +16 -0
- package/dist/browser/wasm-worker.d.ts +1 -0
- package/dist/browser/wasm-worker.js +2 -0
- package/dist/browser/wasm.d.ts +17 -0
- package/dist/browser/wasm.js +94 -0
- package/dist/core/arguments.d.ts +4 -0
- package/dist/core/arguments.js +19 -0
- package/dist/core/capabilities.d.ts +90 -9
- package/dist/core/capabilities.js +111 -10
- package/dist/core/connection.d.ts +203 -2
- package/dist/core/connection.js +810 -17
- package/dist/core/conversion.d.ts +153 -0
- package/dist/core/conversion.js +191 -0
- package/dist/core/document.d.ts +188 -0
- package/dist/core/document.js +457 -0
- package/dist/core/edit.d.ts +446 -0
- package/dist/core/edit.js +1994 -0
- package/dist/core/engines.d.ts +64 -0
- package/dist/core/engines.js +74 -0
- package/dist/core/errors.d.ts +164 -2
- package/dist/core/errors.js +209 -3
- package/dist/core/exploration.d.ts +71 -0
- package/dist/core/exploration.js +116 -0
- package/dist/core/graphs.d.ts +15 -0
- package/dist/core/graphs.js +3 -0
- package/dist/core/index.d.ts +30 -9
- package/dist/core/index.js +14 -5
- package/dist/core/model.d.ts +168 -8
- package/dist/core/model.js +258 -31
- package/dist/core/package.d.ts +2 -0
- package/dist/core/package.js +2 -0
- package/dist/core/query.d.ts +50 -0
- package/dist/core/query.js +197 -0
- package/dist/core/render-view.d.ts +97 -0
- package/dist/core/render-view.js +120 -0
- package/dist/core/sources.d.ts +34 -0
- package/dist/core/sources.js +134 -0
- package/dist/core/status.d.ts +9 -2
- package/dist/core/status.js +24 -8
- package/dist/core/typed.d.ts +71 -0
- package/dist/core/typed.js +245 -0
- package/dist/core/values.d.ts +127 -8
- package/dist/core/values.js +536 -30
- package/dist/core/verdict.d.ts +271 -0
- package/dist/core/verdict.js +567 -0
- package/dist/core/wasm.d.ts +114 -0
- package/dist/core/wasm.js +514 -0
- package/dist/generated/sysml_pb.d.ts +1080 -22
- package/dist/generated/sysml_pb.js +200 -100
- package/dist/node/binary.d.ts +2 -0
- package/dist/node/binary.js +8 -6
- package/dist/node/generate-cli.d.ts +2 -0
- package/dist/node/generate-cli.js +10 -0
- package/dist/node/generate.d.ts +99 -0
- package/dist/node/generate.js +757 -0
- package/dist/node/index.d.ts +29 -2
- package/dist/node/index.js +211 -10
- package/dist/node/service.d.ts +5 -3
- package/dist/node/service.js +32 -26
- package/dist/node/wasm-source.d.ts +18 -0
- package/dist/node/wasm-source.js +103 -0
- package/dist/node/wasm-worker.d.ts +1 -0
- package/dist/node/wasm-worker.js +10 -0
- package/dist/node/wasm.d.ts +17 -0
- package/dist/node/wasm.js +63 -0
- package/package.json +19 -7
- package/release-digests.json +105 -0
|
@@ -0,0 +1,1994 @@
|
|
|
1
|
+
// Changing a loaded model and writing it back, with its layout intact.
|
|
2
|
+
//
|
|
3
|
+
// An edit is described, not typed out: an Editor collects operations naming
|
|
4
|
+
// elements the way a read result names them, and `apply()` has the service
|
|
5
|
+
// perform them on the source it parsed. The service edits the bytes the
|
|
6
|
+
// operations reach and nothing else, so comments, blank lines and indentation
|
|
7
|
+
// outside an edited span come back unchanged, and it re-parses what it edited
|
|
8
|
+
// before returning it.
|
|
9
|
+
import { create } from "@bufbuild/protobuf";
|
|
10
|
+
import { AddCommentEditSchema, AddConnectionEditSchema, AddDocumentationEditSchema, AddImportEditSchema, AddMemberEditSchema, AddMetadataEditSchema, AddMetadataPrefixEditSchema, AddNoteEditSchema, AddRequirementConstraintEditSchema, AddSatisfyEditSchema, AddSequenceEditSchema, AddTransitionEditSchema, AddVerifyEditSchema, DeleteEditSchema, EditFailure, EditOperationSchema, MetadataFeatureValueSchema, MoveEditSchema, RenameEditSchema, SetValueEditSchema, } from "../generated/sysml_pb.js";
|
|
11
|
+
import { CAPABILITY_ACTION_BODY_STATEMENT_AUTHORING, CAPABILITY_APPLY_EDITS, CAPABILITY_AUTHORING, CAPABILITY_COMMENT_AUTHORING, CAPABILITY_CONNECTION_AUTHORING, CAPABILITY_CONSTRAINT_BODY_AUTHORING, CAPABILITY_DOCUMENTATION_AUTHORING, CAPABILITY_IMPORT_AUTHORING, CAPABILITY_IMPLICIT_PARAMETERS, CAPABILITY_MEMBER_MODIFIERS, CAPABILITY_METADATA_AUTHORING, CAPABILITY_METADATA_PREFIX_AUTHORING, CAPABILITY_REQUIREMENT_CONSTRAINT_AUTHORING, CAPABILITY_SATISFY_AUTHORING, CAPABILITY_SEQUENCE_AUTHORING, CAPABILITY_STATE_ACTION_AUTHORING, CAPABILITY_TRANSITION_AUTHORING, CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING, byCodeUnit, requireCapability, upgradeRemedy, } from "./capabilities.js";
|
|
12
|
+
import { Conversion, FORMAT_SYSML } from "./conversion.js";
|
|
13
|
+
import { DeleteReferencedError, EditError, EditResultError, EditTargetError, IllegalMemberKindError, InvalidEditError, MemberNameTakenError, MoveReferencedError, NoEditsError, OverlappingEditsError, OwnerInsideTargetError, OwnerNotFoundError, OwnerNotNamespaceError, ReferencedElsewhereError, RenameReferencedError, Referrer, } from "./errors.js";
|
|
14
|
+
import { decodeDiagnostic } from "./model.js";
|
|
15
|
+
/** One byte range an operation replaced in the source it saw. */
|
|
16
|
+
export class AppliedEdit {
|
|
17
|
+
/** Position of the operation in the editor. */
|
|
18
|
+
operationIndex;
|
|
19
|
+
/** Element edited, by the id it was named with. */
|
|
20
|
+
target;
|
|
21
|
+
/** Byte offset where the replacement starts. */
|
|
22
|
+
offset;
|
|
23
|
+
/** Number of bytes replaced; zero when text was only inserted. */
|
|
24
|
+
length;
|
|
25
|
+
/** The bytes that were there. */
|
|
26
|
+
oldText;
|
|
27
|
+
/** What replaced them. */
|
|
28
|
+
newText;
|
|
29
|
+
/** The document the bytes belong to, named as the parse named it. */
|
|
30
|
+
document;
|
|
31
|
+
constructor(init) {
|
|
32
|
+
this.operationIndex = init.operationIndex;
|
|
33
|
+
this.target = init.target;
|
|
34
|
+
this.offset = init.offset;
|
|
35
|
+
this.length = init.length;
|
|
36
|
+
this.oldText = init.oldText;
|
|
37
|
+
this.newText = init.newText;
|
|
38
|
+
this.document = init.document;
|
|
39
|
+
}
|
|
40
|
+
toString() {
|
|
41
|
+
return `${this.target}: ${JSON.stringify(this.oldText)} -> ${JSON.stringify(this.newText)}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** The edited notation of one document of the model. */
|
|
45
|
+
export class EditedDocument {
|
|
46
|
+
/** The document's name as the parse named it. */
|
|
47
|
+
name;
|
|
48
|
+
/** The edited notation, byte-identical to the source outside the edited spans. */
|
|
49
|
+
content;
|
|
50
|
+
constructor(init) {
|
|
51
|
+
this.name = init.name;
|
|
52
|
+
this.content = init.content;
|
|
53
|
+
}
|
|
54
|
+
toString() {
|
|
55
|
+
return this.content;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The edited notation, as a {@link Conversion}. */
|
|
59
|
+
export class EditResult extends Conversion {
|
|
60
|
+
/** What each operation changed, grouped by document, in source order within one. */
|
|
61
|
+
applied;
|
|
62
|
+
/** The edited notation of every document the edits rewrote, edited document first. */
|
|
63
|
+
documents;
|
|
64
|
+
constructor(init) {
|
|
65
|
+
super(init);
|
|
66
|
+
this.applied = init.applied;
|
|
67
|
+
this.documents = init.documents;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** Refusal kinds, as the wire enum names them, and the error each raises. */
|
|
71
|
+
const FAILURE_ERRORS = new Map([
|
|
72
|
+
["EDIT_FAILURE_NO_OPERATIONS", NoEditsError],
|
|
73
|
+
["EDIT_FAILURE_UNKNOWN_TARGET", EditTargetError],
|
|
74
|
+
["EDIT_FAILURE_AMBIGUOUS_TARGET", EditTargetError],
|
|
75
|
+
["EDIT_FAILURE_NOT_VALUED", EditTargetError],
|
|
76
|
+
["EDIT_FAILURE_NOT_NAMED", EditTargetError],
|
|
77
|
+
["EDIT_FAILURE_INVALID_VALUE", InvalidEditError],
|
|
78
|
+
["EDIT_FAILURE_INVALID_NAME", InvalidEditError],
|
|
79
|
+
["EDIT_FAILURE_RENAME_REFERENCED", RenameReferencedError],
|
|
80
|
+
["EDIT_FAILURE_OVERLAPPING_EDITS", OverlappingEditsError],
|
|
81
|
+
["EDIT_FAILURE_RESULT_INVALID", EditResultError],
|
|
82
|
+
["EDIT_FAILURE_OWNER_UNKNOWN", OwnerNotFoundError],
|
|
83
|
+
["EDIT_FAILURE_OWNER_NOT_NAMESPACE", OwnerNotNamespaceError],
|
|
84
|
+
["EDIT_FAILURE_ILLEGAL_KIND", IllegalMemberKindError],
|
|
85
|
+
["EDIT_FAILURE_MEMBER_NAME_TAKEN", MemberNameTakenError],
|
|
86
|
+
["EDIT_FAILURE_DELETE_REFERENCED", DeleteReferencedError],
|
|
87
|
+
["EDIT_FAILURE_OWNER_INSIDE_TARGET", OwnerInsideTargetError],
|
|
88
|
+
["EDIT_FAILURE_MOVE_REFERENCED", MoveReferencedError],
|
|
89
|
+
["EDIT_FAILURE_REFERENCED_ELSEWHERE", ReferencedElsewhereError],
|
|
90
|
+
]);
|
|
91
|
+
const FAILURE_NAMES = new Map([
|
|
92
|
+
[EditFailure.UNSPECIFIED, "EDIT_FAILURE_UNSPECIFIED"],
|
|
93
|
+
[EditFailure.NO_OPERATIONS, "EDIT_FAILURE_NO_OPERATIONS"],
|
|
94
|
+
[EditFailure.UNKNOWN_TARGET, "EDIT_FAILURE_UNKNOWN_TARGET"],
|
|
95
|
+
[EditFailure.AMBIGUOUS_TARGET, "EDIT_FAILURE_AMBIGUOUS_TARGET"],
|
|
96
|
+
[EditFailure.NOT_VALUED, "EDIT_FAILURE_NOT_VALUED"],
|
|
97
|
+
[EditFailure.INVALID_VALUE, "EDIT_FAILURE_INVALID_VALUE"],
|
|
98
|
+
[EditFailure.INVALID_NAME, "EDIT_FAILURE_INVALID_NAME"],
|
|
99
|
+
[EditFailure.NOT_NAMED, "EDIT_FAILURE_NOT_NAMED"],
|
|
100
|
+
[EditFailure.RENAME_REFERENCED, "EDIT_FAILURE_RENAME_REFERENCED"],
|
|
101
|
+
[EditFailure.OVERLAPPING_EDITS, "EDIT_FAILURE_OVERLAPPING_EDITS"],
|
|
102
|
+
[EditFailure.RESULT_INVALID, "EDIT_FAILURE_RESULT_INVALID"],
|
|
103
|
+
[EditFailure.OWNER_UNKNOWN, "EDIT_FAILURE_OWNER_UNKNOWN"],
|
|
104
|
+
[EditFailure.OWNER_NOT_NAMESPACE, "EDIT_FAILURE_OWNER_NOT_NAMESPACE"],
|
|
105
|
+
[EditFailure.ILLEGAL_KIND, "EDIT_FAILURE_ILLEGAL_KIND"],
|
|
106
|
+
[EditFailure.MEMBER_NAME_TAKEN, "EDIT_FAILURE_MEMBER_NAME_TAKEN"],
|
|
107
|
+
[EditFailure.DELETE_REFERENCED, "EDIT_FAILURE_DELETE_REFERENCED"],
|
|
108
|
+
[EditFailure.OWNER_INSIDE_TARGET, "EDIT_FAILURE_OWNER_INSIDE_TARGET"],
|
|
109
|
+
[EditFailure.MOVE_REFERENCED, "EDIT_FAILURE_MOVE_REFERENCED"],
|
|
110
|
+
[EditFailure.REFERENCED_ELSEWHERE, "EDIT_FAILURE_REFERENCED_ELSEWHERE"],
|
|
111
|
+
]);
|
|
112
|
+
/** Name a refusal kind, including one this client's enum has no name for. */
|
|
113
|
+
export function failureName(failure) {
|
|
114
|
+
return FAILURE_NAMES.get(failure) ?? `EDIT_FAILURE_${failure}`;
|
|
115
|
+
}
|
|
116
|
+
/** Build the error a refusal kind names. */
|
|
117
|
+
export function errorForFailure(failure, message, options = {}) {
|
|
118
|
+
const ctor = FAILURE_ERRORS.get(failure) ?? EditError;
|
|
119
|
+
return new ctor(message, { failure, ...options });
|
|
120
|
+
}
|
|
121
|
+
/** The referrers of an `ApplyEditsResponse`, each with the document declaring it. */
|
|
122
|
+
export function referrersOf(response) {
|
|
123
|
+
return response.referrers.map((referrer) => new Referrer(referrer.name, referrer.document));
|
|
124
|
+
}
|
|
125
|
+
/** Read an `ApplyEditsResponse` as an {@link EditResult}. */
|
|
126
|
+
export function editResultOf(response, appliedSource = FORMAT_SYSML) {
|
|
127
|
+
return new EditResult({
|
|
128
|
+
content: response.content,
|
|
129
|
+
fromFormat: appliedSource,
|
|
130
|
+
toFormat: appliedSource,
|
|
131
|
+
diagnostics: [],
|
|
132
|
+
experimental: false,
|
|
133
|
+
experimentalNotice: "",
|
|
134
|
+
applied: response.applied.map((edit) => new AppliedEdit({
|
|
135
|
+
operationIndex: edit.operationIndex,
|
|
136
|
+
target: edit.target,
|
|
137
|
+
offset: edit.offset,
|
|
138
|
+
length: edit.length,
|
|
139
|
+
oldText: edit.oldText,
|
|
140
|
+
newText: edit.newText,
|
|
141
|
+
document: edit.document,
|
|
142
|
+
})),
|
|
143
|
+
documents: response.documents.map((document) => new EditedDocument({ name: document.name, content: document.content })),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/** The refusal an `ApplyEditsResponse` carries, as a typed error, or undefined. */
|
|
147
|
+
export function editErrorOf(response) {
|
|
148
|
+
if (response.error === "") {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
return errorForFailure(failureName(response.failure), response.error, {
|
|
152
|
+
diagnostics: response.diagnostics.map(decodeDiagnostic),
|
|
153
|
+
referringElements: response.referringElements,
|
|
154
|
+
referrers: referrersOf(response),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function nameOf(value) {
|
|
158
|
+
if (value === null) {
|
|
159
|
+
return "null";
|
|
160
|
+
}
|
|
161
|
+
return typeof value;
|
|
162
|
+
}
|
|
163
|
+
function sequenceText(label, value, optional = false) {
|
|
164
|
+
if (value === undefined && optional) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (typeof value !== "string") {
|
|
168
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(value)}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function sequenceTuple(owner, keyword, slots, fields) {
|
|
172
|
+
const operation = [
|
|
173
|
+
"add_sequence", owner, keyword, slots.ref, slots.memberKind, slots.memberName, slots.typeName, slots.after,
|
|
174
|
+
];
|
|
175
|
+
if (fields !== undefined && Object.keys(fields).length > 0) {
|
|
176
|
+
operation.push(fields);
|
|
177
|
+
}
|
|
178
|
+
return operation;
|
|
179
|
+
}
|
|
180
|
+
function sequenceOptions(after, multiplicity) {
|
|
181
|
+
sequenceText("multiplicity", multiplicity, true);
|
|
182
|
+
sequenceText("after", after, true);
|
|
183
|
+
return [after ?? "", multiplicity];
|
|
184
|
+
}
|
|
185
|
+
function sequenceKeywordOptions(keyword, options) {
|
|
186
|
+
if (options[1] !== undefined && keyword !== "then") {
|
|
187
|
+
throw new RangeError("multiplicity requires then=True");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function sequenceStatement(owner, keyword, memberKind = "", fields = {}, options) {
|
|
191
|
+
const rest = { ...fields };
|
|
192
|
+
const opts = options?.options ??
|
|
193
|
+
sequenceOptions((options?.after ?? rest["after"]), (options?.multiplicity ?? rest["multiplicity"]));
|
|
194
|
+
delete rest["after"];
|
|
195
|
+
delete rest["multiplicity"];
|
|
196
|
+
const [after, multiplicity] = opts;
|
|
197
|
+
if (multiplicity !== undefined) {
|
|
198
|
+
rest["multiplicity"] = multiplicity;
|
|
199
|
+
}
|
|
200
|
+
return sequenceTuple(owner, keyword, {
|
|
201
|
+
ref: options?.ref ?? "",
|
|
202
|
+
memberKind,
|
|
203
|
+
memberName: options?.memberName ?? "",
|
|
204
|
+
typeName: options?.typeName ?? "",
|
|
205
|
+
after,
|
|
206
|
+
}, rest);
|
|
207
|
+
}
|
|
208
|
+
/** Chainable action-body items for nested `if` and loop statements. */
|
|
209
|
+
export class Body {
|
|
210
|
+
#operations = [];
|
|
211
|
+
/** The body items collected so far. */
|
|
212
|
+
get operations() {
|
|
213
|
+
return [...this.#operations];
|
|
214
|
+
}
|
|
215
|
+
#keyword(then) {
|
|
216
|
+
if (then === undefined) {
|
|
217
|
+
return this.#operations.length === 0 ? "" : "then";
|
|
218
|
+
}
|
|
219
|
+
if (typeof then !== "boolean") {
|
|
220
|
+
throw new TypeError(`then must be bool or None, not ${nameOf(then)}`);
|
|
221
|
+
}
|
|
222
|
+
return then ? "then" : "";
|
|
223
|
+
}
|
|
224
|
+
#addStatement(kind, options) {
|
|
225
|
+
const fields = { ...options };
|
|
226
|
+
delete fields.chained;
|
|
227
|
+
delete fields.typeName;
|
|
228
|
+
const opts = sequenceOptions(fields["after"], fields["multiplicity"]);
|
|
229
|
+
delete fields.after;
|
|
230
|
+
delete fields.multiplicity;
|
|
231
|
+
const keyword = this.#keyword(options.chained);
|
|
232
|
+
sequenceKeywordOptions(keyword, opts);
|
|
233
|
+
this.#operations.push(sequenceStatement("", keyword, kind, fields, {
|
|
234
|
+
typeName: options.typeName ?? "",
|
|
235
|
+
options: opts,
|
|
236
|
+
}));
|
|
237
|
+
return this;
|
|
238
|
+
}
|
|
239
|
+
/** Emit `first <ref>;`. */
|
|
240
|
+
addFirst(ref) {
|
|
241
|
+
sequenceText("ref", ref);
|
|
242
|
+
this.#operations.push(sequenceStatement("", "first", "", {}, { ref }));
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
245
|
+
/** Emit `then [m] <ref>;` or `then [m] <kind> <action> : <type>;`. */
|
|
246
|
+
addThen(ref, action, options = {}) {
|
|
247
|
+
const { type, kind = "action", multiplicity } = options;
|
|
248
|
+
for (const [label, text] of [
|
|
249
|
+
["ref", ref],
|
|
250
|
+
["action", action],
|
|
251
|
+
["type", type],
|
|
252
|
+
["kind", kind],
|
|
253
|
+
["multiplicity", multiplicity],
|
|
254
|
+
]) {
|
|
255
|
+
sequenceText(label, text, true);
|
|
256
|
+
}
|
|
257
|
+
const opts = ["", multiplicity];
|
|
258
|
+
if ((ref === undefined) === (action === undefined)) {
|
|
259
|
+
throw new RangeError("exactly one of ref and action is required");
|
|
260
|
+
}
|
|
261
|
+
if (ref !== undefined) {
|
|
262
|
+
if (type !== undefined || kind !== "action") {
|
|
263
|
+
throw new RangeError("a then reference takes no type or kind");
|
|
264
|
+
}
|
|
265
|
+
this.#operations.push(sequenceStatement("", "then", "", {}, { ref, options: opts }));
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
this.#operations.push(sequenceStatement("", "then", kind, {}, {
|
|
269
|
+
memberName: action,
|
|
270
|
+
typeName: type ?? "",
|
|
271
|
+
options: opts,
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
return this;
|
|
275
|
+
}
|
|
276
|
+
/** Emit `<kind> <name> : <type>;`. */
|
|
277
|
+
addAction(name, options = {}) {
|
|
278
|
+
const { type, kind = "action" } = options;
|
|
279
|
+
for (const [label, text] of [
|
|
280
|
+
["name", name],
|
|
281
|
+
["type", type],
|
|
282
|
+
["kind", kind],
|
|
283
|
+
]) {
|
|
284
|
+
sequenceText(label, text, true);
|
|
285
|
+
}
|
|
286
|
+
this.#operations.push(sequenceStatement("", "", kind, {}, {
|
|
287
|
+
memberName: name ?? "",
|
|
288
|
+
typeName: type ?? "",
|
|
289
|
+
}));
|
|
290
|
+
return this;
|
|
291
|
+
}
|
|
292
|
+
/** Emit `accept <payload> [: <type>] [via <via>];` or `then [m] accept ...;`. */
|
|
293
|
+
addAccept(payload, options = {}) {
|
|
294
|
+
const { type, via, then, multiplicity } = options;
|
|
295
|
+
sequenceText("payload", payload);
|
|
296
|
+
sequenceText("type", type, true);
|
|
297
|
+
sequenceText("via", via, true);
|
|
298
|
+
return this.#addStatement("accept", {
|
|
299
|
+
chained: then,
|
|
300
|
+
typeName: type ?? "",
|
|
301
|
+
parameter: payload,
|
|
302
|
+
via: via ?? "",
|
|
303
|
+
multiplicity,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
/** Emit `send <payload> [via <via>] [to <to>];` or `then [m] send ...;`. */
|
|
307
|
+
addSend(payload, options = {}) {
|
|
308
|
+
const { to, via, then, multiplicity } = options;
|
|
309
|
+
sequenceText("payload", payload);
|
|
310
|
+
sequenceText("to", to, true);
|
|
311
|
+
sequenceText("via", via, true);
|
|
312
|
+
return this.#addStatement("send", {
|
|
313
|
+
chained: then,
|
|
314
|
+
value: payload,
|
|
315
|
+
target: to ?? "",
|
|
316
|
+
via: via ?? "",
|
|
317
|
+
multiplicity,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/** Emit `assign <target> := <value>;` or `then [m] assign ...;`. */
|
|
321
|
+
addAssign(target, value, options = {}) {
|
|
322
|
+
const { then, multiplicity } = options;
|
|
323
|
+
sequenceText("target", target);
|
|
324
|
+
sequenceText("value", value);
|
|
325
|
+
return this.#addStatement("assign", { chained: then, target, value, multiplicity });
|
|
326
|
+
}
|
|
327
|
+
/** Emit `if <condition> { <body> } [else { <elseBody> }]` or `then [m] if ...`. */
|
|
328
|
+
addIf(condition, body, elseBody, options = {}) {
|
|
329
|
+
const { then, multiplicity } = options;
|
|
330
|
+
sequenceText("condition", condition);
|
|
331
|
+
if (!(body instanceof Body)) {
|
|
332
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
333
|
+
}
|
|
334
|
+
if (elseBody !== undefined && !(elseBody instanceof Body)) {
|
|
335
|
+
throw new TypeError(`else_body must be Body or None, not ${nameOf(elseBody)}`);
|
|
336
|
+
}
|
|
337
|
+
return this.#addStatement("if", {
|
|
338
|
+
chained: then,
|
|
339
|
+
condition,
|
|
340
|
+
body: body.operations,
|
|
341
|
+
else_body: elseBody !== undefined && elseBody.operations.length > 0 ? elseBody.operations : [],
|
|
342
|
+
multiplicity,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
/** Emit `while <condition> { <body> } [until <until>];` or `then [m] while ...`. */
|
|
346
|
+
addWhile(condition, body, options = {}) {
|
|
347
|
+
const { until, then, multiplicity } = options;
|
|
348
|
+
sequenceText("condition", condition);
|
|
349
|
+
sequenceText("until", until, true);
|
|
350
|
+
if (!(body instanceof Body)) {
|
|
351
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
352
|
+
}
|
|
353
|
+
return this.#addStatement("while", {
|
|
354
|
+
chained: then,
|
|
355
|
+
condition,
|
|
356
|
+
until: until ?? "",
|
|
357
|
+
body: body.operations,
|
|
358
|
+
multiplicity,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/** Emit `loop { <body> } [until <until>];` or `then [m] loop ...`. */
|
|
362
|
+
addLoop(body, options = {}) {
|
|
363
|
+
const { until, then, multiplicity } = options;
|
|
364
|
+
sequenceText("until", until, true);
|
|
365
|
+
if (!(body instanceof Body)) {
|
|
366
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
367
|
+
}
|
|
368
|
+
return this.#addStatement("loop", {
|
|
369
|
+
chained: then,
|
|
370
|
+
until: until ?? "",
|
|
371
|
+
body: body.operations,
|
|
372
|
+
multiplicity,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/** Emit `for <variable> [: <type>] in <collection> { <body> }` or `then [m] for ...`. */
|
|
376
|
+
addFor(variable, collection, body, options = {}) {
|
|
377
|
+
const { type, then, multiplicity } = options;
|
|
378
|
+
sequenceText("variable", variable);
|
|
379
|
+
sequenceText("collection", collection);
|
|
380
|
+
sequenceText("type", type, true);
|
|
381
|
+
if (!(body instanceof Body)) {
|
|
382
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
383
|
+
}
|
|
384
|
+
return this.#addStatement("for", {
|
|
385
|
+
chained: then,
|
|
386
|
+
parameter: variable,
|
|
387
|
+
value: collection,
|
|
388
|
+
typeName: type ?? "",
|
|
389
|
+
body: body.operations,
|
|
390
|
+
multiplicity,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/** Emit `terminate [<occurrence>];` or `then [m] terminate ...;`. */
|
|
394
|
+
addTerminate(occurrence, options = {}) {
|
|
395
|
+
const { then, multiplicity } = options;
|
|
396
|
+
sequenceText("occurrence", occurrence, true);
|
|
397
|
+
return this.#addStatement("terminate", {
|
|
398
|
+
chained: then,
|
|
399
|
+
value: occurrence ?? "",
|
|
400
|
+
multiplicity,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/** Emit `if <guard> then <ref>;`. */
|
|
404
|
+
addGuardedThen(guard, ref) {
|
|
405
|
+
sequenceText("guard", guard);
|
|
406
|
+
sequenceText("ref", ref);
|
|
407
|
+
this.#operations.push(sequenceStatement("", "if", "", { condition: guard }, { ref }));
|
|
408
|
+
return this;
|
|
409
|
+
}
|
|
410
|
+
/** Emit `else <ref>;`. */
|
|
411
|
+
addElse(ref) {
|
|
412
|
+
sequenceText("ref", ref);
|
|
413
|
+
this.#operations.push(sequenceStatement("", "else", "", {}, { ref }));
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
function notationReferences(label, values) {
|
|
418
|
+
if (values === undefined) {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
const references = typeof values === "string" ? [values] : [...values];
|
|
422
|
+
if (!Array.isArray(references)) {
|
|
423
|
+
throw new TypeError(`${label} must be a notation string or sequence of strings`);
|
|
424
|
+
}
|
|
425
|
+
if (!references.every((reference) => typeof reference === "string")) {
|
|
426
|
+
throw new TypeError(`${label} must contain only notation strings`);
|
|
427
|
+
}
|
|
428
|
+
return references;
|
|
429
|
+
}
|
|
430
|
+
function parameterPairs(parameters, argument) {
|
|
431
|
+
if (parameters === undefined) {
|
|
432
|
+
return [];
|
|
433
|
+
}
|
|
434
|
+
if (!Array.isArray(parameters)) {
|
|
435
|
+
throw new TypeError(`${argument} must be a list of 2-tuples of strings, not ${nameOf(parameters)}`);
|
|
436
|
+
}
|
|
437
|
+
return parameters.map((pair, index) => {
|
|
438
|
+
if (!Array.isArray(pair) || pair.length !== 2) {
|
|
439
|
+
throw new TypeError(`${argument}[${index}] must be a 2-tuple of strings`);
|
|
440
|
+
}
|
|
441
|
+
if (!pair.every((value) => typeof value === "string")) {
|
|
442
|
+
throw new TypeError(`${argument}[${index}] name and type must be strings`);
|
|
443
|
+
}
|
|
444
|
+
return [pair[0], pair[1]];
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function optionalText(value, argument) {
|
|
448
|
+
if (value !== undefined && typeof value !== "string") {
|
|
449
|
+
throw new TypeError(`${argument} must be notation text or None, not ${nameOf(value)}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function targetId(target) {
|
|
453
|
+
if (typeof target === "string") {
|
|
454
|
+
return target;
|
|
455
|
+
}
|
|
456
|
+
const ident = target?.id;
|
|
457
|
+
if (typeof ident === "string" && ident !== "") {
|
|
458
|
+
return ident;
|
|
459
|
+
}
|
|
460
|
+
throw new TypeError(`target must be a symbol id (FQN) or a Symbol, not ${nameOf(target)}`);
|
|
461
|
+
}
|
|
462
|
+
function ownerId(owner) {
|
|
463
|
+
return typeof owner === "string" ? owner : targetId(owner);
|
|
464
|
+
}
|
|
465
|
+
/** Operations to perform on a loaded model, and the call that performs them. */
|
|
466
|
+
export class Editor {
|
|
467
|
+
#modelHash;
|
|
468
|
+
#apply;
|
|
469
|
+
#operations = [];
|
|
470
|
+
#applied = false;
|
|
471
|
+
constructor(modelHash, apply) {
|
|
472
|
+
this.#modelHash = modelHash;
|
|
473
|
+
this.#apply = apply;
|
|
474
|
+
}
|
|
475
|
+
/** The operations collected so far, in the order they were added. */
|
|
476
|
+
get operations() {
|
|
477
|
+
return [...this.#operations];
|
|
478
|
+
}
|
|
479
|
+
/** Whether this editor has been applied. */
|
|
480
|
+
get applied() {
|
|
481
|
+
return this.#applied;
|
|
482
|
+
}
|
|
483
|
+
/** How many operations are collected. */
|
|
484
|
+
get length() {
|
|
485
|
+
return this.#operations.length;
|
|
486
|
+
}
|
|
487
|
+
/** Set the value expression of one of the model's features. */
|
|
488
|
+
setValue(target, value) {
|
|
489
|
+
if (typeof value !== "string") {
|
|
490
|
+
throw new TypeError(`value must be SysML notation for an expression, not ${nameOf(value)}: ` +
|
|
491
|
+
"write it as it should read in the file");
|
|
492
|
+
}
|
|
493
|
+
this.#add(["set_value", targetId(target), value]);
|
|
494
|
+
return this;
|
|
495
|
+
}
|
|
496
|
+
/** Rename one of the model's declarations. */
|
|
497
|
+
rename(target, newName) {
|
|
498
|
+
if (typeof newName !== "string") {
|
|
499
|
+
throw new TypeError(`new_name must be a name, not ${nameOf(newName)}`);
|
|
500
|
+
}
|
|
501
|
+
this.#add(["rename", targetId(target), newName]);
|
|
502
|
+
return this;
|
|
503
|
+
}
|
|
504
|
+
/** Add one declaration, using strings for all SysML/KerML notation. */
|
|
505
|
+
addMember(owner, kind, name, options = {}) {
|
|
506
|
+
const { type, multiplicity, value, specializes, abstract = false, redefines, default: isDefault = false, direction, metadata, expression, doc, } = options;
|
|
507
|
+
if (typeof kind !== "string") {
|
|
508
|
+
throw new TypeError(`kind must be notation text, not ${nameOf(kind)}`);
|
|
509
|
+
}
|
|
510
|
+
for (const [label, text] of [
|
|
511
|
+
["kind", kind],
|
|
512
|
+
["type", type],
|
|
513
|
+
["multiplicity", multiplicity],
|
|
514
|
+
["value", value],
|
|
515
|
+
["expression", expression],
|
|
516
|
+
]) {
|
|
517
|
+
if (text !== undefined && typeof text !== "string") {
|
|
518
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (typeof name !== "string") {
|
|
522
|
+
throw new TypeError(`name must be notation text, not ${nameOf(name)}`);
|
|
523
|
+
}
|
|
524
|
+
const ownerName = ownerId(owner);
|
|
525
|
+
const specializeList = notationReferences("specializes", specializes);
|
|
526
|
+
const redefineList = notationReferences("redefines", redefines);
|
|
527
|
+
const hasMetadata = metadata !== undefined;
|
|
528
|
+
const metadataList = notationReferences("metadata", metadata);
|
|
529
|
+
if (typeof abstract !== "boolean") {
|
|
530
|
+
throw new TypeError("abstract must be bool");
|
|
531
|
+
}
|
|
532
|
+
if (typeof isDefault !== "boolean") {
|
|
533
|
+
throw new TypeError("default must be bool");
|
|
534
|
+
}
|
|
535
|
+
if (direction !== undefined && typeof direction !== "string") {
|
|
536
|
+
throw new TypeError(`direction must be notation text, not ${nameOf(direction)}`);
|
|
537
|
+
}
|
|
538
|
+
if (doc !== undefined && typeof doc !== "string") {
|
|
539
|
+
throw new TypeError(`doc must be text, not ${nameOf(doc)}`);
|
|
540
|
+
}
|
|
541
|
+
const base = [
|
|
542
|
+
"add_member", ownerName, kind, name, type ?? "", multiplicity ?? "",
|
|
543
|
+
value ?? "", [...specializeList],
|
|
544
|
+
];
|
|
545
|
+
if (abstract ||
|
|
546
|
+
redefineList.length > 0 ||
|
|
547
|
+
isDefault ||
|
|
548
|
+
direction !== undefined ||
|
|
549
|
+
hasMetadata ||
|
|
550
|
+
kind === "ref" ||
|
|
551
|
+
kind === "return" ||
|
|
552
|
+
kind === "" ||
|
|
553
|
+
expression !== undefined ||
|
|
554
|
+
doc !== undefined) {
|
|
555
|
+
base.push(abstract, [...redefineList], isDefault, direction ?? "");
|
|
556
|
+
}
|
|
557
|
+
if (hasMetadata) {
|
|
558
|
+
base.push([...metadataList]);
|
|
559
|
+
}
|
|
560
|
+
if (expression !== undefined || doc !== undefined) {
|
|
561
|
+
base.push(expression ?? "");
|
|
562
|
+
}
|
|
563
|
+
if (doc !== undefined) {
|
|
564
|
+
base.push(doc);
|
|
565
|
+
}
|
|
566
|
+
this.#add(base);
|
|
567
|
+
return this;
|
|
568
|
+
}
|
|
569
|
+
/** Add an `objective` member, unnamed when `name` is omitted. */
|
|
570
|
+
addObjective(owner, options = {}) {
|
|
571
|
+
const { name, type } = options;
|
|
572
|
+
if (name !== undefined && typeof name !== "string") {
|
|
573
|
+
throw new TypeError(`name must be notation text, not ${nameOf(name)}`);
|
|
574
|
+
}
|
|
575
|
+
if (type !== undefined && typeof type !== "string") {
|
|
576
|
+
throw new TypeError(`type must be notation text, not ${nameOf(type)}`);
|
|
577
|
+
}
|
|
578
|
+
return this.addMember(owner, "objective", name ?? "", { type });
|
|
579
|
+
}
|
|
580
|
+
/** Add `verify <requirement>;` to a verification case or its objective. */
|
|
581
|
+
addVerify(owner, requirement) {
|
|
582
|
+
if (typeof requirement !== "string") {
|
|
583
|
+
throw new TypeError(`requirement must be notation text, not ${nameOf(requirement)}`);
|
|
584
|
+
}
|
|
585
|
+
this.#add(["add_verify", ownerId(owner), requirement]);
|
|
586
|
+
return this;
|
|
587
|
+
}
|
|
588
|
+
/** Add a `metadata` usage or `@` shorthand with optional values. */
|
|
589
|
+
addMetadata(owner, metadataType, options = {}) {
|
|
590
|
+
const { values, name, about, shorthand = false } = options;
|
|
591
|
+
if (typeof metadataType !== "string") {
|
|
592
|
+
throw new TypeError(`metadata_type must be notation text, not ${nameOf(metadataType)}`);
|
|
593
|
+
}
|
|
594
|
+
if (name !== undefined && typeof name !== "string") {
|
|
595
|
+
throw new TypeError(`name must be notation text, not ${nameOf(name)}`);
|
|
596
|
+
}
|
|
597
|
+
if (typeof shorthand !== "boolean") {
|
|
598
|
+
throw new TypeError("shorthand must be bool");
|
|
599
|
+
}
|
|
600
|
+
let bindings;
|
|
601
|
+
if (values === undefined) {
|
|
602
|
+
bindings = [];
|
|
603
|
+
}
|
|
604
|
+
else if (Array.isArray(values)) {
|
|
605
|
+
bindings = values;
|
|
606
|
+
}
|
|
607
|
+
else if (typeof values === "object") {
|
|
608
|
+
bindings = Object.entries(values);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
throw new TypeError("values must be a mapping or a sequence of (feature, value) pairs");
|
|
612
|
+
}
|
|
613
|
+
const normalized = bindings.map((pair, index) => {
|
|
614
|
+
if (!Array.isArray(pair) || pair.length !== 2) {
|
|
615
|
+
throw new TypeError(`values[${index}] must be a pair of strings`);
|
|
616
|
+
}
|
|
617
|
+
if (!pair.every((text) => typeof text === "string")) {
|
|
618
|
+
throw new TypeError(`values[${index}] feature and value must be strings`);
|
|
619
|
+
}
|
|
620
|
+
return [pair[0], pair[1]];
|
|
621
|
+
});
|
|
622
|
+
const aboutList = notationReferences("about", about);
|
|
623
|
+
this.#add([
|
|
624
|
+
"add_metadata", ownerId(owner), metadataType, name ?? "", aboutList, normalized, shorthand,
|
|
625
|
+
]);
|
|
626
|
+
return this;
|
|
627
|
+
}
|
|
628
|
+
/** Add a metadata prefix to an existing declaration. */
|
|
629
|
+
addMetadataPrefix(target, metadataType) {
|
|
630
|
+
if (typeof metadataType !== "string") {
|
|
631
|
+
throw new TypeError(`metadata_type must be notation text, not ${nameOf(metadataType)}`);
|
|
632
|
+
}
|
|
633
|
+
this.#add(["add_metadata_prefix", ownerId(target), metadataType]);
|
|
634
|
+
return this;
|
|
635
|
+
}
|
|
636
|
+
/** Add `doc /* body *\/` as the first body member of a declaration. */
|
|
637
|
+
addDocumentation(target, body, options = {}) {
|
|
638
|
+
const { name, locale, replace = false } = options;
|
|
639
|
+
if (typeof body !== "string") {
|
|
640
|
+
throw new TypeError(`body must be text, not ${nameOf(body)}`);
|
|
641
|
+
}
|
|
642
|
+
for (const [label, text] of [
|
|
643
|
+
["name", name],
|
|
644
|
+
["locale", locale],
|
|
645
|
+
]) {
|
|
646
|
+
if (text !== undefined && typeof text !== "string") {
|
|
647
|
+
throw new TypeError(`${label} must be text, not ${nameOf(text)}`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (typeof replace !== "boolean") {
|
|
651
|
+
throw new TypeError(`replace must be a bool, not ${nameOf(replace)}`);
|
|
652
|
+
}
|
|
653
|
+
this.#add([
|
|
654
|
+
"add_documentation", targetId(target), body, name ?? "", locale ?? "", replace,
|
|
655
|
+
]);
|
|
656
|
+
return this;
|
|
657
|
+
}
|
|
658
|
+
/** Add `comment [name] [about a, b] [locale "..."] /* body *\/` to a body. */
|
|
659
|
+
addComment(owner, body, options = {}) {
|
|
660
|
+
const { name, about, locale } = options;
|
|
661
|
+
if (typeof body !== "string") {
|
|
662
|
+
throw new TypeError(`body must be text, not ${nameOf(body)}`);
|
|
663
|
+
}
|
|
664
|
+
for (const [label, text] of [
|
|
665
|
+
["name", name],
|
|
666
|
+
["locale", locale],
|
|
667
|
+
]) {
|
|
668
|
+
if (text !== undefined && typeof text !== "string") {
|
|
669
|
+
throw new TypeError(`${label} must be text, not ${nameOf(text)}`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (typeof about === "string") {
|
|
673
|
+
throw new TypeError("about must be a sequence of names or symbols, not one name");
|
|
674
|
+
}
|
|
675
|
+
const aboutList = (about ?? []).map((element) => targetId(element));
|
|
676
|
+
this.#add(["add_comment", ownerId(owner), body, name ?? "", aboutList, locale ?? ""]);
|
|
677
|
+
return this;
|
|
678
|
+
}
|
|
679
|
+
/** Write the line note `// text` on its own line above a declaration. */
|
|
680
|
+
addNote(target, text) {
|
|
681
|
+
if (typeof text !== "string") {
|
|
682
|
+
throw new TypeError(`text must be text, not ${nameOf(text)}`);
|
|
683
|
+
}
|
|
684
|
+
if (text.includes("\n") || text.includes("\r")) {
|
|
685
|
+
throw new RangeError("a note is one line: its text may not contain a line break");
|
|
686
|
+
}
|
|
687
|
+
this.#add(["add_note", targetId(target), text]);
|
|
688
|
+
return this;
|
|
689
|
+
}
|
|
690
|
+
/** Add a `satisfy` usage to a body that admits behavior usages. */
|
|
691
|
+
addSatisfy(owner, requirement, options = {}) {
|
|
692
|
+
const { by, asserted = false, negated = false } = options;
|
|
693
|
+
if (typeof requirement !== "string") {
|
|
694
|
+
throw new TypeError(`requirement must be notation text, not ${nameOf(requirement)}`);
|
|
695
|
+
}
|
|
696
|
+
if (by !== undefined && typeof by !== "string") {
|
|
697
|
+
throw new TypeError(`by must be notation text, not ${nameOf(by)}`);
|
|
698
|
+
}
|
|
699
|
+
if (typeof asserted !== "boolean" || typeof negated !== "boolean") {
|
|
700
|
+
throw new TypeError("asserted and negated must be bool");
|
|
701
|
+
}
|
|
702
|
+
this.#add(["add_satisfy", ownerId(owner), requirement, by ?? "", asserted, negated]);
|
|
703
|
+
return this;
|
|
704
|
+
}
|
|
705
|
+
/** Add a `require` or `assume` constraint to a requirement-like body. */
|
|
706
|
+
addRequirementConstraint(owner, kind, expression, name) {
|
|
707
|
+
for (const [label, text] of [
|
|
708
|
+
["kind", kind],
|
|
709
|
+
["expression", expression],
|
|
710
|
+
]) {
|
|
711
|
+
if (typeof text !== "string") {
|
|
712
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (name !== undefined && typeof name !== "string") {
|
|
716
|
+
throw new TypeError(`name must be notation text, not ${nameOf(name)}`);
|
|
717
|
+
}
|
|
718
|
+
this.#add(["add_requirement_constraint", ownerId(owner), kind, expression, name ?? ""]);
|
|
719
|
+
return this;
|
|
720
|
+
}
|
|
721
|
+
/** Add a transition with optional trigger, guard and effect clauses. */
|
|
722
|
+
addTransition(owner, source, target, options = {}) {
|
|
723
|
+
const { name, trigger, guard, effect } = options;
|
|
724
|
+
if (typeof source !== "string") {
|
|
725
|
+
throw new TypeError(`source must be notation text, not ${nameOf(source)}`);
|
|
726
|
+
}
|
|
727
|
+
if (typeof target !== "string") {
|
|
728
|
+
throw new TypeError(`target must be notation text, not ${nameOf(target)}`);
|
|
729
|
+
}
|
|
730
|
+
for (const [label, text] of [
|
|
731
|
+
["name", name],
|
|
732
|
+
["trigger", trigger],
|
|
733
|
+
["guard", guard],
|
|
734
|
+
["effect", effect],
|
|
735
|
+
]) {
|
|
736
|
+
if (text !== undefined && typeof text !== "string") {
|
|
737
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
this.#add([
|
|
741
|
+
"add_transition", ownerId(owner), name ?? "", source, target,
|
|
742
|
+
trigger ?? "", guard ?? "", effect ?? "", false,
|
|
743
|
+
]);
|
|
744
|
+
return this;
|
|
745
|
+
}
|
|
746
|
+
/** Add an entry transition to target in a state body. */
|
|
747
|
+
addEntryTransition(owner, target) {
|
|
748
|
+
if (typeof target !== "string") {
|
|
749
|
+
throw new TypeError(`target must be notation text, not ${nameOf(target)}`);
|
|
750
|
+
}
|
|
751
|
+
this.#add(["add_transition", ownerId(owner), "", "", target, "", "", "", true]);
|
|
752
|
+
return this;
|
|
753
|
+
}
|
|
754
|
+
/** Emit `first <ref>;`. */
|
|
755
|
+
addFirst(owner, ref, options = {}) {
|
|
756
|
+
const { after } = options;
|
|
757
|
+
if (typeof ref !== "string") {
|
|
758
|
+
throw new TypeError(`ref must be notation text, not ${nameOf(ref)}`);
|
|
759
|
+
}
|
|
760
|
+
if (after !== undefined && typeof after !== "string") {
|
|
761
|
+
throw new TypeError(`after must be a member name, not ${nameOf(after)}`);
|
|
762
|
+
}
|
|
763
|
+
const opts = [after ?? "", undefined];
|
|
764
|
+
this.#add(sequenceStatement(ownerId(owner), "first", "", {}, { ref, options: opts }));
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
/** Emit `then [m] <ref>;` or `then [m] <kind> <name> : <type>;`. */
|
|
768
|
+
addThen(owner, options = {}) {
|
|
769
|
+
const { ref, action, type, after, kind = "action", multiplicity } = options;
|
|
770
|
+
for (const [label, text] of [
|
|
771
|
+
["ref", ref],
|
|
772
|
+
["action", action],
|
|
773
|
+
["type", type],
|
|
774
|
+
["after", after],
|
|
775
|
+
["kind", kind],
|
|
776
|
+
["multiplicity", multiplicity],
|
|
777
|
+
]) {
|
|
778
|
+
if (text !== undefined && typeof text !== "string") {
|
|
779
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
const opts = [after ?? "", multiplicity];
|
|
783
|
+
if ((ref === undefined) === (action === undefined)) {
|
|
784
|
+
throw new RangeError("exactly one of ref and action is required");
|
|
785
|
+
}
|
|
786
|
+
if (ref !== undefined && (type !== undefined || kind !== "action")) {
|
|
787
|
+
throw new RangeError("a then reference takes no type or kind");
|
|
788
|
+
}
|
|
789
|
+
if (ref !== undefined) {
|
|
790
|
+
this.#add(sequenceStatement(ownerId(owner), "then", "", {}, { ref, options: opts }));
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
this.#add(sequenceStatement(ownerId(owner), "then", kind, {}, {
|
|
794
|
+
memberName: action,
|
|
795
|
+
typeName: type ?? "",
|
|
796
|
+
options: opts,
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
799
|
+
return this;
|
|
800
|
+
}
|
|
801
|
+
/** Emit `then [m] accept <payload> [: <type>] [via <via>];`. */
|
|
802
|
+
addAccept(owner, payload, options = {}) {
|
|
803
|
+
const { type, via, then = true, multiplicity, after } = options;
|
|
804
|
+
sequenceText("payload", payload);
|
|
805
|
+
sequenceText("type", type, true);
|
|
806
|
+
sequenceText("via", via, true);
|
|
807
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
808
|
+
if (typeof then !== "boolean") {
|
|
809
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
810
|
+
}
|
|
811
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
812
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "accept", {
|
|
813
|
+
parameter: payload,
|
|
814
|
+
via: via ?? "",
|
|
815
|
+
}, {
|
|
816
|
+
typeName: type ?? "",
|
|
817
|
+
options: opts,
|
|
818
|
+
}));
|
|
819
|
+
return this;
|
|
820
|
+
}
|
|
821
|
+
/** Emit `then [m] send <payload> [via <via>] [to <to>];`. */
|
|
822
|
+
addSend(owner, payload, options = {}) {
|
|
823
|
+
const { to, via, then = true, multiplicity, after } = options;
|
|
824
|
+
sequenceText("payload", payload);
|
|
825
|
+
sequenceText("to", to, true);
|
|
826
|
+
sequenceText("via", via, true);
|
|
827
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
828
|
+
if (typeof then !== "boolean") {
|
|
829
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
830
|
+
}
|
|
831
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
832
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "send", {
|
|
833
|
+
value: payload,
|
|
834
|
+
target: to ?? "",
|
|
835
|
+
via: via ?? "",
|
|
836
|
+
}, {
|
|
837
|
+
options: opts,
|
|
838
|
+
}));
|
|
839
|
+
return this;
|
|
840
|
+
}
|
|
841
|
+
/** Emit `then [m] assign <target> := <value>;`. */
|
|
842
|
+
addAssign(owner, target, value, options = {}) {
|
|
843
|
+
const { then = true, multiplicity, after } = options;
|
|
844
|
+
sequenceText("target", target);
|
|
845
|
+
sequenceText("value", value);
|
|
846
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
847
|
+
if (typeof then !== "boolean") {
|
|
848
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
849
|
+
}
|
|
850
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
851
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "assign", {
|
|
852
|
+
target,
|
|
853
|
+
value,
|
|
854
|
+
}, {
|
|
855
|
+
options: opts,
|
|
856
|
+
}));
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
859
|
+
/** Emit `then [m] if <condition> { <body> } [else { <elseBody> }]`. */
|
|
860
|
+
addIf(owner, condition, body, elseBody, options = {}) {
|
|
861
|
+
const { then = true, multiplicity, after } = options;
|
|
862
|
+
sequenceText("condition", condition);
|
|
863
|
+
if (!(body instanceof Body)) {
|
|
864
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
865
|
+
}
|
|
866
|
+
if (elseBody !== undefined && !(elseBody instanceof Body)) {
|
|
867
|
+
throw new TypeError(`else_body must be Body or None, not ${nameOf(elseBody)}`);
|
|
868
|
+
}
|
|
869
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
870
|
+
if (typeof then !== "boolean") {
|
|
871
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
872
|
+
}
|
|
873
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
874
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "if", {
|
|
875
|
+
condition,
|
|
876
|
+
body: body.operations,
|
|
877
|
+
else_body: elseBody !== undefined && elseBody.operations.length > 0 ? elseBody.operations : [],
|
|
878
|
+
}, {
|
|
879
|
+
options: opts,
|
|
880
|
+
}));
|
|
881
|
+
return this;
|
|
882
|
+
}
|
|
883
|
+
/** Emit `then [m] while <condition> { <body> } [until <until>];`. */
|
|
884
|
+
addWhile(owner, condition, body, options = {}) {
|
|
885
|
+
const { until, then = true, multiplicity, after } = options;
|
|
886
|
+
sequenceText("condition", condition);
|
|
887
|
+
sequenceText("until", until, true);
|
|
888
|
+
if (!(body instanceof Body)) {
|
|
889
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
890
|
+
}
|
|
891
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
892
|
+
if (typeof then !== "boolean") {
|
|
893
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
894
|
+
}
|
|
895
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
896
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "while", {
|
|
897
|
+
condition,
|
|
898
|
+
until: until ?? "",
|
|
899
|
+
body: body.operations,
|
|
900
|
+
}, {
|
|
901
|
+
options: opts,
|
|
902
|
+
}));
|
|
903
|
+
return this;
|
|
904
|
+
}
|
|
905
|
+
/** Emit `then [m] loop { <body> } [until <until>];`. */
|
|
906
|
+
addLoop(owner, body, options = {}) {
|
|
907
|
+
const { until, then = true, multiplicity, after } = options;
|
|
908
|
+
sequenceText("until", until, true);
|
|
909
|
+
if (!(body instanceof Body)) {
|
|
910
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
911
|
+
}
|
|
912
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
913
|
+
if (typeof then !== "boolean") {
|
|
914
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
915
|
+
}
|
|
916
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
917
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "loop", {
|
|
918
|
+
until: until ?? "",
|
|
919
|
+
body: body.operations,
|
|
920
|
+
}, {
|
|
921
|
+
options: opts,
|
|
922
|
+
}));
|
|
923
|
+
return this;
|
|
924
|
+
}
|
|
925
|
+
/** Emit `then [m] for <variable> [: <type>] in <collection> { <body> }`. */
|
|
926
|
+
addFor(owner, variable, collection, body, options = {}) {
|
|
927
|
+
const { type, then = true, multiplicity, after } = options;
|
|
928
|
+
sequenceText("variable", variable);
|
|
929
|
+
sequenceText("collection", collection);
|
|
930
|
+
sequenceText("type", type, true);
|
|
931
|
+
if (!(body instanceof Body)) {
|
|
932
|
+
throw new TypeError(`body must be Body, not ${nameOf(body)}`);
|
|
933
|
+
}
|
|
934
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
935
|
+
if (typeof then !== "boolean") {
|
|
936
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
937
|
+
}
|
|
938
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
939
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "for", {
|
|
940
|
+
parameter: variable,
|
|
941
|
+
value: collection,
|
|
942
|
+
body: body.operations,
|
|
943
|
+
}, {
|
|
944
|
+
typeName: type ?? "",
|
|
945
|
+
options: opts,
|
|
946
|
+
}));
|
|
947
|
+
return this;
|
|
948
|
+
}
|
|
949
|
+
/** Emit `then [m] terminate [<occurrence>];`. */
|
|
950
|
+
addTerminate(owner, occurrence, options = {}) {
|
|
951
|
+
const { then = true, multiplicity, after } = options;
|
|
952
|
+
sequenceText("occurrence", occurrence, true);
|
|
953
|
+
const opts = sequenceOptions(after, multiplicity);
|
|
954
|
+
if (typeof then !== "boolean") {
|
|
955
|
+
throw new TypeError(`then must be bool, not ${nameOf(then)}`);
|
|
956
|
+
}
|
|
957
|
+
sequenceKeywordOptions(then ? "then" : "", opts);
|
|
958
|
+
this.#add(sequenceStatement(ownerId(owner), then ? "then" : "", "terminate", {
|
|
959
|
+
value: occurrence ?? "",
|
|
960
|
+
}, {
|
|
961
|
+
options: opts,
|
|
962
|
+
}));
|
|
963
|
+
return this;
|
|
964
|
+
}
|
|
965
|
+
/** Emit `if <guard> then <ref>;`. */
|
|
966
|
+
addGuardedThen(owner, guard, ref, options = {}) {
|
|
967
|
+
const { after } = options;
|
|
968
|
+
sequenceText("guard", guard);
|
|
969
|
+
sequenceText("ref", ref);
|
|
970
|
+
const opts = sequenceOptions(after);
|
|
971
|
+
this.#add(sequenceStatement(ownerId(owner), "if", "", { condition: guard }, { ref, options: opts }));
|
|
972
|
+
return this;
|
|
973
|
+
}
|
|
974
|
+
/** Emit `else <ref>;`. */
|
|
975
|
+
addElse(owner, ref, options = {}) {
|
|
976
|
+
const { after } = options;
|
|
977
|
+
sequenceText("ref", ref);
|
|
978
|
+
const opts = sequenceOptions(after);
|
|
979
|
+
this.#add(sequenceStatement(ownerId(owner), "else", "", {}, { ref, options: opts }));
|
|
980
|
+
return this;
|
|
981
|
+
}
|
|
982
|
+
/** Add an import declaration to a namespace body or the document root. */
|
|
983
|
+
addImport(owner, target, options = {}) {
|
|
984
|
+
const { visibility, recursive = false, all = false, filter } = options;
|
|
985
|
+
if (typeof target !== "string") {
|
|
986
|
+
throw new TypeError(`target must be notation text, not ${nameOf(target)}`);
|
|
987
|
+
}
|
|
988
|
+
if (visibility !== undefined && typeof visibility !== "string") {
|
|
989
|
+
throw new TypeError(`visibility must be notation text or None, not ${nameOf(visibility)}`);
|
|
990
|
+
}
|
|
991
|
+
if (typeof recursive !== "boolean") {
|
|
992
|
+
throw new TypeError(`recursive must be a bool, not ${nameOf(recursive)}`);
|
|
993
|
+
}
|
|
994
|
+
if (typeof all !== "boolean") {
|
|
995
|
+
throw new TypeError(`all must be a bool, not ${nameOf(all)}`);
|
|
996
|
+
}
|
|
997
|
+
let filters;
|
|
998
|
+
if (filter === undefined) {
|
|
999
|
+
filters = [];
|
|
1000
|
+
}
|
|
1001
|
+
else if (typeof filter === "string") {
|
|
1002
|
+
filters = [filter];
|
|
1003
|
+
}
|
|
1004
|
+
else if (Array.isArray(filter)) {
|
|
1005
|
+
filters = filter;
|
|
1006
|
+
}
|
|
1007
|
+
else {
|
|
1008
|
+
throw new TypeError(`filter must be notation text or a list of it, not ${nameOf(filter)}`);
|
|
1009
|
+
}
|
|
1010
|
+
for (const [index, expression] of filters.entries()) {
|
|
1011
|
+
if (typeof expression !== "string") {
|
|
1012
|
+
throw new TypeError(`filter[${index}] must be notation text`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
this.#add([
|
|
1016
|
+
"add_import", ownerId(owner), visibility ?? "", target, recursive, all, filters,
|
|
1017
|
+
]);
|
|
1018
|
+
return this;
|
|
1019
|
+
}
|
|
1020
|
+
/** Add a `require constraint` to a requirement-like body. */
|
|
1021
|
+
addRequireConstraint(owner, expression, name) {
|
|
1022
|
+
return this.addRequirementConstraint(owner, "require", expression, name);
|
|
1023
|
+
}
|
|
1024
|
+
/** Add an `assume constraint` to a requirement-like body. */
|
|
1025
|
+
addAssumeConstraint(owner, expression, name) {
|
|
1026
|
+
return this.addRequirementConstraint(owner, "assume", expression, name);
|
|
1027
|
+
}
|
|
1028
|
+
/** Add a connection-like usage between two feature references. */
|
|
1029
|
+
addConnection(owner, kind, from, to, options = {}) {
|
|
1030
|
+
const { name, type } = options;
|
|
1031
|
+
for (const [label, text] of [
|
|
1032
|
+
["kind", kind],
|
|
1033
|
+
["from_", from],
|
|
1034
|
+
["to", to],
|
|
1035
|
+
]) {
|
|
1036
|
+
if (typeof text !== "string") {
|
|
1037
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
for (const [label, text] of [
|
|
1041
|
+
["name", name],
|
|
1042
|
+
["type", type],
|
|
1043
|
+
]) {
|
|
1044
|
+
if (text !== undefined && typeof text !== "string") {
|
|
1045
|
+
throw new TypeError(`${label} must be notation text, not ${nameOf(text)}`);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
this.#add(["add_connection", ownerId(owner), kind, from, to, name ?? "", type ?? ""]);
|
|
1049
|
+
return this;
|
|
1050
|
+
}
|
|
1051
|
+
/** Add an `allocation ... allocate from to` usage. */
|
|
1052
|
+
addAllocation(owner, from, to, options = {}) {
|
|
1053
|
+
return this.addConnection(owner, "allocation", from, to, options);
|
|
1054
|
+
}
|
|
1055
|
+
/** Add a `flow ... from from to` usage. */
|
|
1056
|
+
addFlow(owner, from, to, options = {}) {
|
|
1057
|
+
return this.addConnection(owner, "flow", from, to, options);
|
|
1058
|
+
}
|
|
1059
|
+
/** Add a `succession ... first from then to` usage. */
|
|
1060
|
+
addSuccession(owner, from, to, options = {}) {
|
|
1061
|
+
return this.addConnection(owner, "succession", from, to, options);
|
|
1062
|
+
}
|
|
1063
|
+
/** Delete a declaration, optionally removing declarations that refer to it. */
|
|
1064
|
+
delete(target, options = {}) {
|
|
1065
|
+
const { cascade = false } = options;
|
|
1066
|
+
if (typeof cascade !== "boolean") {
|
|
1067
|
+
throw new TypeError("cascade must be bool");
|
|
1068
|
+
}
|
|
1069
|
+
this.#add(["delete", targetId(target), cascade]);
|
|
1070
|
+
return this;
|
|
1071
|
+
}
|
|
1072
|
+
/** Move a declaration into another namespace of the same document. */
|
|
1073
|
+
move(target, owner) {
|
|
1074
|
+
this.#add(["move", targetId(target), ownerId(owner)]);
|
|
1075
|
+
return this;
|
|
1076
|
+
}
|
|
1077
|
+
/** Have the service perform these operations and return the edited model. */
|
|
1078
|
+
async apply() {
|
|
1079
|
+
if (this.#applied) {
|
|
1080
|
+
throw new Error("this editor has already been applied: it describes an edit of the model it was made " +
|
|
1081
|
+
"from, so build another editor from the edited model rather than applying this one twice");
|
|
1082
|
+
}
|
|
1083
|
+
if (this.#operations.length === 0) {
|
|
1084
|
+
throw new NoEditsError("this editor has no operations: add an edit before applying it", { failure: "EDIT_FAILURE_NO_OPERATIONS" });
|
|
1085
|
+
}
|
|
1086
|
+
const result = await this.#apply(this.#modelHash, this.#operations);
|
|
1087
|
+
this.#applied = true;
|
|
1088
|
+
return result;
|
|
1089
|
+
}
|
|
1090
|
+
#add(operation) {
|
|
1091
|
+
if (this.#applied) {
|
|
1092
|
+
throw new Error("this editor has already been applied: build another editor from the edited model " +
|
|
1093
|
+
"to edit further");
|
|
1094
|
+
}
|
|
1095
|
+
this.#operations.push(operation);
|
|
1096
|
+
}
|
|
1097
|
+
toString() {
|
|
1098
|
+
return `Editor(modelHash=${JSON.stringify(this.#modelHash)}, operations=${this.#operations.length}, applied=${this.#applied})`;
|
|
1099
|
+
}
|
|
1100
|
+
addPackage(owner, name, options = {}) {
|
|
1101
|
+
return this.addMember(owner, "package", name, options);
|
|
1102
|
+
}
|
|
1103
|
+
addPartDef(owner, name, options = {}) {
|
|
1104
|
+
return this.addMember(owner, "part def", name, options);
|
|
1105
|
+
}
|
|
1106
|
+
addPart(owner, name, options = {}) {
|
|
1107
|
+
return this.addMember(owner, "part", name, options);
|
|
1108
|
+
}
|
|
1109
|
+
addAttributeDef(owner, name, options = {}) {
|
|
1110
|
+
return this.addMember(owner, "attribute def", name, options);
|
|
1111
|
+
}
|
|
1112
|
+
addAttribute(owner, name, options = {}) {
|
|
1113
|
+
return this.addMember(owner, "attribute", name, options);
|
|
1114
|
+
}
|
|
1115
|
+
addItemDef(owner, name, options = {}) {
|
|
1116
|
+
return this.addMember(owner, "item def", name, options);
|
|
1117
|
+
}
|
|
1118
|
+
addItem(owner, name, options = {}) {
|
|
1119
|
+
return this.addMember(owner, "item", name, options);
|
|
1120
|
+
}
|
|
1121
|
+
addPortDef(owner, name, options = {}) {
|
|
1122
|
+
return this.addMember(owner, "port def", name, options);
|
|
1123
|
+
}
|
|
1124
|
+
addPort(owner, name, options = {}) {
|
|
1125
|
+
return this.addMember(owner, "port", name, options);
|
|
1126
|
+
}
|
|
1127
|
+
addClass(owner, name, options = {}) {
|
|
1128
|
+
return this.addMember(owner, "class", name, options);
|
|
1129
|
+
}
|
|
1130
|
+
addStruct(owner, name, options = {}) {
|
|
1131
|
+
return this.addMember(owner, "struct", name, options);
|
|
1132
|
+
}
|
|
1133
|
+
addDatatype(owner, name, options = {}) {
|
|
1134
|
+
return this.addMember(owner, "datatype", name, options);
|
|
1135
|
+
}
|
|
1136
|
+
addClassifier(owner, name, options = {}) {
|
|
1137
|
+
return this.addMember(owner, "classifier", name, options);
|
|
1138
|
+
}
|
|
1139
|
+
addFeature(owner, name, options = {}) {
|
|
1140
|
+
return this.addMember(owner, "feature", name, options);
|
|
1141
|
+
}
|
|
1142
|
+
addAssoc(owner, name, options = {}) {
|
|
1143
|
+
return this.addMember(owner, "assoc", name, options);
|
|
1144
|
+
}
|
|
1145
|
+
addBehavior(owner, name, options = {}) {
|
|
1146
|
+
return this.addMember(owner, "behavior", name, options);
|
|
1147
|
+
}
|
|
1148
|
+
addFunction(owner, name, options = {}) {
|
|
1149
|
+
return this.addMember(owner, "function", name, options);
|
|
1150
|
+
}
|
|
1151
|
+
addPredicate(owner, name, options = {}) {
|
|
1152
|
+
return this.addMember(owner, "predicate", name, options);
|
|
1153
|
+
}
|
|
1154
|
+
addInteraction(owner, name, options = {}) {
|
|
1155
|
+
return this.addMember(owner, "interaction", name, options);
|
|
1156
|
+
}
|
|
1157
|
+
addMetaclass(owner, name, options = {}) {
|
|
1158
|
+
return this.addMember(owner, "metaclass", name, options);
|
|
1159
|
+
}
|
|
1160
|
+
/** Add a `calc def` with input parameters and an optional result. */
|
|
1161
|
+
addCalcDef(owner, name, options = {}) {
|
|
1162
|
+
const { inputs, returnType, returnExpression, expression, ...rest } = options;
|
|
1163
|
+
const parameters = parameterPairs(inputs, "inputs");
|
|
1164
|
+
optionalText(returnType, "return_type");
|
|
1165
|
+
optionalText(returnExpression, "return_expression");
|
|
1166
|
+
optionalText(expression, "expression");
|
|
1167
|
+
if (expression !== undefined && returnExpression !== undefined) {
|
|
1168
|
+
throw new RangeError("expression and return_expression both bind the result; give one");
|
|
1169
|
+
}
|
|
1170
|
+
if (returnExpression !== undefined && (returnType === undefined || returnType === "")) {
|
|
1171
|
+
throw new RangeError("return_expression requires return_type");
|
|
1172
|
+
}
|
|
1173
|
+
const ownerName = ownerId(owner);
|
|
1174
|
+
this.addMember(ownerName, "calc def", name, { expression, ...rest });
|
|
1175
|
+
const qualifiedName = ownerName === "" ? name : `${ownerName}::${name}`;
|
|
1176
|
+
for (const [parameterName, parameterType] of parameters) {
|
|
1177
|
+
this.addParameter(qualifiedName, "in", parameterName, { type: parameterType });
|
|
1178
|
+
}
|
|
1179
|
+
if (returnType !== undefined || returnExpression !== undefined) {
|
|
1180
|
+
this.addReturn(qualifiedName, { type: returnType, value: returnExpression });
|
|
1181
|
+
}
|
|
1182
|
+
return this;
|
|
1183
|
+
}
|
|
1184
|
+
/** Add a `calc` with input parameters and an optional result. */
|
|
1185
|
+
addCalc(owner, name, options = {}) {
|
|
1186
|
+
const { inputs, returnType, returnExpression, expression, ...rest } = options;
|
|
1187
|
+
const parameters = parameterPairs(inputs, "inputs");
|
|
1188
|
+
optionalText(returnType, "return_type");
|
|
1189
|
+
optionalText(returnExpression, "return_expression");
|
|
1190
|
+
optionalText(expression, "expression");
|
|
1191
|
+
if (expression !== undefined && returnExpression !== undefined) {
|
|
1192
|
+
throw new RangeError("expression and return_expression both bind the result; give one");
|
|
1193
|
+
}
|
|
1194
|
+
if (returnExpression !== undefined && (returnType === undefined || returnType === "")) {
|
|
1195
|
+
throw new RangeError("return_expression requires return_type");
|
|
1196
|
+
}
|
|
1197
|
+
const ownerName = ownerId(owner);
|
|
1198
|
+
this.addMember(ownerName, "calc", name, { expression, ...rest });
|
|
1199
|
+
const qualifiedName = ownerName === "" ? name : `${ownerName}::${name}`;
|
|
1200
|
+
for (const [parameterName, parameterType] of parameters) {
|
|
1201
|
+
this.addParameter(qualifiedName, "in", parameterName, { type: parameterType });
|
|
1202
|
+
}
|
|
1203
|
+
if (returnType !== undefined || returnExpression !== undefined) {
|
|
1204
|
+
this.addReturn(qualifiedName, { type: returnType, value: returnExpression });
|
|
1205
|
+
}
|
|
1206
|
+
return this;
|
|
1207
|
+
}
|
|
1208
|
+
/** Add a directional parameter usage. */
|
|
1209
|
+
addParameter(owner, direction, name, options = {}) {
|
|
1210
|
+
const { kind, ...rest } = options;
|
|
1211
|
+
return this.addMember(owner, kind ?? "", name, { ...rest, direction });
|
|
1212
|
+
}
|
|
1213
|
+
/** Add a return parameter member. */
|
|
1214
|
+
addReturn(owner, options = {}) {
|
|
1215
|
+
const { name, ...rest } = options;
|
|
1216
|
+
return this.addMember(owner, "return", name ?? "", rest);
|
|
1217
|
+
}
|
|
1218
|
+
/** Add an `action def` with input and output parameters. */
|
|
1219
|
+
addActionDef(owner, name, options = {}) {
|
|
1220
|
+
const { inputs, outputs, ...rest } = options;
|
|
1221
|
+
const inputParameters = parameterPairs(inputs, "inputs");
|
|
1222
|
+
const outputParameters = parameterPairs(outputs, "outputs");
|
|
1223
|
+
const ownerName = ownerId(owner);
|
|
1224
|
+
this.addMember(ownerName, "action def", name, rest);
|
|
1225
|
+
const qualifiedName = ownerName === "" ? name : `${ownerName}::${name}`;
|
|
1226
|
+
for (const [parameterName, parameterType] of inputParameters) {
|
|
1227
|
+
this.addParameter(qualifiedName, "in", parameterName, { type: parameterType });
|
|
1228
|
+
}
|
|
1229
|
+
for (const [parameterName, parameterType] of outputParameters) {
|
|
1230
|
+
this.addParameter(qualifiedName, "out", parameterName, { type: parameterType });
|
|
1231
|
+
}
|
|
1232
|
+
return this;
|
|
1233
|
+
}
|
|
1234
|
+
/** Add an `action` with input and output parameters. */
|
|
1235
|
+
addAction(owner, name, options = {}) {
|
|
1236
|
+
const { inputs, outputs, ...rest } = options;
|
|
1237
|
+
const inputParameters = parameterPairs(inputs, "inputs");
|
|
1238
|
+
const outputParameters = parameterPairs(outputs, "outputs");
|
|
1239
|
+
const ownerName = ownerId(owner);
|
|
1240
|
+
this.addMember(ownerName, "action", name, rest);
|
|
1241
|
+
const qualifiedName = ownerName === "" ? name : `${ownerName}::${name}`;
|
|
1242
|
+
for (const [parameterName, parameterType] of inputParameters) {
|
|
1243
|
+
this.addParameter(qualifiedName, "in", parameterName, { type: parameterType });
|
|
1244
|
+
}
|
|
1245
|
+
for (const [parameterName, parameterType] of outputParameters) {
|
|
1246
|
+
this.addParameter(qualifiedName, "out", parameterName, { type: parameterType });
|
|
1247
|
+
}
|
|
1248
|
+
return this;
|
|
1249
|
+
}
|
|
1250
|
+
/** Add a `perform action name : Type` usage. */
|
|
1251
|
+
addPerformAction(owner, name, options = {}) {
|
|
1252
|
+
return this.addMember(owner, "perform action", name, options);
|
|
1253
|
+
}
|
|
1254
|
+
/** Add a `perform <action>;` usage named by its referenced action. */
|
|
1255
|
+
addPerform(owner, action, options = {}) {
|
|
1256
|
+
return this.addMember(owner, "perform", action, options);
|
|
1257
|
+
}
|
|
1258
|
+
/** Add an `exhibit state` usage. */
|
|
1259
|
+
addExhibitState(owner, name, options = {}) {
|
|
1260
|
+
return this.addMember(owner, "exhibit state", name, options);
|
|
1261
|
+
}
|
|
1262
|
+
/** Add an `exhibit <state>;` usage named by its referenced state. */
|
|
1263
|
+
addExhibit(owner, state) {
|
|
1264
|
+
return this.addMember(owner, "exhibit", state);
|
|
1265
|
+
}
|
|
1266
|
+
/** Add an `entry`, `do` or `exit` action to a state body. */
|
|
1267
|
+
addStateAction(owner, kind, name, options = {}) {
|
|
1268
|
+
const { type } = options;
|
|
1269
|
+
if (typeof kind !== "string") {
|
|
1270
|
+
throw new TypeError(`kind must be notation text, not ${nameOf(kind)}`);
|
|
1271
|
+
}
|
|
1272
|
+
if (kind !== "entry" && kind !== "do" && kind !== "exit") {
|
|
1273
|
+
throw new RangeError("kind must be 'entry', 'do' or 'exit'");
|
|
1274
|
+
}
|
|
1275
|
+
return this.addMember(owner, `${kind} action`, name, { type });
|
|
1276
|
+
}
|
|
1277
|
+
addStateDef(owner, name, options = {}) {
|
|
1278
|
+
return this.addMember(owner, "state def", name, options);
|
|
1279
|
+
}
|
|
1280
|
+
addState(owner, name, options = {}) {
|
|
1281
|
+
return this.addMember(owner, "state", name, options);
|
|
1282
|
+
}
|
|
1283
|
+
/** Add a `constraint def`; `expression` writes `{ … }`, `value` writes `= …`. */
|
|
1284
|
+
addConstraintDef(owner, name, options = {}) {
|
|
1285
|
+
return this.addMember(owner, "constraint def", name, options);
|
|
1286
|
+
}
|
|
1287
|
+
/** Add a `constraint`; `expression` writes `{ … }`, `value` writes `= …`. */
|
|
1288
|
+
addConstraint(owner, name, options = {}) {
|
|
1289
|
+
return this.addMember(owner, "constraint", name, options);
|
|
1290
|
+
}
|
|
1291
|
+
/** Add an asserted constraint usage, optionally negated. */
|
|
1292
|
+
addAssertConstraint(owner, options = {}) {
|
|
1293
|
+
const { name, type, expression, negated = false } = options;
|
|
1294
|
+
if (typeof negated !== "boolean") {
|
|
1295
|
+
throw new TypeError("negated must be bool");
|
|
1296
|
+
}
|
|
1297
|
+
return this.addMember(owner, negated ? "assert not constraint" : "assert constraint", name ?? "", {
|
|
1298
|
+
type,
|
|
1299
|
+
expression,
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
/** Add an anonymous `assert` usage; the assertion is not named by ref. */
|
|
1303
|
+
addAssert(owner, ref, options = {}) {
|
|
1304
|
+
const { negated = false } = options;
|
|
1305
|
+
if (typeof ref !== "string") {
|
|
1306
|
+
throw new TypeError(`ref must be notation text, not ${nameOf(ref)}`);
|
|
1307
|
+
}
|
|
1308
|
+
if (typeof negated !== "boolean") {
|
|
1309
|
+
throw new TypeError("negated must be bool");
|
|
1310
|
+
}
|
|
1311
|
+
return this.addMember(owner, negated ? "assert not" : "assert", ref);
|
|
1312
|
+
}
|
|
1313
|
+
addRequirementDef(owner, name, options = {}) {
|
|
1314
|
+
return this.addMember(owner, "requirement def", name, options);
|
|
1315
|
+
}
|
|
1316
|
+
addRequirement(owner, name, options = {}) {
|
|
1317
|
+
return this.addMember(owner, "requirement", name, options);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
const SEQUENCE_DETAIL_FIELDS = new Set([
|
|
1321
|
+
"condition", "value", "target", "via", "until", "body", "else_body",
|
|
1322
|
+
"multiplicity", "parameter",
|
|
1323
|
+
]);
|
|
1324
|
+
const ACTION_BODY_MEMBER_KINDS = new Set([
|
|
1325
|
+
"accept", "send", "assign", "if", "while", "loop", "for", "terminate",
|
|
1326
|
+
]);
|
|
1327
|
+
const ASSERTED_CONSTRAINT_KINDS = new Set([
|
|
1328
|
+
"assert", "assert not", "assert constraint", "assert not constraint",
|
|
1329
|
+
]);
|
|
1330
|
+
const STATE_ACTION_KINDS = new Set([
|
|
1331
|
+
"exhibit state", "exhibit", "entry action", "do action", "exit action",
|
|
1332
|
+
]);
|
|
1333
|
+
const EDIT_CAPABILITY_ORDER = [
|
|
1334
|
+
CAPABILITY_APPLY_EDITS,
|
|
1335
|
+
CAPABILITY_AUTHORING,
|
|
1336
|
+
CAPABILITY_CONNECTION_AUTHORING,
|
|
1337
|
+
CAPABILITY_SATISFY_AUTHORING,
|
|
1338
|
+
CAPABILITY_REQUIREMENT_CONSTRAINT_AUTHORING,
|
|
1339
|
+
CAPABILITY_TRANSITION_AUTHORING,
|
|
1340
|
+
CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING,
|
|
1341
|
+
CAPABILITY_METADATA_AUTHORING,
|
|
1342
|
+
CAPABILITY_METADATA_PREFIX_AUTHORING,
|
|
1343
|
+
CAPABILITY_SEQUENCE_AUTHORING,
|
|
1344
|
+
CAPABILITY_ACTION_BODY_STATEMENT_AUTHORING,
|
|
1345
|
+
CAPABILITY_IMPORT_AUTHORING,
|
|
1346
|
+
CAPABILITY_DOCUMENTATION_AUTHORING,
|
|
1347
|
+
CAPABILITY_COMMENT_AUTHORING,
|
|
1348
|
+
CAPABILITY_MEMBER_MODIFIERS,
|
|
1349
|
+
CAPABILITY_IMPLICIT_PARAMETERS,
|
|
1350
|
+
CAPABILITY_CONSTRAINT_BODY_AUTHORING,
|
|
1351
|
+
CAPABILITY_STATE_ACTION_AUTHORING,
|
|
1352
|
+
];
|
|
1353
|
+
// Capabilities an edit request needs as a whole, checked once every operation is read.
|
|
1354
|
+
const EDIT_CAPABILITIES_CHECKED_LAST = [
|
|
1355
|
+
CAPABILITY_ACTION_BODY_STATEMENT_AUTHORING,
|
|
1356
|
+
CAPABILITY_MEMBER_MODIFIERS,
|
|
1357
|
+
CAPABILITY_IMPLICIT_PARAMETERS,
|
|
1358
|
+
];
|
|
1359
|
+
function allText(values) {
|
|
1360
|
+
return values.every((value) => typeof value === "string");
|
|
1361
|
+
}
|
|
1362
|
+
function memberExtras(extra) {
|
|
1363
|
+
let metadata = [];
|
|
1364
|
+
let bodyExpression = "";
|
|
1365
|
+
let doc = "";
|
|
1366
|
+
if (extra.length > 0) {
|
|
1367
|
+
if (typeof extra[0] === "string") {
|
|
1368
|
+
bodyExpression = extra[0];
|
|
1369
|
+
if (extra.length === 2) {
|
|
1370
|
+
doc = extra[1];
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
metadata = extra[0];
|
|
1375
|
+
if (extra.length > 1) {
|
|
1376
|
+
bodyExpression = extra[1];
|
|
1377
|
+
}
|
|
1378
|
+
if (extra.length > 2) {
|
|
1379
|
+
doc = extra[2];
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
if (typeof bodyExpression !== "string") {
|
|
1384
|
+
throw new RangeError("malformed add_member operation: expression must be notation text");
|
|
1385
|
+
}
|
|
1386
|
+
if (typeof doc !== "string") {
|
|
1387
|
+
throw new RangeError("malformed add_member operation: doc must be text");
|
|
1388
|
+
}
|
|
1389
|
+
return [metadata, bodyExpression, doc];
|
|
1390
|
+
}
|
|
1391
|
+
/** Whether a wire-form sequence member carries an action-body statement. */
|
|
1392
|
+
function wireSequenceExtended(add, depth = 0) {
|
|
1393
|
+
if (depth > 128) {
|
|
1394
|
+
throw new RangeError("nested action-body items exceed the maximum depth");
|
|
1395
|
+
}
|
|
1396
|
+
let extended = add.keyword === "if" ||
|
|
1397
|
+
add.keyword === "else" ||
|
|
1398
|
+
(add.keyword === "" && add.memberKind !== "") ||
|
|
1399
|
+
ACTION_BODY_MEMBER_KINDS.has(add.memberKind);
|
|
1400
|
+
extended =
|
|
1401
|
+
extended ||
|
|
1402
|
+
add.condition !== "" ||
|
|
1403
|
+
add.value !== "" ||
|
|
1404
|
+
add.target !== "" ||
|
|
1405
|
+
add.via !== "" ||
|
|
1406
|
+
add.until !== "" ||
|
|
1407
|
+
add.multiplicity !== "" ||
|
|
1408
|
+
add.parameter !== "" ||
|
|
1409
|
+
add.body.length > 0 ||
|
|
1410
|
+
add.elseBody.length > 0;
|
|
1411
|
+
for (const child of [...add.body, ...add.elseBody]) {
|
|
1412
|
+
extended = wireSequenceExtended(child, depth + 1) || extended;
|
|
1413
|
+
}
|
|
1414
|
+
return extended;
|
|
1415
|
+
}
|
|
1416
|
+
function addSequenceMessage(add, operationData, depth = 0) {
|
|
1417
|
+
if (depth > 128) {
|
|
1418
|
+
throw new RangeError("nested action-body items exceed the maximum depth");
|
|
1419
|
+
}
|
|
1420
|
+
if (!Array.isArray(operationData) || (operationData.length !== 8 && operationData.length !== 9)) {
|
|
1421
|
+
throw new RangeError("malformed add_sequence operation: expected 8 or 9 fields");
|
|
1422
|
+
}
|
|
1423
|
+
const data = operationData;
|
|
1424
|
+
const [, owner, keyword, ref, memberKind, memberName, typeName, after] = data;
|
|
1425
|
+
if (!allText([owner, keyword, ref, memberKind, memberName, typeName, after])) {
|
|
1426
|
+
throw new RangeError("malformed add_sequence operation: fields must be text");
|
|
1427
|
+
}
|
|
1428
|
+
const fields = data.length === 9 ? data[8] : {};
|
|
1429
|
+
if (typeof fields !== "object" || fields === null || Array.isArray(fields)) {
|
|
1430
|
+
throw new RangeError("malformed add_sequence operation: ninth field must be a dictionary");
|
|
1431
|
+
}
|
|
1432
|
+
const record = fields;
|
|
1433
|
+
const unknown = Object.keys(record).filter((key) => !SEQUENCE_DETAIL_FIELDS.has(key));
|
|
1434
|
+
if (unknown.length > 0) {
|
|
1435
|
+
unknown.sort(byCodeUnit);
|
|
1436
|
+
throw new RangeError(`malformed add_sequence operation: unknown fields ${unknown.join(", ")}`);
|
|
1437
|
+
}
|
|
1438
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1439
|
+
if (key === "body" || key === "else_body") {
|
|
1440
|
+
if (!Array.isArray(value)) {
|
|
1441
|
+
throw new RangeError(`malformed add_sequence operation: ${key} must be a list`);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
else if (typeof value !== "string") {
|
|
1445
|
+
throw new RangeError(`malformed add_sequence operation: ${key} must be text`);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
add.owner = owner;
|
|
1449
|
+
add.keyword = keyword;
|
|
1450
|
+
add.ref = ref;
|
|
1451
|
+
add.memberKind = memberKind;
|
|
1452
|
+
add.memberName = memberName;
|
|
1453
|
+
add.type = typeName;
|
|
1454
|
+
add.after = after;
|
|
1455
|
+
for (const key of ["condition", "value", "target", "via", "until", "multiplicity", "parameter"]) {
|
|
1456
|
+
if (key in record) {
|
|
1457
|
+
add[key] = record[key];
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
let extended = keyword === "if" ||
|
|
1461
|
+
keyword === "else" ||
|
|
1462
|
+
(keyword === "" && memberKind !== "") ||
|
|
1463
|
+
ACTION_BODY_MEMBER_KINDS.has(memberKind);
|
|
1464
|
+
extended = extended || Object.keys(record).some((key) => SEQUENCE_DETAIL_FIELDS.has(key));
|
|
1465
|
+
for (const child of record["body"] ?? []) {
|
|
1466
|
+
const childEdit = create(AddSequenceEditSchema);
|
|
1467
|
+
add.body.push(childEdit);
|
|
1468
|
+
extended = addSequenceMessage(childEdit, child, depth + 1) || extended;
|
|
1469
|
+
}
|
|
1470
|
+
for (const child of record["else_body"] ?? []) {
|
|
1471
|
+
const childEdit = create(AddSequenceEditSchema);
|
|
1472
|
+
add.elseBody.push(childEdit);
|
|
1473
|
+
extended = addSequenceMessage(childEdit, child, depth + 1) || extended;
|
|
1474
|
+
}
|
|
1475
|
+
return extended;
|
|
1476
|
+
}
|
|
1477
|
+
/** Translates edit tuples into an `ApplyEditsRequest`'s operations, gating capabilities. */
|
|
1478
|
+
export class EditRequestBuilder {
|
|
1479
|
+
#info;
|
|
1480
|
+
#requested = new Set([CAPABILITY_APPLY_EDITS]);
|
|
1481
|
+
constructor(info) {
|
|
1482
|
+
this.#info = info;
|
|
1483
|
+
}
|
|
1484
|
+
/** The operations the tuples describe, and the capabilities they need. A
|
|
1485
|
+
* wire `EditOperation` passes through as written — how a caller sends an
|
|
1486
|
+
* operation the tuple form cannot write — gated on the fields it carries. */
|
|
1487
|
+
build(operations) {
|
|
1488
|
+
const built = operations.map((data) => this.add(data));
|
|
1489
|
+
return { operations: built, capabilities: this.finish() };
|
|
1490
|
+
}
|
|
1491
|
+
add(operationData) {
|
|
1492
|
+
if (typeof operationData === "object" && "$typeName" in operationData) {
|
|
1493
|
+
return this.wire(operationData);
|
|
1494
|
+
}
|
|
1495
|
+
const operation = create(EditOperationSchema);
|
|
1496
|
+
switch (operationData[0]) {
|
|
1497
|
+
case "set_value":
|
|
1498
|
+
this.#setValue(operation, operationData);
|
|
1499
|
+
break;
|
|
1500
|
+
case "rename":
|
|
1501
|
+
this.#rename(operation, operationData);
|
|
1502
|
+
break;
|
|
1503
|
+
case "add_member":
|
|
1504
|
+
this.#addMember(operation, operationData);
|
|
1505
|
+
break;
|
|
1506
|
+
case "add_connection":
|
|
1507
|
+
this.#addConnection(operation, operationData);
|
|
1508
|
+
break;
|
|
1509
|
+
case "add_satisfy":
|
|
1510
|
+
this.#addSatisfy(operation, operationData);
|
|
1511
|
+
break;
|
|
1512
|
+
case "add_requirement_constraint":
|
|
1513
|
+
this.#addRequirementConstraint(operation, operationData);
|
|
1514
|
+
break;
|
|
1515
|
+
case "add_transition":
|
|
1516
|
+
this.#addTransition(operation, operationData);
|
|
1517
|
+
break;
|
|
1518
|
+
case "add_verify":
|
|
1519
|
+
this.#addVerify(operation, operationData);
|
|
1520
|
+
break;
|
|
1521
|
+
case "add_metadata":
|
|
1522
|
+
this.#addMetadata(operation, operationData);
|
|
1523
|
+
break;
|
|
1524
|
+
case "add_metadata_prefix":
|
|
1525
|
+
this.#addMetadataPrefix(operation, operationData);
|
|
1526
|
+
break;
|
|
1527
|
+
case "add_sequence":
|
|
1528
|
+
this.#addSequence(operation, operationData);
|
|
1529
|
+
break;
|
|
1530
|
+
case "add_import":
|
|
1531
|
+
this.#addImport(operation, operationData);
|
|
1532
|
+
break;
|
|
1533
|
+
case "add_documentation":
|
|
1534
|
+
this.#addDocumentation(operation, operationData);
|
|
1535
|
+
break;
|
|
1536
|
+
case "add_comment":
|
|
1537
|
+
this.#addComment(operation, operationData);
|
|
1538
|
+
break;
|
|
1539
|
+
case "add_note":
|
|
1540
|
+
this.#addNote(operation, operationData);
|
|
1541
|
+
break;
|
|
1542
|
+
case "delete":
|
|
1543
|
+
this.#delete(operation, operationData);
|
|
1544
|
+
break;
|
|
1545
|
+
case "move":
|
|
1546
|
+
this.#move(operation, operationData);
|
|
1547
|
+
break;
|
|
1548
|
+
default:
|
|
1549
|
+
throw new RangeError(`unknown edit operation ${JSON.stringify(operationData[0])}: expected set_value, rename, ` +
|
|
1550
|
+
"add_member, add_connection, add_satisfy, add_requirement_constraint, " +
|
|
1551
|
+
"add_transition, add_verify, add_metadata, add_metadata_prefix, add_sequence, " +
|
|
1552
|
+
"add_import, add_documentation, add_comment, add_note, delete or move");
|
|
1553
|
+
}
|
|
1554
|
+
return operation;
|
|
1555
|
+
}
|
|
1556
|
+
/** Gate an operation already in wire form on the fields it carries. */
|
|
1557
|
+
wire(operation) {
|
|
1558
|
+
const detail = operation.operation.value;
|
|
1559
|
+
switch (operation.operation.case) {
|
|
1560
|
+
case "setValue":
|
|
1561
|
+
case "rename":
|
|
1562
|
+
break;
|
|
1563
|
+
case "addMember":
|
|
1564
|
+
this.#require(CAPABILITY_AUTHORING);
|
|
1565
|
+
this.#note(CAPABILITY_IMPLICIT_PARAMETERS, detail["kind"] === "");
|
|
1566
|
+
if (detail["kind"] === "objective" && detail["name"] === "") {
|
|
1567
|
+
this.#require(CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING);
|
|
1568
|
+
}
|
|
1569
|
+
if (detail["doc"] !== undefined && detail["doc"] !== "") {
|
|
1570
|
+
this.#require(CAPABILITY_DOCUMENTATION_AUTHORING);
|
|
1571
|
+
}
|
|
1572
|
+
if ((detail["metadataPrefixes"] ?? []).length > 0) {
|
|
1573
|
+
this.#require(CAPABILITY_METADATA_AUTHORING);
|
|
1574
|
+
}
|
|
1575
|
+
if (detail["bodyExpression"] !== "" ||
|
|
1576
|
+
ASSERTED_CONSTRAINT_KINDS.has(detail["kind"])) {
|
|
1577
|
+
this.#require(CAPABILITY_CONSTRAINT_BODY_AUTHORING);
|
|
1578
|
+
}
|
|
1579
|
+
if (STATE_ACTION_KINDS.has(detail["kind"])) {
|
|
1580
|
+
this.#require(CAPABILITY_STATE_ACTION_AUTHORING);
|
|
1581
|
+
}
|
|
1582
|
+
this.#note(CAPABILITY_MEMBER_MODIFIERS, detail["isAbstract"] === true ||
|
|
1583
|
+
(detail["redefines"] ?? []).length > 0 ||
|
|
1584
|
+
detail["isDefault"] === true ||
|
|
1585
|
+
detail["direction"] !== "" ||
|
|
1586
|
+
detail["kind"] === "ref" ||
|
|
1587
|
+
detail["kind"] === "return");
|
|
1588
|
+
break;
|
|
1589
|
+
case "addConnection":
|
|
1590
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_CONNECTION_AUTHORING);
|
|
1591
|
+
break;
|
|
1592
|
+
case "addSatisfy":
|
|
1593
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_SATISFY_AUTHORING);
|
|
1594
|
+
break;
|
|
1595
|
+
case "addRequirementConstraint":
|
|
1596
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_REQUIREMENT_CONSTRAINT_AUTHORING);
|
|
1597
|
+
break;
|
|
1598
|
+
case "addTransition":
|
|
1599
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_TRANSITION_AUTHORING);
|
|
1600
|
+
break;
|
|
1601
|
+
case "addVerify":
|
|
1602
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING);
|
|
1603
|
+
break;
|
|
1604
|
+
case "addMetadata":
|
|
1605
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_METADATA_AUTHORING);
|
|
1606
|
+
break;
|
|
1607
|
+
case "addMetadataPrefix":
|
|
1608
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_METADATA_PREFIX_AUTHORING);
|
|
1609
|
+
break;
|
|
1610
|
+
case "addSequence":
|
|
1611
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_SEQUENCE_AUTHORING);
|
|
1612
|
+
this.#note(CAPABILITY_ACTION_BODY_STATEMENT_AUTHORING, wireSequenceExtended(detail));
|
|
1613
|
+
break;
|
|
1614
|
+
case "addImport":
|
|
1615
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_IMPORT_AUTHORING);
|
|
1616
|
+
break;
|
|
1617
|
+
case "addDocumentation":
|
|
1618
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_DOCUMENTATION_AUTHORING);
|
|
1619
|
+
break;
|
|
1620
|
+
case "addComment":
|
|
1621
|
+
case "addNote":
|
|
1622
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_COMMENT_AUTHORING);
|
|
1623
|
+
break;
|
|
1624
|
+
case "delete":
|
|
1625
|
+
case "move":
|
|
1626
|
+
this.#require(CAPABILITY_AUTHORING);
|
|
1627
|
+
break;
|
|
1628
|
+
default:
|
|
1629
|
+
throw new RangeError(`unknown edit operation ${JSON.stringify(operation.operation.case)}`);
|
|
1630
|
+
}
|
|
1631
|
+
return operation;
|
|
1632
|
+
}
|
|
1633
|
+
finish() {
|
|
1634
|
+
for (const capability of EDIT_CAPABILITIES_CHECKED_LAST) {
|
|
1635
|
+
if (this.#requested.has(capability)) {
|
|
1636
|
+
this.#require(capability);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
return EDIT_CAPABILITY_ORDER.filter((capability) => this.#requested.has(capability));
|
|
1640
|
+
}
|
|
1641
|
+
#require(...capabilities) {
|
|
1642
|
+
for (const capability of capabilities) {
|
|
1643
|
+
requireCapability(this.#info, capability, upgradeRemedy(capability));
|
|
1644
|
+
this.#requested.add(capability);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
#note(capability, needed = true) {
|
|
1648
|
+
if (needed) {
|
|
1649
|
+
this.#requested.add(capability);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
#setValue(operation, operationData) {
|
|
1653
|
+
const [, target, text] = operationData;
|
|
1654
|
+
operation.operation = {
|
|
1655
|
+
case: "setValue",
|
|
1656
|
+
value: create(SetValueEditSchema, {
|
|
1657
|
+
target: target,
|
|
1658
|
+
value: text,
|
|
1659
|
+
}),
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
#rename(operation, operationData) {
|
|
1663
|
+
const [, target, text] = operationData;
|
|
1664
|
+
operation.operation = {
|
|
1665
|
+
case: "rename",
|
|
1666
|
+
value: create(RenameEditSchema, {
|
|
1667
|
+
target: target,
|
|
1668
|
+
newName: text,
|
|
1669
|
+
}),
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
#addMember(operation, operationData) {
|
|
1673
|
+
if (![8, 12, 13, 14, 15].includes(operationData.length)) {
|
|
1674
|
+
throw new RangeError("malformed add_member operation: expected 8, 12, 13, 14 or 15 fields");
|
|
1675
|
+
}
|
|
1676
|
+
const [, owner, memberKind, name, typeName, multiplicity, value, specializes] = operationData;
|
|
1677
|
+
const modifiers = operationData.length >= 12 ? operationData.slice(8, 12) : [];
|
|
1678
|
+
const [metadata, bodyExpression, doc] = memberExtras(operationData.slice(12));
|
|
1679
|
+
this.#require(CAPABILITY_AUTHORING);
|
|
1680
|
+
this.#note(CAPABILITY_IMPLICIT_PARAMETERS, memberKind === "");
|
|
1681
|
+
if (memberKind === "objective" && name === "") {
|
|
1682
|
+
this.#require(CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING);
|
|
1683
|
+
}
|
|
1684
|
+
const add = create(AddMemberEditSchema);
|
|
1685
|
+
add.owner = owner;
|
|
1686
|
+
add.kind = memberKind;
|
|
1687
|
+
add.name = name;
|
|
1688
|
+
add.type = typeName;
|
|
1689
|
+
add.multiplicity = multiplicity;
|
|
1690
|
+
add.value = value;
|
|
1691
|
+
add.specializes = [...specializes];
|
|
1692
|
+
if (doc !== "") {
|
|
1693
|
+
this.#require(CAPABILITY_DOCUMENTATION_AUTHORING);
|
|
1694
|
+
add.doc = doc;
|
|
1695
|
+
}
|
|
1696
|
+
if (modifiers.length > 0) {
|
|
1697
|
+
this.#memberModifiers(add, memberKind, modifiers);
|
|
1698
|
+
}
|
|
1699
|
+
if (!Array.isArray(metadata) || !allText(metadata)) {
|
|
1700
|
+
throw new RangeError("malformed add_member metadata: expected a sequence of notation strings");
|
|
1701
|
+
}
|
|
1702
|
+
add.metadataPrefixes = [...metadata];
|
|
1703
|
+
if (metadata.length > 0) {
|
|
1704
|
+
this.#require(CAPABILITY_METADATA_AUTHORING);
|
|
1705
|
+
}
|
|
1706
|
+
add.bodyExpression = bodyExpression;
|
|
1707
|
+
if (bodyExpression !== "" || ASSERTED_CONSTRAINT_KINDS.has(memberKind)) {
|
|
1708
|
+
this.#require(CAPABILITY_CONSTRAINT_BODY_AUTHORING);
|
|
1709
|
+
}
|
|
1710
|
+
if (STATE_ACTION_KINDS.has(memberKind)) {
|
|
1711
|
+
this.#require(CAPABILITY_STATE_ACTION_AUTHORING);
|
|
1712
|
+
}
|
|
1713
|
+
operation.operation = { case: "addMember", value: add };
|
|
1714
|
+
}
|
|
1715
|
+
#memberModifiers(add, memberKind, modifiers) {
|
|
1716
|
+
const [abstract, redefines, isDefault, direction] = modifiers;
|
|
1717
|
+
if (typeof abstract !== "boolean" || typeof isDefault !== "boolean") {
|
|
1718
|
+
throw new RangeError("malformed add_member modifiers: abstract and default must be bool");
|
|
1719
|
+
}
|
|
1720
|
+
if (typeof redefines === "string" || !Array.isArray(redefines) || !allText(redefines)) {
|
|
1721
|
+
throw new RangeError("malformed add_member modifiers: redefines must be a sequence");
|
|
1722
|
+
}
|
|
1723
|
+
if (typeof direction !== "string") {
|
|
1724
|
+
throw new RangeError("malformed add_member modifiers: direction must be notation text");
|
|
1725
|
+
}
|
|
1726
|
+
add.isAbstract = abstract;
|
|
1727
|
+
add.redefines = [...redefines];
|
|
1728
|
+
add.isDefault = isDefault;
|
|
1729
|
+
add.direction = direction;
|
|
1730
|
+
this.#note(CAPABILITY_MEMBER_MODIFIERS, abstract || redefines.length > 0 || isDefault || direction !== "" ||
|
|
1731
|
+
memberKind === "ref" || memberKind === "return");
|
|
1732
|
+
}
|
|
1733
|
+
#addConnection(operation, operationData) {
|
|
1734
|
+
if (operationData.length !== 7) {
|
|
1735
|
+
throw new RangeError("malformed add_connection operation: expected 7 fields");
|
|
1736
|
+
}
|
|
1737
|
+
const [, owner, connectionKind, fromEnd, toEnd, name, typeName] = operationData;
|
|
1738
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_CONNECTION_AUTHORING);
|
|
1739
|
+
operation.operation = {
|
|
1740
|
+
case: "addConnection",
|
|
1741
|
+
value: create(AddConnectionEditSchema, {
|
|
1742
|
+
owner: owner,
|
|
1743
|
+
kind: connectionKind,
|
|
1744
|
+
fromEnd: fromEnd,
|
|
1745
|
+
toEnd: toEnd,
|
|
1746
|
+
name: name,
|
|
1747
|
+
type: typeName,
|
|
1748
|
+
}),
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
#addSatisfy(operation, operationData) {
|
|
1752
|
+
if (operationData.length !== 6) {
|
|
1753
|
+
throw new RangeError("malformed add_satisfy operation: expected 6 fields");
|
|
1754
|
+
}
|
|
1755
|
+
const [, owner, requirement, satisfyingFeature, asserted, negated] = operationData;
|
|
1756
|
+
if (typeof asserted !== "boolean" || typeof negated !== "boolean") {
|
|
1757
|
+
throw new RangeError("malformed add_satisfy operation: asserted and negated must be bool");
|
|
1758
|
+
}
|
|
1759
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_SATISFY_AUTHORING);
|
|
1760
|
+
operation.operation = {
|
|
1761
|
+
case: "addSatisfy",
|
|
1762
|
+
value: create(AddSatisfyEditSchema, {
|
|
1763
|
+
owner: owner,
|
|
1764
|
+
requirement: requirement,
|
|
1765
|
+
satisfyingFeature: satisfyingFeature,
|
|
1766
|
+
isAsserted: asserted,
|
|
1767
|
+
isNegated: negated,
|
|
1768
|
+
}),
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
#addRequirementConstraint(operation, operationData) {
|
|
1772
|
+
if (operationData.length !== 5) {
|
|
1773
|
+
throw new RangeError("malformed add_requirement_constraint operation: expected 5 fields");
|
|
1774
|
+
}
|
|
1775
|
+
const [, owner, constraintKind, expression, name] = operationData;
|
|
1776
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_REQUIREMENT_CONSTRAINT_AUTHORING);
|
|
1777
|
+
operation.operation = {
|
|
1778
|
+
case: "addRequirementConstraint",
|
|
1779
|
+
value: create(AddRequirementConstraintEditSchema, {
|
|
1780
|
+
owner: owner,
|
|
1781
|
+
kind: constraintKind,
|
|
1782
|
+
expression: expression,
|
|
1783
|
+
name: name,
|
|
1784
|
+
}),
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
#addTransition(operation, operationData) {
|
|
1788
|
+
if (operationData.length !== 9) {
|
|
1789
|
+
throw new RangeError("malformed add_transition operation: expected 9 fields");
|
|
1790
|
+
}
|
|
1791
|
+
const [, owner, name, source, target, trigger, guard, effect, initial] = operationData;
|
|
1792
|
+
if (!allText([owner, name, source, target, trigger, guard, effect]) || typeof initial !== "boolean") {
|
|
1793
|
+
throw new RangeError("malformed add_transition operation: text fields and initial must be valid");
|
|
1794
|
+
}
|
|
1795
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_TRANSITION_AUTHORING);
|
|
1796
|
+
operation.operation = {
|
|
1797
|
+
case: "addTransition",
|
|
1798
|
+
value: create(AddTransitionEditSchema, {
|
|
1799
|
+
owner: owner,
|
|
1800
|
+
name: name,
|
|
1801
|
+
source: source,
|
|
1802
|
+
target: target,
|
|
1803
|
+
trigger: trigger,
|
|
1804
|
+
guard: guard,
|
|
1805
|
+
effect: effect,
|
|
1806
|
+
initial,
|
|
1807
|
+
}),
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
#addVerify(operation, operationData) {
|
|
1811
|
+
if (operationData.length !== 3) {
|
|
1812
|
+
throw new RangeError("malformed add_verify operation: expected 3 fields");
|
|
1813
|
+
}
|
|
1814
|
+
const [, owner, requirement] = operationData;
|
|
1815
|
+
if (typeof owner !== "string" || typeof requirement !== "string") {
|
|
1816
|
+
throw new RangeError("malformed add_verify operation: fields must be notation text");
|
|
1817
|
+
}
|
|
1818
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_VERIFICATION_OBJECTIVE_AUTHORING);
|
|
1819
|
+
operation.operation = {
|
|
1820
|
+
case: "addVerify",
|
|
1821
|
+
value: create(AddVerifyEditSchema, { owner, requirement }),
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
#addMetadata(operation, operationData) {
|
|
1825
|
+
if (operationData.length !== 7) {
|
|
1826
|
+
throw new RangeError("malformed add_metadata operation: expected 7 fields");
|
|
1827
|
+
}
|
|
1828
|
+
const [, owner, metadataType, name, about, values, shorthand] = operationData;
|
|
1829
|
+
if (!allText([owner, metadataType, name])) {
|
|
1830
|
+
throw new RangeError("malformed add_metadata operation: text fields must be strings");
|
|
1831
|
+
}
|
|
1832
|
+
if (typeof shorthand !== "boolean") {
|
|
1833
|
+
throw new RangeError("malformed add_metadata operation: shorthand must be bool");
|
|
1834
|
+
}
|
|
1835
|
+
if (!Array.isArray(about) || !allText(about)) {
|
|
1836
|
+
throw new RangeError("malformed add_metadata operation: about must be notation strings");
|
|
1837
|
+
}
|
|
1838
|
+
if (typeof values === "string" || !Array.isArray(values)) {
|
|
1839
|
+
throw new RangeError("malformed add_metadata operation: values must be feature-value pairs");
|
|
1840
|
+
}
|
|
1841
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_METADATA_AUTHORING);
|
|
1842
|
+
const bindings = values.map((pair, index) => {
|
|
1843
|
+
if (!Array.isArray(pair) || pair.length !== 2 || !allText(pair)) {
|
|
1844
|
+
throw new RangeError(`malformed add_metadata operation: values[${index}] must be a pair of strings`);
|
|
1845
|
+
}
|
|
1846
|
+
return create(MetadataFeatureValueSchema, {
|
|
1847
|
+
feature: pair[0],
|
|
1848
|
+
value: pair[1],
|
|
1849
|
+
});
|
|
1850
|
+
});
|
|
1851
|
+
operation.operation = {
|
|
1852
|
+
case: "addMetadata",
|
|
1853
|
+
value: create(AddMetadataEditSchema, {
|
|
1854
|
+
owner: owner,
|
|
1855
|
+
metadataType: metadataType,
|
|
1856
|
+
name: name,
|
|
1857
|
+
about: [...about],
|
|
1858
|
+
values: bindings,
|
|
1859
|
+
shorthand,
|
|
1860
|
+
}),
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
#addMetadataPrefix(operation, operationData) {
|
|
1864
|
+
if (operationData.length !== 3) {
|
|
1865
|
+
throw new RangeError("malformed add_metadata_prefix operation: expected 3 fields");
|
|
1866
|
+
}
|
|
1867
|
+
const [, target, metadataType] = operationData;
|
|
1868
|
+
if (typeof target !== "string" || typeof metadataType !== "string") {
|
|
1869
|
+
throw new RangeError("malformed add_metadata_prefix operation: fields must be notation text");
|
|
1870
|
+
}
|
|
1871
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_METADATA_PREFIX_AUTHORING);
|
|
1872
|
+
operation.operation = {
|
|
1873
|
+
case: "addMetadataPrefix",
|
|
1874
|
+
value: create(AddMetadataPrefixEditSchema, { target, metadataType }),
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
#addSequence(operation, operationData) {
|
|
1878
|
+
if (!Array.isArray(operationData) || (operationData.length !== 8 && operationData.length !== 9)) {
|
|
1879
|
+
throw new RangeError("malformed add_sequence operation: expected 8 or 9 fields");
|
|
1880
|
+
}
|
|
1881
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_SEQUENCE_AUTHORING);
|
|
1882
|
+
const add = create(AddSequenceEditSchema);
|
|
1883
|
+
this.#note(CAPABILITY_ACTION_BODY_STATEMENT_AUTHORING, addSequenceMessage(add, operationData));
|
|
1884
|
+
operation.operation = { case: "addSequence", value: add };
|
|
1885
|
+
}
|
|
1886
|
+
#addImport(operation, operationData) {
|
|
1887
|
+
if (operationData.length !== 7) {
|
|
1888
|
+
throw new RangeError("malformed add_import operation: expected 7 fields");
|
|
1889
|
+
}
|
|
1890
|
+
const [, owner, visibility, target, recursive, importAll, filters] = operationData;
|
|
1891
|
+
if (!allText([owner, visibility, target]) ||
|
|
1892
|
+
typeof recursive !== "boolean" ||
|
|
1893
|
+
typeof importAll !== "boolean" ||
|
|
1894
|
+
!Array.isArray(filters) ||
|
|
1895
|
+
!allText(filters)) {
|
|
1896
|
+
throw new RangeError("malformed add_import operation: text fields, recursive, import_all and filters must be valid");
|
|
1897
|
+
}
|
|
1898
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_IMPORT_AUTHORING);
|
|
1899
|
+
operation.operation = {
|
|
1900
|
+
case: "addImport",
|
|
1901
|
+
value: create(AddImportEditSchema, {
|
|
1902
|
+
owner: owner,
|
|
1903
|
+
visibility: visibility,
|
|
1904
|
+
target: target,
|
|
1905
|
+
isRecursive: recursive,
|
|
1906
|
+
isImportAll: importAll,
|
|
1907
|
+
filters: [...filters],
|
|
1908
|
+
}),
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
#addDocumentation(operation, operationData) {
|
|
1912
|
+
if (operationData.length !== 6) {
|
|
1913
|
+
throw new RangeError("malformed add_documentation operation: expected 6 fields");
|
|
1914
|
+
}
|
|
1915
|
+
const [, target, body, name, locale, replace] = operationData;
|
|
1916
|
+
if (!allText([target, body, name, locale]) || typeof replace !== "boolean") {
|
|
1917
|
+
throw new RangeError("malformed add_documentation operation: text fields and replace must be valid");
|
|
1918
|
+
}
|
|
1919
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_DOCUMENTATION_AUTHORING);
|
|
1920
|
+
operation.operation = {
|
|
1921
|
+
case: "addDocumentation",
|
|
1922
|
+
value: create(AddDocumentationEditSchema, {
|
|
1923
|
+
target: target,
|
|
1924
|
+
body: body,
|
|
1925
|
+
name: name,
|
|
1926
|
+
locale: locale,
|
|
1927
|
+
replace,
|
|
1928
|
+
}),
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
#addComment(operation, operationData) {
|
|
1932
|
+
if (operationData.length !== 6) {
|
|
1933
|
+
throw new RangeError("malformed add_comment operation: expected 6 fields");
|
|
1934
|
+
}
|
|
1935
|
+
const [, owner, body, name, about, locale] = operationData;
|
|
1936
|
+
const aboutList = Array.isArray(about) ? about : null;
|
|
1937
|
+
if (!allText([owner, body, name, locale]) || aboutList === null || !allText(aboutList)) {
|
|
1938
|
+
throw new RangeError("malformed add_comment operation: text fields and about must be valid");
|
|
1939
|
+
}
|
|
1940
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_COMMENT_AUTHORING);
|
|
1941
|
+
operation.operation = {
|
|
1942
|
+
case: "addComment",
|
|
1943
|
+
value: create(AddCommentEditSchema, {
|
|
1944
|
+
owner: owner,
|
|
1945
|
+
body: body,
|
|
1946
|
+
name: name,
|
|
1947
|
+
about: [...aboutList],
|
|
1948
|
+
locale: locale,
|
|
1949
|
+
}),
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
#addNote(operation, operationData) {
|
|
1953
|
+
if (operationData.length !== 3 || !allText(operationData.slice(1))) {
|
|
1954
|
+
throw new RangeError("malformed add_note operation: expected target and text");
|
|
1955
|
+
}
|
|
1956
|
+
const [, target, text] = operationData;
|
|
1957
|
+
this.#require(CAPABILITY_AUTHORING, CAPABILITY_COMMENT_AUTHORING);
|
|
1958
|
+
operation.operation = {
|
|
1959
|
+
case: "addNote",
|
|
1960
|
+
value: create(AddNoteEditSchema, {
|
|
1961
|
+
target: target,
|
|
1962
|
+
text: text,
|
|
1963
|
+
}),
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
#delete(operation, operationData) {
|
|
1967
|
+
if (operationData.length !== 3 || typeof operationData[2] !== "boolean") {
|
|
1968
|
+
throw new RangeError("malformed delete operation: expected target and bool cascade");
|
|
1969
|
+
}
|
|
1970
|
+
const [, target, cascade] = operationData;
|
|
1971
|
+
this.#require(CAPABILITY_AUTHORING);
|
|
1972
|
+
operation.operation = {
|
|
1973
|
+
case: "delete",
|
|
1974
|
+
value: create(DeleteEditSchema, {
|
|
1975
|
+
target: target,
|
|
1976
|
+
cascade,
|
|
1977
|
+
}),
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
#move(operation, operationData) {
|
|
1981
|
+
if (operationData.length !== 3) {
|
|
1982
|
+
throw new RangeError("malformed move operation: expected target and owner");
|
|
1983
|
+
}
|
|
1984
|
+
const [, target, owner] = operationData;
|
|
1985
|
+
this.#require(CAPABILITY_AUTHORING);
|
|
1986
|
+
operation.operation = {
|
|
1987
|
+
case: "move",
|
|
1988
|
+
value: create(MoveEditSchema, {
|
|
1989
|
+
target: target,
|
|
1990
|
+
owner: owner,
|
|
1991
|
+
}),
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
}
|