@plitzi/sdk-schema 0.36.1 → 0.37.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# @plitzi/sdk-schema
|
|
2
2
|
|
|
3
|
+
## 0.37.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- - A `button` takes a `title`: shown as a tooltip, and its accessible name when it has no text of its own — an
|
|
8
|
+
icon-only button without one was announced as nothing. The builder's settings offer it as "Tooltip". The JSON export
|
|
9
|
+
is two files, `schema.json` and `style.json`, shown as tabs; each group of changes in the export folds away.
|
|
10
|
+
|
|
11
|
+
- `BindingTransformer.params` is `Record<string, string | number | boolean>`: a param the builder draws as a checkbox
|
|
12
|
+
(`styleVariant`'s `append`) is stored as the boolean it is and read as one, so the type now says so. The MCP compares
|
|
13
|
+
such a param with its catalog options as text. A `container` may be an `li`, for the rows of a list. The export
|
|
14
|
+
keeps every element id with `keepIds`, and the header's Publish dialog opens wider.
|
|
15
|
+
|
|
16
|
+
- **The builder exports the space on screen — on paid plans.** **Export** in the header opens a wide dialog: the
|
|
17
|
+
format as tabs (TypeScript in one file, TypeScript with a file per page as a `.zip`, or JSON), generated as soon as
|
|
18
|
+
it is picked, shown read-only in CodeMirror — beside a file list when there are several — with one row of actions,
|
|
19
|
+
**Copy** and **Download**. What the export tidied is a one-line note that unfolds into readable groups. On the free
|
|
20
|
+
plan the dialog explains what export is instead; the server refuses the code with a 402 (`limit: 'spaceExport'`)
|
|
21
|
+
whatever the builder shows. `useSpaceExport` is the one way into it, for anything else that needs the space as files.
|
|
22
|
+
|
|
23
|
+
- **A space document can be read back into code.** `specFromSpace(documents)` turns a `{ schema, style }` pair — a
|
|
24
|
+
builder export, a seed checked in as JSON — into the `SpaceSpec` that authors it, and `specToSource(spec, { exportName })`
|
|
25
|
+
writes that spec out as the TypeScript a person would write: one factory call per element, the attributes a type
|
|
26
|
+
already defaults to left out, the longhands the document stores written back as `padding: 10px 20px`, and every class
|
|
27
|
+
something names as a `styles()` declaration held in a variable. `split: true` writes one file per page and per layout.
|
|
28
|
+
`compareSpaces(expected, actual)` proves the round trip: it lists every way two pairs render differently — the tree,
|
|
29
|
+
the attributes, the rules that apply to each element whatever its selector is called, bindings, flows, pages, layouts
|
|
30
|
+
and settings — and nothing else. A selector used by one element becomes that element's own `css`; one shared, or
|
|
31
|
+
spelled out anywhere in the document (a `customCss` rule, a template), stays a class under its own name; an element id
|
|
32
|
+
nothing refers to is left out and derived again.
|
|
33
|
+
|
|
34
|
+
What an older builder left behind is repaired on the way, and every repair is reported in `corrections` rather than
|
|
35
|
+
made silently: element types that no longer exist (`navbar` → a `list` laid out in a row, `navbarItem` → `listItem`),
|
|
36
|
+
a hover stored as a class of its own (`card:hover`) folded into the class, fields and attributes no component reads,
|
|
37
|
+
a setting nothing reads, a link `target` of `_blank` (the component adds the underscore, so it rendered as `__blank`),
|
|
38
|
+
`null` or an empty list where an attribute is unset, a binding to a source nothing publishes, a flow with a step that
|
|
39
|
+
runs nothing, and a global callback registered on the wrong module. A CSS property the style editor cannot hold is
|
|
40
|
+
kept in `customCss` under the same selector, so the page still renders it.
|
|
41
|
+
|
|
42
|
+
- **What a spec can say grew to what a document holds.** A selector carries its states and variants beside its rules —
|
|
43
|
+
`styles('card', { css, states: { hover }, variants: { active } })`, and `states` on an element's own `css` — and the
|
|
44
|
+
selector cache is now written by `processSelector`, the function the style editor writes it with. An element type's
|
|
45
|
+
defaults (`elements`) take states, variants, per-breakpoint rules and `slots` for its other selectors (a modal's
|
|
46
|
+
`rootContainer`). A space declares `layouts` — shells that pages render inside, named by `layout: { id, slot }` on a
|
|
47
|
+
page or on another layout, with the slot checked to be inside the shell — and a page takes `keepState` and
|
|
48
|
+
`stateStorage`. `visible: false` starts an element hidden for a flow to reveal, and `loadStrategy` is carried through.
|
|
49
|
+
An element's own selector never takes the name of a declared class any more, however that class happens to be called.
|
|
50
|
+
|
|
51
|
+
- `elementAttributeNames` lists, as data, the attributes each built-in element can be authored with — `null` for one
|
|
52
|
+
that takes any. It is generated from the elements' attribute types (`yarn generate:attribute-names`) and a type test
|
|
53
|
+
fails when the two disagree. `defaultAttributes(type)` and `defaultLabel(type)` expose what a factory fills in.
|
|
54
|
+
|
|
55
|
+
- `CustomAttributes` accepts any attribute, because the component a custom element's `renderType` names reads its own
|
|
56
|
+
attributes off it; and `FormControlAttributes` declares `previewError`, which `withFieldValue` reads.
|
|
57
|
+
|
|
58
|
+
### Patch Changes
|
|
59
|
+
|
|
60
|
+
- Updated dependencies
|
|
61
|
+
- @plitzi/sdk-shared@0.37.0
|
|
62
|
+
- @plitzi/sdk-style@0.37.0
|
|
63
|
+
|
|
64
|
+
## 0.36.2
|
|
65
|
+
|
|
66
|
+
### Patch Changes
|
|
67
|
+
|
|
68
|
+
- v0.36.2
|
|
69
|
+
- Updated dependencies
|
|
70
|
+
- @plitzi/sdk-shared@0.36.2
|
|
71
|
+
- @plitzi/sdk-style@0.36.2
|
|
72
|
+
|
|
3
73
|
## 0.36.1
|
|
4
74
|
|
|
5
75
|
### Patch Changes
|
|
@@ -74,7 +74,12 @@ declare class FlatMap {
|
|
|
74
74
|
updateVariable: (variable: SchemaVariable) => boolean;
|
|
75
75
|
removeVariables: (variables: string[]) => boolean;
|
|
76
76
|
removeVariable: (variable: string) => boolean;
|
|
77
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Everything an element is rendered inside, nearest first — its ancestors, and past a root the shell that root is
|
|
79
|
+
* shown in. A layout names its own shell the way a page does, so the walk continues through as many as are nested;
|
|
80
|
+
* `visited` keeps a document whose shells name each other from walking forever.
|
|
81
|
+
*/
|
|
82
|
+
parentTree: (elementId: Element["id"], visited?: Set<Element["id"]>) => string[];
|
|
78
83
|
childTree: (elementId: Element["id"]) => string[];
|
|
79
84
|
isValidElement: (element?: Partial<Element>) => boolean;
|
|
80
85
|
flatAsTemplate: (style: Style, elementId: Element["id"], excludeRoot?: boolean) => {
|
package/dist/helpers/FlatMap.mjs
CHANGED
|
@@ -151,17 +151,19 @@ var p = class {
|
|
|
151
151
|
return this.variables = this.variables.filter((t) => e.includes(t.name)), t !== this.variables.length;
|
|
152
152
|
};
|
|
153
153
|
removeVariable = (e) => this.removeVariables([e]);
|
|
154
|
-
parentTree = (e) => {
|
|
155
|
-
let
|
|
156
|
-
if (!
|
|
154
|
+
parentTree = (e, t = /* @__PURE__ */ new Set()) => {
|
|
155
|
+
let n = this.flat[e], r = [];
|
|
156
|
+
if (!n) return r;
|
|
157
157
|
do {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
let i = s(n, "definition.type");
|
|
159
|
+
if ((i === "page" || i === "layoutContainer") && !t.has(n.id)) {
|
|
160
|
+
t.add(n.id);
|
|
161
|
+
let e = s(n, "attributes.layout"), i = s(n, "attributes.layoutContainer");
|
|
162
|
+
e && i && r.push(i, ...this.parentTree(i, t));
|
|
161
163
|
}
|
|
162
|
-
e !==
|
|
163
|
-
} while (
|
|
164
|
-
return
|
|
164
|
+
e !== n.id && r.push(n.id), n = s(this.flat, s(n, "definition.parentId"), void 0);
|
|
165
|
+
} while (n);
|
|
166
|
+
return r;
|
|
165
167
|
};
|
|
166
168
|
childTree = (e) => {
|
|
167
169
|
let t = this.flat[e];
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { isValidElementId as e } from "./elementId.mjs";
|
|
2
|
+
import { findLayoutCycle as t } from "@plitzi/sdk-shared/schema/layoutChain";
|
|
2
3
|
//#region src/helpers/schemaValidator.ts
|
|
3
|
-
var
|
|
4
|
-
let
|
|
4
|
+
var n = (n) => {
|
|
5
|
+
let r = [], i = [], { flat: a, pages: o, pageFolders: s, variables: c } = n, l = (e) => !!a[e], u = (e) => a[e], d = () => Object.values(a).filter((e) => e?.definition?.type === "layoutContainer" && !e.definition.parentId).map((e) => e.id), f = () => [...o, ...d()], p = () => !a || typeof a != "object" ? (r.push({
|
|
5
6
|
code: "INVALID_FLAT",
|
|
6
7
|
message: "Schema.flat must be a valid Record<string, Element>"
|
|
7
|
-
}), !1) : Array.isArray(
|
|
8
|
+
}), !1) : Array.isArray(o) ? Array.isArray(s) ? Array.isArray(c) ? !0 : (r.push({
|
|
8
9
|
code: "INVALID_VARIABLES",
|
|
9
10
|
message: "Schema.variables must be an array"
|
|
10
|
-
}), !1) : (
|
|
11
|
+
}), !1) : (r.push({
|
|
11
12
|
code: "INVALID_PAGE_FOLDERS",
|
|
12
13
|
message: "Schema.pageFolders must be an array"
|
|
13
|
-
}), !1) : (
|
|
14
|
+
}), !1) : (r.push({
|
|
14
15
|
code: "INVALID_PAGES",
|
|
15
16
|
message: "Schema.pages must be an array of element IDs"
|
|
16
|
-
}), !1),
|
|
17
|
-
Object.entries(
|
|
17
|
+
}), !1), m = () => {
|
|
18
|
+
Object.entries(a).forEach(([e, t]) => {
|
|
18
19
|
if (!t) {
|
|
19
|
-
|
|
20
|
+
r.push({
|
|
20
21
|
code: "NULL_ELEMENT",
|
|
21
22
|
message: `Element with id "${e}" is null or undefined`,
|
|
22
23
|
elementId: e
|
|
@@ -24,163 +25,163 @@ var t = (t) => {
|
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
if (!t.id) {
|
|
27
|
-
|
|
28
|
+
r.push({
|
|
28
29
|
code: "MISSING_ID",
|
|
29
30
|
message: `Element at key "${e}" has no id property`
|
|
30
31
|
});
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
|
-
if (t.id !== e &&
|
|
34
|
+
if (t.id !== e && r.push({
|
|
34
35
|
code: "ID_MISMATCH",
|
|
35
36
|
message: `Element id "${t.id}" doesn't match flat key "${e}"`,
|
|
36
37
|
elementId: e
|
|
37
38
|
}), !t.definition) {
|
|
38
|
-
|
|
39
|
+
r.push({
|
|
39
40
|
code: "MISSING_DEFINITION",
|
|
40
41
|
message: "Element missing definition",
|
|
41
42
|
elementId: e
|
|
42
43
|
});
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
|
-
let { definition:
|
|
46
|
-
|
|
46
|
+
let { definition: n, attributes: a } = t;
|
|
47
|
+
n.type || r.push({
|
|
47
48
|
code: "MISSING_TYPE",
|
|
48
49
|
message: "Element missing definition.type",
|
|
49
50
|
elementId: e
|
|
50
|
-
}),
|
|
51
|
+
}), n.label === void 0 && r.push({
|
|
51
52
|
code: "MISSING_LABEL",
|
|
52
53
|
message: "Element missing definition.label",
|
|
53
54
|
elementId: e
|
|
54
|
-
}),
|
|
55
|
+
}), n.rootId || r.push({
|
|
55
56
|
code: "MISSING_ROOT_ID",
|
|
56
57
|
message: "Element missing definition.rootId",
|
|
57
58
|
elementId: e
|
|
58
|
-
}), (!
|
|
59
|
+
}), (!n.styleSelectors || typeof n.styleSelectors != "object") && r.push({
|
|
59
60
|
code: "MISSING_STYLE_SELECTORS",
|
|
60
61
|
message: "Element missing or invalid definition.styleSelectors",
|
|
61
62
|
elementId: e
|
|
62
|
-
}), a ||
|
|
63
|
+
}), a || i.push({
|
|
63
64
|
code: "MISSING_ATTRIBUTES",
|
|
64
65
|
message: "Element has no attributes",
|
|
65
66
|
elementId: e
|
|
66
|
-
}),
|
|
67
|
-
|
|
67
|
+
}), n.items && (Array.isArray(n.items) ? n.items.forEach((t, n) => {
|
|
68
|
+
l(t) || r.push({
|
|
68
69
|
code: "ORPHANED_ITEM_REFERENCE",
|
|
69
|
-
message: `Element "${e}" references non-existent child "${t}" at items[${
|
|
70
|
+
message: `Element "${e}" references non-existent child "${t}" at items[${n}]`,
|
|
70
71
|
elementId: e,
|
|
71
72
|
details: {
|
|
72
73
|
childId: t,
|
|
73
|
-
index:
|
|
74
|
+
index: n
|
|
74
75
|
}
|
|
75
76
|
});
|
|
76
|
-
}) :
|
|
77
|
+
}) : r.push({
|
|
77
78
|
code: "INVALID_ITEMS",
|
|
78
79
|
message: "Element.definition.items must be an array",
|
|
79
80
|
elementId: e
|
|
80
|
-
})),
|
|
81
|
+
})), n.parentId && !l(n.parentId) && r.push({
|
|
81
82
|
code: "ORPHANED_PARENT_REFERENCE",
|
|
82
|
-
message: `Element "${e}" has parentId "${
|
|
83
|
+
message: `Element "${e}" has parentId "${n.parentId}" but parent doesn't exist in flat`,
|
|
83
84
|
elementId: e,
|
|
84
|
-
details: { parentId:
|
|
85
|
+
details: { parentId: n.parentId }
|
|
85
86
|
});
|
|
86
87
|
});
|
|
87
|
-
},
|
|
88
|
-
Object.values(
|
|
88
|
+
}, h = () => {
|
|
89
|
+
Object.values(a).forEach((e) => {
|
|
89
90
|
if (!e?.definition) return;
|
|
90
|
-
let { id: t, definition:
|
|
91
|
-
if (
|
|
92
|
-
let
|
|
93
|
-
|
|
91
|
+
let { id: t, definition: n } = e;
|
|
92
|
+
if (n.items && n.items.forEach((e) => {
|
|
93
|
+
let n = u(e);
|
|
94
|
+
n?.definition && n.definition.parentId !== t && r.push({
|
|
94
95
|
code: "PARENT_CHILD_MISMATCH",
|
|
95
|
-
message: `Element "${e}" is in "${t}".items but has parentId "${
|
|
96
|
+
message: `Element "${e}" is in "${t}".items but has parentId "${n.definition.parentId}"`,
|
|
96
97
|
elementId: e,
|
|
97
98
|
details: {
|
|
98
99
|
expectedParent: t,
|
|
99
|
-
actualParent:
|
|
100
|
+
actualParent: n.definition.parentId
|
|
100
101
|
}
|
|
101
102
|
});
|
|
102
|
-
}),
|
|
103
|
-
let e =
|
|
103
|
+
}), n.parentId) {
|
|
104
|
+
let e = u(n.parentId);
|
|
104
105
|
if (!e?.definition) return;
|
|
105
106
|
let i = e.definition.items || [];
|
|
106
|
-
i.includes(t) ||
|
|
107
|
+
i.includes(t) || r.push({
|
|
107
108
|
code: "PARENT_CHILD_MISMATCH",
|
|
108
|
-
message: `Element "${t}" has parentId "${
|
|
109
|
+
message: `Element "${t}" has parentId "${n.parentId}" but parent doesn't have it in items`,
|
|
109
110
|
elementId: t,
|
|
110
111
|
details: {
|
|
111
|
-
parentId:
|
|
112
|
+
parentId: n.parentId,
|
|
112
113
|
parentItems: i
|
|
113
114
|
}
|
|
114
115
|
});
|
|
115
116
|
}
|
|
116
117
|
});
|
|
117
|
-
},
|
|
118
|
-
let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Set(),
|
|
119
|
-
if (t.has(i)) return
|
|
118
|
+
}, g = () => {
|
|
119
|
+
let e = /* @__PURE__ */ new Set(), t = /* @__PURE__ */ new Set(), n = (i) => {
|
|
120
|
+
if (t.has(i)) return r.push({
|
|
120
121
|
code: "CIRCULAR_REFERENCE",
|
|
121
122
|
message: `Circular reference detected involving element "${i}"`,
|
|
122
123
|
elementId: i
|
|
123
124
|
}), !0;
|
|
124
125
|
if (e.has(i)) return !1;
|
|
125
126
|
e.add(i), t.add(i);
|
|
126
|
-
let a =
|
|
127
|
+
let a = u(i);
|
|
127
128
|
if (a?.definition?.items) {
|
|
128
|
-
for (let e of a.definition.items) if (
|
|
129
|
+
for (let e of a.definition.items) if (n(e)) return t.delete(i), !0;
|
|
129
130
|
}
|
|
130
131
|
let o = /* @__PURE__ */ new Set(), s = a;
|
|
131
132
|
for (; s?.definition?.parentId;) {
|
|
132
|
-
if (o.has(s.id)) return
|
|
133
|
+
if (o.has(s.id)) return r.push({
|
|
133
134
|
code: "CIRCULAR_PARENT_REFERENCE",
|
|
134
135
|
message: `Circular parent reference detected involving element "${s.id}"`,
|
|
135
136
|
elementId: s.id
|
|
136
137
|
}), !0;
|
|
137
|
-
o.add(s.id), s =
|
|
138
|
+
o.add(s.id), s = u(s.definition.parentId);
|
|
138
139
|
}
|
|
139
140
|
return t.delete(i), !1;
|
|
140
141
|
};
|
|
141
|
-
Object.keys(
|
|
142
|
-
e.has(t) ||
|
|
142
|
+
Object.keys(a).forEach((t) => {
|
|
143
|
+
e.has(t) || n(t);
|
|
143
144
|
});
|
|
144
|
-
},
|
|
145
|
+
}, _ = () => {
|
|
145
146
|
let e = /* @__PURE__ */ new Set(), t = 0;
|
|
146
|
-
|
|
147
|
-
e.has(
|
|
147
|
+
o.forEach((n, i) => {
|
|
148
|
+
e.has(n) && r.push({
|
|
148
149
|
code: "DUPLICATE_PAGE",
|
|
149
|
-
message: `Duplicate page ID "${
|
|
150
|
-
elementId:
|
|
151
|
-
}), e.add(
|
|
152
|
-
let a =
|
|
150
|
+
message: `Duplicate page ID "${n}" in pages array at index ${i}`,
|
|
151
|
+
elementId: n
|
|
152
|
+
}), e.add(n);
|
|
153
|
+
let a = u(n);
|
|
153
154
|
if (!a) {
|
|
154
|
-
|
|
155
|
+
r.push({
|
|
155
156
|
code: "INVALID_PAGE_REFERENCE",
|
|
156
|
-
message: `Page ID "${
|
|
157
|
-
elementId:
|
|
157
|
+
message: `Page ID "${n}" in pages array doesn't exist in flat`,
|
|
158
|
+
elementId: n
|
|
158
159
|
});
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
161
|
-
a.definition.type !== "page" &&
|
|
162
|
+
a.definition.type !== "page" && r.push({
|
|
162
163
|
code: "INVALID_PAGE_TYPE",
|
|
163
|
-
message: `Element "${
|
|
164
|
-
elementId:
|
|
165
|
-
}), a.definition.rootId !==
|
|
164
|
+
message: `Element "${n}" in pages array has type "${a.definition.type}" instead of "page"`,
|
|
165
|
+
elementId: n
|
|
166
|
+
}), a.definition.rootId !== n && r.push({
|
|
166
167
|
code: "PAGE_ROOT_ID_MISMATCH",
|
|
167
|
-
message: `Page "${
|
|
168
|
-
elementId:
|
|
168
|
+
message: `Page "${n}" has rootId "${a.definition.rootId}" instead of its own ID`,
|
|
169
|
+
elementId: n
|
|
169
170
|
}), a.attributes?.default && t++;
|
|
170
|
-
}), t === 0 &&
|
|
171
|
+
}), t === 0 && o.length > 0 ? i.push({
|
|
171
172
|
code: "NO_DEFAULT_PAGE",
|
|
172
173
|
message: "No default page found in schema"
|
|
173
|
-
}) : t > 1 &&
|
|
174
|
+
}) : t > 1 && i.push({
|
|
174
175
|
code: "MULTIPLE_DEFAULT_PAGES",
|
|
175
176
|
message: `Found ${t} default pages, expected 1`
|
|
176
177
|
});
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
let t =
|
|
178
|
+
}, v = () => {
|
|
179
|
+
f().forEach((e) => {
|
|
180
|
+
let t = u(e);
|
|
180
181
|
if (!t) return;
|
|
181
|
-
let
|
|
182
|
-
let i =
|
|
183
|
-
i?.definition && (i.definition.rootId !== e &&
|
|
182
|
+
let n = (t) => {
|
|
183
|
+
let i = u(t);
|
|
184
|
+
i?.definition && (i.definition.rootId !== e && r.push({
|
|
184
185
|
code: "ROOT_ID_MISMATCH",
|
|
185
186
|
message: `Element "${t}" has rootId "${i.definition.rootId}" but should be "${e}" (its root's ID)`,
|
|
186
187
|
elementId: t,
|
|
@@ -188,98 +189,110 @@ var t = (t) => {
|
|
|
188
189
|
expectedRootId: e,
|
|
189
190
|
actualRootId: i.definition.rootId
|
|
190
191
|
}
|
|
191
|
-
}), i.definition.items && i.definition.items.forEach((e) =>
|
|
192
|
+
}), i.definition.items && i.definition.items.forEach((e) => n(e)));
|
|
192
193
|
};
|
|
193
|
-
t.definition.items && t.definition.items.forEach((e) =>
|
|
194
|
+
t.definition.items && t.definition.items.forEach((e) => n(e));
|
|
194
195
|
});
|
|
195
|
-
},
|
|
196
|
+
}, y = () => {
|
|
196
197
|
let e = /* @__PURE__ */ new Set();
|
|
197
|
-
|
|
198
|
+
s.forEach((t, n) => {
|
|
198
199
|
if (!t.id) {
|
|
199
|
-
|
|
200
|
+
r.push({
|
|
200
201
|
code: "INVALID_FOLDER",
|
|
201
|
-
message: `Page folder at index ${
|
|
202
|
+
message: `Page folder at index ${n} has no id`
|
|
202
203
|
});
|
|
203
204
|
return;
|
|
204
205
|
}
|
|
205
|
-
e.has(t.id) &&
|
|
206
|
+
e.has(t.id) && r.push({
|
|
206
207
|
code: "DUPLICATE_FOLDER",
|
|
207
208
|
message: `Duplicate folder ID "${t.id}"`,
|
|
208
209
|
elementId: t.id
|
|
209
|
-
}), e.add(t.id), t.parentId && (e.has(t.parentId) ||
|
|
210
|
+
}), e.add(t.id), t.parentId && (e.has(t.parentId) || r.push({
|
|
210
211
|
code: "ORPHANED_FOLDER_PARENT",
|
|
211
212
|
message: `Folder "${t.id}" has non-existent parent "${t.parentId}"`,
|
|
212
213
|
elementId: t.id,
|
|
213
214
|
details: { parentId: t.parentId }
|
|
214
215
|
}));
|
|
215
216
|
});
|
|
216
|
-
},
|
|
217
|
+
}, b = () => {
|
|
218
|
+
let e = /* @__PURE__ */ new Set();
|
|
219
|
+
d().forEach((n) => {
|
|
220
|
+
let i = t(u, n);
|
|
221
|
+
i.length === 0 || e.has(i[0]) || (i.forEach((t) => e.add(t)), r.push({
|
|
222
|
+
code: "LAYOUT_CYCLE",
|
|
223
|
+
message: `Layouts sit inside each other in a circle: ${i.join(" → ")}`,
|
|
224
|
+
elementId: n,
|
|
225
|
+
details: { cycle: i }
|
|
226
|
+
}));
|
|
227
|
+
});
|
|
228
|
+
}, x = (e) => {
|
|
217
229
|
let t = /* @__PURE__ */ new Set(), n = (e) => {
|
|
218
230
|
if (t.has(e)) return;
|
|
219
231
|
t.add(e);
|
|
220
|
-
let r =
|
|
232
|
+
let r = u(e);
|
|
221
233
|
r?.definition?.items && r.definition.items.forEach((e) => n(e));
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
t
|
|
234
|
+
}, r = /* @__PURE__ */ new Set(), o = (e) => {
|
|
235
|
+
if (r.has(e)) return;
|
|
236
|
+
r.add(e), n(e);
|
|
237
|
+
let t = u(e)?.attributes;
|
|
238
|
+
t?.layout && o(t.layout), t?.layoutContainer && n(t.layoutContainer);
|
|
226
239
|
};
|
|
227
|
-
e ?
|
|
228
|
-
let
|
|
229
|
-
Object.keys(
|
|
230
|
-
let n =
|
|
231
|
-
n && !t.has(e) && n.definition.type !== "page" && !
|
|
240
|
+
e ? o(e) : f().forEach(o);
|
|
241
|
+
let s = new Set(f());
|
|
242
|
+
Object.keys(a).forEach((e) => {
|
|
243
|
+
let n = u(e);
|
|
244
|
+
n && !t.has(e) && n.definition.type !== "page" && !s.has(e) && i.push({
|
|
232
245
|
code: "ORPHANED_ELEMENT",
|
|
233
246
|
message: `Element "${e}" is not reachable from any page or layout`,
|
|
234
247
|
elementId: e
|
|
235
248
|
});
|
|
236
249
|
});
|
|
237
|
-
},
|
|
250
|
+
}, S = () => {
|
|
238
251
|
let e = /* @__PURE__ */ new Set();
|
|
239
|
-
|
|
252
|
+
c.forEach((t, n) => {
|
|
240
253
|
if (!t.name) {
|
|
241
|
-
|
|
254
|
+
r.push({
|
|
242
255
|
code: "INVALID_VARIABLE",
|
|
243
|
-
message: `Variable at index ${
|
|
256
|
+
message: `Variable at index ${n} has no name`
|
|
244
257
|
});
|
|
245
258
|
return;
|
|
246
259
|
}
|
|
247
|
-
e.has(t.name) &&
|
|
260
|
+
e.has(t.name) && r.push({
|
|
248
261
|
code: "DUPLICATE_VARIABLE",
|
|
249
262
|
message: `Duplicate variable name "${t.name}"`,
|
|
250
263
|
details: { variableName: t.name }
|
|
251
|
-
}), e.add(t.name), t.type ||
|
|
264
|
+
}), e.add(t.name), t.type || i.push({
|
|
252
265
|
code: "MISSING_VARIABLE_TYPE",
|
|
253
266
|
message: `Variable "${t.name}" has no type`,
|
|
254
267
|
details: { variableName: t.name }
|
|
255
268
|
});
|
|
256
269
|
});
|
|
257
|
-
},
|
|
258
|
-
Object.values(
|
|
259
|
-
t && (e(t.id) ||
|
|
270
|
+
}, C = () => {
|
|
271
|
+
Object.values(a).forEach((t) => {
|
|
272
|
+
t && (e(t.id) || r.push({
|
|
260
273
|
code: "INVALID_ELEMENT_ID",
|
|
261
274
|
message: `Element "${t.id}" has a name that must start with a letter, then letters, numbers, hyphens and underscores (no hyphen or underscore at the start, and no hyphen at the end)`,
|
|
262
275
|
elementId: t.id
|
|
263
276
|
}));
|
|
264
277
|
});
|
|
265
|
-
},
|
|
266
|
-
let t = /* @__PURE__ */ new Set(),
|
|
267
|
-
Object.values(
|
|
268
|
-
if (!
|
|
269
|
-
t.add(
|
|
270
|
-
let
|
|
271
|
-
|
|
272
|
-
}), Object.values(
|
|
273
|
-
|
|
278
|
+
}, w = (e) => {
|
|
279
|
+
let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Map();
|
|
280
|
+
Object.values(a).forEach((r) => {
|
|
281
|
+
if (!r) return;
|
|
282
|
+
t.add(r.id);
|
|
283
|
+
let a = e?.[r.definition.type];
|
|
284
|
+
a && i.set(r.id, a), n.add(r.definition.type);
|
|
285
|
+
}), Object.values(a).forEach((a) => {
|
|
286
|
+
a && a.definition.bindings && Object.entries(a.definition.bindings).forEach(([o, s]) => {
|
|
274
287
|
(s ?? []).forEach((s) => {
|
|
275
288
|
let c = s.source.split(".")[0], l = c.indexOf("_");
|
|
276
289
|
if (l === -1) return;
|
|
277
|
-
let u = c.slice(0, l), d = c.slice(l + 1), f = `Element "${
|
|
290
|
+
let u = c.slice(0, l), d = c.slice(l + 1), f = `Element "${a.id}" binds ${o}.${s.to} to "${s.source}"`;
|
|
278
291
|
if (!t.has(d)) {
|
|
279
|
-
(e ||
|
|
292
|
+
(e || n.has(u)) && r.push({
|
|
280
293
|
code: "UNRESOLVED_BINDING_SOURCE",
|
|
281
294
|
message: `${f}, but no element answers to the name "${d}"`,
|
|
282
|
-
elementId:
|
|
295
|
+
elementId: a.id,
|
|
283
296
|
details: {
|
|
284
297
|
source: s.source,
|
|
285
298
|
elementId: d
|
|
@@ -287,11 +300,11 @@ var t = (t) => {
|
|
|
287
300
|
});
|
|
288
301
|
return;
|
|
289
302
|
}
|
|
290
|
-
let p =
|
|
291
|
-
p && p !== u &&
|
|
303
|
+
let p = i.get(d);
|
|
304
|
+
p && p !== u && r.push({
|
|
292
305
|
code: "MISMATCHED_BINDING_SOURCE",
|
|
293
306
|
message: `${f}, but "${d}" publishes its source as "${p}_${d}"`,
|
|
294
|
-
elementId:
|
|
307
|
+
elementId: a.id,
|
|
295
308
|
details: {
|
|
296
309
|
source: s.source,
|
|
297
310
|
elementId: d,
|
|
@@ -301,54 +314,54 @@ var t = (t) => {
|
|
|
301
314
|
});
|
|
302
315
|
});
|
|
303
316
|
});
|
|
304
|
-
},
|
|
305
|
-
let t = new Set(Object.keys(
|
|
306
|
-
Object.values(
|
|
307
|
-
let i =
|
|
317
|
+
}, T = () => {
|
|
318
|
+
let t = new Set(Object.keys(a));
|
|
319
|
+
Object.values(a).forEach((n) => {
|
|
320
|
+
let i = n?.definition.interactions;
|
|
308
321
|
if (!i) return;
|
|
309
322
|
let a = Object.keys(i);
|
|
310
323
|
Object.entries(i).forEach(([i, o]) => {
|
|
311
|
-
e(i) ||
|
|
324
|
+
e(i) || r.push({
|
|
312
325
|
code: "INVALID_INTERACTION_ID",
|
|
313
|
-
message: `Interaction "${i}" on element "${
|
|
314
|
-
elementId:
|
|
315
|
-
}), o.id !== i &&
|
|
326
|
+
message: `Interaction "${i}" on element "${n.id}" has a name that must start with a letter, then letters, numbers, hyphens and underscores — a later step reads it as {{ ${i}.field }}`,
|
|
327
|
+
elementId: n.id
|
|
328
|
+
}), o.id !== i && r.push({
|
|
316
329
|
code: "INTERACTION_ID_MISMATCH",
|
|
317
|
-
message: `Interaction id "${o.id}" on element "${
|
|
318
|
-
elementId:
|
|
330
|
+
message: `Interaction id "${o.id}" on element "${n.id}" doesn't match its key "${i}"`,
|
|
331
|
+
elementId: n.id
|
|
319
332
|
}), ["beforeNode", "afterNode"].forEach((e) => {
|
|
320
333
|
let t = o[e];
|
|
321
|
-
t && !a.includes(t) &&
|
|
334
|
+
t && !a.includes(t) && r.push({
|
|
322
335
|
code: "BROKEN_FLOW_LINK",
|
|
323
|
-
message: `Interaction "${o.id}" on element "${
|
|
324
|
-
elementId:
|
|
336
|
+
message: `Interaction "${o.id}" on element "${n.id}" names ${e} "${t}", which is not a node of this flow`,
|
|
337
|
+
elementId: n.id
|
|
325
338
|
});
|
|
326
|
-
}), o.flowId && !a.includes(o.flowId) &&
|
|
339
|
+
}), o.flowId && !a.includes(o.flowId) && r.push({
|
|
327
340
|
code: "BROKEN_FLOW_ID",
|
|
328
|
-
message: `Interaction "${o.id}" on element "${
|
|
329
|
-
elementId:
|
|
330
|
-
}), o.type === "callback" && o.elementId && !t.has(o.elementId) &&
|
|
341
|
+
message: `Interaction "${o.id}" on element "${n.id}" belongs to flow "${o.flowId}", which is not a node of this flow`,
|
|
342
|
+
elementId: n.id
|
|
343
|
+
}), o.type === "callback" && o.elementId && !t.has(o.elementId) && r.push({
|
|
331
344
|
code: "UNRESOLVED_INTERACTION_TARGET",
|
|
332
|
-
message: `Interaction "${o.id}" on element "${
|
|
333
|
-
elementId:
|
|
345
|
+
message: `Interaction "${o.id}" on element "${n.id}" runs against "${o.elementId}", but no element answers to that name`,
|
|
346
|
+
elementId: n.id
|
|
334
347
|
});
|
|
335
348
|
});
|
|
336
349
|
});
|
|
337
|
-
},
|
|
338
|
-
Object.values(
|
|
350
|
+
}, E = () => {
|
|
351
|
+
Object.values(a).forEach((e) => {
|
|
339
352
|
if (!e || !e.definition.bindings) return;
|
|
340
353
|
let t = /* @__PURE__ */ new Set();
|
|
341
|
-
Object.entries(e.definition.bindings).forEach(([
|
|
354
|
+
Object.entries(e.definition.bindings).forEach(([n, i]) => {
|
|
342
355
|
(i ?? []).forEach((i) => {
|
|
343
356
|
if (!i.id) {
|
|
344
|
-
|
|
357
|
+
r.push({
|
|
345
358
|
code: "MISSING_BINDING_ID",
|
|
346
|
-
message: `Element "${e.id}" has a ${
|
|
359
|
+
message: `Element "${e.id}" has a ${n} binding to "${i.to}" with no id`,
|
|
347
360
|
elementId: e.id
|
|
348
361
|
});
|
|
349
362
|
return;
|
|
350
363
|
}
|
|
351
|
-
t.has(i.id) &&
|
|
364
|
+
t.has(i.id) && r.push({
|
|
352
365
|
code: "DUPLICATE_BINDING_ID",
|
|
353
366
|
message: `Element "${e.id}" has two bindings called "${i.id}"`,
|
|
354
367
|
elementId: e.id,
|
|
@@ -360,22 +373,22 @@ var t = (t) => {
|
|
|
360
373
|
};
|
|
361
374
|
return { validate: (e) => {
|
|
362
375
|
let { baseElementId: t } = e ?? {};
|
|
363
|
-
return
|
|
364
|
-
valid:
|
|
365
|
-
errors:
|
|
366
|
-
warnings:
|
|
376
|
+
return p() ? (m(), h(), g(), _(), v(), y(), b(), x(t), S(), C(), w(e?.sourceTypes), E(), T(), {
|
|
377
|
+
valid: r.length === 0,
|
|
378
|
+
errors: r,
|
|
379
|
+
warnings: i
|
|
367
380
|
}) : {
|
|
368
381
|
valid: !1,
|
|
369
|
-
errors:
|
|
370
|
-
warnings:
|
|
382
|
+
errors: r,
|
|
383
|
+
warnings: i
|
|
371
384
|
};
|
|
372
385
|
} };
|
|
373
|
-
},
|
|
374
|
-
let i =
|
|
386
|
+
}, r = (e, t) => n(e).validate(t), i = (e, t, n) => {
|
|
387
|
+
let i = r(e, t);
|
|
375
388
|
if (!i.valid) {
|
|
376
|
-
let e = `Invalid schema${
|
|
389
|
+
let e = `Invalid schema${n ? ` (${n})` : ""}: ${i.errors.map((e) => e.message).join("; ")}`;
|
|
377
390
|
throw Error(e);
|
|
378
391
|
}
|
|
379
|
-
},
|
|
392
|
+
}, a = (e, t) => r(e, t).valid;
|
|
380
393
|
//#endregion
|
|
381
|
-
export {
|
|
394
|
+
export { i as assertSchemaValid, a as isSchemaValid, r as validateSchema };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Element, Style } from '@plitzi/sdk-shared';
|
|
2
|
+
/**
|
|
3
|
+
* Why an element's style applies to nothing, if it does not.
|
|
4
|
+
*
|
|
5
|
+
* An `apiContainer` whose `subType` is empty — its default, the builder's "Container Tag: None" — renders its
|
|
6
|
+
* children and no element of its own, so a class with rules, a variant or a style binding on it styles nothing: its
|
|
7
|
+
* children sit straight in the parent's layout, and the gap or padding written on the provider is not there. Found on
|
|
8
|
+
* real spaces as a counter card with no card, a sticky sidebar that did not stick, and page sections that lost the
|
|
9
|
+
* gap between them.
|
|
10
|
+
*
|
|
11
|
+
* Needs the style document: the builder and the authoring package both give every element a class of its own, rules
|
|
12
|
+
* or not, so a class name on its own says nothing. A provider with no tag is legal and often right.
|
|
13
|
+
*/
|
|
14
|
+
export declare const styleWithoutTag: (element: Element, style: Pick<Style, "platform">) => string | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/helpers/styleWithoutTag.ts
|
|
2
|
+
var e = (e) => Object.keys(e ?? {}).length > 0, t = (t) => Object.values(t ?? {}).some(e), n = (n) => e(n.default) || t(n.states) || Object.values(n.variants ?? {}).some((n) => e(n.default) || t(n.states)), r = (e, t) => Object.values(e.platform).some((e) => Object.hasOwn(e, t) && Object.values(e[t].attributes).some(n)), i = (e, t) => {
|
|
3
|
+
if (e.definition.type !== "apiContainer") return;
|
|
4
|
+
let n = e.attributes.subType;
|
|
5
|
+
if (typeof n == "string" && n !== "") return;
|
|
6
|
+
let { styleSelectors: i, initialState: a, bindings: o } = e.definition, s = Object.values(i).some((e) => e.split(" ").some((e) => e && r(t, e))), c = Object.keys(a?.styleVariant ?? {}).length > 0, l = (o?.style?.length ?? 0) > 0;
|
|
7
|
+
if (s || c || l) return "has style but no `subType` (container tag), so it renders no element of its own and the style applies to nothing — its children lay out in its parent. Give it a tag (a `subType` of `div`, `section`, …) or move the style onto its parent or a child.";
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { i as styleWithoutTag };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plitzi/sdk-schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"license": "AGPL-3.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"types": "./dist/helpers/schemaValidator.d.ts",
|
|
27
27
|
"import": "./dist/helpers/schemaValidator.mjs"
|
|
28
28
|
},
|
|
29
|
+
"./helpers/styleWithoutTag": {
|
|
30
|
+
"types": "./dist/helpers/styleWithoutTag.d.ts",
|
|
31
|
+
"import": "./dist/helpers/styleWithoutTag.mjs"
|
|
32
|
+
},
|
|
29
33
|
"./index": {
|
|
30
34
|
"types": "./dist/index.d.ts",
|
|
31
35
|
"import": "./dist/index.mjs"
|
|
@@ -45,14 +49,14 @@
|
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
47
51
|
"@plitzi/plitzi-ui": "^1.6.21",
|
|
48
|
-
"@plitzi/sdk-shared": "0.
|
|
49
|
-
"@plitzi/sdk-style": "0.
|
|
52
|
+
"@plitzi/sdk-shared": "0.37.0",
|
|
53
|
+
"@plitzi/sdk-style": "0.37.0",
|
|
50
54
|
"prop-types": "^15.8.1"
|
|
51
55
|
},
|
|
52
56
|
"devDependencies": {
|
|
53
57
|
"eslint": "^9.39.5",
|
|
54
58
|
"typescript": "^6.0.3",
|
|
55
59
|
"vite": "^8.3.0",
|
|
56
|
-
"vitest": "^5.0.
|
|
60
|
+
"vitest": "^5.0.1"
|
|
57
61
|
}
|
|
58
62
|
}
|