@shlomoa/openui-spec 0.1.1 → 0.3.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
@@ -9,13 +9,37 @@ This repository contains a technology-independent specification for a Web UI fra
9
9
  Contributor and developer entry points for this repository:
10
10
 
11
11
  - [Contributing guide](CONTRIBUTING.md) — local setup and validation basics.
12
+ - [Changelog](CHANGELOG.md) — release notes, breaking changes, and upgrade guidance.
12
13
 
13
- ## OpenUI JSON API
14
+ ## OpenUI JSON packages
14
15
 
15
- The [`@shlomoa/openui-spec`](package.json) package provides `OpenUiJson` for
16
- loading, validating, and editing an OpenUI JSON document. It bundles
17
- `spec/openui.schema.json` and `spec/openui.json`; validation checks both the
18
- schema and the object types supported by the canonical catalog.
16
+ Both implementations bundle `spec/openui.schema.json` and `spec/openui.json`.
17
+ Validation checks the document shape, exact
18
+ [known object type](https://openui-spec.readthedocs.io/en/latest/#known-object-type)
19
+ membership, and globally unique object IDs.
20
+
21
+ ### Python
22
+
23
+ The [`openui-spec`](https://pypi.org/project/openui-spec/) package requires
24
+ Python 3.12 or newer and installs two command-line tools:
25
+
26
+ - `openui_spec` validates and edits documents with `validate`, `add`, `remove`,
27
+ and `modify` commands.
28
+ - `compare_openui_spec` structurally compares two documents and emits a
29
+ deterministic JSON changelog.
30
+
31
+ ```bash
32
+ python -m pip install openui-spec
33
+ openui_spec validate --input document.json
34
+ openui_spec add --input document.json --parent root --object '{"id":"newTable","type":"Table"}'
35
+ compare_openui_spec reference.json updated.json --output changelog.json
36
+ ```
37
+
38
+ ### TypeScript and Node.js
39
+
40
+ The [`@shlomoa/openui-spec`](https://www.npmjs.com/package/@shlomoa/openui-spec)
41
+ package provides the typed `OpenUiJson` document API and the equivalent
42
+ `ng-openui-spec` editing CLI.
19
43
 
20
44
  ```bash
21
45
  npm install @shlomoa/openui-spec
@@ -27,19 +51,28 @@ import { OpenUiJson } from "@shlomoa/openui-spec";
27
51
  const document = OpenUiJson.load("input.json");
28
52
  document.validate();
29
53
  document.add("root", { id: "newTable", type: "Table" });
54
+ document.updateAttributes("newTable", { title: "Updated" });
30
55
  document.save("output.json");
31
56
  ```
32
57
 
33
- The published [OpenUI JSON editing](https://openui-spec.readthedocs.io/en/latest/tooling/editing/)
34
- tooling page documents the API and command-line interface.
35
-
36
- The package also installs an `ng-openui-spec` CLI to validate or apply one change
37
- in place (pass `--output` to write to a different file):
38
-
39
58
  ```bash
40
- ng-openui-spec validate --input spec/openui.json
41
- ng-openui-spec add --input document.json --parent root --object '{"id":"newTable","type":"Table"}'
42
- ng-openui-spec remove --input document.json --id newTable
43
- ng-openui-spec modify --input document.json --id table --attrs '{"title":"Updated"}'
44
- ng-openui-spec modify --input document.json --id table --object '{"id":"table","type":"Grid"}'
59
+ ng-openui-spec validate --input document.json
45
60
  ```
61
+
62
+ ### Essentials
63
+
64
+ - An OpenUI document is a tree with a root `id` of `root`; every node has a
65
+ unique camelCase `id` and an exact, case-sensitive catalog `type`.
66
+ - Put non-hierarchical values in `attrs` as strings or `null`, and nested objects
67
+ in `children`.
68
+ - Editing commands revalidate the result and update `--input` in place. Pass
69
+ `--output` to preserve the source document.
70
+ - Values accepted by `--object` and `--attrs` can be inline JSON or paths to JSON
71
+ files.
72
+
73
+ ### Documentation
74
+
75
+ - [Specification overview and artifact model](https://openui-spec.readthedocs.io/en/latest/#specification-artifacts-grammar-vs-catalog)
76
+ - [OpenUI JSON editing API and CLI reference](https://openui-spec.readthedocs.io/en/latest/tooling/editing/)
77
+ - [OpenUI JSON comparison guide](https://openui-spec.readthedocs.io/en/latest/tooling/comparison/)
78
+ - [OpenUI document examples](https://openui-spec.readthedocs.io/en/latest/examples/)
@@ -42,6 +42,7 @@ export declare class OpenUiJson {
42
42
  }): void;
43
43
  updateAttributes(objectId: string, attributes: Record<string, string | null>): void;
44
44
  private validateChild;
45
+ private catalogTypes;
45
46
  private validateNode;
46
47
  private find;
47
48
  private findParent;
package/dist/src/index.js CHANGED
@@ -77,15 +77,15 @@ class OpenUiJson {
77
77
  if (!validator(this.document)) {
78
78
  throw new OpenUiValidationError(formatValidationErrors(validator.errors));
79
79
  }
80
- const supportedTypes = new Set([...this.walk(catalog)].map((node) => node.type));
80
+ const knownTypes = this.catalogTypes(catalog);
81
81
  const seenIds = new Set();
82
82
  for (const node of this.walk(this.document)) {
83
83
  if (seenIds.has(node.id)) {
84
84
  throw new OpenUiValidationError(`duplicate object id: ${node.id}`);
85
85
  }
86
86
  seenIds.add(node.id);
87
- if (!supportedTypes.has(node.type)) {
88
- throw new OpenUiValidationError(`unsupported object type: ${node.type}`);
87
+ if (!knownTypes.has(node.type)) {
88
+ throw new OpenUiValidationError(`unknown OpenUI object type: ${node.type}`);
89
89
  }
90
90
  }
91
91
  }
@@ -164,18 +164,21 @@ class OpenUiJson {
164
164
  validateChild(child) {
165
165
  this.validateNode(child, false);
166
166
  const catalog = this.loadJson(this.options.catalog, this.options.catalogPath, "catalog");
167
- const supportedTypes = new Set([...this.walk(catalog)].map((node) => node.type));
167
+ const knownTypes = this.catalogTypes(catalog);
168
168
  const seenIds = new Set();
169
169
  for (const node of this.walk(child)) {
170
170
  if (seenIds.has(node.id)) {
171
171
  throw new OpenUiJsonError(`duplicate object id: ${node.id}`);
172
172
  }
173
173
  seenIds.add(node.id);
174
- if (!supportedTypes.has(node.type)) {
175
- throw new OpenUiJsonError(`unsupported object type: ${node.type}`);
174
+ if (!knownTypes.has(node.type)) {
175
+ throw new OpenUiJsonError(`unknown OpenUI object type: ${node.type}`);
176
176
  }
177
177
  }
178
178
  }
179
+ catalogTypes(catalog) {
180
+ return new Set([...this.walk(catalog)].map((node) => node.type));
181
+ }
179
182
  validateNode(node, isRoot) {
180
183
  const schema = this.loadJson(this.options.schema, this.options.schemaPath, "schema");
181
184
  const definition = isRoot ? schema : { $ref: "#/$defs/element", $defs: schema.$defs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shlomoa/openui-spec",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Load, validate, and edit OpenUI JSON documents.",
5
5
  "scripts": {
6
6
  "build:tsc": "tsc -p tsconfig.json",
package/spec/openui.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "root",
3
3
  "type": "html",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "attrs": {
6
6
  "name": "OpenUI",
7
7
  "description": "Technology-independent Web UI framework specification",
@@ -54,6 +54,35 @@
54
54
  }
55
55
  ]
56
56
  },
57
+ {
58
+ "id": "route",
59
+ "type": "Route",
60
+ "attrs": {
61
+ "title": "Route",
62
+ "purpose": "A route maps a location pattern to application content or redirects the location to another route.",
63
+ "scopeDocument": "scopes/Application/route.scope.md",
64
+ "status": "draft"
65
+ },
66
+ "children": [
67
+ {
68
+ "id": "routeInstance",
69
+ "type": "Route",
70
+ "attrs": {
71
+ "[path]": null,
72
+ "[target]": null,
73
+ "[title]": null,
74
+ "[redirectTo]": null,
75
+ "[access]": null
76
+ },
77
+ "children": [
78
+ {
79
+ "id": "routeChildRoute",
80
+ "type": "Route"
81
+ }
82
+ ]
83
+ }
84
+ ]
85
+ },
57
86
  {
58
87
  "id": "navigation",
59
88
  "type": "Navigation",
@@ -83,6 +112,58 @@
83
112
  }
84
113
  ]
85
114
  },
115
+ {
116
+ "id": "navItem",
117
+ "type": "NavItem",
118
+ "attrs": {
119
+ "title": "Navigation item",
120
+ "purpose": "A navigation item presents one labelled application route as a user-selectable destination.",
121
+ "scopeDocument": "scopes/Application/nav_item.scope.md",
122
+ "status": "draft"
123
+ },
124
+ "children": [
125
+ {
126
+ "id": "navItemInstance",
127
+ "type": "NavItem",
128
+ "attrs": {
129
+ "[label]": null,
130
+ "[route]": null,
131
+ "[icon]": null,
132
+ "[disabled]": null
133
+ }
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "id": "navGroup",
139
+ "type": "NavGroup",
140
+ "attrs": {
141
+ "title": "Navigation group",
142
+ "purpose": "A navigation group labels and organizes related navigation destinations.",
143
+ "scopeDocument": "scopes/Application/nav_group.scope.md",
144
+ "status": "draft"
145
+ },
146
+ "children": [
147
+ {
148
+ "id": "navGroupInstance",
149
+ "type": "NavGroup",
150
+ "attrs": {
151
+ "[label]": null,
152
+ "[expanded]": null
153
+ },
154
+ "children": [
155
+ {
156
+ "id": "navGroupNavigationItem",
157
+ "type": "NavItem"
158
+ },
159
+ {
160
+ "id": "navGroupNavigationGroup",
161
+ "type": "NavGroup"
162
+ }
163
+ ]
164
+ }
165
+ ]
166
+ },
86
167
  {
87
168
  "id": "toolBars",
88
169
  "type": "ToolBars",
@@ -96,19 +177,62 @@
96
177
  {
97
178
  "id": "toolBarsInstance",
98
179
  "type": "ToolBar",
180
+ "attrs": {
181
+ "[ariaLabel]": null
182
+ },
99
183
  "children": [
100
184
  {
101
185
  "id": "toolBarsToolBarRow",
102
186
  "type": "ToolBarRow"
103
- },
187
+ }
188
+ ]
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ "id": "toolBarRow",
194
+ "type": "ToolBarRow",
195
+ "attrs": {
196
+ "title": "Tool bar row",
197
+ "purpose": "A tool bar row orders command actions within a tool bar.",
198
+ "scopeDocument": "scopes/Application/tool_bar_row.scope.md",
199
+ "status": "draft"
200
+ },
201
+ "children": [
202
+ {
203
+ "id": "toolBarRowInstance",
204
+ "type": "ToolBarRow",
205
+ "children": [
104
206
  {
105
- "id": "toolBarsToolAction",
207
+ "id": "toolBarRowToolAction",
106
208
  "type": "ToolAction"
107
209
  }
108
210
  ]
109
211
  }
110
212
  ]
111
213
  },
214
+ {
215
+ "id": "toolAction",
216
+ "type": "ToolAction",
217
+ "attrs": {
218
+ "title": "Tool action",
219
+ "purpose": "A tool action is a labelled application command exposed from a tool bar.",
220
+ "scopeDocument": "scopes/Application/tool_action.scope.md",
221
+ "status": "draft"
222
+ },
223
+ "children": [
224
+ {
225
+ "id": "toolActionInstance",
226
+ "type": "ToolAction",
227
+ "attrs": {
228
+ "[label]": null,
229
+ "[icon]": null,
230
+ "[disabled]": null,
231
+ "(activate)": null
232
+ }
233
+ }
234
+ ]
235
+ },
112
236
  {
113
237
  "id": "favicon",
114
238
  "type": "Favicon",
@@ -147,7 +271,8 @@
147
271
  "type": "html",
148
272
  "attrs": {
149
273
  "[lang]": null,
150
- "[dir]": null
274
+ "[dir]": null,
275
+ "[title]": null
151
276
  },
152
277
  "children": [
153
278
  {
@@ -657,7 +782,7 @@
657
782
  "type": "ShellPage",
658
783
  "attrs": {
659
784
  "title": "Shell page",
660
- "purpose": "A page shell with no business-object content that connects application routing and navigation.",
785
+ "purpose": "A page shell with no business-object content that presents application routing and navigation.",
661
786
  "scopeDocument": "scopes/Pages/shell_page.scope.md",
662
787
  "status": "draft"
663
788
  },