@istar-ts/actions 0.15.0-beta.3 → 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.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;
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;
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, {
@@ -581,6 +588,48 @@ function createActionDispatcher(store, options = {}) {
581
588
  s.replace(next);
582
589
  return;
583
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
+ }
584
633
  default: reject(`unknown operation "${operation.kind}"`, "", "unknown-action");
585
634
  }
586
635
  };
@@ -593,7 +642,8 @@ function createActionDispatcher(store, options = {}) {
593
642
  for (const [key, value] of Object.entries(properties)) if (value === null) delete next[key];
594
643
  else if (typeof value === "string") next[key] = value;
595
644
  else reject(`the value of "${key}" must be a string or null`, id, "invalid-value");
596
- const customProperties = Object.keys(next).length > 0 ? next : null;
645
+ if (Object.keys(next).length === 0 && owner.customProperties === void 0) return;
646
+ const customProperties = next;
597
647
  if (model.elements.has(id)) s.updateElement(id, { customProperties });
598
648
  else s.updateLink(id, { customProperties });
599
649
  };