@istar-ts/actions 0.15.0-beta.2 → 0.15.0-beta.4
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 +86 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -2
- package/dist/index.d.ts +65 -2
- package/dist/index.js +87 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,10 @@ import { AnyIstarModel, AnyMetamodel, AnyPaletteDescription, CustomProperties, D
|
|
|
2
2
|
//#region src/actions.d.ts
|
|
3
3
|
/** GLSP's custom arguments: a flat map of primitives. */
|
|
4
4
|
export type Args = Readonly<Record<string, string | number | boolean>>;
|
|
5
|
+
/** A value that survives a JSON round trip, for `istar.updateModelExtra`'s `set`. */
|
|
6
|
+
export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
|
|
7
|
+
readonly [key: string]: JsonValue;
|
|
8
|
+
};
|
|
5
9
|
export interface Point {
|
|
6
10
|
readonly x: number;
|
|
7
11
|
readonly y: number;
|
|
@@ -243,8 +247,35 @@ export interface SetFileMetamodelOperation extends Operation {
|
|
|
243
247
|
readonly kind: "istar.setFileMetamodel";
|
|
244
248
|
readonly block: FileMetamodel | null;
|
|
245
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Sets, removes or reads back the model's own top-level keys that core keeps in `model.extra`
|
|
252
|
+
* (e.g. a dialect's `metamodelVersion`): `set`'s keys are written (other extra keys and their
|
|
253
|
+
* order are kept; a new key is appended where `toPistar` would place it), `remove`'s keys are
|
|
254
|
+
* deleted. `tool` sets (or, with `null`, removes) the model's own `tool` field in the same undo
|
|
255
|
+
* step — `set`/`remove` reject it, and every key core models itself (`actors`, `orphans`,
|
|
256
|
+
* `dependencies`, `links`, `display`, `istar`, `saveDate`, `diagram`), and `"metamodel"` (use
|
|
257
|
+
* `istar.setFileMetamodel` for that one). One undo step.
|
|
258
|
+
*/
|
|
259
|
+
export interface UpdateModelExtraOperation extends Operation {
|
|
260
|
+
readonly kind: "istar.updateModelExtra";
|
|
261
|
+
readonly set?: Readonly<Record<string, JsonValue>>;
|
|
262
|
+
readonly remove?: readonly string[];
|
|
263
|
+
readonly tool?: string | null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Replaces the whole model from a piStar save file, as one undo step: ids, order and extra keys
|
|
267
|
+
* come from `model`, so a parse → `toPistar` round trip is byte-identical to it. Parsed with
|
|
268
|
+
* `parsePistar` under the dispatcher's own metamodel (`fileMetamodel` as in `parsePistar`); a
|
|
269
|
+
* parse error rejects with a typed diagnostic, the model untouched. Pre-guards and postGuards
|
|
270
|
+
* run against this operation, the model before and the model after, like any other operation.
|
|
271
|
+
*/
|
|
272
|
+
export interface ReplaceModelOperation extends Operation {
|
|
273
|
+
readonly kind: "istar.replaceModel";
|
|
274
|
+
readonly model: PistarFile;
|
|
275
|
+
readonly fileMetamodel?: boolean;
|
|
276
|
+
}
|
|
246
277
|
/** Every operation, keyed by `kind`. */
|
|
247
|
-
export type IstarOperation<EK extends string = ElementKind, LK extends string = LinkKind> = CreateNodeOperation<EK> | CreateEdgeOperation<EK, LK> | DeleteElementOperation | ChangeBoundsOperation | ChangeContainerOperation | ReconnectEdgeOperation | ApplyLabelEditOperation | CompoundOperation<EK, LK> | AddLegOperation | RemoveLegOperation | SetCollapsedOperation | UpdateDiagramOperation | UpdatePropertiesOperation | UpdateDisplayOperation | SetLinkValueOperation | UpdateLinkDisplayOperation | ChangeLinkKindOperation<LK> | SetFileMetamodelOperation;
|
|
278
|
+
export type IstarOperation<EK extends string = ElementKind, LK extends string = LinkKind> = CreateNodeOperation<EK> | CreateEdgeOperation<EK, LK> | DeleteElementOperation | ChangeBoundsOperation | ChangeContainerOperation | ReconnectEdgeOperation | ApplyLabelEditOperation | CompoundOperation<EK, LK> | AddLegOperation | RemoveLegOperation | SetCollapsedOperation | UpdateDiagramOperation | UpdatePropertiesOperation | UpdateDisplayOperation | SetLinkValueOperation | UpdateLinkDisplayOperation | ChangeLinkKindOperation<LK> | SetFileMetamodelOperation | UpdateModelExtraOperation | ReplaceModelOperation;
|
|
248
279
|
export interface RequestModelAction extends RequestAction {
|
|
249
280
|
readonly kind: "requestModel";
|
|
250
281
|
readonly options?: Args;
|
|
@@ -281,6 +312,11 @@ export interface ModelSourceChangedAction extends Action {
|
|
|
281
312
|
* created with `sendModel`). An iStar extra; a mirror uses this to know the exact state.
|
|
282
313
|
*/
|
|
283
314
|
readonly canRedo?: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* The `origin` the causing `dispatch` was given, echoed back; absent for a change with none
|
|
317
|
+
* (a host's direct `store.load`, for instance). See `ActionDispatcher.dispatch`.
|
|
318
|
+
*/
|
|
319
|
+
readonly origin?: string;
|
|
284
320
|
}
|
|
285
321
|
/** Saves the model through the host's `save` callback. */
|
|
286
322
|
export interface SaveModelAction extends Action {
|
|
@@ -293,6 +329,8 @@ export interface SetDirtyStateAction extends Action {
|
|
|
293
329
|
readonly kind: "setDirtyState";
|
|
294
330
|
readonly isDirty: boolean;
|
|
295
331
|
readonly reason?: DirtyStateChangeReason;
|
|
332
|
+
/** The `origin` of the dispatch that caused this dirty-state change, echoed back; see `ModelSourceChangedAction.origin`. */
|
|
333
|
+
readonly origin?: string;
|
|
296
334
|
}
|
|
297
335
|
export interface UndoAction extends Action {
|
|
298
336
|
readonly kind: "glspUndo";
|
|
@@ -489,8 +527,31 @@ export type ActionListener<EK extends string = ElementKind, LK extends string =
|
|
|
489
527
|
* Sends actions and hears what the other side sends back. Implemented in-process by
|
|
490
528
|
* `createActionDispatcher`; a transport implements it over a message port.
|
|
491
529
|
*/
|
|
530
|
+
/** `dispatch`'s second argument. */
|
|
531
|
+
export interface DispatchOptions {
|
|
532
|
+
/**
|
|
533
|
+
* Echoed back as `origin` on every `ModelSourceChangedAction` and `SetDirtyStateAction` this
|
|
534
|
+
* dispatch causes (each change of a compound, both events of an undo or redo), so a listener
|
|
535
|
+
* can tell which surface or client made a change without tracking it itself. Carried through
|
|
536
|
+
* `createRemoteDispatcher`/`connectDispatcher` and by `createMirrorStore`'s dispatcher, so it
|
|
537
|
+
* survives a round trip to a remote host. Optional: a change with no origin (a host's direct
|
|
538
|
+
* `store.load`, for instance) has none.
|
|
539
|
+
*/
|
|
540
|
+
readonly origin?: string;
|
|
541
|
+
}
|
|
492
542
|
export interface ActionDispatcher<EK extends string = ElementKind, LK extends string = LinkKind> {
|
|
493
|
-
|
|
543
|
+
/**
|
|
544
|
+
* For a **local** dispatcher (`createActionDispatcher`), an operation, `glspUndo`/`glspRedo`,
|
|
545
|
+
* and the guards and postGuards around them, are applied to the store and every listener
|
|
546
|
+
* (`subscribe`, the store's own) is notified synchronously, before this call's promise is
|
|
547
|
+
* created to resolve — only `saveModel` awaits. Code that needs to see the result of an action
|
|
548
|
+
* before its own next statement (not just before `await dispatch(...)` resolves) can rely on
|
|
549
|
+
* this for a local dispatcher; it does **not** hold across a `createRemoteDispatcher`/
|
|
550
|
+
* `connectDispatcher` transport or a `createMirrorStore` mirror, where a change always arrives
|
|
551
|
+
* after a message round trip. Use `origin` ({@link DispatchOptions}) instead where that
|
|
552
|
+
* portability matters — it is carried through both.
|
|
553
|
+
*/
|
|
554
|
+
dispatch<A extends IstarAction<EK, LK>>(action: A, options?: DispatchOptions): Promise<ActionResult<ResponseFor<A>>>;
|
|
494
555
|
/**
|
|
495
556
|
* Hears the actions the dispatcher sends: `ModelSourceChangedAction` after every model change,
|
|
496
557
|
* `SetDirtyStateAction` when the dirty state flips, and the notifications dispatched to it.
|
|
@@ -724,6 +785,8 @@ export interface JsonRpcRequest {
|
|
|
724
785
|
readonly id: JsonRpcId;
|
|
725
786
|
readonly method: string;
|
|
726
787
|
readonly params?: unknown;
|
|
788
|
+
/** The dispatch's `origin` (`DispatchOptions.origin`), carried to the server's `dispatch`. */
|
|
789
|
+
readonly origin?: string;
|
|
727
790
|
}
|
|
728
791
|
/** A subscriber event, sent with no `id`: it expects no response. */
|
|
729
792
|
export interface JsonRpcNotification {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { AnyIstarModel, AnyMetamodel, AnyPaletteDescription, CustomProperties, D
|
|
|
2
2
|
//#region src/actions.d.ts
|
|
3
3
|
/** GLSP's custom arguments: a flat map of primitives. */
|
|
4
4
|
export type Args = Readonly<Record<string, string | number | boolean>>;
|
|
5
|
+
/** A value that survives a JSON round trip, for `istar.updateModelExtra`'s `set`. */
|
|
6
|
+
export type JsonValue = string | number | boolean | null | readonly JsonValue[] | {
|
|
7
|
+
readonly [key: string]: JsonValue;
|
|
8
|
+
};
|
|
5
9
|
export interface Point {
|
|
6
10
|
readonly x: number;
|
|
7
11
|
readonly y: number;
|
|
@@ -243,8 +247,35 @@ export interface SetFileMetamodelOperation extends Operation {
|
|
|
243
247
|
readonly kind: "istar.setFileMetamodel";
|
|
244
248
|
readonly block: FileMetamodel | null;
|
|
245
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* Sets, removes or reads back the model's own top-level keys that core keeps in `model.extra`
|
|
252
|
+
* (e.g. a dialect's `metamodelVersion`): `set`'s keys are written (other extra keys and their
|
|
253
|
+
* order are kept; a new key is appended where `toPistar` would place it), `remove`'s keys are
|
|
254
|
+
* deleted. `tool` sets (or, with `null`, removes) the model's own `tool` field in the same undo
|
|
255
|
+
* step — `set`/`remove` reject it, and every key core models itself (`actors`, `orphans`,
|
|
256
|
+
* `dependencies`, `links`, `display`, `istar`, `saveDate`, `diagram`), and `"metamodel"` (use
|
|
257
|
+
* `istar.setFileMetamodel` for that one). One undo step.
|
|
258
|
+
*/
|
|
259
|
+
export interface UpdateModelExtraOperation extends Operation {
|
|
260
|
+
readonly kind: "istar.updateModelExtra";
|
|
261
|
+
readonly set?: Readonly<Record<string, JsonValue>>;
|
|
262
|
+
readonly remove?: readonly string[];
|
|
263
|
+
readonly tool?: string | null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Replaces the whole model from a piStar save file, as one undo step: ids, order and extra keys
|
|
267
|
+
* come from `model`, so a parse → `toPistar` round trip is byte-identical to it. Parsed with
|
|
268
|
+
* `parsePistar` under the dispatcher's own metamodel (`fileMetamodel` as in `parsePistar`); a
|
|
269
|
+
* parse error rejects with a typed diagnostic, the model untouched. Pre-guards and postGuards
|
|
270
|
+
* run against this operation, the model before and the model after, like any other operation.
|
|
271
|
+
*/
|
|
272
|
+
export interface ReplaceModelOperation extends Operation {
|
|
273
|
+
readonly kind: "istar.replaceModel";
|
|
274
|
+
readonly model: PistarFile;
|
|
275
|
+
readonly fileMetamodel?: boolean;
|
|
276
|
+
}
|
|
246
277
|
/** Every operation, keyed by `kind`. */
|
|
247
|
-
export type IstarOperation<EK extends string = ElementKind, LK extends string = LinkKind> = CreateNodeOperation<EK> | CreateEdgeOperation<EK, LK> | DeleteElementOperation | ChangeBoundsOperation | ChangeContainerOperation | ReconnectEdgeOperation | ApplyLabelEditOperation | CompoundOperation<EK, LK> | AddLegOperation | RemoveLegOperation | SetCollapsedOperation | UpdateDiagramOperation | UpdatePropertiesOperation | UpdateDisplayOperation | SetLinkValueOperation | UpdateLinkDisplayOperation | ChangeLinkKindOperation<LK> | SetFileMetamodelOperation;
|
|
278
|
+
export type IstarOperation<EK extends string = ElementKind, LK extends string = LinkKind> = CreateNodeOperation<EK> | CreateEdgeOperation<EK, LK> | DeleteElementOperation | ChangeBoundsOperation | ChangeContainerOperation | ReconnectEdgeOperation | ApplyLabelEditOperation | CompoundOperation<EK, LK> | AddLegOperation | RemoveLegOperation | SetCollapsedOperation | UpdateDiagramOperation | UpdatePropertiesOperation | UpdateDisplayOperation | SetLinkValueOperation | UpdateLinkDisplayOperation | ChangeLinkKindOperation<LK> | SetFileMetamodelOperation | UpdateModelExtraOperation | ReplaceModelOperation;
|
|
248
279
|
export interface RequestModelAction extends RequestAction {
|
|
249
280
|
readonly kind: "requestModel";
|
|
250
281
|
readonly options?: Args;
|
|
@@ -281,6 +312,11 @@ export interface ModelSourceChangedAction extends Action {
|
|
|
281
312
|
* created with `sendModel`). An iStar extra; a mirror uses this to know the exact state.
|
|
282
313
|
*/
|
|
283
314
|
readonly canRedo?: boolean;
|
|
315
|
+
/**
|
|
316
|
+
* The `origin` the causing `dispatch` was given, echoed back; absent for a change with none
|
|
317
|
+
* (a host's direct `store.load`, for instance). See `ActionDispatcher.dispatch`.
|
|
318
|
+
*/
|
|
319
|
+
readonly origin?: string;
|
|
284
320
|
}
|
|
285
321
|
/** Saves the model through the host's `save` callback. */
|
|
286
322
|
export interface SaveModelAction extends Action {
|
|
@@ -293,6 +329,8 @@ export interface SetDirtyStateAction extends Action {
|
|
|
293
329
|
readonly kind: "setDirtyState";
|
|
294
330
|
readonly isDirty: boolean;
|
|
295
331
|
readonly reason?: DirtyStateChangeReason;
|
|
332
|
+
/** The `origin` of the dispatch that caused this dirty-state change, echoed back; see `ModelSourceChangedAction.origin`. */
|
|
333
|
+
readonly origin?: string;
|
|
296
334
|
}
|
|
297
335
|
export interface UndoAction extends Action {
|
|
298
336
|
readonly kind: "glspUndo";
|
|
@@ -489,8 +527,31 @@ export type ActionListener<EK extends string = ElementKind, LK extends string =
|
|
|
489
527
|
* Sends actions and hears what the other side sends back. Implemented in-process by
|
|
490
528
|
* `createActionDispatcher`; a transport implements it over a message port.
|
|
491
529
|
*/
|
|
530
|
+
/** `dispatch`'s second argument. */
|
|
531
|
+
export interface DispatchOptions {
|
|
532
|
+
/**
|
|
533
|
+
* Echoed back as `origin` on every `ModelSourceChangedAction` and `SetDirtyStateAction` this
|
|
534
|
+
* dispatch causes (each change of a compound, both events of an undo or redo), so a listener
|
|
535
|
+
* can tell which surface or client made a change without tracking it itself. Carried through
|
|
536
|
+
* `createRemoteDispatcher`/`connectDispatcher` and by `createMirrorStore`'s dispatcher, so it
|
|
537
|
+
* survives a round trip to a remote host. Optional: a change with no origin (a host's direct
|
|
538
|
+
* `store.load`, for instance) has none.
|
|
539
|
+
*/
|
|
540
|
+
readonly origin?: string;
|
|
541
|
+
}
|
|
492
542
|
export interface ActionDispatcher<EK extends string = ElementKind, LK extends string = LinkKind> {
|
|
493
|
-
|
|
543
|
+
/**
|
|
544
|
+
* For a **local** dispatcher (`createActionDispatcher`), an operation, `glspUndo`/`glspRedo`,
|
|
545
|
+
* and the guards and postGuards around them, are applied to the store and every listener
|
|
546
|
+
* (`subscribe`, the store's own) is notified synchronously, before this call's promise is
|
|
547
|
+
* created to resolve — only `saveModel` awaits. Code that needs to see the result of an action
|
|
548
|
+
* before its own next statement (not just before `await dispatch(...)` resolves) can rely on
|
|
549
|
+
* this for a local dispatcher; it does **not** hold across a `createRemoteDispatcher`/
|
|
550
|
+
* `connectDispatcher` transport or a `createMirrorStore` mirror, where a change always arrives
|
|
551
|
+
* after a message round trip. Use `origin` ({@link DispatchOptions}) instead where that
|
|
552
|
+
* portability matters — it is carried through both.
|
|
553
|
+
*/
|
|
554
|
+
dispatch<A extends IstarAction<EK, LK>>(action: A, options?: DispatchOptions): Promise<ActionResult<ResponseFor<A>>>;
|
|
494
555
|
/**
|
|
495
556
|
* Hears the actions the dispatcher sends: `ModelSourceChangedAction` after every model change,
|
|
496
557
|
* `SetDirtyStateAction` when the dirty state flips, and the notifications dispatched to it.
|
|
@@ -724,6 +785,8 @@ export interface JsonRpcRequest {
|
|
|
724
785
|
readonly id: JsonRpcId;
|
|
725
786
|
readonly method: string;
|
|
726
787
|
readonly params?: unknown;
|
|
788
|
+
/** The dispatch's `origin` (`DispatchOptions.origin`), carried to the server's `dispatch`. */
|
|
789
|
+
readonly origin?: string;
|
|
727
790
|
}
|
|
728
791
|
/** A subscriber event, sent with no `id`: it expects no response. */
|
|
729
792
|
export interface JsonRpcNotification {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DIAGNOSTIC_SEVERITIES, FILE_METAMODEL_KEY, LINK_SELECTOR, MetamodelError, ModelOperationError, canLink, connectResultToDiagnostic, createEmptyModel, dependencyEnds, effectiveLinkKind, elementBehavesLike, inheritMetamodel, inheritSourceLayout, linkBehavesLike, metamodelOf, parsePistar, toPistarObject, validateModel, validateModelProperties, withFileMetamodel } from "@istar-ts/core";
|
|
1
|
+
import { DIAGNOSTIC_SEVERITIES, FILE_METAMODEL_KEY, LINK_SELECTOR, MetamodelError, ModelOperationError, PistarParseError, TOP_LEVEL_KEYS, canLink, connectResultToDiagnostic, createEmptyModel, dependencyEnds, effectiveLinkKind, elementBehavesLike, hostMetamodelOf, inheritMetamodel, inheritSourceLayout, linkBehavesLike, metamodelOf, parsePistar, toPistarObject, validateModel, validateModelProperties, withFileMetamodel } from "@istar-ts/core";
|
|
2
2
|
//#region src/actions.ts
|
|
3
3
|
const OPERATION_KIND_LIST = [
|
|
4
4
|
"createNode",
|
|
@@ -18,7 +18,9 @@ const OPERATION_KIND_LIST = [
|
|
|
18
18
|
"istar.setLinkValue",
|
|
19
19
|
"istar.updateLinkDisplay",
|
|
20
20
|
"istar.changeLinkKind",
|
|
21
|
-
"istar.setFileMetamodel"
|
|
21
|
+
"istar.setFileMetamodel",
|
|
22
|
+
"istar.updateModelExtra",
|
|
23
|
+
"istar.replaceModel"
|
|
22
24
|
];
|
|
23
25
|
const REQUEST_KIND_LIST = [
|
|
24
26
|
"requestModel",
|
|
@@ -275,6 +277,11 @@ function rejectCheck(check, endpoints) {
|
|
|
275
277
|
function metaOf(model) {
|
|
276
278
|
return metamodelOf(model);
|
|
277
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Keys `istar.updateModelExtra`'s `set`/`remove` must not touch: every key core models itself
|
|
282
|
+
* (`TOP_LEVEL_KEYS`, which includes `tool`), and `"metamodel"` (`istar.setFileMetamodel`'s own).
|
|
283
|
+
*/
|
|
284
|
+
const RESERVED_EXTRA_KEYS = /* @__PURE__ */ new Set([...TOP_LEVEL_KEYS, FILE_METAMODEL_KEY]);
|
|
278
285
|
/** A model with `links` changed, as the core operations derive one. */
|
|
279
286
|
function withLinks(model, links) {
|
|
280
287
|
return inheritMetamodel(model, inheritSourceLayout(model, {
|
|
@@ -315,17 +322,25 @@ function createActionDispatcher(store, options = {}) {
|
|
|
315
322
|
const listeners = /* @__PURE__ */ new Set();
|
|
316
323
|
let saved = s.getModel();
|
|
317
324
|
let dirty = false;
|
|
325
|
+
/**
|
|
326
|
+
* The origin of the dispatch currently applying synchronously (set by `dispatch`, around the
|
|
327
|
+
* synchronous part of `run`): what the store's subscription below echoes on
|
|
328
|
+
* `sourceModelChanged`/`setDirtyState`. `undefined` for a change not caused by a dispatch (a
|
|
329
|
+
* host's direct `store.load`) or once `run`'s synchronous part has returned.
|
|
330
|
+
*/
|
|
331
|
+
let currentOrigin;
|
|
318
332
|
const emit = (action) => {
|
|
319
333
|
for (const listener of listeners) listener(action);
|
|
320
334
|
};
|
|
321
|
-
const updateDirty = (reason) => {
|
|
335
|
+
const updateDirty = (reason, origin) => {
|
|
322
336
|
const next = s.getModel() !== saved;
|
|
323
337
|
if (next === dirty) return;
|
|
324
338
|
dirty = next;
|
|
325
339
|
emit({
|
|
326
340
|
kind: "setDirtyState",
|
|
327
341
|
isDirty: next,
|
|
328
|
-
reason
|
|
342
|
+
reason,
|
|
343
|
+
...origin === void 0 ? {} : { origin }
|
|
329
344
|
});
|
|
330
345
|
};
|
|
331
346
|
const REASONS = {
|
|
@@ -345,9 +360,10 @@ function createActionDispatcher(store, options = {}) {
|
|
|
345
360
|
...options.sendModel ? {
|
|
346
361
|
canUndo: s.canUndo(),
|
|
347
362
|
canRedo: s.canRedo()
|
|
348
|
-
} : {}
|
|
363
|
+
} : {},
|
|
364
|
+
...currentOrigin === void 0 ? {} : { origin: currentOrigin }
|
|
349
365
|
});
|
|
350
|
-
updateDirty(REASONS[event.source]);
|
|
366
|
+
updateDirty(REASONS[event.source], currentOrigin);
|
|
351
367
|
});
|
|
352
368
|
const requireElement = (id) => s.getModel().elements.get(id) ?? reject(`unknown element "${id}"`, id, "unknown-element");
|
|
353
369
|
const runGuards = (operation, warnings) => {
|
|
@@ -572,6 +588,48 @@ function createActionDispatcher(store, options = {}) {
|
|
|
572
588
|
s.replace(next);
|
|
573
589
|
return;
|
|
574
590
|
}
|
|
591
|
+
case "istar.updateModelExtra": {
|
|
592
|
+
const { set, remove, tool } = operation;
|
|
593
|
+
for (const key of [...Object.keys(set ?? {}), ...remove ?? []]) {
|
|
594
|
+
if (!RESERVED_EXTRA_KEYS.has(key)) continue;
|
|
595
|
+
if (key === "tool") reject(`"tool" is a modelled field: set it with this operation's own "tool"`, "", "reserved-key");
|
|
596
|
+
if (key === FILE_METAMODEL_KEY) reject(`"${key}" is set with istar.setFileMetamodel, not updateModelExtra`, "", "reserved-key");
|
|
597
|
+
reject(`"${key}" is modelled by the file itself and cannot be an extra key`, "", "reserved-key");
|
|
598
|
+
}
|
|
599
|
+
let withTool = model;
|
|
600
|
+
if (tool === null) {
|
|
601
|
+
const { tool: _tool, ...rest } = withTool;
|
|
602
|
+
withTool = rest;
|
|
603
|
+
} else if (tool !== void 0) withTool = {
|
|
604
|
+
...withTool,
|
|
605
|
+
tool
|
|
606
|
+
};
|
|
607
|
+
const extra = { ...withTool.extra };
|
|
608
|
+
if (set) for (const [key, value] of Object.entries(set)) extra[key] = value;
|
|
609
|
+
if (remove) for (const key of remove) delete extra[key];
|
|
610
|
+
const { extra: _extra, ...rest } = withTool;
|
|
611
|
+
const next = {
|
|
612
|
+
...rest,
|
|
613
|
+
...Object.keys(extra).length > 0 ? { extra } : {}
|
|
614
|
+
};
|
|
615
|
+
s.replace(inheritMetamodel(model, inheritSourceLayout(model, next)));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
case "istar.replaceModel": {
|
|
619
|
+
const host = hostMetamodelOf(model) ?? meta;
|
|
620
|
+
let next;
|
|
621
|
+
try {
|
|
622
|
+
next = parsePistar(operation.model, {
|
|
623
|
+
metamodel: host,
|
|
624
|
+
fileMetamodel: operation.fileMetamodel ?? false
|
|
625
|
+
});
|
|
626
|
+
} catch (error) {
|
|
627
|
+
if (error instanceof PistarParseError) reject(error.message, "", "parse-error");
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
630
|
+
s.replace(next);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
575
633
|
default: reject(`unknown operation "${operation.kind}"`, "", "unknown-action");
|
|
576
634
|
}
|
|
577
635
|
};
|
|
@@ -584,7 +642,8 @@ function createActionDispatcher(store, options = {}) {
|
|
|
584
642
|
for (const [key, value] of Object.entries(properties)) if (value === null) delete next[key];
|
|
585
643
|
else if (typeof value === "string") next[key] = value;
|
|
586
644
|
else reject(`the value of "${key}" must be a string or null`, id, "invalid-value");
|
|
587
|
-
|
|
645
|
+
if (Object.keys(next).length === 0 && owner.customProperties === void 0) return;
|
|
646
|
+
const customProperties = next;
|
|
588
647
|
if (model.elements.has(id)) s.updateElement(id, { customProperties });
|
|
589
648
|
else s.updateLink(id, { customProperties });
|
|
590
649
|
};
|
|
@@ -744,7 +803,7 @@ function createActionDispatcher(store, options = {}) {
|
|
|
744
803
|
default: return;
|
|
745
804
|
}
|
|
746
805
|
};
|
|
747
|
-
const run = async (action) => {
|
|
806
|
+
const run = async (action, origin) => {
|
|
748
807
|
if (isOperation(action)) return runOperation(action);
|
|
749
808
|
if (isRequestAction(action)) return {
|
|
750
809
|
ok: true,
|
|
@@ -769,7 +828,7 @@ function createActionDispatcher(store, options = {}) {
|
|
|
769
828
|
const model = s.getModel();
|
|
770
829
|
await options.save(toPistarObject(model, { saveDate: /* @__PURE__ */ new Date() }), action);
|
|
771
830
|
saved = model;
|
|
772
|
-
updateDirty("save");
|
|
831
|
+
updateDirty("save", origin);
|
|
773
832
|
return {
|
|
774
833
|
ok: true,
|
|
775
834
|
createdIds: []
|
|
@@ -789,9 +848,17 @@ function createActionDispatcher(store, options = {}) {
|
|
|
789
848
|
}
|
|
790
849
|
};
|
|
791
850
|
return {
|
|
792
|
-
async dispatch(action) {
|
|
851
|
+
async dispatch(action, dispatchOptions) {
|
|
852
|
+
const origin = dispatchOptions?.origin;
|
|
853
|
+
currentOrigin = origin;
|
|
854
|
+
let pending;
|
|
855
|
+
try {
|
|
856
|
+
pending = run(action, origin);
|
|
857
|
+
} finally {
|
|
858
|
+
currentOrigin = void 0;
|
|
859
|
+
}
|
|
793
860
|
try {
|
|
794
|
-
return await
|
|
861
|
+
return await pending;
|
|
795
862
|
} catch (error) {
|
|
796
863
|
if (error instanceof Rejection) return {
|
|
797
864
|
ok: false,
|
|
@@ -1015,8 +1082,8 @@ function createMirrorStore(remote, options = {}) {
|
|
|
1015
1082
|
return {
|
|
1016
1083
|
ready,
|
|
1017
1084
|
dispatcher: {
|
|
1018
|
-
async dispatch(action) {
|
|
1019
|
-
const result = await r.dispatch(action);
|
|
1085
|
+
async dispatch(action, options) {
|
|
1086
|
+
const result = await r.dispatch(action, options);
|
|
1020
1087
|
await tail;
|
|
1021
1088
|
return result;
|
|
1022
1089
|
},
|
|
@@ -1102,8 +1169,9 @@ function connectDispatcher(port, dispatcher) {
|
|
|
1102
1169
|
port.onmessage = (ev) => {
|
|
1103
1170
|
const message = ev.data;
|
|
1104
1171
|
if (!isRecord(message) || message.jsonrpc !== JSONRPC_VERSION) return;
|
|
1105
|
-
const { id, method, params } = message;
|
|
1172
|
+
const { id, method, params, origin } = message;
|
|
1106
1173
|
const hasId = isJsonRpcId(id);
|
|
1174
|
+
const dispatchOptions = typeof origin === "string" ? { origin } : void 0;
|
|
1107
1175
|
if (typeof method !== "string" || method.length === 0) {
|
|
1108
1176
|
if (hasId) port.postMessage(errorMessage(id, -32601, "method not found"));
|
|
1109
1177
|
return;
|
|
@@ -1114,10 +1182,10 @@ function connectDispatcher(port, dispatcher) {
|
|
|
1114
1182
|
}
|
|
1115
1183
|
const action = params;
|
|
1116
1184
|
if (!hasId) {
|
|
1117
|
-
dispatcher.dispatch(action);
|
|
1185
|
+
dispatcher.dispatch(action, dispatchOptions);
|
|
1118
1186
|
return;
|
|
1119
1187
|
}
|
|
1120
|
-
dispatcher.dispatch(action).then((result) => {
|
|
1188
|
+
dispatcher.dispatch(action, dispatchOptions).then((result) => {
|
|
1121
1189
|
port.postMessage({
|
|
1122
1190
|
jsonrpc: JSONRPC_VERSION,
|
|
1123
1191
|
id,
|
|
@@ -1161,7 +1229,7 @@ function createRemoteDispatcher(port) {
|
|
|
1161
1229
|
}
|
|
1162
1230
|
};
|
|
1163
1231
|
return {
|
|
1164
|
-
dispatch(action) {
|
|
1232
|
+
dispatch(action, dispatchOptions) {
|
|
1165
1233
|
const id = nextId++;
|
|
1166
1234
|
return new Promise((resolve, reject) => {
|
|
1167
1235
|
pending.set(id, {
|
|
@@ -1172,7 +1240,8 @@ function createRemoteDispatcher(port) {
|
|
|
1172
1240
|
jsonrpc: JSONRPC_VERSION,
|
|
1173
1241
|
id,
|
|
1174
1242
|
method: action.kind,
|
|
1175
|
-
params: action
|
|
1243
|
+
params: action,
|
|
1244
|
+
...dispatchOptions?.origin === void 0 ? {} : { origin: dispatchOptions.origin }
|
|
1176
1245
|
});
|
|
1177
1246
|
});
|
|
1178
1247
|
},
|