@istar-ts/core 0.11.0 → 0.13.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 +143 -2
- package/dist/index.cjs +1044 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +375 -22
- package/dist/index.d.ts +375 -22
- package/dist/index.js +1016 -199
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,8 +216,9 @@ const model = parsePistar(text, { metamodel: RATIONAL_AGENTS });
|
|
|
216
216
|
Either way, an optional `check(context)` predicate runs last. Dependency-category kinds must
|
|
217
217
|
behave like `istar.DependencyLink`; `addDependency({ …, linkKind })` creates them.
|
|
218
218
|
|
|
219
|
-
- **Names.** Kinds are namespaced (`rationalAgents.Plan`). `istar.`
|
|
220
|
-
name or `pistarType` that collides with an existing one
|
|
219
|
+
- **Names.** Kinds are namespaced (`rationalAgents.Plan`). The root metamodel's prefix (`istar.` for
|
|
220
|
+
iStar 2.0) is reserved for it, and a name or `pistarType` that collides with an existing one
|
|
221
|
+
throws `MetamodelError`. Extensions
|
|
221
222
|
stack, and a later one may build on an earlier one's kinds.
|
|
222
223
|
- **The model remembers its metamodel.** `parsePistar`, `createEmptyModel(undefined, { metamodel })`
|
|
223
224
|
and `createModelStore(undefined, { metamodel })` associate it, and every operation and store
|
|
@@ -251,6 +252,137 @@ rather than writing a type that can't be read back.
|
|
|
251
252
|
|
|
252
253
|
See [docs/metamodel-extensions.md](../../docs/metamodel-extensions.md) for the design.
|
|
253
254
|
|
|
255
|
+
## Root metamodels
|
|
256
|
+
|
|
257
|
+
iStar 2.0 is one root metamodel: a whole modelling language rather than a dialect of another.
|
|
258
|
+
`defineMetamodel` builds others, such as the KAOS and NFR languages of
|
|
259
|
+
[DSM3-goals](https://github.com/vieirin/istar-ts/issues/7), from the same element and link
|
|
260
|
+
declarations that extensions use. `ISTAR_2_0` is itself `defineMetamodel(ISTAR_2_0_DECLARATION)`.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { createEmptyModel, defineMetamodel, extendMetamodel, parsePistar } from '@istar-ts/core';
|
|
264
|
+
|
|
265
|
+
const KAOS = defineMetamodel({
|
|
266
|
+
name: 'kaos', // the metamodel's name and framework
|
|
267
|
+
prefix: 'kaos', // its namespace, reserved against extensions
|
|
268
|
+
elements: [
|
|
269
|
+
{ kind: 'kaos.Goal', category: 'node', size: { width: 135, height: 36 } },
|
|
270
|
+
{ kind: 'kaos.Softgoal', behavesLike: 'kaos.Goal' },
|
|
271
|
+
{ kind: 'kaos.Agent', category: 'node' },
|
|
272
|
+
],
|
|
273
|
+
links: [
|
|
274
|
+
{
|
|
275
|
+
kind: 'kaos.ResponsibilityAssignment',
|
|
276
|
+
rules: { sources: ['kaos.Agent'], targets: ['node'], sameActor: false },
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
file: {
|
|
280
|
+
tool: 'dsm3-goals',
|
|
281
|
+
extra: { metamodelVersion: '1.0' },
|
|
282
|
+
keys: [
|
|
283
|
+
'actors',
|
|
284
|
+
'orphans',
|
|
285
|
+
'dependencies',
|
|
286
|
+
'links',
|
|
287
|
+
'display',
|
|
288
|
+
'tool',
|
|
289
|
+
'metamodelVersion',
|
|
290
|
+
'saveDate',
|
|
291
|
+
'diagram',
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const model = parsePistar(text, { metamodel: KAOS });
|
|
297
|
+
const blank = createEmptyModel(undefined, { metamodel: KAOS }); // tool "dsm3-goals", no "istar"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
- **Declarations.** Kinds are declared in order, with the same validation and errors as
|
|
301
|
+
`extendMetamodel`. `behavesLike` and link rules refer to kinds declared before them. A root may
|
|
302
|
+
use any namespace. Nodes default to DSM3-goals' containment rules: inner element, dependum and
|
|
303
|
+
on paper. A root's link kind may give neither `rules` nor `behavesLike`, as iStar 2.0's do:
|
|
304
|
+
their rules are built into the library, and any other such kind accepts no link.
|
|
305
|
+
- **Identity.** `metamodel.framework` is the root's name (`istar-2.0`, `kaos`). `prefix` is its
|
|
306
|
+
reserved namespace, so `extendMetamodel(KAOS, …)` rejects `kaos.*` kinds. Extensions keep both,
|
|
307
|
+
and `file`; `name` gains the extensions' names (`kaos+ops`).
|
|
308
|
+
- **Files.** `file` describes the files of models that weren't read from one. `createEmptyModel`
|
|
309
|
+
records `file.tool` (default `pistar.2.1.0`), `file.istar` only when set (iStar 2.0 sets
|
|
310
|
+
`"2.0"`), and `file.extra` as extra top-level keys. `toPistar` orders top-level keys like the
|
|
311
|
+
source file, else like `file.keys`, else like piStar.
|
|
312
|
+
- **Which root a file uses.** `detectFramework(text, [ISTAR_2_0, KAOS, NFR])` picks the root whose
|
|
313
|
+
`prefix` is the namespace of the file's first `type` (`detectPrefix`). For a file without
|
|
314
|
+
types, it picks the only root whose `file.tool` is the file's `tool`; otherwise it returns
|
|
315
|
+
`undefined`.
|
|
316
|
+
|
|
317
|
+
### Hub links
|
|
318
|
+
|
|
319
|
+
A link kind with `arity: 'n'` has several sources and one target, like KAOS's AND-refinement. In a
|
|
320
|
+
model it is a hub element, a leg from each source to the hub, and an end from the hub to the
|
|
321
|
+
target, as DSM3-goals draws and saves it. The declaration generates the three kinds from the
|
|
322
|
+
base kind's name: `<kind>Hub`, `<kind>Start` (legs) and `<kind>End`.
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
const KAOS = defineMetamodel<KaosElementKind, KaosLinkKind>({
|
|
326
|
+
// …
|
|
327
|
+
links: [
|
|
328
|
+
{
|
|
329
|
+
kind: 'kaos.RefinementLink',
|
|
330
|
+
arity: 'n',
|
|
331
|
+
rules: {
|
|
332
|
+
sources: ['kaos.BehavioralGoal', 'kaos.Softgoal'],
|
|
333
|
+
targets: ['node'],
|
|
334
|
+
sameActor: false,
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const made = store.connect({ kind: 'kaos.RefinementLink', source: goalA, target: parent });
|
|
341
|
+
if (made.ok && made.hub) {
|
|
342
|
+
// made.hub: { hub, legs: [leg], end }. The hub sits centred between goalA and parent.
|
|
343
|
+
store.connect({ kind: 'kaos.RefinementLink', source: goalB, target: made.hub.hub.id }); // 2nd leg
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
- **Kinds.** The hub kind has `role: 'hub'` and `of` (the base kind), a 20×20 circle shape and
|
|
348
|
+
`canBeOnPaper`. Legs and ends have `role: 'leg' | 'end'`. `parts` renames them and `hubSize`
|
|
349
|
+
resizes the hub. Generated kinds aren't inferred into the metamodel's kind types, so name
|
|
350
|
+
them in `defineMetamodel`'s type arguments if you need them typed.
|
|
351
|
+
- **Rules.** The `node`, `actor` and `*` selectors never match a hub. A link of another kind
|
|
352
|
+
towards a hub fails with code `hub-target`. A source gets one leg per hub, and a hub has one
|
|
353
|
+
end. The base kind's `sameActor` holds between each leg's source and the end's target.
|
|
354
|
+
- **Operations.** `connect` with the base kind creates the hub, a leg and the end, or adds a leg
|
|
355
|
+
when the target is one of its hubs. `input.id` and `customProperties` go to the hub; `label` and
|
|
356
|
+
`name` go to the end. `addLeg(model, hubId, source)` and `removeLeg(model, legId)` exist in
|
|
357
|
+
operations and on the store. Removing a hub's end or its last leg removes the hub. Removing the
|
|
358
|
+
hub removes its legs and end.
|
|
359
|
+
- **Queries.** Use `hubLinkOf(model, hubId)`, `hubOfLink(model, linkId)`,
|
|
360
|
+
`isHubElement(metamodel, element)`, `elementCenter` and `defaultHubPosition`.
|
|
361
|
+
`validateModel` reports a hub without an end or legs as `incomplete-hub`, with the hub's id as
|
|
362
|
+
`linkId`.
|
|
363
|
+
- **On disk.** An entry in `links` holds `{ hub, starts, end }`, with no id of its own and no
|
|
364
|
+
`text` on the hub, as DSM3-goals writes it. The entry's unknown keys are kept on the hub's
|
|
365
|
+
`extra[HUB_ENTRY_KEY]` and written back. Hubs never appear in `orphans`.
|
|
366
|
+
|
|
367
|
+
### Links that target links
|
|
368
|
+
|
|
369
|
+
A link kind may point at another link, like NFR's Argument, which is a Claim about a Contribution.
|
|
370
|
+
Its `rules.targets` holds `link` (any link) or link kind names:
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
{ kind: 'nfr.ArgumentLink', rules: { sources: ['nfr.Claim'], targets: ['link'], sameActor: false } }
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
- **Checks.** `canLink(model, claim, contributionLinkId, 'nfr.ArgumentLink')` looks the target up
|
|
377
|
+
among elements, then links. Element selectors never match a link, and link selectors never
|
|
378
|
+
match an element. The self-link, dependum and same-actor rules don't apply to a link target.
|
|
379
|
+
iStar 2.0's built-in rules refuse one.
|
|
380
|
+
- **Predicates.** `LinkRuleContext.target` is an `IstarElement` or an `IstarLink`, so narrow it
|
|
381
|
+
with `isLinkRef`. `linkTargetOf(model, link)` returns what a link points at.
|
|
382
|
+
- **Models.** `validateModel` checks links that target links after the others, whatever their
|
|
383
|
+
order in the file. Removing a link removes the links that target it. `linksOf(model, linkId)`
|
|
384
|
+
lists them.
|
|
385
|
+
|
|
254
386
|
## A model's own metamodel
|
|
255
387
|
|
|
256
388
|
A file can declare the constructs it adds to iStar in a top-level `"metamodel"` block, so they
|
|
@@ -304,6 +436,15 @@ toPistar(extended); // writes the "metamodel" block
|
|
|
304
436
|
into a store (`store.load` or `store.replace`) to keep editing.
|
|
305
437
|
- `pistarType` works as for any extension, so `istar.<Name>` types on disk still read.
|
|
306
438
|
|
|
439
|
+
## Diagnostics
|
|
440
|
+
|
|
441
|
+
`GoalDiagnostic` (`{ elementId, key?, severity, message, source?, range?, code?, data? }`) is the
|
|
442
|
+
data shape for findings about elements and links. Analysers publish them per source to a
|
|
443
|
+
`createDiagnosticsStore()`; `getAll()` returns the union of every source, deduplicated by
|
|
444
|
+
`(elementId, key, message)`. `fromLspDiagnostics` and `fromNodeIdDiagnostics` adapt LSP and MutRoSe
|
|
445
|
+
diagnostics. Diagnostics are never part of the model and never serialized. `@istar-ts/react`
|
|
446
|
+
shows them as badges and in the inspector (see its README).
|
|
447
|
+
|
|
307
448
|
## Credits
|
|
308
449
|
|
|
309
450
|
The metamodel, link constraints, element shapes, and file format are derived from
|