@zenwave360/architecture-manifest 0.0.0-stage → 1.0.0-next.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/LICENSE +21 -0
- package/README.md +474 -2
- package/kotlin/json-schema-ref-parser-kmp.js +2491 -0
- package/kotlin/json-schema-ref-parser-kmp.js.map +1 -0
- package/kotlin/kotlin-kotlin-stdlib.js +8349 -0
- package/kotlin/kotlin-kotlin-stdlib.js.map +1 -0
- package/kotlin/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.js +15 -0
- package/kotlin/kotlin_org_jetbrains_kotlin_kotlin_dom_api_compat.js.map +1 -0
- package/kotlin/kotlinx-atomicfu.js +174 -0
- package/kotlin/kotlinx-atomicfu.js.map +1 -0
- package/kotlin/kotlinx-coroutines-core.js +3562 -0
- package/kotlin/kotlinx-coroutines-core.js.map +1 -0
- package/kotlin/okio-parent-okio.js +2509 -0
- package/kotlin/okio-parent-okio.js.map +1 -0
- package/kotlin/snakeyaml-engine-kmp.js +5842 -0
- package/kotlin/snakeyaml-engine-kmp.js.map +1 -0
- package/kotlin/urlencoder-urlencoder-lib.js +322 -0
- package/kotlin/urlencoder-urlencoder-lib.js.map +1 -0
- package/kotlin/zenwave-architecture-architecture-manifest.js +22 -0
- package/kotlin/zenwave-architecture-architecture-manifest.js.map +1 -0
- package/package.json +19 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ZenWave's Authors and Contributors
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,3 +1,475 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ZenWave Architecture
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://search.maven.org/artifact/io.zenwave360.architecture/architecture-manifest)
|
|
4
|
+
[](https://github.com/ZenWave360/zenwave-architecture/actions/workflows/publish-maven-snapshots.yml)
|
|
5
|
+
[](https://github.com/ZenWave360/zenwave-architecture/actions/workflows/main.yml)
|
|
6
|
+
[](https://github.com/ZenWave360/zenwave-architecture/actions/workflows/main.yml)
|
|
7
|
+
[](https://github.com/ZenWave360/zenwave-architecture/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
ZenWave Architecture is the architecture contract shared by ZenWave tools. A hand-authored Architecture Manifest (`zenwave-architecture.yml`) describes domains, services, documentation, and artifacts; the Kotlin Multiplatform library resolves that content deterministically from workspace, Git, Apicurio Registry, generic Artifactory, or Maven sources. Its source-aware editor can update existing scalar values without reserializing the surrounding YAML.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
- Versioned schema: `https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json`
|
|
13
|
+
- Latest schema: `https://schemas.zenwave360.io/zenwave-architecture/latest/schema.json`
|
|
14
|
+
- Detailed authoring guide: [docs/source-resolution-guide.md](docs/source-resolution-guide.md)
|
|
15
|
+
- Normative source-resolution specification: [docs/source-resolution-specification.md](docs/source-resolution-specification.md)
|
|
16
|
+
- Workspace diagnostic codes: [docs/diagnostics.md](docs/diagnostics.md)
|
|
17
|
+
|
|
18
|
+
## Complete manifest
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
# schema-test: valid
|
|
22
|
+
# yaml-language-server: $schema=https://schemas.zenwave360.io/zenwave-architecture/1.0/schema.json
|
|
23
|
+
|
|
24
|
+
config:
|
|
25
|
+
title: Arcadia architecture
|
|
26
|
+
groupIdExpression: "io.arcadia.${service.id}"
|
|
27
|
+
|
|
28
|
+
contentResolution:
|
|
29
|
+
- workspace
|
|
30
|
+
- git
|
|
31
|
+
- apicurio
|
|
32
|
+
- artifactory
|
|
33
|
+
- maven
|
|
34
|
+
|
|
35
|
+
sources:
|
|
36
|
+
workspace:
|
|
37
|
+
basePathExpression: "../../${domain.id}/${subdomain.id}/${service.id}"
|
|
38
|
+
|
|
39
|
+
git:
|
|
40
|
+
provider: gitlab
|
|
41
|
+
server: https://gitlab.com
|
|
42
|
+
|
|
43
|
+
apicurio:
|
|
44
|
+
server: https://registry.example.com
|
|
45
|
+
|
|
46
|
+
artifactory:
|
|
47
|
+
server: https://artifacts.example.com
|
|
48
|
+
|
|
49
|
+
maven:
|
|
50
|
+
provider: artifactory
|
|
51
|
+
server: https://artifacts.example.com/artifactory
|
|
52
|
+
repository: maven-releases
|
|
53
|
+
|
|
54
|
+
domains:
|
|
55
|
+
commerce:
|
|
56
|
+
version: 1.0.0
|
|
57
|
+
|
|
58
|
+
services:
|
|
59
|
+
orders:
|
|
60
|
+
id: orders-api
|
|
61
|
+
docs:
|
|
62
|
+
summary: docs/SUMMARY.md
|
|
63
|
+
catalog: docs/EVENT_CATALOG.md
|
|
64
|
+
readme: docs/README.md
|
|
65
|
+
artifacts:
|
|
66
|
+
- type: openapi
|
|
67
|
+
path: contracts/orders.openapi.yaml
|
|
68
|
+
version: 1.1.0
|
|
69
|
+
|
|
70
|
+
subdomains:
|
|
71
|
+
fulfillment:
|
|
72
|
+
version: 2.0.0
|
|
73
|
+
services:
|
|
74
|
+
shipping:
|
|
75
|
+
artifacts:
|
|
76
|
+
- type: asyncapi
|
|
77
|
+
path: contracts/shipping.asyncapi.yaml
|
|
78
|
+
version: 2.0.0
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Domains, subdomains, and services use a non-blank explicit `id` when present and otherwise use their YAML map key. A direct service has an empty `subdomain.id`. Every artifact declares its own required `version`; domain, subdomain, and service versions are optional and apply only to service documents. The primary example deliberately omits artifact `name` and `artifactId`: names are never derived from paths, while artifact IDs use their configured fallback.
|
|
82
|
+
|
|
83
|
+
For a manifest stored at `/work/architecture/manifests/zenwave-architecture.yml`, the `../../` prefix reaches `/work`; the first artifact's workspace candidate is:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
/work/commerce/orders-api/contracts/orders.openapi.yaml
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The same artifact has these remote candidates:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
https://gitlab.com/commerce/orders-api/-/raw/v1.1.0/contracts/orders.openapi.yaml
|
|
93
|
+
https://registry.example.com/apis/registry/v3/groups/orders-api/artifacts/contracts%2Forders.openapi.yaml/versions/1.1.0/content
|
|
94
|
+
https://artifacts.example.com/artifactory/contracts/commerce/orders-api/1.1.0/contracts/orders.openapi.yaml
|
|
95
|
+
https://artifacts.example.com/artifactory/maven-releases/io/arcadia/orders-api/contracts%2Forders.openapi/1.1.0/contracts%2Forders.openapi-1.1.0.jar!/contracts/orders.openapi.yaml
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The empty direct-service subdomain segment is removed without damaging `https://`.
|
|
99
|
+
The Artifactory URL above comes from its preset `contentUrlExpression`; declare the field only when the repository key or layout differs from `artifactory/contracts/...`.
|
|
100
|
+
|
|
101
|
+
## Runtime values
|
|
102
|
+
|
|
103
|
+
Static `config.properties` substitution runs first. It cannot override canonical values or source-local `${server}`. Runtime interpolation is literal: no functions, conditionals, nested expressions, or fallback syntax are supported.
|
|
104
|
+
|
|
105
|
+
| Variable | Value |
|
|
106
|
+
| --- | --- |
|
|
107
|
+
| `${owner.id}` | Resolved ID of the service or domain that declares the artifact. |
|
|
108
|
+
| `${owner.repository}` | Explicit repository of the service or domain artifact owner; unresolved when omitted. |
|
|
109
|
+
| `${domain.id}` | Explicit domain ID, otherwise its map key. |
|
|
110
|
+
| `${subdomain.id}` | Explicit subdomain ID, otherwise its map key; empty for direct services. |
|
|
111
|
+
| `${service.id}` | Explicit service ID, otherwise its map key. |
|
|
112
|
+
| `${service.repository}` | Explicit service repository name; unresolved when omitted. |
|
|
113
|
+
| `${domain.version}` | Explicit domain version, when declared. |
|
|
114
|
+
| `${subdomain.version}` | Explicit subdomain version, when declared. |
|
|
115
|
+
| `${service.version}` | Explicit service version, when declared. |
|
|
116
|
+
| `${artifact.version}` | Declared artifact version; artifact `version` is required. |
|
|
117
|
+
| `${artifact.path}` | Complete declared artifact path. |
|
|
118
|
+
| `${artifact.pathWithoutExtension}` | Complete artifact path with only its final extension removed. |
|
|
119
|
+
| `${artifact.name}` | Explicit non-blank artifact name only; unresolved when omitted. |
|
|
120
|
+
| `${artifact.fileName}` | Final artifact path segment, with every extension. |
|
|
121
|
+
| `${artifact.fileNameWithoutExtension}` | Filename with only its final extension removed. |
|
|
122
|
+
| `${content.path}` | Current artifact path or selected document path. |
|
|
123
|
+
| `${service.docs[key]}` | Declared document path for the literal key. |
|
|
124
|
+
| `${groupId}` | Common resolved group coordinate. |
|
|
125
|
+
| `${artifactId}` | Common resolved artifact coordinate. |
|
|
126
|
+
| `${version}` | Effective content version: `artifact.version` for an artifact, the inherited service, subdomain, or domain version for a service document. |
|
|
127
|
+
|
|
128
|
+
Services and domains may own artifacts. Owner identity and source-control location are deliberately separate; repository identity is never inferred from `owner.id`:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
config:
|
|
132
|
+
sources:
|
|
133
|
+
workspace:
|
|
134
|
+
basePathExpression: "../${service.repository}"
|
|
135
|
+
git:
|
|
136
|
+
provider: github
|
|
137
|
+
server: https://github.com
|
|
138
|
+
contentUrlExpression: "${server}/arcadia-editions/${service.repository}/raw/main/${content.path}"
|
|
139
|
+
maven:
|
|
140
|
+
provider: github
|
|
141
|
+
server: https://maven.pkg.github.com
|
|
142
|
+
repository: "arcadia-editions/${service.repository}"
|
|
143
|
+
domains:
|
|
144
|
+
catalog:
|
|
145
|
+
services:
|
|
146
|
+
products:
|
|
147
|
+
id: catalog.catalog-management.catalog-products
|
|
148
|
+
repository: catalog-products-api
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`maven.repository` accepts the same runtime expressions and is resolved once per artifact, so services publishing to their own GitHub Packages repositories share a single Maven source declaration.
|
|
152
|
+
|
|
153
|
+
If `repository` is omitted, `${service.repository}` stays unresolved and any selected expression that uses it fails with the standard unresolved-runtime-variable diagnostic.
|
|
154
|
+
|
|
155
|
+
Filename behavior is exact:
|
|
156
|
+
|
|
157
|
+
| Path | `artifact.pathWithoutExtension` | `artifact.fileName` | `artifact.fileNameWithoutExtension` |
|
|
158
|
+
| --- | --- | --- | --- |
|
|
159
|
+
| `contracts/orders.openapi.yaml` | `contracts/orders.openapi` | `orders.openapi.yaml` | `orders.openapi` |
|
|
160
|
+
| `archive.tar.gz` | `archive.tar` | `archive.tar.gz` | `archive.tar` |
|
|
161
|
+
| `domain-model.zdl` | `domain-model` | `domain-model.zdl` | `domain-model` |
|
|
162
|
+
| `.gitignore` | `.gitignore` | `.gitignore` | `.gitignore` |
|
|
163
|
+
| `README` | `README` | `README` | `README` |
|
|
164
|
+
|
|
165
|
+
Documents are selected from the service map. Keys may contain letters, numbers, `.`, `_`, and `-`:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
${service.docs[summary]} = docs/SUMMARY.md
|
|
169
|
+
${service.docs[catalog]} = docs/EVENT_CATALOG.md
|
|
170
|
+
${service.docs[readme]} = docs/README.md
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`${service.docs}` alone, an invalid lookup, or a missing key fails before I/O. Loading all docs evaluates the source URL once per entry, so `${content.path}` changes from `docs/SUMMARY.md` to `docs/EVENT_CATALOG.md` and then `docs/README.md`.
|
|
174
|
+
|
|
175
|
+
`${version}` resolves differently for the two kinds of content. For an artifact it is exactly `artifact.version`, which is a required field and never inherits from the service, subdomain, or domain. For a service document it inherits service, then subdomain, then domain. The qualified version expressions expose only their corresponding explicit declaration and do not inherit. Blank versions count as absent. YAML strings and numbers normalize to strings; `config.version` and artifact contents never supply a content version.
|
|
176
|
+
|
|
177
|
+
## Coordinates and explicit overrides
|
|
178
|
+
|
|
179
|
+
Common coordinates resolve as:
|
|
180
|
+
|
|
181
|
+
```text
|
|
182
|
+
groupId = owner.groupId, otherwise config.groupIdExpression
|
|
183
|
+
artifactId = artifact.artifactId, otherwise config.artifactIdExpression
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Defaults are `${owner.id}` and `${artifact.pathWithoutExtension}`. Coordinate expressions cannot recursively reference `${groupId}` or `${artifactId}`. Overrides are valid:
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
domains:
|
|
190
|
+
commerce:
|
|
191
|
+
services:
|
|
192
|
+
orders:
|
|
193
|
+
groupId: io.arcadia.orders
|
|
194
|
+
artifacts:
|
|
195
|
+
- name: public-orders-api
|
|
196
|
+
artifactId: orders-openapi
|
|
197
|
+
type: openapi
|
|
198
|
+
path: contracts/orders.openapi.yaml
|
|
199
|
+
version: 1.1.0
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`${groupId}` and `${artifactId}` can be selected by any artifact provider expression, not only Maven.
|
|
203
|
+
|
|
204
|
+
## Artifact catalog and selections
|
|
205
|
+
|
|
206
|
+
`ManifestArtifactCatalog` resolves every declared artifact together with its owner and effective
|
|
207
|
+
coordinates. Declared artifact `type` values remain an open string namespace: `openapi`,
|
|
208
|
+
`asyncapi`, `zdl`, `zfl`, `grpc`, and future types require no registration with the library.
|
|
209
|
+
|
|
210
|
+
### Consumer artifact references
|
|
211
|
+
|
|
212
|
+
A service may identify the exact artifact that consumes one of its contracts with
|
|
213
|
+
`service.id#artifact.id`. The artifact selector is matched against the effective artifact ID first,
|
|
214
|
+
including `config.artifactIdExpression`; `type:<type>` explicitly selects every artifact of a type.
|
|
215
|
+
Plain selectors fall back to an artifact type only when exactly one artifact matches.
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
# schema-test: valid
|
|
219
|
+
domains:
|
|
220
|
+
catalog:
|
|
221
|
+
services:
|
|
222
|
+
inventory:
|
|
223
|
+
artifacts:
|
|
224
|
+
- type: asyncapi
|
|
225
|
+
path: asyncapi.yml
|
|
226
|
+
version: 1.0.0
|
|
227
|
+
consumers:
|
|
228
|
+
- orders.checkout#asyncapi-client
|
|
229
|
+
orders:
|
|
230
|
+
services:
|
|
231
|
+
checkout:
|
|
232
|
+
id: orders.checkout
|
|
233
|
+
artifacts:
|
|
234
|
+
- type: asyncapi-client
|
|
235
|
+
path: asyncapi-client.yml
|
|
236
|
+
version: 1.0.0
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
`ManifestConsumerIndex` resolves these declarations in both directions and infers compatible
|
|
240
|
+
provider artifacts from `ManifestConsumptionRules`. Suffix-less service references remain accepted
|
|
241
|
+
for backwards compatibility but do not create artifact-level consumption edges.
|
|
242
|
+
|
|
243
|
+
## Semantic architecture graph
|
|
244
|
+
|
|
245
|
+
The `architecture-graph` Kotlin Multiplatform module builds a typed, source-aware graph from a resolved
|
|
246
|
+
manifest and its artifacts. It runs on JVM and Node/JS and deliberately sits above `architecture-manifest`:
|
|
247
|
+
manifest loading remains format-agnostic, while the graph module depends on `dsl-kotlin` for its
|
|
248
|
+
built-in ZDL and ZFL analyzers.
|
|
249
|
+
|
|
250
|
+
```kotlin
|
|
251
|
+
val result = ArchitectureGraph.build(manifest, loader)
|
|
252
|
+
val graph = result.graph
|
|
253
|
+
|
|
254
|
+
val callers = graph.incoming(methodId, ArchitectureEdgeKind.INVOKES)
|
|
255
|
+
val emittedEvents = graph.outgoing(methodId, ArchitectureEdgeKind.EMITS)
|
|
256
|
+
val consumers = graph.consumersOf(channelId)
|
|
257
|
+
val flowProjection = graph.subgraphFrom(flowId, maxDepth = 5)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The initial built-ins contribute:
|
|
261
|
+
|
|
262
|
+
- manifest domain, subdomain, service, and artifact nodes;
|
|
263
|
+
- declared artifact-level consumption plus verified AsyncAPI channel and operation consumption;
|
|
264
|
+
- ZDL API, service, method, entity, and event nodes, including REST/AsyncAPI evidence and API references;
|
|
265
|
+
- ZFL system, flow, start, step, and event nodes, including triggers, outcome handlers, emissions,
|
|
266
|
+
system-to-ZDL declarations, and resolved ZFL-step-to-ZDL-method invocations;
|
|
267
|
+
- cross-artifact links from ZDL methods and events to AsyncAPI channel nodes.
|
|
268
|
+
|
|
269
|
+
Canonical concept nodes use location-independent `zw:` IDs. Artifact-derived representation nodes
|
|
270
|
+
retain their resolved source URI, owner reference, artifact ID, semantic path, and source coordinates,
|
|
271
|
+
and connect to concepts through `REPRESENTS` edges. Concepts and relationships aggregate typed
|
|
272
|
+
evidence with confidence. Loading and analysis failures become diagnostics so one broken artifact
|
|
273
|
+
does not discard the rest of the graph.
|
|
274
|
+
|
|
275
|
+
JVM callers that do not use coroutines can use `BlockingArchitectureGraph.build(...)` or
|
|
276
|
+
`BlockingArchitectureGraphBuilder`. Additional artifact formats remain an open namespace: provide
|
|
277
|
+
one or more `ManifestGraphArtifactAnalyzer` implementations to `ArchitectureGraphBuilder`; matching
|
|
278
|
+
analyzers share the already-loaded artifact content and their contributions are merged. AsyncAPI
|
|
279
|
+
classification and operation-level consumption are supplied by the typed, catalog-agnostic
|
|
280
|
+
`ManifestApiConsumptions` API in `architecture-manifest`; graph and presentation layers project that evidence
|
|
281
|
+
without maintaining their own YAML matching engines.
|
|
282
|
+
|
|
283
|
+
Callers can resolve either a unique effective artifact ID or every artifact of a declared type on
|
|
284
|
+
one owner:
|
|
285
|
+
|
|
286
|
+
```kotlin
|
|
287
|
+
val owner = ManifestOwnerSelector.Repository("orders-api")
|
|
288
|
+
val openApis = catalog.resolveByType(owner, "openapi").artifacts
|
|
289
|
+
val contract = catalog.resolveByArtifactId(owner, "contracts/orders").artifact
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Type resolution also supports library-defined virtual behavior. `asyncapi-all` selects every
|
|
293
|
+
declared `asyncapi` and `asyncapi-client` artifact on exactly one service and returns the concrete
|
|
294
|
+
members; it is never inserted into `ZenWaveManifest.artifacts`.
|
|
295
|
+
|
|
296
|
+
```kotlin
|
|
297
|
+
val catalog = ManifestArtifactCatalog.resolve(manifest, loader)
|
|
298
|
+
val selection = catalog.resolve(
|
|
299
|
+
ManifestArtifactSelector.typeInRepository(
|
|
300
|
+
"orders-api",
|
|
301
|
+
ManifestArtifactSelector.ASYNCAPI_ALL,
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
val bundleMembers: List<ResolvedManifestArtifact> = selection.artifacts
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Repository selectors may generally span several owners. An artifact-ID selector must resolve one
|
|
308
|
+
artifact. A declared type must resolve artifacts on one owner, while `asyncapi-all` must resolve one
|
|
309
|
+
matching service; missing and ambiguous selections fail with `ManifestArtifactSelectionException`.
|
|
310
|
+
|
|
311
|
+
## Workspace query runtime
|
|
312
|
+
|
|
313
|
+
The `workspace-runtime` Kotlin Multiplatform module exposes the adapter-neutral query contract used
|
|
314
|
+
by MCP, Web, CLI, and future LSP integrations. It provides bounded overview, deterministic semantic
|
|
315
|
+
ID resolution, browse, search, relationship traversal, provenance, and diagnostic operations.
|
|
316
|
+
Manifest `aliases` on domains, subdomains, and services redirect historical `zw:` IDs, including
|
|
317
|
+
descendant IDs after a parent rename.
|
|
318
|
+
|
|
319
|
+
The exact reverse-provenance `at` query crosses from an editor's document URI and semantic path to
|
|
320
|
+
the canonical architecture concept. LSP integrations use this operation for `conceptAt`; they do
|
|
321
|
+
not inspect `ArchitectureGraph` or construct `zw:` IDs themselves.
|
|
322
|
+
|
|
323
|
+
```kotlin
|
|
324
|
+
val workspace: WorkspaceQueryApi = WorkspaceRuntime(loader).open(manifestUri)
|
|
325
|
+
val service = workspace.resolve(SemanticId("zw:sales/ordering/order-service"))
|
|
326
|
+
val dependencies = workspace.related(
|
|
327
|
+
SemanticId("zw:sales/ordering/order-service"),
|
|
328
|
+
direction = Direction.OUTGOING,
|
|
329
|
+
confidence = listOf("DECLARED", "RESOLVED", "INFERRED_EXACT"),
|
|
330
|
+
)
|
|
331
|
+
val conceptsAtCursor = workspace.at(
|
|
332
|
+
WorkspaceSourceLocation("file:///workspace/orders/model.zdl", "events.OrderPlaced"),
|
|
333
|
+
)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
The `workspace-mcp` Kotlin Multiplatform module maps the seven tools from the Workspace
|
|
337
|
+
specification onto this same interface. `WorkspaceMcpTools.tools` supplies bounded JSON schemas and
|
|
338
|
+
`WorkspaceMcpTools.call(...)` returns a readable summary plus structured content for direct MCP SDK
|
|
339
|
+
registration.
|
|
340
|
+
|
|
341
|
+
Validate a workspace before starting an adapter:
|
|
342
|
+
|
|
343
|
+
```text
|
|
344
|
+
./gradlew :workspace-cli:installDist
|
|
345
|
+
workspace-cli/build/install/zenwave/bin/zenwave validate /workspace/zenwave-architecture.yml
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Source-aware editing
|
|
349
|
+
|
|
350
|
+
`ZenWaveManifestEditor.updateArtifactVersions` accepts one or more artifact-ID or type selectors.
|
|
351
|
+
It snapshots the root and referenced documents, resolves every selection, patches only the existing
|
|
352
|
+
version scalar ranges, and validates the complete overlay before returning document changes.
|
|
353
|
+
|
|
354
|
+
```kotlin
|
|
355
|
+
val result = editor.updateArtifactVersions(
|
|
356
|
+
manifestUri,
|
|
357
|
+
listOf(
|
|
358
|
+
ManifestArtifactVersionUpdate(
|
|
359
|
+
ManifestArtifactSelector.typeInRepository(
|
|
360
|
+
"orders-api",
|
|
361
|
+
ManifestArtifactSelector.ASYNCAPI_ALL,
|
|
362
|
+
),
|
|
363
|
+
"1.4.0",
|
|
364
|
+
),
|
|
365
|
+
),
|
|
366
|
+
)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The returned `ManifestDocumentTextUpdate` values preserve unrelated formatting, comments, scalar
|
|
370
|
+
quote style, and source ownership through external `$ref` documents. The editor does not persist
|
|
371
|
+
them; callers can compare `originalText` before writing. JVM and Java callers can use
|
|
372
|
+
`BlockingZenWaveManifestEditor` with the default file and HTTP(S) document reader.
|
|
373
|
+
|
|
374
|
+
`updateScalars` is the lower-level API for updating another existing owner or artifact scalar.
|
|
375
|
+
Structural edits such as adding or removing manifest nodes remain out of scope.
|
|
376
|
+
|
|
377
|
+
## Ordered reads
|
|
378
|
+
|
|
379
|
+
`contentResolution` defaults to `[workspace]`. Only listed sources participate; configuring a source does not activate it. Transport and not-found failures advance to the next applicable source. Apicurio and Maven are artifact-only and are skipped for service documents. A preferred source may reorder or restrict only active sources.
|
|
380
|
+
|
|
381
|
+
Supported absolute URIs bypass the list. Exhausted reads report redacted candidates in attempted order and the final error. Unknown sources, active unconfigured sources, invalid providers, missing required fields, and unresolved expressions fail deterministically before transport I/O where possible.
|
|
382
|
+
|
|
383
|
+
| Source | Artifacts | Service docs |
|
|
384
|
+
| --- | --- | --- |
|
|
385
|
+
| `workspace` | Yes | Yes |
|
|
386
|
+
| `git` | Yes | Yes |
|
|
387
|
+
| `apicurio` | Yes | No |
|
|
388
|
+
| `artifactory` | Yes | Yes |
|
|
389
|
+
| `maven` | Yes | No |
|
|
390
|
+
|
|
391
|
+
See [the detailed source guide](docs/source-resolution-guide.md) for provider defaults, custom Git recipes, encoding, workspace traversal, Artifactory layouts, and Maven Central JAR extraction.
|
|
392
|
+
|
|
393
|
+
## Loading API
|
|
394
|
+
|
|
395
|
+
`ZenWaveManifestLoader` is the canonical multiplatform API. Its loading operations are
|
|
396
|
+
`suspend` functions so JVM and Node.js/Kotlin callers can perform manifest, document, and
|
|
397
|
+
artifact I/O without blocking their server event loop:
|
|
398
|
+
|
|
399
|
+
```kotlin
|
|
400
|
+
val loader = ZenWaveManifestLoader()
|
|
401
|
+
val manifest = loader.load("file:///workspace/zenwave-architecture.yml") // http also work
|
|
402
|
+
val service = manifest.findService("commerce/orders")!!
|
|
403
|
+
val openApis = service.findArtifacts("openapi")
|
|
404
|
+
val text = loader.loadArtifactText(manifest, service, openApis.first())
|
|
405
|
+
val schema = loader.resolveArtifactReference(
|
|
406
|
+
manifest,
|
|
407
|
+
service,
|
|
408
|
+
openApis.first(),
|
|
409
|
+
"schemas/order.yaml",
|
|
410
|
+
)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Java and synchronous JVM integrations can use `BlockingZenWaveManifestLoader`. It exposes the
|
|
414
|
+
same I/O operations without coroutine continuations and accepts either `String` or `java.net.URI`
|
|
415
|
+
for root resources:
|
|
416
|
+
|
|
417
|
+
```java
|
|
418
|
+
var loader = new BlockingZenWaveManifestLoader();
|
|
419
|
+
var manifest = loader.load(URI.create("file:///workspace/zenwave-architecture.yml"));
|
|
420
|
+
var service = manifest.findService("commerce/orders");
|
|
421
|
+
var artifact = service.findArtifact("openapi");
|
|
422
|
+
var options = new ManifestLoadOptions()
|
|
423
|
+
.withPreferredSource("workspace")
|
|
424
|
+
.withFallback(true);
|
|
425
|
+
var text = loader.loadArtifactText(manifest, service, artifact, options);
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
For batch service documents, `loadServiceDocResults` preserves one result per configured
|
|
429
|
+
document, including its resolved resource or error message. `loadAvailableServiceDocs` is the
|
|
430
|
+
convenience variant for generators that only need successfully loaded content.
|
|
431
|
+
`loadServiceDocs` remains the fail-fast variant.
|
|
432
|
+
|
|
433
|
+
Candidate and reference construction remains non-blocking on the common loader. Use
|
|
434
|
+
`ManifestResolvedResource.referenceUri()` when a consumer needs a single URI-like reference,
|
|
435
|
+
including a resource stored inside an archive.
|
|
436
|
+
|
|
437
|
+
## Browsers and Web Workers
|
|
438
|
+
|
|
439
|
+
`architecture-manifest` and `architecture-graph` load and run in a browser or Web Worker with no Node runtime.
|
|
440
|
+
Supply the means of reading content: every manifest, `$ref`, document and artifact read goes
|
|
441
|
+
through the `DocumentLoader`s you pass, and nothing else is needed to load the library.
|
|
442
|
+
|
|
443
|
+
```kotlin
|
|
444
|
+
val loader = ZenWaveManifestLoader(documentLoaders = listOf(myHostReader), archiveEntryLoader = null)
|
|
445
|
+
val manifest = loader.load("https://example.com/zenwave-architecture.yml")
|
|
446
|
+
val graph = ArchitectureGraphBuilder(loader).build(manifest).graph
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Ask what the platform offers before presenting a feature; asking never fails:
|
|
450
|
+
|
|
451
|
+
- `ManifestPlatform.capabilities()` / `isAvailable(ManifestPlatform.ARCHIVE_EXTRACTION)`: reading an
|
|
452
|
+
entry inside an archive (Maven Central/GitHub Packages JARs). Available on the JVM; not on JS unless
|
|
453
|
+
the host passes its own `ManifestArchiveEntryLoader`.
|
|
454
|
+
- `ManifestPlatform.name`: `"jvm"`, `"node"` or `"browser"`.
|
|
455
|
+
- `RefParserPlatform` (from `json-schema-ref-parser-kmp`): filesystem, classpath and HTTP reads,
|
|
456
|
+
reported separately by that library and with its own `CapabilityUnavailableException`.
|
|
457
|
+
|
|
458
|
+
A candidate that needs an unavailable manifest capability fails with
|
|
459
|
+
`ManifestCapabilityUnavailableException`, and the next content-resolution candidate is tried; if none
|
|
460
|
+
succeeds, the `ManifestResourceLoadException` names the capability. The build runs the JS test suites in headless Chromium (`jsBrowserTest`, part of
|
|
461
|
+
`check`; set `CHROME_BIN` if no Chrome, Chromium or Edge is found in a standard location).
|
|
462
|
+
|
|
463
|
+
## Kotlin module and verification
|
|
464
|
+
|
|
465
|
+
`architecture-manifest` contains common parsing, diagnostics, interpolation, and candidate construction for JVM and JavaScript. JVM also provides Maven Central JAR entry extraction; JavaScript hosts can supply an archive-entry loader.
|
|
466
|
+
|
|
467
|
+
Published library coordinates are `io.zenwave360.architecture:architecture-manifest`, `architecture-manifest-jvm`, and `architecture-manifest-js`.
|
|
468
|
+
|
|
469
|
+
Run:
|
|
470
|
+
|
|
471
|
+
```text
|
|
472
|
+
./gradlew :architecture-manifest:jvmTest
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
The suite validates both schemas and every complete README YAML block marked `schema-test: valid`. Release workflow details are in [RELEASING.md](RELEASING.md); this repository is licensed under the [MIT License](LICENSE).
|