@istar-ts/actions 0.15.0-beta.0 → 0.15.0-beta.1
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 +53 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -2
- package/dist/index.d.ts +41 -2
- package/dist/index.js +54 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyIstarModel, AnyPaletteDescription, CustomProperties, DiagnosticSeverity, ElementKind, GoalDiagnostic, IstarModel, LinkKind, Metamodel, ModelChange, ModelStore, PistarFile, PropertySchema, PropertyShape } from "@istar-ts/core";
|
|
1
|
+
import { AnyIstarModel, AnyPaletteDescription, CustomProperties, DiagnosticSeverity, ElementKind, FileMetamodel, GoalDiagnostic, IstarModel, LinkKind, Metamodel, ModelChange, ModelStore, PistarFile, PropertySchema, PropertyShape } from "@istar-ts/core";
|
|
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>>;
|
|
@@ -216,8 +216,35 @@ export interface UpdateLinkDisplayOperation extends Operation {
|
|
|
216
216
|
readonly linkId: string;
|
|
217
217
|
readonly patch: Readonly<Record<string, unknown>>;
|
|
218
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Changes the kind of a set of links: each listed link's id, source, target, order in the file,
|
|
221
|
+
* name, value, custom properties and display are kept; other links are untouched. One undo step
|
|
222
|
+
* for the whole set, all or nothing. A link whose kind is already `elementTypeId` is a no-op;
|
|
223
|
+
* an empty `linkIds` is a no-op too. Rejected (model untouched) if `canLink` refuses any listed
|
|
224
|
+
* link's new kind for its unchanged source and target (e.g. a mixed-refinement rule), or if the
|
|
225
|
+
* link keeps a `value` the new kind's `possibleLabels` disallows.
|
|
226
|
+
*/
|
|
227
|
+
export interface ChangeLinkKindOperation<LK extends string = LinkKind> extends Operation {
|
|
228
|
+
readonly kind: "istar.changeLinkKind";
|
|
229
|
+
readonly linkIds: readonly string[];
|
|
230
|
+
/** The links' new kind, e.g. `istar.OrRefinementLink`. */
|
|
231
|
+
readonly elementTypeId: LK;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Sets (or, with `null` or an empty block, removes) the model file's own `"metamodel"` block:
|
|
235
|
+
* custom element kinds, groupers, link kinds with their rules and line style (`withFileMetamodel`
|
|
236
|
+
* in `@istar-ts/core`). One undo step; undo restores the previous block exactly. Rejected (model
|
|
237
|
+
* untouched) if the block is malformed, collides with the host's kinds, or drops a kind an
|
|
238
|
+
* element or link still uses. A host's dialect-specific checks (e.g. goal-controller's own
|
|
239
|
+
* validation of the block) run as an `ActionGuard` on this operation's kind, so their refusal
|
|
240
|
+
* also comes back as diagnostics.
|
|
241
|
+
*/
|
|
242
|
+
export interface SetFileMetamodelOperation extends Operation {
|
|
243
|
+
readonly kind: "istar.setFileMetamodel";
|
|
244
|
+
readonly block: FileMetamodel | null;
|
|
245
|
+
}
|
|
219
246
|
/** Every operation, keyed by `kind`. */
|
|
220
|
-
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;
|
|
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;
|
|
221
248
|
export interface RequestModelAction extends RequestAction {
|
|
222
249
|
readonly kind: "requestModel";
|
|
223
250
|
readonly options?: Args;
|
|
@@ -477,8 +504,20 @@ export interface ActionDispatcher<EK extends string = ElementKind, LK extends st
|
|
|
477
504
|
* the operation; others are returned with the result.
|
|
478
505
|
*/
|
|
479
506
|
export type ActionGuard<EK extends string = ElementKind, LK extends string = LinkKind> = (operation: IstarOperation<EK, LK>, model: IstarModel<EK, LK>) => readonly GoalDiagnostic[] | void;
|
|
507
|
+
/**
|
|
508
|
+
* A host's rule, run after each operation is applied (and after each operation of a compound
|
|
509
|
+
* one, plus once more on the compound's own result), inside the operation's transaction. Sees
|
|
510
|
+
* the model as the operation leaves it (`modelAfter`) and as it was before (`modelBefore`), so
|
|
511
|
+
* it can check things a pre-guard can't without re-applying the operation itself (goal-controller's
|
|
512
|
+
* edgeV2 engine check is the motivating case). Diagnostics of severity `error` roll the
|
|
513
|
+
* transaction back: the model is untouched and there is no undo entry. Others are returned with
|
|
514
|
+
* the result, merged with pre-guards' warnings.
|
|
515
|
+
*/
|
|
516
|
+
export type ActionPostGuard<EK extends string = ElementKind, LK extends string = LinkKind> = (operation: IstarOperation<EK, LK>, modelAfter: IstarModel<EK, LK>, modelBefore: IstarModel<EK, LK>) => readonly GoalDiagnostic[] | void;
|
|
480
517
|
export interface ActionDispatcherOptions<EK extends string = ElementKind, LK extends string = LinkKind> {
|
|
481
518
|
readonly guards?: readonly ActionGuard<EK, LK>[];
|
|
519
|
+
/** Run after each operation is applied; see `ActionPostGuard`. */
|
|
520
|
+
readonly postGuards?: readonly ActionPostGuard<EK, LK>[];
|
|
482
521
|
/** What `RequestContextActions` offers. Default: `DEFAULT_PALETTE` (iStar 2.0's). */
|
|
483
522
|
readonly palette?: AnyPaletteDescription;
|
|
484
523
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyIstarModel, AnyPaletteDescription, CustomProperties, DiagnosticSeverity, ElementKind, GoalDiagnostic, IstarModel, LinkKind, Metamodel, ModelChange, ModelStore, PistarFile, PropertySchema, PropertyShape } from "@istar-ts/core";
|
|
1
|
+
import { AnyIstarModel, AnyPaletteDescription, CustomProperties, DiagnosticSeverity, ElementKind, FileMetamodel, GoalDiagnostic, IstarModel, LinkKind, Metamodel, ModelChange, ModelStore, PistarFile, PropertySchema, PropertyShape } from "@istar-ts/core";
|
|
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>>;
|
|
@@ -216,8 +216,35 @@ export interface UpdateLinkDisplayOperation extends Operation {
|
|
|
216
216
|
readonly linkId: string;
|
|
217
217
|
readonly patch: Readonly<Record<string, unknown>>;
|
|
218
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* Changes the kind of a set of links: each listed link's id, source, target, order in the file,
|
|
221
|
+
* name, value, custom properties and display are kept; other links are untouched. One undo step
|
|
222
|
+
* for the whole set, all or nothing. A link whose kind is already `elementTypeId` is a no-op;
|
|
223
|
+
* an empty `linkIds` is a no-op too. Rejected (model untouched) if `canLink` refuses any listed
|
|
224
|
+
* link's new kind for its unchanged source and target (e.g. a mixed-refinement rule), or if the
|
|
225
|
+
* link keeps a `value` the new kind's `possibleLabels` disallows.
|
|
226
|
+
*/
|
|
227
|
+
export interface ChangeLinkKindOperation<LK extends string = LinkKind> extends Operation {
|
|
228
|
+
readonly kind: "istar.changeLinkKind";
|
|
229
|
+
readonly linkIds: readonly string[];
|
|
230
|
+
/** The links' new kind, e.g. `istar.OrRefinementLink`. */
|
|
231
|
+
readonly elementTypeId: LK;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Sets (or, with `null` or an empty block, removes) the model file's own `"metamodel"` block:
|
|
235
|
+
* custom element kinds, groupers, link kinds with their rules and line style (`withFileMetamodel`
|
|
236
|
+
* in `@istar-ts/core`). One undo step; undo restores the previous block exactly. Rejected (model
|
|
237
|
+
* untouched) if the block is malformed, collides with the host's kinds, or drops a kind an
|
|
238
|
+
* element or link still uses. A host's dialect-specific checks (e.g. goal-controller's own
|
|
239
|
+
* validation of the block) run as an `ActionGuard` on this operation's kind, so their refusal
|
|
240
|
+
* also comes back as diagnostics.
|
|
241
|
+
*/
|
|
242
|
+
export interface SetFileMetamodelOperation extends Operation {
|
|
243
|
+
readonly kind: "istar.setFileMetamodel";
|
|
244
|
+
readonly block: FileMetamodel | null;
|
|
245
|
+
}
|
|
219
246
|
/** Every operation, keyed by `kind`. */
|
|
220
|
-
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;
|
|
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;
|
|
221
248
|
export interface RequestModelAction extends RequestAction {
|
|
222
249
|
readonly kind: "requestModel";
|
|
223
250
|
readonly options?: Args;
|
|
@@ -477,8 +504,20 @@ export interface ActionDispatcher<EK extends string = ElementKind, LK extends st
|
|
|
477
504
|
* the operation; others are returned with the result.
|
|
478
505
|
*/
|
|
479
506
|
export type ActionGuard<EK extends string = ElementKind, LK extends string = LinkKind> = (operation: IstarOperation<EK, LK>, model: IstarModel<EK, LK>) => readonly GoalDiagnostic[] | void;
|
|
507
|
+
/**
|
|
508
|
+
* A host's rule, run after each operation is applied (and after each operation of a compound
|
|
509
|
+
* one, plus once more on the compound's own result), inside the operation's transaction. Sees
|
|
510
|
+
* the model as the operation leaves it (`modelAfter`) and as it was before (`modelBefore`), so
|
|
511
|
+
* it can check things a pre-guard can't without re-applying the operation itself (goal-controller's
|
|
512
|
+
* edgeV2 engine check is the motivating case). Diagnostics of severity `error` roll the
|
|
513
|
+
* transaction back: the model is untouched and there is no undo entry. Others are returned with
|
|
514
|
+
* the result, merged with pre-guards' warnings.
|
|
515
|
+
*/
|
|
516
|
+
export type ActionPostGuard<EK extends string = ElementKind, LK extends string = LinkKind> = (operation: IstarOperation<EK, LK>, modelAfter: IstarModel<EK, LK>, modelBefore: IstarModel<EK, LK>) => readonly GoalDiagnostic[] | void;
|
|
480
517
|
export interface ActionDispatcherOptions<EK extends string = ElementKind, LK extends string = LinkKind> {
|
|
481
518
|
readonly guards?: readonly ActionGuard<EK, LK>[];
|
|
519
|
+
/** Run after each operation is applied; see `ActionPostGuard`. */
|
|
520
|
+
readonly postGuards?: readonly ActionPostGuard<EK, LK>[];
|
|
482
521
|
/** What `RequestContextActions` offers. Default: `DEFAULT_PALETTE` (iStar 2.0's). */
|
|
483
522
|
readonly palette?: AnyPaletteDescription;
|
|
484
523
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_PALETTE, DIAGNOSTIC_SEVERITIES, FILE_METAMODEL_KEY, LINK_SELECTOR, ModelOperationError, canLink, connectResultToDiagnostic, createEmptyModel, dependencyEnds, effectiveLinkKind, elementBehavesLike, inheritMetamodel, inheritSourceLayout, linkBehavesLike, metamodelOf, parsePistar, toPistarObject, validateModel, validateModelProperties } from "@istar-ts/core";
|
|
1
|
+
import { DEFAULT_PALETTE, 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";
|
|
2
2
|
//#region src/actions.ts
|
|
3
3
|
const OPERATION_KIND_LIST = [
|
|
4
4
|
"createNode",
|
|
@@ -16,7 +16,9 @@ const OPERATION_KIND_LIST = [
|
|
|
16
16
|
"istar.updateProperties",
|
|
17
17
|
"istar.updateDisplay",
|
|
18
18
|
"istar.setLinkValue",
|
|
19
|
-
"istar.updateLinkDisplay"
|
|
19
|
+
"istar.updateLinkDisplay",
|
|
20
|
+
"istar.changeLinkKind",
|
|
21
|
+
"istar.setFileMetamodel"
|
|
20
22
|
];
|
|
21
23
|
const REQUEST_KIND_LIST = [
|
|
22
24
|
"requestModel",
|
|
@@ -273,6 +275,7 @@ function linkIssueDiagnostics(model) {
|
|
|
273
275
|
function createActionDispatcher(store, options = {}) {
|
|
274
276
|
const s = store;
|
|
275
277
|
const guards = options.guards ?? [];
|
|
278
|
+
const postGuards = options.postGuards ?? [];
|
|
276
279
|
const palette = options.palette ?? DEFAULT_PALETTE;
|
|
277
280
|
const sourceModelName = options.sourceModelName ?? "";
|
|
278
281
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -318,8 +321,18 @@ function createActionDispatcher(store, options = {}) {
|
|
|
318
321
|
for (const guard of guards) for (const d of guard(operation, s.getModel()) ?? []) (d.severity === "error" ? errors : warnings).push(d);
|
|
319
322
|
if (errors.length > 0) throw new Rejection(errors);
|
|
320
323
|
};
|
|
324
|
+
const runPostGuards = (operation, before, warnings) => {
|
|
325
|
+
const errors = [];
|
|
326
|
+
for (const guard of postGuards) for (const d of guard(operation, s.getModel(), before) ?? []) (d.severity === "error" ? errors : warnings).push(d);
|
|
327
|
+
if (errors.length > 0) throw new Rejection(errors);
|
|
328
|
+
};
|
|
321
329
|
const apply = (operation, warnings) => {
|
|
322
330
|
runGuards(operation, warnings);
|
|
331
|
+
const before = s.getModel();
|
|
332
|
+
doApply(operation, warnings);
|
|
333
|
+
runPostGuards(operation, before, warnings);
|
|
334
|
+
};
|
|
335
|
+
const doApply = (operation, warnings) => {
|
|
323
336
|
const model = s.getModel();
|
|
324
337
|
const meta = metaOf(model);
|
|
325
338
|
switch (operation.kind) {
|
|
@@ -486,6 +499,45 @@ function createActionDispatcher(store, options = {}) {
|
|
|
486
499
|
s.updateLink(linkId, { label: value });
|
|
487
500
|
return;
|
|
488
501
|
}
|
|
502
|
+
case "istar.changeLinkKind": {
|
|
503
|
+
const { linkIds, elementTypeId: kind } = operation;
|
|
504
|
+
if (linkIds.length === 0) return;
|
|
505
|
+
const ids = new Set(linkIds);
|
|
506
|
+
const links = [...ids].map((id) => model.links.get(id) ?? reject(`unknown link "${id}"`, id, "unknown-element"));
|
|
507
|
+
const definition = meta.links.get(kind) ?? reject(`unknown link kind "${kind}" in ${meta.name}`, "", "unknown-kind");
|
|
508
|
+
const toChange = links.filter((link) => link.kind !== kind);
|
|
509
|
+
if (toChange.length === 0) return;
|
|
510
|
+
const remaining = new Map(model.links);
|
|
511
|
+
for (const id of ids) remaining.delete(id);
|
|
512
|
+
const without = withLinks(model, remaining);
|
|
513
|
+
const values = definition.info.possibleLabels;
|
|
514
|
+
for (const link of toChange) {
|
|
515
|
+
const check = canLink(without, link.source, link.target, kind);
|
|
516
|
+
if (!check.ok) rejectCheck(check, {
|
|
517
|
+
source: link.source,
|
|
518
|
+
target: link.target
|
|
519
|
+
});
|
|
520
|
+
if (link.label != null && values && !values.includes(link.label)) reject(`"${link.label}" is not a value of a ${definition.label} link (${values.join(", ")})`, link.id, "invalid-value");
|
|
521
|
+
}
|
|
522
|
+
const next = new Map(model.links);
|
|
523
|
+
for (const link of toChange) next.set(link.id, {
|
|
524
|
+
...link,
|
|
525
|
+
kind
|
|
526
|
+
});
|
|
527
|
+
s.replace(withLinks(model, next));
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
case "istar.setFileMetamodel": {
|
|
531
|
+
let next;
|
|
532
|
+
try {
|
|
533
|
+
next = withFileMetamodel(model, operation.block);
|
|
534
|
+
} catch (error) {
|
|
535
|
+
if (error instanceof MetamodelError) reject(error.message, "", "invalid-metamodel");
|
|
536
|
+
throw error;
|
|
537
|
+
}
|
|
538
|
+
s.replace(next);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
489
541
|
default: reject(`unknown operation "${operation.kind}"`, "", "unknown-action");
|
|
490
542
|
}
|
|
491
543
|
};
|