flowyaml 0.1.0.0__tar.gz
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.
- flowyaml-0.1.0.0/CHANGELOG.md +20 -0
- flowyaml-0.1.0.0/LICENSE +21 -0
- flowyaml-0.1.0.0/MANIFEST.in +8 -0
- flowyaml-0.1.0.0/NOTICE.md +19 -0
- flowyaml-0.1.0.0/PKG-INFO +453 -0
- flowyaml-0.1.0.0/README.md +416 -0
- flowyaml-0.1.0.0/RELEASING.md +95 -0
- flowyaml-0.1.0.0/docs/flowyaml_v0_spec.md +209 -0
- flowyaml-0.1.0.0/docs/future_fit_ledger.md +28 -0
- flowyaml-0.1.0.0/docs/import_reference.md +213 -0
- flowyaml-0.1.0.0/docs/live_updates.md +186 -0
- flowyaml-0.1.0.0/docs/notebook_and_agent_usage.md +98 -0
- flowyaml-0.1.0.0/examples/distribution.yaml +293 -0
- flowyaml-0.1.0.0/examples/minimal.yaml +21 -0
- flowyaml-0.1.0.0/examples/onboarding.mmd +16 -0
- flowyaml-0.1.0.0/examples/order_handling.bpmn +97 -0
- flowyaml-0.1.0.0/pyproject.toml +61 -0
- flowyaml-0.1.0.0/setup.cfg +4 -0
- flowyaml-0.1.0.0/src/flowyaml/__init__.py +120 -0
- flowyaml-0.1.0.0/src/flowyaml/__main__.py +10 -0
- flowyaml-0.1.0.0/src/flowyaml/_version.py +3 -0
- flowyaml-0.1.0.0/src/flowyaml/api.py +229 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/__init__.py +5 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/elk.LICENSE.md +264 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/elk.VERSION.txt +4 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/elk.bundled.js +6696 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/live.js +173 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/runtime.js +1405 -0
- flowyaml-0.1.0.0/src/flowyaml/assets/styles.css +569 -0
- flowyaml-0.1.0.0/src/flowyaml/cli.py +400 -0
- flowyaml-0.1.0.0/src/flowyaml/errors.py +146 -0
- flowyaml-0.1.0.0/src/flowyaml/importers/__init__.py +13 -0
- flowyaml-0.1.0.0/src/flowyaml/importers/_common.py +204 -0
- flowyaml-0.1.0.0/src/flowyaml/importers/bpmn.py +605 -0
- flowyaml-0.1.0.0/src/flowyaml/importers/mermaid.py +732 -0
- flowyaml-0.1.0.0/src/flowyaml/live.py +349 -0
- flowyaml-0.1.0.0/src/flowyaml/loader.py +167 -0
- flowyaml-0.1.0.0/src/flowyaml/model.py +203 -0
- flowyaml-0.1.0.0/src/flowyaml/py.typed +1 -0
- flowyaml-0.1.0.0/src/flowyaml/renderer.py +443 -0
- flowyaml-0.1.0.0/src/flowyaml/serialize.py +102 -0
- flowyaml-0.1.0.0/src/flowyaml/themes.py +105 -0
- flowyaml-0.1.0.0/src/flowyaml/validation.py +546 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/PKG-INFO +453 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/SOURCES.txt +70 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/dependency_links.txt +1 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/entry_points.txt +2 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/requires.txt +11 -0
- flowyaml-0.1.0.0/src/flowyaml.egg-info/top_level.txt +1 -0
- flowyaml-0.1.0.0/tests/conftest.py +70 -0
- flowyaml-0.1.0.0/tests/fixtures/accented_labels.yaml +28 -0
- flowyaml-0.1.0.0/tests/fixtures/connector_junction.yaml +43 -0
- flowyaml-0.1.0.0/tests/fixtures/hostile_labels.yaml +29 -0
- flowyaml-0.1.0.0/tests/fixtures/import/kitchen_sink.mmd +26 -0
- flowyaml-0.1.0.0/tests/fixtures/import/order.bpmn +74 -0
- flowyaml-0.1.0.0/tests/fixtures/import/prefixes.xml +10 -0
- flowyaml-0.1.0.0/tests/fixtures/import/two_pools.bpmn +28 -0
- flowyaml-0.1.0.0/tests/fixtures/merge_gateway.yaml +26 -0
- flowyaml-0.1.0.0/tests/fixtures/no_edges.yaml +11 -0
- flowyaml-0.1.0.0/tests/test_browser.py +533 -0
- flowyaml-0.1.0.0/tests/test_cli.py +234 -0
- flowyaml-0.1.0.0/tests/test_fragment.py +87 -0
- flowyaml-0.1.0.0/tests/test_import_bpmn.py +387 -0
- flowyaml-0.1.0.0/tests/test_import_mermaid.py +431 -0
- flowyaml-0.1.0.0/tests/test_live.py +404 -0
- flowyaml-0.1.0.0/tests/test_live_browser.py +135 -0
- flowyaml-0.1.0.0/tests/test_loader.py +68 -0
- flowyaml-0.1.0.0/tests/test_model.py +191 -0
- flowyaml-0.1.0.0/tests/test_offline_assets.py +165 -0
- flowyaml-0.1.0.0/tests/test_render_document.py +186 -0
- flowyaml-0.1.0.0/tests/test_serialize.py +65 -0
- flowyaml-0.1.0.0/tests/test_validation.py +296 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable FlowYAML changes are recorded here.
|
|
4
|
+
|
|
5
|
+
## 0.1.0.0 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
First public-package candidate and second implementation build.
|
|
8
|
+
|
|
9
|
+
- Render YAML-defined process flows as standalone, self-sufficient HTML.
|
|
10
|
+
- Render instance-scoped fragments for host applications.
|
|
11
|
+
- Keep an HTTP-served page linked to an actively edited YAML source.
|
|
12
|
+
- Import supported Mermaid flowcharts and BPMN 2.0 XML into the FlowYAML model.
|
|
13
|
+
- Navigate embedded subprocess levels without network access.
|
|
14
|
+
- Provide a Python API and `flowyaml` command-line interface.
|
|
15
|
+
- Bundle `elkjs` 0.9.3 with its EPL-2.0 notice for offline layout.
|
|
16
|
+
- Cover rendering, validation, serialization, imports, live updates, browser
|
|
17
|
+
behavior, accessibility, and offline operation with automated tests.
|
|
18
|
+
|
|
19
|
+
The internal `0.0.0.0` build used YAML-linked delivery by default and did not
|
|
20
|
+
yet include standalone HTML output.
|
flowyaml-0.1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dornelles Multitech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
FlowYAML redistributes the following component as package data.
|
|
4
|
+
|
|
5
|
+
## elkjs 0.9.3
|
|
6
|
+
|
|
7
|
+
- File: `src/flowyaml/assets/elk.bundled.js` (UMD browser bundle)
|
|
8
|
+
- Project: https://github.com/kieler/elkjs
|
|
9
|
+
- Licence: Eclipse Public License 2.0
|
|
10
|
+
- Full licence text: `src/flowyaml/assets/elk.LICENSE.md`
|
|
11
|
+
- Version record: `src/flowyaml/assets/elk.VERSION.txt`
|
|
12
|
+
|
|
13
|
+
The bundle is embedded verbatim and unmodified. FlowYAML wraps it in an
|
|
14
|
+
`if (typeof window.ELK === "undefined") { ... }` guard at render time so that
|
|
15
|
+
mounting several fragments on one page evaluates it once. The wrapper adds no
|
|
16
|
+
code to the bundle itself.
|
|
17
|
+
|
|
18
|
+
The bundle runs its layout in process. FlowYAML never passes `workerUrl`, so no
|
|
19
|
+
web worker is constructed and no script is fetched.
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flowyaml
|
|
3
|
+
Version: 0.1.0.0
|
|
4
|
+
Summary: Callable Python renderer that turns YAML-defined process flows into offline, self-sufficient HTML.
|
|
5
|
+
Author: Dornelles Multitech
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/EngDornelles/flowyaml
|
|
8
|
+
Project-URL: Repository, https://github.com/EngDornelles/flowyaml
|
|
9
|
+
Project-URL: Issues, https://github.com/EngDornelles/flowyaml/issues
|
|
10
|
+
Keywords: yaml,flowchart,diagram,elk,svg,renderer
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
License-File: NOTICE.md
|
|
27
|
+
License-File: src/flowyaml/assets/elk.LICENSE.md
|
|
28
|
+
Requires-Dist: PyYAML>=6.0
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest>=7.4; extra == "test"
|
|
31
|
+
Provides-Extra: browser
|
|
32
|
+
Requires-Dist: playwright>=1.40; extra == "browser"
|
|
33
|
+
Provides-Extra: release
|
|
34
|
+
Requires-Dist: build<2,>=1.2; extra == "release"
|
|
35
|
+
Requires-Dist: twine<8,>=6; extra == "release"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# FlowYAML
|
|
39
|
+
|
|
40
|
+
A callable Python renderer for YAML-defined process flows.
|
|
41
|
+
|
|
42
|
+
Given valid FlowYAML YAML, the package produces either a complete
|
|
43
|
+
self-sufficient single-page HTML document, or a self-contained embeddable
|
|
44
|
+
HTML/CSS/JS fragment for a host DOM application. The generated artifact renders
|
|
45
|
+
in a browser with **no Python process, no server, no npm build and no network
|
|
46
|
+
access**.
|
|
47
|
+
|
|
48
|
+
It also reads two foreign notations, Mermaid flowcharts and BPMN 2.0 XML, into
|
|
49
|
+
the same validated model.
|
|
50
|
+
|
|
51
|
+
That artifact is a snapshot of the YAML as it stood when it was written. While
|
|
52
|
+
the YAML is still being edited, `flowyaml serve` keeps a page attached to the
|
|
53
|
+
file instead, so a save shows up in the open browser — the arrangement the
|
|
54
|
+
delivered ERP DINFRA flowchart uses. See
|
|
55
|
+
[Keeping the HTML linked to the YAML](docs/live_updates.md).
|
|
56
|
+
|
|
57
|
+
v0 is a renderer, not an editor.
|
|
58
|
+
|
|
59
|
+
The first public-package candidate is `0.1.0.0`. The earlier internal
|
|
60
|
+
`0.0.0.0` build was YAML-linked by default and did not yet provide the
|
|
61
|
+
standalone, self-sufficient HTML delivery mode.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install -e .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The only runtime dependency is PyYAML. The browser layout engine is vendored as
|
|
70
|
+
package data, so nothing is fetched at build time or at view time.
|
|
71
|
+
|
|
72
|
+
## Quick start
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from flowyaml import render, render_file, validate, write_html
|
|
76
|
+
|
|
77
|
+
html = render(
|
|
78
|
+
yaml_text,
|
|
79
|
+
model_id="distribution",
|
|
80
|
+
output="document",
|
|
81
|
+
assets="inline",
|
|
82
|
+
theme="dornelles_multitech",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
write_html(yaml_text, "distribution.html")
|
|
86
|
+
errors = validate(yaml_text)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Import an existing diagram instead of writing YAML by hand:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import flowyaml as fyml
|
|
93
|
+
|
|
94
|
+
graph = fyml.read_mermaid("workflow.mmd")
|
|
95
|
+
other = fyml.read_bpmn("process.bpmn")
|
|
96
|
+
|
|
97
|
+
fyml.write_html(graph, "workflow.html")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
flowyaml render examples/distribution.yaml -o distribution.html
|
|
102
|
+
flowyaml render examples/distribution.yaml --output fragment > panel.html
|
|
103
|
+
flowyaml serve examples/distribution.yaml
|
|
104
|
+
flowyaml data examples/distribution.yaml -o distribution.data.json
|
|
105
|
+
flowyaml validate examples/distribution.yaml
|
|
106
|
+
flowyaml models examples/distribution.yaml
|
|
107
|
+
flowyaml import examples/onboarding.mmd -o onboarding.yaml
|
|
108
|
+
flowyaml import examples/order_handling.bpmn -o order.yaml
|
|
109
|
+
flowyaml themes
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Open the written file directly from disk. Nothing else is needed.
|
|
113
|
+
|
|
114
|
+
While you are still editing the YAML, serve it instead:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
flowyaml serve examples/distribution.yaml --open
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Edit the file, save, and the open page follows it. Nothing is rebuilt.
|
|
121
|
+
|
|
122
|
+
For notebook-kernel installation and deterministic LLM/agent invocation, see
|
|
123
|
+
[Notebook and agent usage](docs/notebook_and_agent_usage.md).
|
|
124
|
+
|
|
125
|
+
## Public API
|
|
126
|
+
|
|
127
|
+
| Callable | Purpose |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `render(source, *, model_id=None, output="document", assets="inline", theme="dornelles_multitech", instance_id=None) -> str` | Render YAML text or a path to HTML. |
|
|
130
|
+
| `render_file(path, **options) -> str` | Render the YAML file at `path`. |
|
|
131
|
+
| `write_html(source_or_path, destination, **options) -> Path` | Render and write; returns the written path. |
|
|
132
|
+
| `validate(source) -> tuple[ValidationIssue, ...]` | Structured issues; empty means renderable. |
|
|
133
|
+
| `load(source) -> Diagram` | The immutable normalized model. |
|
|
134
|
+
| `models(source) -> tuple[str, ...]` | Every `meta.id`, in document order. |
|
|
135
|
+
| `read_mermaid(source, *, model_id=None, name=None) -> Diagram` | Import a Mermaid flowchart. |
|
|
136
|
+
| `read_bpmn(source, *, model_id=None, name=None) -> Diagram` | Import a BPMN 2.0 XML process. |
|
|
137
|
+
| `to_yaml(source) -> str` | Canonical FlowYAML YAML for any source or diagram. |
|
|
138
|
+
| `payload(source, *, model_id=None, revision=None) -> dict` | The JSON body a host serves to a `data="url"` page. |
|
|
139
|
+
| `payload_json(source, ...) -> str` | The same body, serialized. |
|
|
140
|
+
| `serve(source, *, host, port, model_id, theme, poll_ms, open_browser) -> None` | Local host that re-reads the YAML per request. |
|
|
141
|
+
|
|
142
|
+
Everywhere the table says `source`, the argument may be YAML text, a path to a
|
|
143
|
+
YAML file, or a `Diagram` — which is what the two importers return, so an
|
|
144
|
+
imported graph goes straight to `render` and `write_html` with no YAML round
|
|
145
|
+
trip.
|
|
146
|
+
|
|
147
|
+
Rendering raises `FlowYAMLValidationError` when any issue exists,
|
|
148
|
+
`FlowYAMLModelError` for an unknown `model_id`, and `FlowYAMLOptionError` for an
|
|
149
|
+
unsupported option value. An importer raises `FlowYAMLImportError`, which
|
|
150
|
+
carries the format, the source name and the offending line where the position
|
|
151
|
+
is recoverable.
|
|
152
|
+
|
|
153
|
+
When `model_id` is omitted the first YAML document opens first. **Every** model
|
|
154
|
+
stays embedded either way, which is what makes subprocess navigation work
|
|
155
|
+
without a server.
|
|
156
|
+
|
|
157
|
+
### Options
|
|
158
|
+
|
|
159
|
+
- `output="document"` emits `<!doctype html>`, metadata, style, data, runtime
|
|
160
|
+
and the diagram mount point.
|
|
161
|
+
- `output="fragment"` emits an instance-scoped mount point plus scoped CSS and
|
|
162
|
+
JS. It assumes no global id, no document-level body style and no server route,
|
|
163
|
+
and it can be mounted more than once on one page.
|
|
164
|
+
- `assets="inline"` is the only implemented v0 asset mode. The boundary stays
|
|
165
|
+
explicit so a future `assets="url"` mode can reference the same vendored asset
|
|
166
|
+
from an approved URL.
|
|
167
|
+
- `data="inline"` embeds the graph, which is what makes the artifact offline
|
|
168
|
+
and permanent. `data="url"` leaves the graph out and has the page fetch it
|
|
169
|
+
from `data_url`, so an edited YAML file reaches a page that is already open.
|
|
170
|
+
`revision_url` and `poll_ms` tune how the page notices a change; `poll_ms=0`
|
|
171
|
+
means it notices on reload only. A `data="url"` page needs an http host:
|
|
172
|
+
browsers refuse the fetch when the page itself was opened over `file://`,
|
|
173
|
+
and the page says so rather than sitting empty. See
|
|
174
|
+
[docs/live_updates.md](docs/live_updates.md).
|
|
175
|
+
- `instance_id` fixes the DOM prefix. Omit it and every render gets a fresh
|
|
176
|
+
random prefix, which is what keeps two fragments from colliding. Pass it when
|
|
177
|
+
you want byte-reproducible output.
|
|
178
|
+
|
|
179
|
+
## YAML contract
|
|
180
|
+
|
|
181
|
+
A source is UTF-8 YAML with one or more documents separated by `---`. One
|
|
182
|
+
document is one flow level.
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
meta:
|
|
186
|
+
id: procurement
|
|
187
|
+
name: Procurement decision flow
|
|
188
|
+
version: "1.0.0"
|
|
189
|
+
date: "2026-08-30"
|
|
190
|
+
|
|
191
|
+
nodes:
|
|
192
|
+
- id: start
|
|
193
|
+
type: startEvent
|
|
194
|
+
label: Request received
|
|
195
|
+
- id: review
|
|
196
|
+
type: task
|
|
197
|
+
label: Review documentation
|
|
198
|
+
- id: decision
|
|
199
|
+
type: gateway
|
|
200
|
+
label: Documentation complete?
|
|
201
|
+
- id: archive
|
|
202
|
+
type: subprocess
|
|
203
|
+
label: Archive and publish
|
|
204
|
+
ref: archive_flow
|
|
205
|
+
- id: end
|
|
206
|
+
type: endEvent
|
|
207
|
+
label: Process closed
|
|
208
|
+
|
|
209
|
+
edges:
|
|
210
|
+
- from: start
|
|
211
|
+
to: review
|
|
212
|
+
- from: review
|
|
213
|
+
to: decision
|
|
214
|
+
- from: decision
|
|
215
|
+
to: archive
|
|
216
|
+
tag: "Yes"
|
|
217
|
+
label: Documentation is complete and can be archived.
|
|
218
|
+
- from: decision
|
|
219
|
+
to: end
|
|
220
|
+
tag: "No"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Node types: `startEvent`, `endEvent`, `intermediateEvent`, `gateway`,
|
|
224
|
+
`subprocess`, `state`, `task`.
|
|
225
|
+
|
|
226
|
+
Optional node key `detail` (alias `description`) becomes a hover and focus
|
|
227
|
+
tooltip. Optional edge keys are `tag` (the compact chip), `label` (the full
|
|
228
|
+
text, shown as a tooltip when it differs from the chip), `kind`
|
|
229
|
+
(`sequence` or `association`) and `style` (`solid` or `dotted`).
|
|
230
|
+
|
|
231
|
+
`actors` and other non-rendering metadata are accepted and preserved on the
|
|
232
|
+
model for forward compatibility. v0 does not render swimlanes or actor columns.
|
|
233
|
+
|
|
234
|
+
> Bare `Yes`, `No`, `On` and `Off` are YAML booleans, not strings. Quote them.
|
|
235
|
+
> FlowYAML rejects a boolean tag with a message that says so.
|
|
236
|
+
|
|
237
|
+
### Validation rules
|
|
238
|
+
|
|
239
|
+
- Each nonempty document needs `meta.id`, a nonempty `nodes` list, and an
|
|
240
|
+
`edges` list (which may be empty).
|
|
241
|
+
- Model ids and node ids are unique in their scopes.
|
|
242
|
+
- Every node needs a supported `type` and a string `label`. A label may be empty
|
|
243
|
+
only on the two connector shapes: a `gateway` used purely as a merge, and an
|
|
244
|
+
`intermediateEvent` used purely as a junction. Every other type carries
|
|
245
|
+
process meaning and needs text.
|
|
246
|
+
- Every edge has `from` and `to`, and both resolve inside the same model.
|
|
247
|
+
- Self-loops are invalid in v0.
|
|
248
|
+
- `subprocess.ref`, when present, must name an embedded model id. Only
|
|
249
|
+
`subprocess` nodes may carry `ref`. A subprocess without a `ref` renders as a
|
|
250
|
+
destination card, not a control.
|
|
251
|
+
- Only `kind: association` and `style: dotted` are accepted beyond the defaults.
|
|
252
|
+
Anything else is rejected until it is explicitly introduced.
|
|
253
|
+
|
|
254
|
+
Each issue carries a stable `code`, a `message`, the `model_id`, the
|
|
255
|
+
`document_index`, the offending `field`, and the YAML `line`/`column` where the
|
|
256
|
+
position is recoverable.
|
|
257
|
+
|
|
258
|
+
## Importing Mermaid and BPMN
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
import flowyaml as fyml
|
|
262
|
+
|
|
263
|
+
graph = fyml.read_mermaid("workflow.mmd") # text or a path
|
|
264
|
+
other = fyml.read_bpmn("process.bpmn") # text or a path
|
|
265
|
+
|
|
266
|
+
fyml.render(graph) # straight to HTML
|
|
267
|
+
fyml.to_yaml(other) # or to editable FlowYAML YAML
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
flowyaml import workflow.mmd -o workflow.yaml
|
|
272
|
+
flowyaml import process.bpmn --format bpmn > process.yaml
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Two rules hold for both importers.
|
|
276
|
+
|
|
277
|
+
- **The output is always valid.** An importer builds plain FlowYAML documents
|
|
278
|
+
and runs them through the same loader and validator as a hand written
|
|
279
|
+
source. There is no second code path.
|
|
280
|
+
- **No geometry, ever.** Nothing an importer writes describes a position, a
|
|
281
|
+
size or any other `ui` metadata. FlowYAML measures wrapped labels in the
|
|
282
|
+
browser and runs ELK there, so a stored coordinate would only fight the
|
|
283
|
+
layout engine. BPMN's whole `bpmndi:BPMNDiagram` section is read past and
|
|
284
|
+
discarded.
|
|
285
|
+
|
|
286
|
+
Identifiers survive when they are safe: a Mermaid `check_stock` and a BPMN
|
|
287
|
+
`Activity_0x9f2c` stay exactly themselves. Only characters that would need
|
|
288
|
+
escaping downstream are folded to `_`, and a collision gains a deterministic
|
|
289
|
+
`_2` suffix.
|
|
290
|
+
|
|
291
|
+
**Mermaid.** `graph` and `flowchart` declarations with any direction; the
|
|
292
|
+
twelve node shapes; solid, thick, dotted and invisible links; edge text in all
|
|
293
|
+
four spellings; chains and `&` groups; frontmatter `title`. Subgraphs flatten
|
|
294
|
+
and keep their membership in a non-rendering `group` key. Styling, class and
|
|
295
|
+
click statements are read and discarded, because FlowYAML owns its theme.
|
|
296
|
+
`A([...])` and `A((...))` become a start, intermediate or end event according
|
|
297
|
+
to node degree, since Mermaid spells the whole event family one way.
|
|
298
|
+
|
|
299
|
+
**BPMN 2.0.** Events, the eight task kinds, the five gateways, sub-processes,
|
|
300
|
+
call activities, data references, text annotations, sequence flows,
|
|
301
|
+
associations, data associations, boundary attachments and message flows.
|
|
302
|
+
Elements are matched by local name, so `bpmn:`, `bpmn2:`, `semantic:` and a
|
|
303
|
+
default namespace all read the same. An expanded `subProcess` becomes a model
|
|
304
|
+
of its own and the node that held it gets the `ref` that opens it, so BPMN
|
|
305
|
+
nesting arrives as FlowYAML's own in-document navigation. Lanes become
|
|
306
|
+
`actors` and a per-node `lane` key.
|
|
307
|
+
|
|
308
|
+
Anything outside those boundaries stops with a `FlowYAMLImportError` naming
|
|
309
|
+
the format, the source and the line: a `sequenceDiagram`, an unclosed shape, a
|
|
310
|
+
self-loop, a subgraph used as an edge endpoint, an unmapped BPMN flow element,
|
|
311
|
+
an unresolved sequence flow, a DTD. Nothing is silently dropped that would
|
|
312
|
+
change the graph.
|
|
313
|
+
|
|
314
|
+
`docs/import_reference.md` carries the full mapping tables and the exact
|
|
315
|
+
parsing boundaries. `examples/onboarding.mmd` and
|
|
316
|
+
`examples/order_handling.bpmn` exercise both surfaces.
|
|
317
|
+
|
|
318
|
+
## Keeping the page linked to the YAML
|
|
319
|
+
|
|
320
|
+
A rendered artifact is a snapshot. `flowyaml serve` is the other half: a
|
|
321
|
+
stdlib host that answers three fixed paths and re-reads the YAML file on every
|
|
322
|
+
data request, so editing and saving updates the page in place.
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
/ the page, rendered with data="url"
|
|
326
|
+
/flowyaml/data.json the graph, re-read and re-validated per request
|
|
327
|
+
/flowyaml/revision.json a content digest, so the page can poll cheaply
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
This is the mechanism the delivered ERP DINFRA flowchart uses, where a Django
|
|
331
|
+
view re-reads `dinfra_workflows.yaml` per request and the static page fetches
|
|
332
|
+
it. To put a linked diagram inside your own application, render the shell with
|
|
333
|
+
`data="url"` and serve `flowyaml.payload(...)` from your own route.
|
|
334
|
+
|
|
335
|
+
Two limits stated plainly: a browser refuses to fetch a sibling file from a
|
|
336
|
+
`file://` page, so a linked page opened from disk reports that instead of
|
|
337
|
+
pretending, and nothing in a browser watches a file, so the update is bounded
|
|
338
|
+
by the poll interval. `docs/live_updates.md` has the whole picture.
|
|
339
|
+
|
|
340
|
+
## Rendering and navigation
|
|
341
|
+
|
|
342
|
+
The engine normalizes every document into an immutable graph model, embeds that
|
|
343
|
+
model as escaped JSON, and hands it to the browser renderer.
|
|
344
|
+
|
|
345
|
+
- Layout is [elkjs](https://github.com/kieler/elkjs) with `elk.algorithm=layered`,
|
|
346
|
+
`elk.direction=RIGHT`, orthogonal routing and `NETWORK_SIMPLEX` placement.
|
|
347
|
+
Node sizes are measured in the browser from the wrapped label, so the geometry
|
|
348
|
+
follows the real font metrics and Python carries no JS layout dependency.
|
|
349
|
+
- Nodes, edges, arrow markers, labels and subprocess controls are SVG.
|
|
350
|
+
- Selecting a `subprocess` does not load a route. It switches the active
|
|
351
|
+
embedded model in the same document and writes the choice to the URL hash, so
|
|
352
|
+
browser Back and Forward walk the levels with no network access.
|
|
353
|
+
A standalone document uses the hash key `model`; a fragment uses its own
|
|
354
|
+
instance prefix as the key, so several instances never read each other's state.
|
|
355
|
+
- Pointer drag pans, wheel zoom is cursor-centred, two-finger pinch zooms on
|
|
356
|
+
touch, and scale is clamped to 0.2x-2.6x. The view fits the viewport on load
|
|
357
|
+
and refits on resize until the reader adjusts it.
|
|
358
|
+
|
|
359
|
+
### Keyboard and accessibility
|
|
360
|
+
|
|
361
|
+
| Key | Action |
|
|
362
|
+
| --- | --- |
|
|
363
|
+
| `Tab` | Move between subprocess controls and detailed labels |
|
|
364
|
+
| `Enter` / `Space` | Open the focused subprocess |
|
|
365
|
+
| `Backspace` | Go up one level |
|
|
366
|
+
| Arrow keys | Pan |
|
|
367
|
+
| `+` / `-` | Zoom |
|
|
368
|
+
| `0` | Fit |
|
|
369
|
+
| `Escape` | Dismiss the tooltip |
|
|
370
|
+
|
|
371
|
+
Subprocess controls are `role="button"` with a visible focus ring. A node or
|
|
372
|
+
chip that carries `detail` folds that text into its own accessible name, so a
|
|
373
|
+
screen reader never has to read it from the shared tooltip element. Meaning
|
|
374
|
+
never depends on colour alone: the seven node types differ by shape and stroke, a
|
|
375
|
+
navigable subprocess carries a BPMN collapsed-marker box, a chevron underline
|
|
376
|
+
and a pointer cursor, and a detailed edge chip carries a `≡` glyph.
|
|
377
|
+
|
|
378
|
+
Every label reaches the DOM through `textContent`, never `innerHTML`, and the
|
|
379
|
+
embedded payload escapes `<`, `>` and `&` as JSON unicode escapes so no label
|
|
380
|
+
can close the script element.
|
|
381
|
+
|
|
382
|
+
Every output path writes UTF-8: `--out`, `write_html`, and rendering to stdout
|
|
383
|
+
alike. The document declares `<meta charset="utf-8">`, so the console codepage
|
|
384
|
+
never decides how an accented label is encoded.
|
|
385
|
+
|
|
386
|
+
## Theme
|
|
387
|
+
|
|
388
|
+
`dornelles_multitech` is the v0 default and, in v0, the only registered theme.
|
|
389
|
+
It is a Work Utilities expression of the parent B+D baseline: canvas `#F7F5F1`,
|
|
390
|
+
surface `#FFFFFF`, ink `#151B24`, shell `#202732` / `#2A313C`, slate `#4B5563`,
|
|
391
|
+
muted `#8A94A3`, structure line `#D8D2C8`, amber `#B46D3A` / `#D99A57` reserved
|
|
392
|
+
for selected and evidence hierarchy, and semantic start `#2F7D4E`, warning
|
|
393
|
+
`#A86716`, danger `#B33A2E`, info `#346A8A`.
|
|
394
|
+
|
|
395
|
+
It keeps the parent system's compact geometry, technical system-sans typography,
|
|
396
|
+
visible focus state, low-radius surfaces (8 px maximum) and print legibility. It
|
|
397
|
+
deliberately does not inherit ERP DINFRA's institutional navy and gold identity.
|
|
398
|
+
|
|
399
|
+
Tokens are written as `--fy-*` custom properties scoped to the instance root, so
|
|
400
|
+
every rule in the stylesheet reads a variable and no colour is hard coded.
|
|
401
|
+
|
|
402
|
+
## Vendored assets
|
|
403
|
+
|
|
404
|
+
`src/flowyaml/assets/elk.bundled.js` is the elkjs 0.9.3 UMD browser bundle
|
|
405
|
+
(~1.6 MB), redistributed under the Eclipse Public License 2.0. See
|
|
406
|
+
`src/flowyaml/assets/elk.LICENSE.md` and `NOTICE.md`. It runs its layout in
|
|
407
|
+
process; no web worker is constructed and no script is fetched. Repeated
|
|
408
|
+
inclusion on one page is guarded by `if (typeof window.ELK === "undefined")`, so
|
|
409
|
+
the bundle evaluates once however many fragments are mounted.
|
|
410
|
+
|
|
411
|
+
## Tests
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
python -m pytest tests -q # everything
|
|
415
|
+
python -m pytest tests -q -m "not browser" # no browser needed
|
|
416
|
+
python -m pytest tests/test_browser.py -q # headless offline checks
|
|
417
|
+
python -m pytest tests/test_live.py -q # the YAML-linked update path
|
|
418
|
+
python -m pytest tests/test_live_browser.py -q # an open page follows an edit
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
The browser tests need `pip install playwright && playwright install chromium`
|
|
422
|
+
and skip cleanly when it is unavailable. They load the generated `file://`
|
|
423
|
+
document with an interception rule that aborts and records every request that
|
|
424
|
+
leaves the file scheme, so a passing run is itself the offline proof. The
|
|
425
|
+
non-browser suite makes the same claim statically, by asserting that the only
|
|
426
|
+
absolute URL FlowYAML writes is the SVG namespace.
|
|
427
|
+
|
|
428
|
+
Release preparation and the account-side Trusted Publishing gates are recorded
|
|
429
|
+
in [RELEASING.md](RELEASING.md).
|
|
430
|
+
|
|
431
|
+
## Not in v0
|
|
432
|
+
|
|
433
|
+
No graph editor, drag-to-reposition, notebook widget, database persistence,
|
|
434
|
+
collaboration, authentication, swimlane, Gantt, map, export button or remote
|
|
435
|
+
asset loading. See `docs/future_fit_ledger.md`.
|
|
436
|
+
|
|
437
|
+
`flowyaml serve` is a local authoring host with no authentication, three fixed
|
|
438
|
+
routes and no filesystem mapping; it is not a production server, and it does
|
|
439
|
+
not make v0 an editor. The YAML is edited in your editor.
|
|
440
|
+
|
|
441
|
+
Importing BPMN is not BPMN compliance. v0 renders seven node types, two edge
|
|
442
|
+
kinds and two edge styles; it has no pool boundary, no swimlane and no
|
|
443
|
+
execution semantics. The importer translates a BPMN file into that model and
|
|
444
|
+
refuses what it cannot, rather than widening the contract.
|
|
445
|
+
|
|
446
|
+
There is no export back to Mermaid or BPMN. `to_yaml` writes FlowYAML.
|
|
447
|
+
|
|
448
|
+
## License
|
|
449
|
+
|
|
450
|
+
FlowYAML is released under the [MIT License](LICENSE). The bundled, unmodified
|
|
451
|
+
`elkjs` layout engine remains under the Eclipse Public License 2.0; its license
|
|
452
|
+
and version records ship with every distribution and are summarized in
|
|
453
|
+
[NOTICE.md](NOTICE.md).
|