@istar-ts/core 0.7.0 → 0.8.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/README.md CHANGED
@@ -171,6 +171,86 @@ const modelIssues = validateModelProperties(model, [resourceSchema]);
171
171
  `read` / `validate` accept `customProperties`, a full element/link, or `undefined`. `write` merges
172
172
  patches, preserves unknown keys, and serializes typed values back to strings.
173
173
 
174
+ ## Extending the metamodel
175
+
176
+ Everything above uses iStar 2.0, which is `ISTAR_2_0`, the default metamodel. Dialects that add
177
+ constructs, such as [piStar-ext](https://ceur-ws.org/Vol-2641/paper_06.pdf)'s iStar4RationalAgents
178
+ with its Planning and Plan nodes, are new metamodels built with `extendMetamodel`:
179
+
180
+ ```ts
181
+ import { ISTAR_2_0, defineMetamodelExtension, extendMetamodel, parsePistar } from '@istar-ts/core';
182
+
183
+ const rationalAgents = defineMetamodelExtension({
184
+ name: 'rationalAgents',
185
+ elements: [
186
+ // Follows the Task rules: it can refine and be refined, be a dependum, need resources…
187
+ { kind: 'rationalAgents.Planning', behavesLike: 'istar.Task', pistarType: 'istar.Planning' },
188
+ // A node with no rules of its own: only links that name it accept it.
189
+ { kind: 'rationalAgents.Plan', category: 'node' },
190
+ ],
191
+ links: [
192
+ {
193
+ kind: 'rationalAgents.GeneratesLink',
194
+ label: 'Generates',
195
+ rules: { sources: ['rationalAgents.Planning'], targets: ['rationalAgents.Plan'] },
196
+ },
197
+ { kind: 'rationalAgents.AlternativeLink', behavesLike: 'istar.OrRefinementLink' },
198
+ ],
199
+ });
200
+
201
+ const RATIONAL_AGENTS = extendMetamodel(ISTAR_2_0, rationalAgents);
202
+ const model = parsePistar(text, { metamodel: RATIONAL_AGENTS });
203
+ ```
204
+
205
+ - **Element kinds** have a `category` (`node` or `actor`), a `label`, a `size` and, for nodes,
206
+ `NodeKindInfo`. `behavesLike` names an existing kind whose link rules the new kind follows,
207
+ and also sets its category and defaults.
208
+ - **Link kinds** either `behavesLike` an existing link kind (its rules, and its meaning for the
209
+ other rules: an Alternative counts as an OR-refinement, so it can't be mixed with AND), or
210
+ declare `rules`:
211
+ - `sources` / `targets`: kind names or the categories `node`, `actor`, `*`. A kind name also
212
+ accepts kinds that behave like it.
213
+ - `sameActor` (default true), `allowDependum` / `allowSelf` (default false), and `unique`
214
+ (`'kind'` by default, `'any'`, or `false`).
215
+
216
+ Either way, an optional `check(context)` predicate runs last. Dependency-category kinds must
217
+ behave like `istar.DependencyLink`; `addDependency({ …, linkKind })` creates them.
218
+
219
+ - **Names.** Kinds are namespaced (`rationalAgents.Plan`). `istar.` is reserved for iStar 2.0, and a
220
+ name or `pistarType` that collides with an existing one throws `MetamodelError`. Extensions
221
+ stack, and a later one may build on an earlier one's kinds.
222
+ - **The model remembers its metamodel.** `parsePistar`, `createEmptyModel(undefined, { metamodel })`
223
+ and `createModelStore(undefined, { metamodel })` associate it, and every operation and store
224
+ snapshot (undo/redo included) keeps it, so `canLink`, `validateModel`, the store and `toPistar`
225
+ need no extra argument. The association lives beside the model (`metamodelOf`), never in it:
226
+ models keep their shape, and nothing reaches JSON or disk. A model rebuilt with an object
227
+ spread loses it; `withMetamodel(model, metamodel)` restores it.
228
+ - **Predicates.** `isActor` / `isNode` answer for iStar 2.0 and take one argument, so they stay
229
+ safe as `filter` callbacks. For extended kinds use `isActorIn(metamodel)` / `isNodeIn(metamodel)`,
230
+ which return a predicate: `elements.filter(isActorIn(metamodelOf(model)))`.
231
+ - **Types.** `ElementKind`, `LinkKind` and the other iStar 2.0 exports are unchanged.
232
+ `IstarModel<EK, LK>`, `ModelStore<EK, LK>` and `Metamodel<EK, LK>` default to them, and
233
+ `extendMetamodel` types the new kinds (`ElementKindOf<typeof RATIONAL_AGENTS>`).
234
+
235
+ ### On disk: `pistarType`
236
+
237
+ A kind is written with its name as `type` (`"type": "rationalAgents.Plan"`) unless it declares
238
+ `pistarType`. Each choice has a cost:
239
+
240
+ | Declaration | Written as | Who can open the file |
241
+ | ------------------------------ | --------------------- | ----------------------------------------------------------------------- |
242
+ | `pistarType: 'istar.Planning'` | `istar.Planning` | istar-ts with the extension, and piStar-ext with the same construct |
243
+ | no `pistarType` | `rationalAgents.Plan` | istar-ts with the extension only; plain piStar rejects the unknown type |
244
+
245
+ piStar-ext writes its new constructs as `istar.<Name>`, so use `pistarType` to exchange files with
246
+ it. Note that piStar-ext keeps the construct _definitions_ (name, shape, source and target kinds)
247
+ only in the browser's localStorage, not in the model file. To read a piStar-ext file, the host must
248
+ supply the matching extension, otherwise its unknown types throw `PistarParseError`, as with any
249
+ unknown type. `toPistar` likewise throws `PistarWriteError` for a kind its metamodel doesn't know,
250
+ rather than writing a type that can't be read back.
251
+
252
+ See [docs/metamodel-extensions.md](../../docs/metamodel-extensions.md) for the design.
253
+
174
254
  ## Credits
175
255
 
176
256
  The metamodel, link constraints, element shapes, and file format are derived from