@istar-ts/core 0.6.0 → 0.8.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 CHANGED
@@ -171,6 +171,83 @@ 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
+ - **Types.** `ElementKind`, `LinkKind` and the other iStar 2.0 exports are unchanged.
229
+ `IstarModel<EK, LK>`, `ModelStore<EK, LK>` and `Metamodel<EK, LK>` default to them, and
230
+ `extendMetamodel` types the new kinds (`ElementKindOf<typeof RATIONAL_AGENTS>`).
231
+
232
+ ### On disk: `pistarType`
233
+
234
+ A kind is written with its name as `type` (`"type": "rationalAgents.Plan"`) unless it declares
235
+ `pistarType`. Each choice has a cost:
236
+
237
+ | Declaration | Written as | Who can open the file |
238
+ | ------------------------------ | --------------------- | ----------------------------------------------------------------------- |
239
+ | `pistarType: 'istar.Planning'` | `istar.Planning` | istar-ts with the extension, and piStar-ext with the same construct |
240
+ | no `pistarType` | `rationalAgents.Plan` | istar-ts with the extension only; plain piStar rejects the unknown type |
241
+
242
+ piStar-ext writes its new constructs as `istar.<Name>`, so use `pistarType` to exchange files with
243
+ it. Note that piStar-ext keeps the construct _definitions_ (name, shape, source and target kinds)
244
+ only in the browser's localStorage, not in the model file. To read a piStar-ext file, the host must
245
+ supply the matching extension, otherwise its unknown types throw `PistarParseError`, as with any
246
+ unknown type. `toPistar` likewise throws `PistarWriteError` for a kind its metamodel doesn't know,
247
+ rather than writing a type that can't be read back.
248
+
249
+ See [docs/metamodel-extensions.md](../../docs/metamodel-extensions.md) for the design.
250
+
174
251
  ## Credits
175
252
 
176
253
  The metamodel, link constraints, element shapes, and file format are derived from