@istar-ts/actions 0.15.0-beta.3 → 0.15.0-beta.5

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/dist/index.cjs CHANGED
@@ -19,7 +19,9 @@ const OPERATION_KIND_LIST = [
19
19
  "istar.setLinkValue",
20
20
  "istar.updateLinkDisplay",
21
21
  "istar.changeLinkKind",
22
- "istar.setFileMetamodel"
22
+ "istar.setFileMetamodel",
23
+ "istar.updateModelExtra",
24
+ "istar.replaceModel"
23
25
  ];
24
26
  const REQUEST_KIND_LIST = [
25
27
  "requestModel",
@@ -64,6 +66,141 @@ function isRequestAction(action) {
64
66
  return REQUEST_KINDS.has(action.kind);
65
67
  }
66
68
  //#endregion
69
+ //#region src/created-ids.ts
70
+ /** How many new element ids, then new link ids, `operation` creates, mirroring the dispatcher's
71
+ * `createdIds` diff (`dispatcher.ts`) and `connect`/`addDependency`/`addLeg`
72
+ * (`packages/core/src/operations.ts`). */
73
+ function countCreatedIds(operation, model) {
74
+ switch (operation.kind) {
75
+ case "createNode": return {
76
+ elements: 1,
77
+ links: 0
78
+ };
79
+ case "createEdge": {
80
+ const metamodel = (0, _istar_ts_core.metamodelOf)(model);
81
+ const definition = metamodel.links.get(operation.elementTypeId);
82
+ if (definition?.category === "dependency") return {
83
+ elements: 1,
84
+ links: 2
85
+ };
86
+ if (definition?.parts && !targetIsHub(operation, model, metamodel)) return {
87
+ elements: 1,
88
+ links: 2
89
+ };
90
+ return {
91
+ elements: 0,
92
+ links: 1
93
+ };
94
+ }
95
+ case "istar.addLeg": return {
96
+ elements: 0,
97
+ links: 1
98
+ };
99
+ case "compound": {
100
+ let elements = 0;
101
+ let links = 0;
102
+ for (const child of operation.operationList) {
103
+ const counts = countCreatedIds(child, model);
104
+ elements += counts.elements;
105
+ links += counts.links;
106
+ }
107
+ return {
108
+ elements,
109
+ links
110
+ };
111
+ }
112
+ default: return {
113
+ elements: 0,
114
+ links: 0
115
+ };
116
+ }
117
+ }
118
+ function targetIsHub(operation, model, metamodel) {
119
+ const targetKind = model.elements.get(operation.targetElementId)?.kind;
120
+ return targetKind !== void 0 && metamodel.elements.get(targetKind)?.role === "hub";
121
+ }
122
+ /** `operation` with ids from `elementIds`/`linkIds` (consumed in model order) injected as the
123
+ * explicit ids its creates take, so a dispatcher that applies it against the same model state
124
+ * creates the same ids. */
125
+ function withCreatedIds(operation, elementIds, linkIds, model) {
126
+ switch (operation.kind) {
127
+ case "createNode": {
128
+ const id = elementIds.shift();
129
+ return id === void 0 ? operation : {
130
+ ...operation,
131
+ args: {
132
+ ...operation.args,
133
+ id
134
+ }
135
+ };
136
+ }
137
+ case "createEdge": {
138
+ const metamodel = (0, _istar_ts_core.metamodelOf)(model);
139
+ const definition = metamodel.links.get(operation.elementTypeId);
140
+ if (definition?.category === "dependency") {
141
+ const dependumId = elementIds.shift();
142
+ const linkIds2 = [linkIds.shift(), linkIds.shift()];
143
+ if (dependumId === void 0 || linkIds2[0] === void 0 || linkIds2[1] === void 0) return operation;
144
+ return {
145
+ ...operation,
146
+ args: {
147
+ ...operation.args,
148
+ dependum: {
149
+ ...operation.args?.dependum,
150
+ id: dependumId
151
+ },
152
+ linkIds: linkIds2
153
+ }
154
+ };
155
+ }
156
+ if (definition?.parts && !targetIsHub(operation, model, metamodel)) {
157
+ const hubId = elementIds.shift();
158
+ const legId = linkIds.shift();
159
+ const endId = linkIds.shift();
160
+ return hubId === void 0 || legId === void 0 || endId === void 0 ? operation : {
161
+ ...operation,
162
+ args: {
163
+ ...operation.args,
164
+ id: hubId,
165
+ legId,
166
+ endId
167
+ }
168
+ };
169
+ }
170
+ const id = linkIds.shift();
171
+ return id === void 0 ? operation : {
172
+ ...operation,
173
+ args: {
174
+ ...operation.args,
175
+ id
176
+ }
177
+ };
178
+ }
179
+ case "istar.addLeg": {
180
+ const id = linkIds.shift();
181
+ return id === void 0 ? operation : {
182
+ ...operation,
183
+ args: {
184
+ ...operation.args,
185
+ id
186
+ }
187
+ };
188
+ }
189
+ case "compound": return {
190
+ ...operation,
191
+ operationList: operation.operationList.map((child) => withCreatedIds(child, elementIds, linkIds, model))
192
+ };
193
+ default: return operation;
194
+ }
195
+ }
196
+ /** `operation`, with `createdIds` (an accepted result's) injected as explicit ids. */
197
+ function injectCreatedIds(operation, createdIds, model) {
198
+ const counts = countCreatedIds(operation, model);
199
+ const elementIds = createdIds.slice(0, counts.elements);
200
+ const linkIds = createdIds.slice(counts.elements, counts.elements + counts.links);
201
+ return withCreatedIds(operation, [...elementIds], [...linkIds], model);
202
+ }
203
+ //#endregion
67
204
  //#region src/hints.ts
68
205
  /**
69
206
  * What a client can know before it asks: type hints from the metamodel, and the palette as
@@ -276,6 +413,11 @@ function rejectCheck(check, endpoints) {
276
413
  function metaOf(model) {
277
414
  return (0, _istar_ts_core.metamodelOf)(model);
278
415
  }
416
+ /**
417
+ * Keys `istar.updateModelExtra`'s `set`/`remove` must not touch: every key core models itself
418
+ * (`TOP_LEVEL_KEYS`, which includes `tool`), and `"metamodel"` (`istar.setFileMetamodel`'s own).
419
+ */
420
+ const RESERVED_EXTRA_KEYS = /* @__PURE__ */ new Set([..._istar_ts_core.TOP_LEVEL_KEYS, _istar_ts_core.FILE_METAMODEL_KEY]);
279
421
  /** A model with `links` changed, as the core operations derive one. */
280
422
  function withLinks(model, links) {
281
423
  return (0, _istar_ts_core.inheritMetamodel)(model, (0, _istar_ts_core.inheritSourceLayout)(model, {
@@ -582,6 +724,48 @@ function createActionDispatcher(store, options = {}) {
582
724
  s.replace(next);
583
725
  return;
584
726
  }
727
+ case "istar.updateModelExtra": {
728
+ const { set, remove, tool } = operation;
729
+ for (const key of [...Object.keys(set ?? {}), ...remove ?? []]) {
730
+ if (!RESERVED_EXTRA_KEYS.has(key)) continue;
731
+ if (key === "tool") reject(`"tool" is a modelled field: set it with this operation's own "tool"`, "", "reserved-key");
732
+ if (key === _istar_ts_core.FILE_METAMODEL_KEY) reject(`"${key}" is set with istar.setFileMetamodel, not updateModelExtra`, "", "reserved-key");
733
+ reject(`"${key}" is modelled by the file itself and cannot be an extra key`, "", "reserved-key");
734
+ }
735
+ let withTool = model;
736
+ if (tool === null) {
737
+ const { tool: _tool, ...rest } = withTool;
738
+ withTool = rest;
739
+ } else if (tool !== void 0) withTool = {
740
+ ...withTool,
741
+ tool
742
+ };
743
+ const extra = { ...withTool.extra };
744
+ if (set) for (const [key, value] of Object.entries(set)) extra[key] = value;
745
+ if (remove) for (const key of remove) delete extra[key];
746
+ const { extra: _extra, ...rest } = withTool;
747
+ const next = {
748
+ ...rest,
749
+ ...Object.keys(extra).length > 0 ? { extra } : {}
750
+ };
751
+ s.replace((0, _istar_ts_core.inheritMetamodel)(model, (0, _istar_ts_core.inheritSourceLayout)(model, next)));
752
+ return;
753
+ }
754
+ case "istar.replaceModel": {
755
+ const host = (0, _istar_ts_core.hostMetamodelOf)(model) ?? meta;
756
+ let next;
757
+ try {
758
+ next = (0, _istar_ts_core.parsePistar)(operation.model, {
759
+ metamodel: host,
760
+ fileMetamodel: operation.fileMetamodel ?? false
761
+ });
762
+ } catch (error) {
763
+ if (error instanceof _istar_ts_core.PistarParseError) reject(error.message, "", "parse-error");
764
+ throw error;
765
+ }
766
+ s.replace(next);
767
+ return;
768
+ }
585
769
  default: reject(`unknown operation "${operation.kind}"`, "", "unknown-action");
586
770
  }
587
771
  };
@@ -594,7 +778,8 @@ function createActionDispatcher(store, options = {}) {
594
778
  for (const [key, value] of Object.entries(properties)) if (value === null) delete next[key];
595
779
  else if (typeof value === "string") next[key] = value;
596
780
  else reject(`the value of "${key}" must be a string or null`, id, "invalid-value");
597
- const customProperties = Object.keys(next).length > 0 ? next : null;
781
+ if (Object.keys(next).length === 0 && owner.customProperties === void 0) return;
782
+ const customProperties = next;
598
783
  if (model.elements.has(id)) s.updateElement(id, { customProperties });
599
784
  else s.updateLink(id, { customProperties });
600
785
  };
@@ -1217,6 +1402,7 @@ exports.createMirrorStore = createMirrorStore;
1217
1402
  exports.createRemoteDispatcher = createRemoteDispatcher;
1218
1403
  exports.diagnosticToMarker = diagnosticToMarker;
1219
1404
  exports.hostToolContext = hostToolContext;
1405
+ exports.injectCreatedIds = injectCreatedIds;
1220
1406
  exports.isOperation = isOperation;
1221
1407
  exports.isRequestAction = isRequestAction;
1222
1408
  exports.markerToDiagnostic = markerToDiagnostic;