@peter.prib/renderengine 0.2.11 → 0.4.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.
Files changed (36) hide show
  1. package/README.md +22 -6
  2. package/REFERENCE.md +117 -2
  3. package/dist/renderengine-workspace.umd.js +2973 -1203
  4. package/dist/renderengine.umd.js +1178 -158
  5. package/package.json +1 -1
  6. package/src/charting/ModelCheckDialog.js +10 -3
  7. package/src/charting/ModelComparisonDialog.js +10 -3
  8. package/src/charting/ModelFileFormat.js +4 -1
  9. package/src/charting/archimate/ArchimateCanvasEditor.js +14 -2
  10. package/src/charting/archimate/ArchimateElementTemplate.js +23 -0
  11. package/src/charting/arrangement/layouts.js +9 -4
  12. package/src/charting/datamodel/DataModel.js +18 -71
  13. package/src/charting/datamodel/DataModelCompared.js +1 -31
  14. package/src/charting/diagramModel/ListCompared.js +35 -0
  15. package/src/charting/model/Benchmark.js +59 -0
  16. package/src/charting/model/EditableModel.js +104 -0
  17. package/src/charting/{datamodel/DataModelEditCommand.js → model/ModelEditCommand.js} +47 -47
  18. package/src/charting/requirements/RequirementsChecker.js +76 -0
  19. package/src/charting/requirements/RequirementsCompared.js +33 -0
  20. package/src/charting/requirements/RequirementsFileFormat.js +32 -0
  21. package/src/charting/requirements/RequirementsImportFormats.js +48 -0
  22. package/src/charting/requirements/RequirementsModel.js +305 -0
  23. package/src/charting/requirements/Storyboard.js +94 -0
  24. package/src/charting/requirements/TrackerExport.js +273 -0
  25. package/src/charting/requirements/index.js +7 -0
  26. package/src/index.js +6 -0
  27. package/src/workspace/Workspace.js +329 -321
  28. package/src/workspace/index.js +2 -0
  29. package/src/workspace/parts/ArchimateCanvasPart.js +31 -1
  30. package/src/workspace/parts/DataModelPart.js +668 -735
  31. package/src/workspace/parts/EditorPart.js +1 -1
  32. package/src/workspace/parts/ModelFile.js +3 -1
  33. package/src/workspace/parts/ModelHosted.js +176 -0
  34. package/src/workspace/parts/ModelsPart.js +146 -7
  35. package/src/workspace/parts/RequirementsPart.js +465 -0
  36. package/src/workspace/storage/ModelLibrary.js +8 -2
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # RenderEngine
2
2
 
3
- **An ArchiMate modeller, a data modeller, a diagram editor and a chart library that run in any browser tab, with
4
- no install and no runtime dependencies.**
3
+ **An ArchiMate modeller, a data modeller, a requirements story map, a diagram editor and a chart library that run
4
+ in any browser tab, with no install and no runtime dependencies.**
5
5
 
6
6
  Open an Archi model, edit it the way you would in Archi, and save it back to the same `.archimate` file, without
7
7
  installing anything. Model data at three levels, governed and checked the way DAMA-DMBOK2 has it, with banking at
@@ -167,6 +167,20 @@ the browser and backed up.
167
167
 
168
168
  ---
169
169
 
170
+ ## Requirements and story maps
171
+
172
+ Bring in the epics and stories your team already has: a Jira, Azure DevOps or GitLab export (CSV or JSON), or a
173
+ storyboard written in Markdown. Read as a **story map** (epics across, releases down) or a **backlog**, each story
174
+ carries its "As a … I want … so that …", its acceptance criteria (read from their own field, a list in the
175
+ description, or Given/When/Then) and its **traces** to what realises it: an ArchiMate requirement or work package,
176
+ a component, a data concept, entity or table. Drag a card to another epic or release; the toolbar says how many
177
+ stories say when they are done and how many trace to something, and **Check model** lists what is missing.
178
+
179
+ - **Every model alike**: kept, compared and merged as the ArchiMate and data models are, a newer tracker export
180
+ brought in over the old one replacing what changed.
181
+ - **Yours to extend**: a host says what can be traced to, what a story's live state is, and how to find stories by
182
+ meaning (a vector store) — the SqlEngine workbench does all three.
183
+
170
184
  ## The diagram editor
171
185
 
172
186
  The ArchiMate modeller is built on a general diagram editor, and every editing feature above is part of that
@@ -237,10 +251,12 @@ page:
237
251
  </script>
238
252
  ```
239
253
 
240
- Use `editorType: 'diagram-editor'` with `DiagramEditorPart` for the diagram editor, and `editorType: 'data-model'`
241
- with `DataModelPart` for the data modeller. The models are plain JavaScript APIs (`ArchimateModel`, `DiagramModel`,
242
- `DataModel`), so you can also build, check, score, search, report on and export models with no UI at all, under
243
- Node too: `new DataModel(spec).checker().run()`, `new SqlDdl().write(model)`.
254
+ Use `editorType: 'diagram-editor'` with `DiagramEditorPart` for the diagram editor, `editorType: 'data-model'`
255
+ with `DataModelPart` for the data modeller, and `editorType: 'requirements'` with `RequirementsPart` for the story
256
+ map. The models are plain JavaScript APIs (`ArchimateModel`, `DiagramModel`, `DataModel`, `RequirementsModel`), so
257
+ you can also build, check, score, search, report on and export models with no UI at all, under Node too:
258
+ `new DataModel(spec).checker().run()`, `new SqlDdl().write(model)`,
259
+ `new RequirementsModel(new TrackerExport().read(csv)).coverage()`.
244
260
 
245
261
  ---
246
262
 
package/REFERENCE.md CHANGED
@@ -881,6 +881,12 @@ types — see `ARCHITECTURE.md`):
881
881
  `LevelSchema.resolveReference(ref)`: the whole text as a table (by id or name, any case), else what is before its
882
882
  last dot as the table and the rest as its column — `{ entity, column }`, or null.
883
883
 
884
+ **Edit ▸ Lay out.** The level shown is laid out again by one of the arrangement's layouts (`LAYOUTS`: grid, circle,
885
+ a tree down or right, force-directed, by kind — the same ones a diagram's Arrange ▸ Layout uses), its relationships
886
+ guiding the trees and the force, only what the perspective draws moving (`part.layoutCommand(kind)`, one undo
887
+ step; where each box goes is kept as a drag would keep it). Not the mapping view or the other views, which lay
888
+ themselves out.
889
+
884
890
  **A dimensional scheme.** At the logical and physical levels an entity may have a role (`EntityRole`, registered as
885
891
  relationship kinds are): a **fact** (`FactRole`, `role: 'fact'`: what is measured, at a stated `grain`, its
886
892
  attributes' `measure` additive, semi-additive or non-additive) or a **dimension** (`DimensionRole`: its `scdType`,
@@ -1623,7 +1629,10 @@ sharing the spec's data cannot reach them). File ▸ New ▸ **From base: …**
1623
1629
  `models.baseItems()`) opens a model as a copy of one; Tools ▸ **Compare with** and **Merge from** offer them, after
1624
1630
  the other open models (`models.toolItems(part)`); File ▸ **Duplicate model** (`models.duplicate(part)`) opens a copy
1625
1631
  of the model in front, written and read back through its own file format. A copy, from a base or duplicated, is named
1626
- "… (copy)" (`ModelsPart.copyName`, the suffix once).
1632
+ "… (copy)" (`ModelsPart.copyName`, the suffix once). A host whose reference models change (a live database
1633
+ whose schema changed) gives them afresh with `models.setBases(specs)`; what is open is left alone. File ▸ New ▸ **View base: …**
1634
+ (`models.viewBase(i)`) opens a base to look at: a tab of its own copy, named "… (view)", that can be laid out and
1635
+ moved about but is never kept (`part.keepOff`).
1627
1636
 
1628
1637
  **Kept in this browser, and backed up.** With `keep: true` in its spec, a `ModelsPart` keeps every model it holds
1629
1638
  in this browser, so work outlives the page without saving a file: a moment after each edit (`ModelsPart.KEEP_DELAY`)
@@ -1640,6 +1649,38 @@ as a history, and under its plain name too (`keepUnstamped`); where a folder can
1640
1649
  permission refused) they download instead. `backups.log()` records each (newest first, `LOG_MAX`), and
1641
1650
  `backups.daysSinceLast()` how long ago the last was. `examples/archimate.html` keeps its models.
1642
1651
 
1652
+ **A host's live status, and its menu items.** A host subclass of `ArchimateCanvasPart` says what it knows of an
1653
+ element that the model does not: `elementStatus(element)` → `{ text, color }` (or null) is drawn as a pill inside
1654
+ the bottom right of each of the element's boxes, asked afresh whenever the view is drawn and never written to the
1655
+ model; `elementMenuExtras(element)` → menu items end a box's right-click menu and the element's menu in the model
1656
+ tree. The canvas editor takes them as options (`ArchimateCanvasEditor`'s `elementStatus`, `elementMenuExtras`;
1657
+ the part hands them over in `editorHooks()`); a box's status is its template's `status` (`statusPill(ctx)`).
1658
+
1659
+ ```js
1660
+ class LiveArchitecture extends ArchimateCanvasPart {
1661
+ elementStatus(el) { const s = states.get(el.id); return s ? { text: s, color: s === 'deployed' ? '#2e7d32' : '#f9a825' } : null; }
1662
+ elementMenuExtras(el) { return transitions(el.id).map(t => ({ label: t, action: () => fire(el.id, t) })); }
1663
+ }
1664
+ ```
1665
+
1666
+ **Kept where the host says.** A host keeps the models itself by giving a library among the workspace's services
1667
+ (`ws.services = { modelLibrary }`, reaching the part as `ctx.modelLibrary`): anything with `ModelLibrary`'s methods
1668
+ (`keep`, `load`, `list`, `openOnes`, `setOpen`, `remove`) and a `label`, which the File menu and the tab menu use in
1669
+ place of **This browser** (`models.keptWhere`). An entry is the model in its editor's file format, so the host
1670
+ stores it as it likes — a database row, a file, a graph read from it. The backup folder's handle stays in the
1671
+ browser's store.
1672
+
1673
+ ```js
1674
+ ws.services = { modelLibrary: new ModelLibrary(myStore, { label: 'The engine' }) }; // myStore: a KeyValueStore
1675
+ ```
1676
+
1677
+ **Proposals.** A model offered as a change to the one in front — by an agent, a colleague, an import — waits as a
1678
+ proposal: `models.propose({ title, rationale, source, spec })`, `spec` a model as the editor takes it (as a base is
1679
+ given). Tools ▸ **Proposals (n)** lists them (`models.proposalItems(part)`); **Review and merge…**
1680
+ (`models.reviewProposal(part, p)`) is the merge dialog with who proposed it and why above the differences
1681
+ (`ModelMergeDialog`'s `note`), so what is taken is chosen and is one undo step; taking it, or **Dismiss**
1682
+ (`models.dismissProposal(p)`), removes it from the list.
1683
+
1643
1684
  ```js
1644
1685
  ws.addParts([{ id: 'models', type: 'models', editorType: 'archimate-canvas', newModel: { analysis: true },
1645
1686
  models: [{ id: 'modeller', title: 'Sample model', elements: [...], views: [...] }] }]);
@@ -1738,7 +1779,8 @@ editor.colourByCommand('property:Lifecycle', { invest: '#98fb98', maintain: '#ff
1738
1779
  **Layouts, in every editor.** The toolbar's **Arrange** menu has **Layout** (**Layout the view** with fewer than two
1739
1780
  shapes selected): **Grid**, **Circle**, **Tree, top down**, **Tree, left to right** (along the lines between the
1740
1781
  shapes: a parent centred over its children; a shape no line reaches is a root), **Force-directed** (lines pull,
1741
- shapes push apart; the same input gives the same result) and **By kind, a row each** (the shape class in the
1782
+ shapes push apart, within a frame round where they were, so one no line reaches stays near the rest; the same
1783
+ input gives the same result) and **By kind, a row each** (the shape class in the
1742
1784
  template editor, the layer on the ArchiMate canvas). The shapes keep the top-left corner of the area they took;
1743
1785
  what a container holds goes with it; one undo step; a locked shape stops it. On the ArchiMate canvas, an element
1744
1786
  with parts on the view (the targets of its composition and aggregation relationships) has **Nest its parts inside**
@@ -2520,6 +2562,79 @@ class. Library code imports its collaborators directly rather than looking them
2520
2562
  `undo()` rather than checking `instanceof Command`. A command built from either bundle therefore
2521
2563
  works in either bundle's history.
2522
2564
 
2565
+ ### Models edited as a whole: `EditableModel` and `ModelHosted`
2566
+
2567
+ A model edited as a whole rather than view by view extends `EditableModel` (`DataModel`, `RequirementsModel`): a
2568
+ `name`; changed since saved (`edited()`, `markSaved()`, `isDirty()`, `onEdit(fn)`); an undo history
2569
+ (`model.history`), every edit one step on it — `model.editCommand(label, change)` keeps the model before and after
2570
+ as its `toJSON()` and puts it back with `restore` (the fields the class lists in `static STATE`); and
2571
+ `compareWith(other)` over its `comparedKinds()`, which merges the other's into it. A list of things with ids is
2572
+ compared and merged by a `ListCompared` subclass (`list(model)`, `content(item)`, `requires(item)`).
2573
+
2574
+ Its part mixes in `ModelHosted` (`class DataModelPart extends ModelHosted(ChartPart)`): the file (`part.file`, a
2575
+ `ModelFile`), `keepId`/`keepEntry()` for a `ModelsPart`, the File menu (Open…, Save, Save As…, **Import ▸** each
2576
+ format that only reads — another tool's export, `format.exports === false`, opened as a new model — then New,
2577
+ Duplicate, Close model, where models are kept), `undoItems()`, `runModelEdit(command)` (then `afterModelEdit()`,
2578
+ the part's redraw) and `readInto({ accept, command, … })` (a file read into the model as one undo step).
2579
+
2580
+ **The bench.** `Benchmark.of(model)` is what a model is measured by — `[{ key, label, value, better: 'lower' |
2581
+ 'higher', format }]`: every model's check warnings and findings (`Benchmark.findings`), and a model's own
2582
+ `benchmark()` beyond them (a data model's Scorecard total, a requirements model's share of stories with
2583
+ acceptance criteria and of stories traced). `Benchmark.compare(before, after)` says of each whether it got better,
2584
+ worse or stayed, `Benchmark.text(rows)` in a line. A `ModelsPart` proposal's review says how the proposal, taken
2585
+ whole, would move them (`modelsPart.proposalBench(part, proposal)`), so an agent's suggestion is judged by the
2586
+ model's own checks. `modelsPart.propose({ title, rationale, source, spec | entry, onSettle })` takes the model as
2587
+ a spec or as an `entry` in one of the editor's file formats (`{ format, data }`, as a kept model is — what a
2588
+ server hands back); `onSettle(outcome, { bench })` hears whether it was `'taken'` (with how what was taken moved
2589
+ the bench) or `'dismissed'`, once, so whoever proposed it can record the outcome. A host adds its own tools for the model in front
2590
+ — an agent to run on it — as `modelTools(part, models)` among the workspace's services (`workspace.services`);
2591
+ their items end the Tools menu.
2592
+
2593
+ ### Requirements: epics, stories and the story map
2594
+
2595
+ `RequirementsModel` holds what a system is to do as an agile team writes it: **epics**, the **stories** that deliver
2596
+ them ("As a … I want … so that …": `as`, `want`, `soThat`), each with **acceptance criteria**, sliced into
2597
+ **releases**, and **traces** from a story to what realises it in the other models — `{ kind, ref, name?, model? }`,
2598
+ `kind` one of `RequirementsModel.TRACE_KINDS` (an ArchiMate requirement or work package, an element, a data
2599
+ concept, entity or table) or any other. `storyMap()` gives the map: `columns` the epics (then *No epic*), `rows`
2600
+ the releases (then *Not planned*), `cell(epicId, releaseId)` the stories there. `tracingTo(ref, kind?)` says what
2601
+ a change to something touches; `coverage()` how complete it is (stories with criteria, traced; each a share);
2602
+ `checker()` (`RequirementsChecker`) what is missing — no criteria, no narrative, traced to nothing (or, with
2603
+ `resolve(trace)`, to something not there), no epic, an empty epic, two stories of one name. Every edit is a
2604
+ command (`saveStoryCommand`, `moveStoryCommand`, `traceCommand`, `bringCommand`, …), merged and compared as the
2605
+ other models are (a story brings its epic and release). Saved as `.requirements.json` (`RequirementsFileFormat`).
2606
+
2607
+ Read from what teams already have — each an import-only format, and `format.spec(text)` for bringing into an
2608
+ open model:
2609
+
2610
+ - **A tracker's export** (`TrackerExportFileFormat`, `TrackerExport`): Jira, Azure DevOps or GitLab, CSV or their
2611
+ API's JSON, told apart by its columns. Epics by issue type (GitLab's from its issues' epic), stories under them,
2612
+ status, priority, points, labels and release (fix version, iteration, milestone); criteria from their own field,
2613
+ else from the description (an "Acceptance criteria" list, or Given/When/Then); the sentence from the description
2614
+ or title. Ids are the tracker's keys, so a newer export brought in replaces what it had. Where each tracker keeps
2615
+ each field is data (`TRACKERS`); a team's own column is given with `new TrackerExport({ columns: { criteria:
2616
+ ['Done when'] } })` (in Jira's JSON, a custom field's key).
2617
+ - **A storyboard** (`StoryboardFileFormat`, `Storyboard`): Markdown — a `#` title names it, `##` are epics (or any
2618
+ heading saying *Epic*), `###` stories (or *Story*); "As a … I want …" list items under an epic are stories; a
2619
+ picture is kept by its caption.
2620
+
2621
+ ```javascript
2622
+ const { RequirementsModel, TrackerExport } = window.RenderEngine;
2623
+ const model = new RequirementsModel(new TrackerExport().read(jiraCsvText));
2624
+ model.storyMap().columns.map(c => c.name); // the epics, across
2625
+ model.coverage().criteriaShare; // how many stories say when they are done
2626
+ ```
2627
+
2628
+ `RequirementsPart` (type `requirements`, in a `ModelsPart` with `editorType: 'requirements'`) shows the model as
2629
+ the **story map** (a card per story — its criteria, traces, points, status — dragged to another epic or release,
2630
+ or before another card; + adds one in a cell) or the **backlog** (a row per story); a card opened edits the story,
2631
+ right-clicked traces it (Trace to…) or removes it. Model ▸ New story / epic / release, **Bring in stories** (from a
2632
+ tracker's export or a storyboard, one undo step), **Check model…**; the toolbar says how complete it is. A host
2633
+ extends it by subclass: `traceTargets()` (what can be traced to, suggested in the dialog), `resolveTrace(trace)`
2634
+ (for the check), `storyStatus(story)` (a live state on the card), and `searchByMeaning(query)` — a promise of
2635
+ `[{ id, score }]` — which adds Model ▸ **Find by meaning…** (results listed in the `ModelCheckDialog`, which takes a
2636
+ `title` and `empty` text for any list of things to open).
2637
+
2523
2638
  ### Toolbars and dialogs
2524
2639
 
2525
2640
  `MenuBar` renders a row of toolbar items and has no controls of its own. Each editor contributes its