@plitzi/sdk-schema 0.30.19 → 0.31.1

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,26 @@
1
1
  # @plitzi/sdk-schema
2
2
 
3
+ ## 0.31.1
4
+
5
+ ### Patch Changes
6
+
7
+ - v0.31.1
8
+ - Updated dependencies
9
+ - @plitzi/sdk-shared@0.31.1
10
+ - @plitzi/sdk-style@0.31.1
11
+
12
+ ## 0.31.0
13
+
14
+ ### Minor Changes
15
+
16
+ - v0.31.0
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+ - @plitzi/sdk-shared@0.31.0
22
+ - @plitzi/sdk-style@0.31.0
23
+
3
24
  ## 0.30.19
4
25
 
5
26
  ### Patch Changes
@@ -1,3 +1,4 @@
1
+ import { SchemaValidationResult } from './schemaValidator';
1
2
  import { Style, Element, Schema, DropPosition, SchemaVariable } from '@plitzi/sdk-shared';
2
3
  export type FlatMapProps = {
3
4
  flat?: Schema['flat'];
@@ -10,9 +11,7 @@ declare class FlatMap {
10
11
  addElement: (data: Element, to: Element["id"], dropPosition?: DropPosition, initialItems?: Record<Element["id"], Element>) => boolean;
11
12
  updateElement: (element?: Element) => boolean;
12
13
  moveElement: (from: Element["id"], to: Element["id"], elementId: Element["id"], dropPosition?: DropPosition) => boolean;
13
- getElement: (elementId: Element["id"]) => Element<Omit<{
14
- [key: string]: unknown;
15
- }, "subType">>;
14
+ getElement: (elementId: Element["id"]) => Element<Record<string, unknown>>;
16
15
  cloneElements: (elementId: Element["id"], parentId?: Element["id"], rootId?: Element["id"], excludeRoot?: boolean) => {
17
16
  acum: Record<Element["id"], Element>;
18
17
  item?: Element;
@@ -34,16 +33,15 @@ declare class FlatMap {
34
33
  elementsStyle: Style;
35
34
  variables: SchemaVariable[];
36
35
  };
37
- getElementVariables: (style: Style, elementId: Element["id"], flat?: Record<string, Element<Omit<{
38
- [key: string]: unknown;
39
- }, "subType">>>, variables?: SchemaVariable[]) => SchemaVariable[];
36
+ validate: () => SchemaValidationResult;
37
+ isValid: () => boolean;
38
+ assertValid: (context?: string) => void;
39
+ getElementVariables: (style: Style, elementId: Element["id"], flat?: Record<string, Element<Record<string, unknown>>>, variables?: SchemaVariable[]) => SchemaVariable[];
40
40
  static getInstance: (props: FlatMapProps) => FlatMap;
41
41
  static addElement: (flat: Schema["flat"], data: Element, to: Element["id"], dropPosition?: DropPosition, initialItems?: Record<Element["id"], Element>) => boolean;
42
42
  static updateElement: (flat: Schema["flat"], element: Element) => boolean;
43
43
  static moveElement: (flat: Schema["flat"], from: Element["id"], to: Element["id"], elementId: Element["id"], dropPosition?: DropPosition) => boolean;
44
- static getElement: (flat: Schema["flat"], elementId: Element["id"]) => Element<Omit<{
45
- [key: string]: unknown;
46
- }, "subType">>;
44
+ static getElement: (flat: Schema["flat"], elementId: Element["id"]) => Element<Record<string, unknown>>;
47
45
  static cloneElements: (flat: Schema["flat"], elementId: Element["id"], parentId?: Element["id"], rootId?: Element["id"], excludeRoot?: boolean) => {
48
46
  acum: Record<Element["id"], Element>;
49
47
  item?: Element;
@@ -66,5 +64,8 @@ declare class FlatMap {
66
64
  variables: SchemaVariable[];
67
65
  };
68
66
  static getElementVariables: (schema: Schema, style: Style, elementId: Element["id"]) => SchemaVariable[];
67
+ static validate: (schema: Schema) => SchemaValidationResult;
68
+ static isValid: (schema: Schema) => boolean;
69
+ static assertValid: (schema: Schema, context?: string) => void;
69
70
  }
70
71
  export default FlatMap;
@@ -1,10 +1,11 @@
1
- import { get as e, set as t } from "@plitzi/plitzi-ui/helpers";
2
- import { generateID as n } from "@plitzi/sdk-shared/helpers/utils";
3
- import { VARIABLE_REGEX as r } from "@plitzi/sdk-shared/schema/schemaConstants";
4
- import { EMPTY_STYLE_SCHEMA as i } from "@plitzi/sdk-shared/style/styleConstants";
5
- import a from "@plitzi/sdk-style/helpers/calculateInheriting";
1
+ import { validateSchema as e } from "./schemaValidator.mjs";
2
+ import { get as t, set as n } from "@plitzi/plitzi-ui/helpers";
3
+ import { generateID as r } from "@plitzi/sdk-shared/helpers/utils";
4
+ import { EMPTY_SCHEMA as i, VARIABLE_REGEX as a } from "@plitzi/sdk-shared/schema/schemaConstants";
5
+ import { EMPTY_STYLE_SCHEMA as o } from "@plitzi/sdk-shared/style/styleConstants";
6
+ import s from "@plitzi/sdk-style/helpers/calculateInheriting";
6
7
  //#region src/helpers/FlatMap.ts
7
- var o = class {
8
+ var c = class {
8
9
  flat;
9
10
  variables;
10
11
  constructor(e = {}) {
@@ -12,7 +13,7 @@ var o = class {
12
13
  if (!t) throw Error("Flat is required");
13
14
  this.flat = t, this.variables = n ?? [];
14
15
  }
15
- addElement = (n, r, i = "inside", a = {}) => {
16
+ addElement = (e, r, i = "inside", a = {}) => {
16
17
  let o;
17
18
  if (i !== "custom") {
18
19
  if (i !== "inside") {
@@ -22,26 +23,26 @@ var o = class {
22
23
  } else o = this.flat[r];
23
24
  if (!o) return !1;
24
25
  }
25
- if (i !== "custom" && (this.flat[n.id] || !Array.isArray(e(o, "definition.items"))) || !this.isValidElement(n)) return !1;
26
- switch (t(this.flat, n.id, n), i) {
26
+ if (i !== "custom" && (this.flat[e.id] || !Array.isArray(t(o, "definition.items"))) || !this.isValidElement(e)) return !1;
27
+ switch (n(this.flat, e.id, e), i) {
27
28
  case "left":
28
29
  case "top": {
29
- let i = e(o, "definition.items", []);
30
- if (i.splice(i.findIndex((e) => e === r), 0, n.id), !o) return !1;
31
- t(this.flat, `${o.id}.definition.items`, i), t(this.flat, `${n.id}.definition.parentId`, o.id), t(this.flat, `${n.id}.definition.rootId`, o.definition.rootId);
30
+ let i = t(o, "definition.items", []);
31
+ if (i.splice(i.findIndex((e) => e === r), 0, e.id), !o) return !1;
32
+ n(this.flat, `${o.id}.definition.items`, i), n(this.flat, `${e.id}.definition.parentId`, o.id), n(this.flat, `${e.id}.definition.rootId`, o.definition.rootId);
32
33
  break;
33
34
  }
34
35
  case "right":
35
36
  case "bottom": {
36
- let i = e(o, "definition.items", []);
37
- if (i.splice(i.findIndex((e) => e === r) + 1, 0, n.id), !o) return !1;
38
- t(this.flat, `${o.id}.definition.items`, i), t(this.flat, `${n.id}.definition.parentId`, o.id), t(this.flat, `${n.id}.definition.rootId`, o.definition.rootId);
37
+ let i = t(o, "definition.items", []);
38
+ if (i.splice(i.findIndex((e) => e === r) + 1, 0, e.id), !o) return !1;
39
+ n(this.flat, `${o.id}.definition.items`, i), n(this.flat, `${e.id}.definition.parentId`, o.id), n(this.flat, `${e.id}.definition.rootId`, o.definition.rootId);
39
40
  break;
40
41
  }
41
42
  case "inside": {
42
- let i = e(o, "definition.items", []);
43
+ let i = t(o, "definition.items", []);
43
44
  if (!o) return !1;
44
- t(this.flat, `${r}.definition.items`, [...i, n.id]), t(this.flat, `${n.id}.definition.parentId`, r), t(this.flat, `${n.id}.definition.rootId`, o.definition.rootId);
45
+ n(this.flat, `${r}.definition.items`, [...i, e.id]), n(this.flat, `${e.id}.definition.parentId`, r), n(this.flat, `${e.id}.definition.rootId`, o.definition.rootId);
45
46
  break;
46
47
  }
47
48
  case "custom": break;
@@ -52,19 +53,19 @@ var o = class {
52
53
  }), !0;
53
54
  };
54
55
  updateElement = (e) => !e || !this.flat[e.id] ? !1 : (this.flat[e.id] = e, !0);
55
- moveElement = (n, r, i, a = "inside") => {
56
- if (i === r || !this.flat[n]) return !1;
56
+ moveElement = (e, r, i, a = "inside") => {
57
+ if (i === r || !this.flat[e]) return !1;
57
58
  let o = this.flat[r];
58
59
  if (!o) return !1;
59
60
  let s = this.flat[r];
60
61
  for (; s;) {
61
- let t = e(s, "definition.parentId");
62
- if (!t) break;
62
+ let e = t(s, "definition.parentId");
63
+ if (!e) break;
63
64
  if (s.id === i) return !1;
64
- s = this.flat[t];
65
+ s = this.flat[e];
65
66
  }
66
67
  if (!s) return !1;
67
- let c = e(this.flat, `${n}.definition.items`, []).filter((e) => e !== i);
68
+ let c = t(this.flat, `${e}.definition.items`, []).filter((e) => e !== i);
68
69
  if ([
69
70
  "left",
70
71
  "top",
@@ -74,64 +75,64 @@ var o = class {
74
75
  if (!o.definition.parentId) return !1;
75
76
  let s = this.flat[o.definition.parentId];
76
77
  if (!s) return !1;
77
- let l = e(s, "definition.items", []);
78
- s.id === n && (l = c);
78
+ let l = t(s, "definition.items", []);
79
+ s.id === e && (l = c);
79
80
  let u = l.findIndex((e) => e === r);
80
- ["right", "bottom"].includes(a) && u++, l.splice(u, 0, i), t(this.flat, `${n}.definition.items`, c), t(this.flat, `${s.id}.definition.items`, l), t(this.flat, `${i}.definition.parentId`, s.id);
81
+ ["right", "bottom"].includes(a) && u++, l.splice(u, 0, i), n(this.flat, `${e}.definition.items`, c), n(this.flat, `${s.id}.definition.items`, l), n(this.flat, `${i}.definition.parentId`, s.id);
81
82
  } else if (a === "inside") {
82
83
  if (!this.flat[r]) return !1;
83
- let a = e(this.flat, `${r}.definition.items`, []);
84
- n === r && (a = c), a = [...a, i], t(this.flat, `${n}.definition.items`, c), t(this.flat, `${r}.definition.items`, a), t(this.flat, `${i}.definition.parentId`, r);
84
+ let a = t(this.flat, `${r}.definition.items`, []);
85
+ e === r && (a = c), a = [...a, i], n(this.flat, `${e}.definition.items`, c), n(this.flat, `${r}.definition.items`, a), n(this.flat, `${i}.definition.parentId`, r);
85
86
  }
86
87
  return !0;
87
88
  };
88
- getElement = (t) => e(this.flat, t);
89
- cloneElements = (r, i = "", a = "", o = !1) => {
89
+ getElement = (e) => t(this.flat, e);
90
+ cloneElements = (e, i = "", a = "", o = !1) => {
90
91
  let s = {
91
92
  acum: {},
92
93
  item: void 0
93
- }, c = {}, l = this.flat[r];
94
+ }, c = {}, l = this.flat[e];
94
95
  if (!l) return s;
95
- let u = [r, ...this.childTree(r)].reduce((e, t) => {
96
- let r = this.flat[t];
97
- return r ? (c[r.id] = n(r.id), a ? {
96
+ let u = [e, ...this.childTree(e)].reduce((e, t) => {
97
+ let n = this.flat[t];
98
+ return n ? (c[n.id] = r(n.id), a ? {
98
99
  ...e,
99
- [r.id]: {
100
- ...r,
100
+ [n.id]: {
101
+ ...n,
101
102
  definition: {
102
- ...r.definition,
103
+ ...n.definition,
103
104
  rootId: a
104
105
  }
105
106
  }
106
107
  } : {
107
108
  ...e,
108
- [r.id]: r
109
+ [n.id]: n
109
110
  }) : e;
110
111
  }, {});
111
112
  try {
112
- let e = JSON.stringify(u);
113
- e = Object.keys(c).reduce((e, t) => e.replace(new RegExp(t, "g"), c[t]), e), s.acum = JSON.parse(e), s.item = s.acum[c[r]];
113
+ let t = JSON.stringify(u);
114
+ t = Object.keys(c).reduce((e, t) => e.replace(new RegExp(t, "g"), c[t]), t), s.acum = JSON.parse(t), s.item = s.acum[c[e]];
114
115
  } catch (e) {
115
116
  return console.error("Error parsing elements", e), {
116
117
  acum: {},
117
118
  item: void 0
118
119
  };
119
120
  }
120
- o && delete s.acum[c[r]];
121
+ o && delete s.acum[c[e]];
121
122
  let d = this.flat[i];
122
- return (!d || !Array.isArray(e(d, "definition.items"))) && (i = e(d, "definition.parentId", e(l, "definition.parentId"))), i && t(s, "item.definition.parentId", i), s;
123
+ return (!d || !Array.isArray(t(d, "definition.items"))) && (i = t(d, "definition.parentId", t(l, "definition.parentId"))), i && n(s, "item.definition.parentId", i), s;
123
124
  };
124
- removeElement = (n, r = !1) => {
125
- let i = this.flat[n];
126
- if (!i || i.definition.type === "page" && !r || r && e(i, "attributes.default", !1)) return !1;
127
- let a = e(i, "definition.items");
125
+ removeElement = (e, r = !1) => {
126
+ let i = this.flat[e];
127
+ if (!i || i.definition.type === "page" && !r || r && t(i, "attributes.default", !1)) return !1;
128
+ let a = t(i, "definition.items");
128
129
  a && a.length > 0 && a.forEach((e) => this.removeElement(e));
129
- let o = e(i, "definition.parentId"), s = o ? this.flat[o] : void 0;
130
+ let o = t(i, "definition.parentId"), s = o ? this.flat[o] : void 0;
130
131
  if (o && s) {
131
- let { definition: { items: e = [] } } = s;
132
- t(s, "definition.items", e.filter((e) => e !== n)), this.flat[o] = s;
132
+ let { definition: { items: t = [] } } = s;
133
+ n(s, "definition.items", t.filter((t) => t !== e)), this.flat[o] = s;
133
134
  }
134
- return delete this.flat[n], !0;
135
+ return delete this.flat[e], !0;
135
136
  };
136
137
  addVariables = (e) => {
137
138
  if (e && e.length > 0) {
@@ -152,22 +153,22 @@ var o = class {
152
153
  return this.variables = this.variables.filter((t) => e.includes(t.name)), t !== this.variables.length;
153
154
  };
154
155
  removeVariable = (e) => this.removeVariables([e]);
155
- parentTree = (t) => {
156
- let n = this.flat[t], r = [];
156
+ parentTree = (e) => {
157
+ let n = this.flat[e], r = [];
157
158
  if (!n) return r;
158
159
  do {
159
- if (e(n, "definition.type") === "page") {
160
- let t = e(n, "attributes.layout"), i = e(n, "attributes.layoutContainer");
161
- t && i && r.push(i, ...this.parentTree(i));
160
+ if (t(n, "definition.type") === "page") {
161
+ let e = t(n, "attributes.layout"), i = t(n, "attributes.layoutContainer");
162
+ e && i && r.push(i, ...this.parentTree(i));
162
163
  }
163
- t !== n.id && r.push(n.id), n = e(this.flat, e(n, "definition.parentId"), void 0);
164
+ e !== n.id && r.push(n.id), n = t(this.flat, t(n, "definition.parentId"), void 0);
164
165
  } while (n);
165
166
  return r;
166
167
  };
167
- childTree = (t) => {
168
- let n = this.flat[t];
168
+ childTree = (e) => {
169
+ let n = this.flat[e];
169
170
  if (!n) return [];
170
- let r = [], i = e(n, "definition.items");
171
+ let r = [], i = t(n, "definition.items");
171
172
  return i && i.forEach((e) => r.push(e, ...this.childTree(e))), r;
172
173
  };
173
174
  isValidElement = (e) => {
@@ -177,57 +178,81 @@ var o = class {
177
178
  let { type: i, label: a, styleSelectors: o, rootId: s } = r;
178
179
  return !(!i || a === void 0 || typeof o != "object" || s === void 0);
179
180
  };
180
- flatAsTemplate = (n, r, o = !1) => {
181
- let s = i, c = [];
181
+ flatAsTemplate = (e, r, i = !1) => {
182
+ let a = {
183
+ ...o,
184
+ platform: {
185
+ desktop: {},
186
+ tablet: {},
187
+ mobile: {}
188
+ }
189
+ }, c = [];
182
190
  if (!r) return {
183
191
  elements: {
184
192
  acum: {},
185
193
  item: void 0
186
194
  },
187
- elementsStyle: s,
195
+ elementsStyle: a,
188
196
  variables: c
189
197
  };
190
- let l = e(this.flat, r);
198
+ let l = t(this.flat, r);
191
199
  if (!l) return {
192
200
  elements: {
193
201
  acum: {},
194
202
  item: void 0
195
203
  },
196
- elementsStyle: s,
204
+ elementsStyle: a,
197
205
  variables: c
198
206
  };
199
207
  let u = this.cloneElements(r, l.definition.parentId);
200
- return u.item ? (Object.values(u.acum).forEach((e) => {
201
- let { id: r } = e;
202
- if (t(u.acum, `${r}.definition.rootId`, u.item?.id), a(e, void 0, this.flat, n.platform, {}, { includeSelf: !0 }).tree.forEach((e) => {
203
- let { displayMode: t, name: r } = e;
204
- !(r in s.platform[t]) && r in n.platform[t] && (s.platform[t][r] = n.platform[t][r]);
208
+ return u.item ? (Object.values(u.acum).forEach((t) => {
209
+ let { id: r } = t;
210
+ if (n(u.acum, `${r}.definition.rootId`, u.item?.id), s(t, t.definition.type, this.flat, e.platform, {}, { includeSelf: !0 }).tree.forEach((t) => {
211
+ let { displayMode: n, name: r } = t;
212
+ !(r in a.platform[n]) && r in e.platform[n] && (a.platform[n][r] = e.platform[n][r]);
205
213
  }), this.variables.length > 0) {
206
- let e = this.getElementVariables(n, r, u.acum);
207
- c = [...c, ...e];
214
+ let t = this.getElementVariables(e, r, u.acum);
215
+ c = [...c, ...t];
208
216
  }
209
- }), t(u.acum, `${u.item.id}.definition.parentId`, null), o && delete u.acum[u.item.id], c.length > 1 && (c = [...new Set(c)]), {
217
+ }), n(u.acum, `${u.item.id}.definition.parentId`, null), i && delete u.acum[u.item.id], c.length > 1 && (c = [...new Set(c)]), {
210
218
  elements: u,
211
- elementsStyle: s,
219
+ elementsStyle: a,
212
220
  variables: c
213
221
  }) : {
214
222
  elements: {
215
223
  acum: {},
216
224
  item: void 0
217
225
  },
218
- elementsStyle: s,
226
+ elementsStyle: a,
219
227
  variables: c
220
228
  };
221
229
  };
222
- getElementVariables = (t, n, i = this.flat, a = this.variables) => {
223
- let o = [], s = e(i, `${n}.definition.styleSelectors`);
230
+ validate = () => e({
231
+ ...i.schema,
232
+ flat: this.flat,
233
+ variables: this.variables
234
+ });
235
+ isValid = () => this.validate().valid;
236
+ assertValid = (t) => {
237
+ let n = e({
238
+ ...i.schema,
239
+ flat: this.flat,
240
+ variables: this.variables
241
+ });
242
+ if (!n.valid) {
243
+ let e = `Invalid schema${t ? ` (${t})` : ""}: ${n.errors.map((e) => e.message).join("; ")}`;
244
+ throw Error(e);
245
+ }
246
+ };
247
+ getElementVariables = (e, n, r = this.flat, i = this.variables) => {
248
+ let o = [], s = t(r, `${n}.definition.styleSelectors`);
224
249
  if (!s) return o;
225
- let c = new RegExp(r, "g");
226
- return Object.values(s).filter(Boolean).forEach((e) => {
227
- Object.values(t.platform).forEach((t) => {
228
- let n = t[e];
250
+ let c = new RegExp(a, "g");
251
+ return Object.values(s).filter(Boolean).forEach((t) => {
252
+ Object.values(e.platform).forEach((e) => {
253
+ let n = e[t];
229
254
  n && [...JSON.stringify(n.attributes).matchAll(c)].forEach((e) => {
230
- let t = a.find((t) => t.name === e[1] || t.name === e[2]);
255
+ let t = i.find((t) => t.name === e[1] || t.name === e[2]);
231
256
  t && !o.find((e) => e.name === t.name) && o.push(t);
232
257
  });
233
258
  });
@@ -262,6 +287,15 @@ var o = class {
262
287
  variables: i
263
288
  }).getElementVariables(t, n);
264
289
  };
290
+ static validate = (t) => e(t);
291
+ static isValid = (t) => e(t).valid;
292
+ static assertValid = (t, n) => {
293
+ let r = e(t);
294
+ if (!r.valid) {
295
+ let e = `Invalid schema${n ? ` (${n})` : ""}: ${r.errors.map((e) => e.message).join("; ")}`;
296
+ throw Error(e);
297
+ }
298
+ };
265
299
  };
266
300
  //#endregion
267
- export { o as default };
301
+ export { c as default };
@@ -0,0 +1,18 @@
1
+ import { Schema } from '@plitzi/sdk-shared';
2
+ export type SchemaValidationOptions = {
3
+ baseElementId?: string;
4
+ };
5
+ export type SchemaValidationError = {
6
+ code: string;
7
+ message: string;
8
+ elementId?: string;
9
+ details?: unknown;
10
+ };
11
+ export type SchemaValidationResult = {
12
+ valid: boolean;
13
+ errors: SchemaValidationError[];
14
+ warnings: SchemaValidationError[];
15
+ };
16
+ export declare const validateSchema: (schema: Schema, options?: SchemaValidationOptions) => SchemaValidationResult;
17
+ export declare const assertSchemaValid: (schema: Schema, options?: SchemaValidationOptions, context?: string) => void;
18
+ export declare const isSchemaValid: (schema: Schema, options?: SchemaValidationOptions) => boolean;
@@ -0,0 +1,281 @@
1
+ //#region src/helpers/schemaValidator.ts
2
+ var e = (e) => {
3
+ let t = [], n = [], { flat: r, pages: i, pageFolders: a, variables: o } = e, s = (e) => !!r[e], c = (e) => r[e], l = () => !r || typeof r != "object" ? (t.push({
4
+ code: "INVALID_FLAT",
5
+ message: "Schema.flat must be a valid Record<string, Element>"
6
+ }), !1) : Array.isArray(i) ? Array.isArray(a) ? Array.isArray(o) ? !0 : (t.push({
7
+ code: "INVALID_VARIABLES",
8
+ message: "Schema.variables must be an array"
9
+ }), !1) : (t.push({
10
+ code: "INVALID_PAGE_FOLDERS",
11
+ message: "Schema.pageFolders must be an array"
12
+ }), !1) : (t.push({
13
+ code: "INVALID_PAGES",
14
+ message: "Schema.pages must be an array of element IDs"
15
+ }), !1), u = () => {
16
+ Object.entries(r).forEach(([e, r]) => {
17
+ if (!r) {
18
+ t.push({
19
+ code: "NULL_ELEMENT",
20
+ message: `Element with id "${e}" is null or undefined`,
21
+ elementId: e
22
+ });
23
+ return;
24
+ }
25
+ if (!r.id) {
26
+ t.push({
27
+ code: "MISSING_ID",
28
+ message: `Element at key "${e}" has no id property`
29
+ });
30
+ return;
31
+ }
32
+ if (r.id !== e && t.push({
33
+ code: "ID_MISMATCH",
34
+ message: `Element id "${r.id}" doesn't match flat key "${e}"`,
35
+ elementId: e
36
+ }), !r.definition) {
37
+ t.push({
38
+ code: "MISSING_DEFINITION",
39
+ message: "Element missing definition",
40
+ elementId: e
41
+ });
42
+ return;
43
+ }
44
+ let { definition: i, attributes: a } = r;
45
+ i.type || t.push({
46
+ code: "MISSING_TYPE",
47
+ message: "Element missing definition.type",
48
+ elementId: e
49
+ }), i.label === void 0 && t.push({
50
+ code: "MISSING_LABEL",
51
+ message: "Element missing definition.label",
52
+ elementId: e
53
+ }), i.rootId || t.push({
54
+ code: "MISSING_ROOT_ID",
55
+ message: "Element missing definition.rootId",
56
+ elementId: e
57
+ }), (!i.styleSelectors || typeof i.styleSelectors != "object") && t.push({
58
+ code: "MISSING_STYLE_SELECTORS",
59
+ message: "Element missing or invalid definition.styleSelectors",
60
+ elementId: e
61
+ }), a || n.push({
62
+ code: "MISSING_ATTRIBUTES",
63
+ message: "Element has no attributes",
64
+ elementId: e
65
+ }), i.items && (Array.isArray(i.items) ? i.items.forEach((n, r) => {
66
+ s(n) || t.push({
67
+ code: "ORPHANED_ITEM_REFERENCE",
68
+ message: `Element "${e}" references non-existent child "${n}" at items[${r}]`,
69
+ elementId: e,
70
+ details: {
71
+ childId: n,
72
+ index: r
73
+ }
74
+ });
75
+ }) : t.push({
76
+ code: "INVALID_ITEMS",
77
+ message: "Element.definition.items must be an array",
78
+ elementId: e
79
+ })), i.parentId && !s(i.parentId) && t.push({
80
+ code: "ORPHANED_PARENT_REFERENCE",
81
+ message: `Element "${e}" has parentId "${i.parentId}" but parent doesn't exist in flat`,
82
+ elementId: e,
83
+ details: { parentId: i.parentId }
84
+ });
85
+ });
86
+ }, d = () => {
87
+ Object.values(r).forEach((e) => {
88
+ if (!e?.definition) return;
89
+ let { id: n, definition: r } = e;
90
+ if (r.items && r.items.forEach((e) => {
91
+ let r = c(e);
92
+ r?.definition && r.definition.parentId !== n && t.push({
93
+ code: "PARENT_CHILD_MISMATCH",
94
+ message: `Element "${e}" is in "${n}".items but has parentId "${r.definition.parentId}"`,
95
+ elementId: e,
96
+ details: {
97
+ expectedParent: n,
98
+ actualParent: r.definition.parentId
99
+ }
100
+ });
101
+ }), r.parentId) {
102
+ let e = c(r.parentId);
103
+ if (!e?.definition) return;
104
+ let i = e.definition.items || [];
105
+ i.includes(n) || t.push({
106
+ code: "PARENT_CHILD_MISMATCH",
107
+ message: `Element "${n}" has parentId "${r.parentId}" but parent doesn't have it in items`,
108
+ elementId: n,
109
+ details: {
110
+ parentId: r.parentId,
111
+ parentItems: i
112
+ }
113
+ });
114
+ }
115
+ });
116
+ }, f = () => {
117
+ let e = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Set(), i = (r) => {
118
+ if (n.has(r)) return t.push({
119
+ code: "CIRCULAR_REFERENCE",
120
+ message: `Circular reference detected involving element "${r}"`,
121
+ elementId: r
122
+ }), !0;
123
+ if (e.has(r)) return !1;
124
+ e.add(r), n.add(r);
125
+ let a = c(r);
126
+ if (a?.definition?.items) {
127
+ for (let e of a.definition.items) if (i(e)) return n.delete(r), !0;
128
+ }
129
+ let o = /* @__PURE__ */ new Set(), s = a;
130
+ for (; s?.definition?.parentId;) {
131
+ if (o.has(s.id)) return t.push({
132
+ code: "CIRCULAR_PARENT_REFERENCE",
133
+ message: `Circular parent reference detected involving element "${s.id}"`,
134
+ elementId: s.id
135
+ }), !0;
136
+ o.add(s.id), s = c(s.definition.parentId);
137
+ }
138
+ return n.delete(r), !1;
139
+ };
140
+ Object.keys(r).forEach((t) => {
141
+ e.has(t) || i(t);
142
+ });
143
+ }, p = () => {
144
+ let e = /* @__PURE__ */ new Set(), r = 0;
145
+ i.forEach((n, i) => {
146
+ e.has(n) && t.push({
147
+ code: "DUPLICATE_PAGE",
148
+ message: `Duplicate page ID "${n}" in pages array at index ${i}`,
149
+ elementId: n
150
+ }), e.add(n);
151
+ let a = c(n);
152
+ if (!a) {
153
+ t.push({
154
+ code: "INVALID_PAGE_REFERENCE",
155
+ message: `Page ID "${n}" in pages array doesn't exist in flat`,
156
+ elementId: n
157
+ });
158
+ return;
159
+ }
160
+ a.definition.type !== "page" && t.push({
161
+ code: "INVALID_PAGE_TYPE",
162
+ message: `Element "${n}" in pages array has type "${a.definition.type}" instead of "page"`,
163
+ elementId: n
164
+ }), a.definition.rootId !== n && t.push({
165
+ code: "PAGE_ROOT_ID_MISMATCH",
166
+ message: `Page "${n}" has rootId "${a.definition.rootId}" instead of its own ID`,
167
+ elementId: n
168
+ }), a.attributes?.default && r++;
169
+ }), r === 0 && i.length > 0 ? n.push({
170
+ code: "NO_DEFAULT_PAGE",
171
+ message: "No default page found in schema"
172
+ }) : r > 1 && n.push({
173
+ code: "MULTIPLE_DEFAULT_PAGES",
174
+ message: `Found ${r} default pages, expected 1`
175
+ });
176
+ }, m = () => {
177
+ i.forEach((e) => {
178
+ let n = c(e);
179
+ if (!n) return;
180
+ let r = (n) => {
181
+ let i = c(n);
182
+ i?.definition && (i.definition.rootId !== e && t.push({
183
+ code: "ROOT_ID_MISMATCH",
184
+ message: `Element "${n}" has rootId "${i.definition.rootId}" but should be "${e}" (page's ID)`,
185
+ elementId: n,
186
+ details: {
187
+ expectedRootId: e,
188
+ actualRootId: i.definition.rootId
189
+ }
190
+ }), i.definition.items && i.definition.items.forEach((e) => r(e)));
191
+ };
192
+ n.definition.items && n.definition.items.forEach((e) => r(e));
193
+ });
194
+ }, h = () => {
195
+ let e = /* @__PURE__ */ new Set();
196
+ a.forEach((n, r) => {
197
+ if (!n.id) {
198
+ t.push({
199
+ code: "INVALID_FOLDER",
200
+ message: `Page folder at index ${r} has no id`
201
+ });
202
+ return;
203
+ }
204
+ e.has(n.id) && t.push({
205
+ code: "DUPLICATE_FOLDER",
206
+ message: `Duplicate folder ID "${n.id}"`,
207
+ elementId: n.id
208
+ }), e.add(n.id), n.parentId && (e.has(n.parentId) || t.push({
209
+ code: "ORPHANED_FOLDER_PARENT",
210
+ message: `Folder "${n.id}" has non-existent parent "${n.parentId}"`,
211
+ elementId: n.id,
212
+ details: { parentId: n.parentId }
213
+ }));
214
+ });
215
+ }, g = (e) => {
216
+ let t = /* @__PURE__ */ new Set(), a = (e) => {
217
+ if (t.has(e)) return;
218
+ t.add(e);
219
+ let n = c(e);
220
+ n?.definition?.items && n.definition.items.forEach((e) => a(e));
221
+ };
222
+ if (!e) i.forEach((e) => {
223
+ a(e);
224
+ let t = c(e);
225
+ t?.attributes?.layoutContainer && a(t.attributes.layoutContainer);
226
+ });
227
+ else if (e) {
228
+ a(e);
229
+ let t = c(e);
230
+ t?.attributes?.layoutContainer && a(t.attributes.layoutContainer);
231
+ }
232
+ Object.keys(r).forEach((e) => {
233
+ let r = c(e);
234
+ r && !t.has(e) && r.definition.type !== "page" && n.push({
235
+ code: "ORPHANED_ELEMENT",
236
+ message: `Element "${e}" is not reachable from any page`,
237
+ elementId: e
238
+ });
239
+ });
240
+ }, _ = () => {
241
+ let e = /* @__PURE__ */ new Set();
242
+ o.forEach((r, i) => {
243
+ if (!r.name) {
244
+ t.push({
245
+ code: "INVALID_VARIABLE",
246
+ message: `Variable at index ${i} has no name`
247
+ });
248
+ return;
249
+ }
250
+ e.has(r.name) && t.push({
251
+ code: "DUPLICATE_VARIABLE",
252
+ message: `Duplicate variable name "${r.name}"`,
253
+ details: { variableName: r.name }
254
+ }), e.add(r.name), r.type || n.push({
255
+ code: "MISSING_VARIABLE_TYPE",
256
+ message: `Variable "${r.name}" has no type`,
257
+ details: { variableName: r.name }
258
+ });
259
+ });
260
+ };
261
+ return { validate: (e) => {
262
+ let { baseElementId: r } = e ?? {};
263
+ return l() ? (u(), d(), f(), p(), m(), h(), g(r), _(), {
264
+ valid: t.length === 0,
265
+ errors: t,
266
+ warnings: n
267
+ }) : {
268
+ valid: !1,
269
+ errors: t,
270
+ warnings: n
271
+ };
272
+ } };
273
+ }, t = (t, n) => e(t).validate(n), n = (e, n, r) => {
274
+ let i = t(e, n);
275
+ if (!i.valid) {
276
+ let e = `Invalid schema${r ? ` (${r})` : ""}: ${i.errors.map((e) => e.message).join("; ")}`;
277
+ throw Error(e);
278
+ }
279
+ }, r = (e, n) => t(e, n).valid;
280
+ //#endregion
281
+ export { n as assertSchemaValid, r as isSchemaValid, t as validateSchema };
package/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ import { default as FlatMap } from './helpers/FlatMap';
2
2
  import { default as SchemaReducer } from './SchemaReducer';
3
3
  export * from './SchemaReducer';
4
4
  export * from './helpers/FlatMap';
5
+ export * from './helpers/schemaValidator';
5
6
  export { FlatMap, SchemaReducer };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
- import e from "./helpers/FlatMap.mjs";
2
- import t, { SchemaActions as n } from "./SchemaReducer.mjs";
3
- export { e as FlatMap, n as SchemaActions, t as SchemaReducer };
1
+ import { assertSchemaValid as e, isSchemaValid as t, validateSchema as n } from "./helpers/schemaValidator.mjs";
2
+ import r from "./helpers/FlatMap.mjs";
3
+ import i, { SchemaActions as a } from "./SchemaReducer.mjs";
4
+ export { r as FlatMap, a as SchemaActions, i as SchemaReducer, e as assertSchemaValid, t as isSchemaValid, n as validateSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-schema",
3
- "version": "0.30.19",
3
+ "version": "0.31.1",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist"
@@ -18,6 +18,10 @@
18
18
  "types": "./dist/helpers/FlatMap.d.ts",
19
19
  "import": "./dist/helpers/FlatMap.mjs"
20
20
  },
21
+ "./helpers/schemaValidator": {
22
+ "types": "./dist/helpers/schemaValidator.d.ts",
23
+ "import": "./dist/helpers/schemaValidator.mjs"
24
+ },
21
25
  "./index": {
22
26
  "types": "./dist/index.d.ts",
23
27
  "import": "./dist/index.mjs"
@@ -36,15 +40,15 @@
36
40
  "build:prod": "vite build && node ../sdk-shared/scripts/generate-exports.mjs"
37
41
  },
38
42
  "dependencies": {
39
- "@plitzi/plitzi-ui": "^1.6.2",
40
- "@plitzi/sdk-shared": "0.30.19",
41
- "@plitzi/sdk-style": "0.30.19",
43
+ "@plitzi/plitzi-ui": "^1.6.9",
44
+ "@plitzi/sdk-shared": "0.31.1",
45
+ "@plitzi/sdk-style": "0.31.1",
42
46
  "prop-types": "^15.8.1"
43
47
  },
44
48
  "devDependencies": {
45
49
  "eslint": "^9.39.4",
46
50
  "typescript": "^6.0.3",
47
- "vite": "^8.0.10",
48
- "vitest": "^4.1.5"
51
+ "vite": "^8.0.14",
52
+ "vitest": "^4.1.7"
49
53
  }
50
54
  }