@memizy/oqse 0.1.1 → 0.1.3

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/dist/index.js CHANGED
@@ -15,58 +15,58 @@ function i(e) {
15
15
  function a(e) {
16
16
  return e.type === "mcq-multi";
17
17
  }
18
- function ee(e) {
18
+ function o(e) {
19
19
  return e.type === "short-answer";
20
20
  }
21
- function te(e) {
21
+ function ee(e) {
22
22
  return e.type === "fill-in-blanks";
23
23
  }
24
- function ne(e) {
24
+ function te(e) {
25
25
  return e.type === "fill-in-select";
26
26
  }
27
- function re(e) {
27
+ function ne(e) {
28
28
  return e.type === "match-pairs";
29
29
  }
30
- function ie(e) {
30
+ function re(e) {
31
31
  return e.type === "match-complex";
32
32
  }
33
- function ae(e) {
33
+ function ie(e) {
34
34
  return e.type === "sort-items";
35
35
  }
36
- function oe(e) {
36
+ function ae(e) {
37
37
  return e.type === "slider";
38
38
  }
39
- function se(e) {
39
+ function oe(e) {
40
40
  return e.type === "pin-on-image";
41
41
  }
42
- function ce(e) {
42
+ function se(e) {
43
43
  return e.type === "categorize";
44
44
  }
45
- function le(e) {
45
+ function ce(e) {
46
46
  return e.type === "timeline";
47
47
  }
48
- function ue(e) {
48
+ function le(e) {
49
49
  return e.type === "matrix";
50
50
  }
51
- function de(e) {
51
+ function ue(e) {
52
52
  return e.type === "math-input";
53
53
  }
54
- function fe(e) {
54
+ function de(e) {
55
55
  return e.type === "diagram-label";
56
56
  }
57
- function pe(e) {
57
+ function fe(e) {
58
58
  return e.type === "open-ended";
59
59
  }
60
- function me(e) {
60
+ function pe(e) {
61
61
  return e.type === "numeric-input";
62
62
  }
63
- function he(e) {
63
+ function me(e) {
64
64
  return e.type === "pin-on-model";
65
65
  }
66
- function ge(e) {
66
+ function he(e) {
67
67
  return e.type === "chess-puzzle";
68
68
  }
69
- function o(e) {
69
+ function s(e) {
70
70
  return [
71
71
  "note",
72
72
  "flashcard",
@@ -74,53 +74,180 @@ function o(e) {
74
74
  "short-answer"
75
75
  ].includes(e.type);
76
76
  }
77
+ function ge(e) {
78
+ return !s(e);
79
+ }
80
+ //#endregion
81
+ //#region src/manifest.ts
82
+ var c = [
83
+ "render",
84
+ "edit",
85
+ "validate",
86
+ "import",
87
+ "export"
88
+ ], l = [
89
+ "markdown",
90
+ "math",
91
+ "rtl",
92
+ "html",
93
+ "syntax-highlighting",
94
+ "mermaid",
95
+ "smiles",
96
+ "abc-notation",
97
+ "text-to-speech",
98
+ "voice-input",
99
+ "handwriting-recognition"
100
+ ], u = [
101
+ "hints",
102
+ "explanation",
103
+ "incorrectFeedback",
104
+ "sources",
105
+ "relatedItems",
106
+ "dependencyItems",
107
+ "timeLimit",
108
+ "lang",
109
+ "topic",
110
+ "pedagogy"
111
+ ], d = [
112
+ "description",
113
+ "thumbnail",
114
+ "subject",
115
+ "language",
116
+ "author",
117
+ "contributors",
118
+ "license",
119
+ "ageRestriction",
120
+ "tags",
121
+ "tagDefinitions",
122
+ "sourceMaterials",
123
+ "estimatedTime",
124
+ "prerequisites",
125
+ "translations",
126
+ "relatedSets"
127
+ ];
128
+ function f(e) {
129
+ return /^\d+\.\d+$/.test(e);
130
+ }
77
131
  function _e(e) {
78
- return !o(e);
132
+ return /^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(e);
133
+ }
134
+ function p(e, t) {
135
+ let [n = 0, r = 0] = e.split(".").map(Number), [i = 0, a = 0] = t.split(".").map(Number);
136
+ return n === i ? r - a : n - i;
137
+ }
138
+ function ve(e) {
139
+ if (typeof e != "object" || !e) return !1;
140
+ let t = e, n = t.author;
141
+ return typeof t.version == "string" && typeof t.id == "string" && typeof t.appName == "string" && t.appName !== "" && (n === void 0 || typeof n == "object" && !!n) && typeof t.capabilities == "object" && t.capabilities !== null && Array.isArray(t.capabilities.actions);
142
+ }
143
+ function ye(e, t) {
144
+ return !(!f(e) || t.minOqseVersion && f(t.minOqseVersion) && p(e, t.minOqseVersion) < 0 || t.maxOqseVersion && f(t.maxOqseVersion) && p(e, t.maxOqseVersion) > 0);
145
+ }
146
+ //#endregion
147
+ //#region node_modules/uuid/dist/regex.js
148
+ var be = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
149
+ //#endregion
150
+ //#region node_modules/uuid/dist/validate.js
151
+ function xe(e) {
152
+ return typeof e == "string" && be.test(e);
153
+ }
154
+ //#endregion
155
+ //#region node_modules/uuid/dist/stringify.js
156
+ var m = [];
157
+ for (let e = 0; e < 256; ++e) m.push((e + 256).toString(16).slice(1));
158
+ function Se(e, t = 0) {
159
+ return (m[e[t + 0]] + m[e[t + 1]] + m[e[t + 2]] + m[e[t + 3]] + "-" + m[e[t + 4]] + m[e[t + 5]] + "-" + m[e[t + 6]] + m[e[t + 7]] + "-" + m[e[t + 8]] + m[e[t + 9]] + "-" + m[e[t + 10]] + m[e[t + 11]] + m[e[t + 12]] + m[e[t + 13]] + m[e[t + 14]] + m[e[t + 15]]).toLowerCase();
160
+ }
161
+ //#endregion
162
+ //#region node_modules/uuid/dist/rng.js
163
+ var h, Ce = new Uint8Array(16);
164
+ function we() {
165
+ if (!h) {
166
+ if (typeof crypto > "u" || !crypto.getRandomValues) throw Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
167
+ h = crypto.getRandomValues.bind(crypto);
168
+ }
169
+ return h(Ce);
170
+ }
171
+ //#endregion
172
+ //#region node_modules/uuid/dist/v7.js
173
+ var g = {};
174
+ function Te(e, t, n) {
175
+ let r;
176
+ if (e) r = De(e.random ?? e.rng?.() ?? we(), e.msecs, e.seq, t, n);
177
+ else {
178
+ let e = Date.now(), i = we();
179
+ Ee(g, e, i), r = De(i, g.msecs, g.seq, t, n);
180
+ }
181
+ return t ?? Se(r);
182
+ }
183
+ function Ee(e, t, n) {
184
+ return e.msecs ??= -Infinity, e.seq ??= 0, t > e.msecs ? (e.seq = n[6] << 23 | n[7] << 16 | n[8] << 8 | n[9], e.msecs = t) : (e.seq = e.seq + 1 | 0, e.seq === 0 && e.msecs++), e;
185
+ }
186
+ function De(e, t, n, r, i = 0) {
187
+ if (e.length < 16) throw Error("Random bytes length must be >= 16");
188
+ if (!r) r = new Uint8Array(16), i = 0;
189
+ else if (i < 0 || i + 16 > r.length) throw RangeError(`UUID byte range ${i}:${i + 15} is out of buffer bounds`);
190
+ return t ??= Date.now(), n ??= e[6] * 127 << 24 | e[7] << 16 | e[8] << 8 | e[9], r[i++] = t / 1099511627776 & 255, r[i++] = t / 4294967296 & 255, r[i++] = t / 16777216 & 255, r[i++] = t / 65536 & 255, r[i++] = t / 256 & 255, r[i++] = t & 255, r[i++] = 112 | n >>> 28 & 15, r[i++] = n >>> 20 & 255, r[i++] = 128 | n >>> 14 & 63, r[i++] = n >>> 6 & 255, r[i++] = n << 2 & 255 | e[10] & 3, r[i++] = e[11], r[i++] = e[12], r[i++] = e[13], r[i++] = e[14], r[i++] = e[15], r;
191
+ }
192
+ //#endregion
193
+ //#region src/utils.ts
194
+ function Oe(e) {
195
+ return xe(e);
196
+ }
197
+ function ke() {
198
+ return Te();
199
+ }
200
+ function _(e, t = 10, n = 0) {
201
+ if (n > t) throw Error(`OQSE Security Error: Maximum nesting depth exceeded limit of ${t} levels.`);
202
+ if (typeof e == "object" && e) {
203
+ let r = Array.isArray(e) ? e : Object.values(e);
204
+ for (let e of r) _(e, t, n + 1);
205
+ }
79
206
  }
80
207
  //#endregion
81
208
  //#region src/oqseValidation.ts
82
- var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string().min(2, "K+-d jazyka mus+� m+�t alespo+� 2 znaky").regex(/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/, "Neplatn+� form+�t BCP 47 (nap+�. \"cs\", \"en-US\")"), l = e.string().min(1, "Identifik+�tor licence nesm+� b+�t pr+�zdn+�"), u = e.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})?)?$/, "Neplatn+� form+�t ISO 8601 (nap+�. \"2025-11-21T14:30:00Z\")"), d = e.string().url({ message: "Mus+� b+�t platn+� absolutn+� URL adresa" }), f = e.string().regex(/^[a-z0-9_-]+$/, "Kl+��� assetu mus+� obsahovat pouze mal+� p+�smena, ��+�sla, poml��ky a podtr+�+�tka"), p = e.string().min(1, "Text nesm+� b+�t pr+�zdn+�"), m = e.string().min(1, "Obsah nesm+� b+�t pr+�zdn+�"), h = e.string().optional(), g = e.object({
83
- lang: c,
84
- value: e.string().min(1, "URI titulk+� nesm+� b+�t pr+�zdn+�"),
209
+ var v = e.string().uuid({ message: "Invalid UUID format" }), y = e.string().min(2, "Language code must have at least 2 characters").regex(/^[a-z]{2,3}(-[A-Z][a-z]{3})?(-[A-Z]{2})?$/, "Invalid BCP 47 format (e.g. \"cs\", \"en-US\")"), b = e.string().min(1, "License identifier must not be empty"), x = e.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?(Z|[+-]\d{2}:\d{2})?)?$/, "Invalid ISO 8601 format (e.g. \"2025-11-21T14:30:00Z\")"), S = e.string().url({ message: "Must be a valid absolute URL" }), C = e.string().regex(/^[a-z0-9_-]+$/, "Asset key must contain only lowercase letters, numbers, hyphens and underscores"), w = e.string().min(1, "Text must not be empty"), T = e.string().min(1, "Content must not be empty"), E = e.string().max(1e4, "Question must not be longer than 10000 characters").optional(), D = e.string().regex(/^[a-zA-Z0-9_-]{1,64}$/, "Token must be alphanumeric, max 64 chars"), O = e.object({
210
+ lang: y,
211
+ value: e.string().min(1, "Subtitle URI must not be empty").regex(/\.(vtt|srt)(\?.*)?$/i, "Subtitle URI must end with .vtt or .srt"),
85
212
  label: e.string().optional(),
86
213
  kind: e.enum([
87
214
  "captions",
88
215
  "subtitles",
89
216
  "descriptions"
90
217
  ]).optional()
91
- }), ve = e.object({
218
+ }), k = e.object({
92
219
  type: e.enum([
93
220
  "image",
94
221
  "audio",
95
222
  "video",
96
223
  "model"
97
224
  ]),
98
- value: e.string().min(1, "URI m+�dia nesm+� b+�t pr+�zdn+�"),
225
+ value: e.string().min(1, "Media URI must not be empty"),
99
226
  mimeType: e.string().optional(),
100
227
  altText: e.string().optional(),
101
- transcript: m.optional(),
102
- caption: m.optional(),
228
+ transcript: T.optional(),
229
+ caption: T.optional(),
103
230
  width: e.number().int().positive().optional(),
104
231
  height: e.number().int().positive().optional(),
105
232
  start: e.number().nonnegative().optional(),
106
233
  end: e.number().positive().optional(),
107
234
  loop: e.boolean().optional(),
108
- subtitles: e.array(g).optional(),
109
- license: l.optional(),
235
+ subtitles: e.array(O).optional(),
236
+ license: b.optional(),
110
237
  attribution: e.string().optional(),
111
238
  checksums: e.record(e.string(), e.string()).optional()
112
239
  }).refine((e) => e.start !== void 0 && e.end !== void 0 ? e.start < e.end : !0, {
113
- message: "Po��+�te��n+� ��as mus+� b+�t men+�+� ne+� koncov+� ��as",
240
+ message: "Start time must be less than end time",
114
241
  path: ["end"]
115
242
  }).refine((e) => !(e.type === "image" && !e.altText), {
116
- message: "Obr+�zky mus+� m+�t definovan+� alternativn+� text (altText) pro p+�+�stupnost",
243
+ message: "Images must have alternative text (altText) defined for accessibility",
117
244
  path: ["altText"]
118
- }), _ = e.record(f, ve), v = e.object({
119
- name: p,
245
+ }), A = e.record(C, k), j = e.object({
246
+ name: w,
120
247
  role: e.string().optional(),
121
- email: e.string().email("Neplatn+� form+�t e-mailov+� adresy").optional(),
122
- url: d.optional()
123
- }), y = e.enum([
248
+ email: e.string().email("Invalid email format").optional(),
249
+ url: S.optional()
250
+ }), M = e.enum([
124
251
  "url",
125
252
  "doi",
126
253
  "isbn",
@@ -131,16 +258,16 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
131
258
  "image",
132
259
  "model",
133
260
  "other"
134
- ]), b = e.object({
135
- id: p,
136
- type: y,
137
- value: p,
138
- title: p,
261
+ ]), N = e.object({
262
+ id: w,
263
+ type: M,
264
+ value: w,
265
+ title: w,
139
266
  description: e.string().optional(),
140
- authors: e.array(p).optional(),
141
- publishedDate: u.optional(),
142
- retrievedAt: u.optional(),
143
- license: l.optional()
267
+ authors: e.array(w).optional(),
268
+ publishedDate: x.optional(),
269
+ retrievedAt: x.optional(),
270
+ license: b.optional()
144
271
  }).refine((e) => {
145
272
  if ([
146
273
  "url",
@@ -155,261 +282,252 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
155
282
  }
156
283
  return !0;
157
284
  }, {
158
- message: "Pro typy url, pdf, video, audio a image mus+� b+�t value platn+� URL adresa",
285
+ message: "For url, pdf, video, audio and image types, value must be a valid URL",
159
286
  path: ["value"]
160
- }), x = e.object({
161
- id: p,
287
+ }), P = e.object({
288
+ id: w,
162
289
  location: e.string().optional(),
163
- quote: m.optional()
164
- }), S = e.object({
165
- wikidataId: e.string().regex(/^Q\d+$/, "Wikidata ID mus+� m+�t form+�t Q n+�sledovan+� ��+�slem").optional(),
290
+ quote: T.optional()
291
+ }), Ae = e.object({
292
+ wikidataId: e.string().regex(/^Q\d+$/, "Wikidata ID must have format Q followed by number").optional(),
166
293
  description: e.string().optional()
167
- }), C = e.record(e.string(), S), w = e.object({
168
- features: e.array(e.string().min(1, "Kl+��� funkce nesm+� b+�t pr+�zdn+�")).optional(),
169
- latexPackages: e.array(e.string().min(1, "N+�zev bal+���ku nesm+� b+�t pr+�zdn+�")).optional(),
170
- itemProperties: e.array(e.string().min(1, "Kl+��� vlastnosti nesm+� b+�t pr+�zdn+�")).optional(),
171
- metaProperties: e.array(e.string().min(1, "Kl+��� vlastnosti nesm+� b+�t pr+�zdn+�")).optional()
172
- }), T = e.enum([
173
- "official",
174
- "experimental",
175
- "proprietary"
176
- ]), ye = e.enum([
177
- "math",
178
- "media-image",
179
- "media-audio",
180
- "media-video",
181
- "hotspots",
182
- "complex-pairing",
183
- "open-text"
184
- ]), E = e.object({
185
- name: p,
186
- type: T,
187
- vendor: e.string().optional()
188
- }).refine((e) => !(e.type === "proprietary" && !e.vendor), {
189
- message: "Propriet+�rn+� funkce mus+� m+�t definovan+� vendor (nap+�. \"memizy.com\")",
190
- path: ["vendor"]
191
- }), D = e.object({
192
- lang: c,
193
- id: s,
194
- title: p,
195
- downloadUrl: d.optional()
196
- }), O = e.object({
197
- id: s,
198
- title: p,
199
- downloadUrl: d.optional()
200
- }), k = e.enum([
294
+ }), je = e.record(e.string(), Ae), F = e.object({
295
+ features: e.array(e.string().refine((e) => l.includes(e) || e.startsWith("x-"), { message: "Feature must be an official key or have prefix \"x-\"" })).optional(),
296
+ latexPackages: e.array(e.string().min(1, "Package name must not be empty")).optional(),
297
+ itemProperties: e.array(e.string().refine((e) => u.includes(e) || e.startsWith("x-"), { message: "Item property must be an official key or have prefix \"x-\"" })).optional(),
298
+ metaProperties: e.array(e.string().refine((e) => d.includes(e) || e.startsWith("x-"), { message: "Meta property must be an official key or have prefix \"x-\"" })).optional()
299
+ }), Me = e.object({
300
+ lang: y,
301
+ id: v,
302
+ title: w,
303
+ downloadUrl: S.optional()
304
+ }), I = e.object({
305
+ id: v,
306
+ title: w,
307
+ downloadUrl: S.optional()
308
+ }), Ne = e.enum([
201
309
  "remember",
202
310
  "understand",
203
311
  "apply",
204
312
  "analyze",
205
313
  "evaluate",
206
314
  "create"
207
- ]), A = e.enum([
315
+ ]), Pe = e.enum([
208
316
  "low",
209
317
  "medium",
210
318
  "high"
211
- ]), j = e.object({
212
- bloomLevel: k.optional(),
319
+ ]), Fe = e.object({
320
+ bloomLevel: Ne.optional(),
213
321
  irtDifficulty: e.number().optional(),
214
322
  irtDiscrimination: e.number().optional(),
215
- irtGuessing: e.number().min(0).max(1, "IRT guessing mus+� b+�t mezi 0 a 1").optional(),
323
+ irtGuessing: e.number().min(0).max(1, "IRT guessing must be between 0 and 1").optional(),
216
324
  avgTime: e.number().positive().optional(),
217
- cognitiveLoad: A.optional(),
325
+ cognitiveLoad: Pe.optional(),
218
326
  partialCredit: e.boolean().optional(),
219
- penaltyPerWrong: e.number().min(0).max(1, "Penalizace mus+� b+�t mezi 0 a 1").optional()
220
- }), M = e.enum(["katex", "mathjax"]), N = e.object({
221
- renderer: M.optional(),
222
- packages: e.array(e.string()).optional()
223
- }), P = e.object({
224
- id: s,
225
- language: c,
226
- title: p.max(500, "Titulek nesm+� b+�t del+�+� ne+� 500 znak+�"),
227
- description: m.max(5e3, "Popis nesm+� b+�t del+�+� ne+� 5000 znak+�").optional(),
228
- thumbnail: f.optional(),
229
- assets: _.optional(),
327
+ penaltyPerWrong: e.number().min(0).max(1, "Penalty must be between 0 and 1").optional()
328
+ }), L = e.object({
329
+ id: v,
330
+ language: y,
331
+ title: w.max(500, "Title must not be longer than 500 characters"),
332
+ description: T.max(5e3, "Description must not be longer than 5000 characters").optional(),
333
+ thumbnail: C.optional(),
334
+ assets: A.optional(),
230
335
  ageMin: e.number().int().nonnegative().optional(),
231
336
  ageMax: e.number().int().nonnegative().optional(),
232
337
  subject: e.string().optional(),
233
- createdAt: u,
234
- updatedAt: u,
235
- author: v.optional(),
236
- contributors: e.array(v).optional(),
237
- license: l.optional(),
238
- licenseUrl: d.optional(),
239
- requirements: w.optional(),
240
- requiredFeatures: e.array(E).optional(),
241
- tags: e.array(p).optional(),
242
- tagDefinitions: C.optional(),
243
- translations: e.array(D).optional(),
244
- sourceMaterials: e.array(b).optional(),
338
+ createdAt: x,
339
+ updatedAt: x,
340
+ author: j.optional(),
341
+ contributors: e.array(j).optional(),
342
+ license: b.optional(),
343
+ licenseUrl: S.optional(),
344
+ requirements: F.optional(),
345
+ tags: e.array(w).optional(),
346
+ tagDefinitions: je.optional(),
347
+ translations: e.array(Me).optional(),
348
+ sourceMaterials: e.array(N).optional(),
245
349
  estimatedTime: e.number().positive().optional(),
246
- prerequisites: e.array(O).optional(),
247
- relatedSets: e.array(O).optional(),
248
- mathSettings: N.optional(),
350
+ prerequisites: e.array(I).optional(),
351
+ relatedSets: e.array(I).optional(),
249
352
  customData: e.record(e.string(), e.unknown()).optional(),
250
353
  appSpecific: e.record(e.string(), e.unknown()).optional()
251
354
  }).refine((e) => e.ageMin !== void 0 && e.ageMax !== void 0 ? e.ageMin <= e.ageMax : !0, {
252
- message: "Minim+�ln+� v��k (ageMin) mus+� b+�t men+�+� nebo roven maxim+�ln+�mu v��ku (ageMax)",
355
+ message: "Minimum age (ageMin) must be less than or equal to maximum age (ageMax)",
253
356
  path: ["ageMax"]
254
357
  }).refine((e) => new Date(e.createdAt) <= new Date(e.updatedAt), {
255
- message: "Datum vytvo+�en+� (createdAt) mus+� b+�t p+�ed nebo stejn+� jako datum aktualizace (updatedAt)",
358
+ message: "Creation date (createdAt) must be before or equal to update date (updatedAt)",
256
359
  path: ["updatedAt"]
257
- }), F = e.object({
258
- id: s,
360
+ }), R = e.object({
361
+ id: v,
259
362
  type: e.string(),
260
- assets: _.optional(),
261
- lang: c.optional(),
262
- tags: e.array(p).optional(),
263
- difficulty: e.number().int().min(1, "Obt+�+�nost mus+� b+�t minim+�ln�� 1").max(5, "Obt+�+�nost mus+� b+�t maxim+�ln�� 5").optional(),
264
- timeLimit: e.number().positive("��asov+� limit mus+� b+�t kladn+� ��+�slo").optional(),
265
- hints: e.array(m.max(2e3, "N+�pov��da nesm+� b+�t del+�+� ne+� 2000 znak+�")).max(20, "Maxim+�ln�� 20 n+�pov��d na polo+�ku").optional(),
266
- explanation: m.max(1e4, "Vysv��tlen+� nesm+� b+�t del+�+� ne+� 10000 znak+�").optional(),
267
- incorrectFeedback: m.optional(),
268
- sources: e.array(x).optional(),
269
- relatedItems: e.array(s).optional(),
270
- dependencyItems: e.array(s).optional(),
271
- pedagogy: j.optional(),
363
+ assets: A.optional(),
364
+ lang: y.optional(),
365
+ tags: e.array(w).optional(),
366
+ topic: w.optional(),
367
+ difficulty: e.number().int().min(1, "Difficulty must be at least 1").max(5, "Difficulty must be at most 5").optional(),
368
+ timeLimit: e.number().positive("Time limit must be a positive number").optional(),
369
+ hints: e.array(T.max(2e3, "Hint must not be longer than 2000 characters")).max(20, "Maximum 20 hints per item").optional(),
370
+ explanation: T.max(1e4, "Explanation must not be longer than 10000 characters").optional(),
371
+ incorrectFeedback: T.optional(),
372
+ sources: e.array(P).optional(),
373
+ relatedItems: e.array(v).optional(),
374
+ dependencyItems: e.array(v).optional(),
375
+ pedagogy: Fe.optional(),
272
376
  customData: e.record(e.string(), e.unknown()).optional(),
273
377
  appSpecific: e.record(e.string(), e.unknown()).optional()
274
- }), I = e.object({
275
- options: e.array(m).min(1, "Mus+� b+�t alespo+� 1 mo+�nost"),
378
+ }), Ie = e.object({
379
+ options: e.array(T).min(1, "Must have at least 1 option"),
276
380
  correctIndex: e.number().int().nonnegative()
277
381
  }).refine((e) => e.correctIndex < e.options.length, {
278
- message: "Index spr+�vn+� odpov��di odkazuje na neexistuj+�c+� mo+�nost",
382
+ message: "Correct answer index references non-existent option",
279
383
  path: ["correctIndex"]
280
- }), L = e.object({
384
+ }), z = e.object({
281
385
  type: e.literal("rect"),
282
386
  label: e.string().optional(),
283
- x: e.number().min(0).max(100, "X mus+� b+�t v rozmez+� 0-100%"),
284
- y: e.number().min(0).max(100, "Y mus+� b+�t v rozmez+� 0-100%"),
285
- width: e.number().min(0).max(100, "+�+�+�ka mus+� b+�t v rozmez+� 0-100%"),
286
- height: e.number().min(0).max(100, "V+�+�ka mus+� b+�t v rozmez+� 0-100%")
287
- }), R = e.object({
387
+ x: e.number().min(0).max(100, "X must be in range 0-100%"),
388
+ y: e.number().min(0).max(100, "Y must be in range 0-100%"),
389
+ width: e.number().min(0).max(100, "Width must be in range 0-100%"),
390
+ height: e.number().min(0).max(100, "Height must be in range 0-100%")
391
+ }), B = e.object({
288
392
  type: e.literal("circle"),
289
393
  label: e.string().optional(),
290
- x: e.number().min(0).max(100, "X mus+� b+�t v rozmez+� 0-100%"),
291
- y: e.number().min(0).max(100, "Y mus+� b+�t v rozmez+� 0-100%"),
292
- radius: e.number().min(0).max(100, "Polom��r mus+� b+�t v rozmez+� 0-100%")
293
- }), z = e.object({
394
+ x: e.number().min(0).max(100, "X must be in range 0-100%"),
395
+ y: e.number().min(0).max(100, "Y must be in range 0-100%"),
396
+ radius: e.number().min(0).max(100, "Radius must be in range 0-100%")
397
+ }), V = e.object({
294
398
  type: e.literal("polygon"),
295
399
  label: e.string().optional(),
296
400
  points: e.array(e.object({
297
401
  x: e.number().min(0).max(100),
298
402
  y: e.number().min(0).max(100)
299
- })).min(3, "Polygon mus+� m+�t alespo+� 3 body")
300
- }), B = e.object({
403
+ })).min(3, "Polygon must have at least 3 points")
404
+ }), H = e.object({
301
405
  type: e.literal("mesh"),
302
406
  label: e.string().optional(),
303
- targetName: p
304
- }), V = e.discriminatedUnion("type", [
305
- L,
306
- R,
407
+ targetName: w
408
+ }), Le = e.discriminatedUnion("type", [
307
409
  z,
308
- B
309
- ]), H = e.object({
410
+ B,
411
+ V,
412
+ H
413
+ ]), Re = e.discriminatedUnion("type", [
414
+ z,
415
+ B,
416
+ V
417
+ ]), U = e.object({
310
418
  x: e.number(),
311
419
  y: e.number(),
312
420
  z: e.number()
313
- }), U = e.object({
314
- position: H.optional(),
315
- target: H.optional()
316
- }), be = e.object({
317
- id: p,
318
- text: m,
421
+ }), ze = e.object({
422
+ position: U.optional(),
423
+ target: U.optional()
424
+ }), Be = e.object({
425
+ id: w,
426
+ text: T,
319
427
  correctCategoryIndex: e.number().int().nonnegative()
320
- }), xe = e.enum([
428
+ }), Ve = e.enum([
321
429
  "year",
322
430
  "month",
323
431
  "day",
324
432
  "datetime"
325
- ]), Se = e.object({
326
- id: p,
327
- text: m,
328
- date: u,
329
- precision: xe.optional()
330
- }), Ce = e.union([
331
- L.extend({ correctLabelIndex: e.number().int().nonnegative() }),
332
- R.extend({ correctLabelIndex: e.number().int().nonnegative() }),
333
- z.extend({ correctLabelIndex: e.number().int().nonnegative() })
334
- ]), we = e.object({
335
- label: p,
336
- percentage: e.number().min(0, "Procenta mus+� b+�t nez+�porn+�").max(100, "Procenta nesm+� p+�es+�hnout 100"),
433
+ ]), He = e.object({
434
+ id: w,
435
+ text: T,
436
+ date: x,
437
+ precision: Ve.optional()
438
+ }), Ue = e.union([
439
+ z.extend({ correctLabelIndex: e.number().int().nonnegative() }),
440
+ B.extend({ correctLabelIndex: e.number().int().nonnegative() }),
441
+ V.extend({ correctLabelIndex: e.number().int().nonnegative() })
442
+ ]), W = e.object({
443
+ label: w,
444
+ percentage: e.number().min(0, "Percentage must be non-negative").max(100, "Percentage must not exceed 100"),
337
445
  description: e.string().optional()
338
- }), Te = e.object({ criteria: e.array(we).min(1, "Rubrika mus+� m+�t alespo+� 1 krit+�rium") }).refine((e) => e.criteria.reduce((e, t) => e + t.percentage, 0) > 0, {
339
- message: "Sou��et procent v+�ech krit+�ri+� mus+� b+�t v��t+�+� ne+� 0",
446
+ }), We = e.object({ criteria: e.array(W).min(1, "Rubric must have at least 1 criterion") }).refine((e) => e.criteria.reduce((e, t) => e + t.percentage, 0) > 0, {
447
+ message: "Sum of percentages of all criteria must be greater than 0",
340
448
  path: ["criteria"]
341
- }), Ee = e.object({
449
+ }), Ge = e.object({
342
450
  min: e.number(),
343
451
  max: e.number()
344
452
  }).refine((e) => e.min <= e.max, {
345
- message: "Minim+�ln+� hodnota mus+� b+�t men+�+� nebo rovna maxim+�ln+� hodnot��",
453
+ message: "Minimum value must be less than or equal to maximum value",
346
454
  path: ["max"]
347
- }), De = F.extend({
455
+ }), Ke = R.extend({
348
456
  type: e.literal("note"),
349
457
  title: e.string().optional(),
350
- content: m.max(1e4, "Obsah nesm+� b+�t del+�+� ne+� 10000 znak+�")
351
- }), Oe = F.extend({
458
+ content: T.max(1e4, "Content must not be longer than 10000 characters"),
459
+ hiddenContent: T.max(1e4, "Content must not be longer than 10000 characters").optional()
460
+ }), qe = R.extend({
352
461
  type: e.literal("flashcard"),
353
- front: m.max(1e4, "P+�edn+� strana nesm+� b+�t del+�+� ne+� 10000 znak+�"),
354
- back: m.max(1e4, "Zadn+� strana nesm+� b+�t del+�+� ne+� 10000 znak+�")
355
- }), ke = F.extend({
462
+ front: T.max(1e4, "Front side must not be longer than 10000 characters"),
463
+ back: T.max(1e4, "Back side must not be longer than 10000 characters")
464
+ }), Je = R.extend({
356
465
  type: e.literal("true-false"),
357
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
466
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
358
467
  answer: e.boolean()
359
- }), Ae = F.extend({
468
+ }), Ye = R.extend({
360
469
  type: e.literal("mcq-single"),
361
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
362
- options: e.array(m.max(2e3, "Mo+�nost nesm+� b+�t del+�+� ne+� 2000 znak+�")).min(2, "Ot+�zka mus+� m+�t alespo+� 2 mo+�nosti").max(100, "Maxim+�ln�� 100 mo+�nost+�"),
470
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
471
+ options: e.array(T.max(2e3, "Option must not be longer than 2000 characters")).min(2, "Question must have at least 2 options").max(100, "Maximum 100 options"),
363
472
  correctIndex: e.number().int().nonnegative(),
364
473
  shuffleOptions: e.boolean().optional(),
365
- optionExplanations: e.array(e.union([m, e.null()])).optional()
474
+ optionExplanations: e.array(e.union([T, e.null()])).optional()
366
475
  }).refine((e) => e.correctIndex < e.options.length, {
367
- message: "Index spr+�vn+� odpov��di odkazuje na neexistuj+�c+� mo+�nost",
476
+ message: "Correct answer index references non-existent option",
368
477
  path: ["correctIndex"]
369
478
  }).refine((e) => e.optionExplanations ? e.optionExplanations.length === e.options.length : !0, {
370
- message: "Po��et vysv��tlen+� mo+�nost+� mus+� b+�t stejn+� jako po��et mo+�nost+�",
479
+ message: "Number of option explanations must match number of options",
371
480
  path: ["optionExplanations"]
372
- }), je = F.extend({
481
+ }).refine((e) => new Set(e.options.map((e) => e.toLowerCase())).size === e.options.length, {
482
+ message: "Options must not contain duplicates (case-insensitive)",
483
+ path: ["options"]
484
+ }), Xe = R.extend({
373
485
  type: e.literal("mcq-multi"),
374
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
375
- options: e.array(m.max(2e3, "Mo+�nost nesm+� b+�t del+�+� ne+� 2000 znak+�")).min(2, "Ot+�zka mus+� m+�t alespo+� 2 mo+�nosti").max(100, "Maxim+�ln�� 100 mo+�nost+�"),
376
- correctIndices: e.array(e.number().int().nonnegative()).min(1, "Mus+� b+�t alespo+� 1 spr+�vn+� odpov����"),
486
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
487
+ options: e.array(T.max(2e3, "Option must not be longer than 2000 characters")).min(2, "Question must have at least 2 options").max(100, "Maximum 100 options"),
488
+ correctIndices: e.array(e.number().int().nonnegative()).min(1, "Must have at least 1 correct answer"),
377
489
  minSelections: e.number().int().positive().optional(),
378
490
  maxSelections: e.number().int().positive().optional(),
379
491
  shuffleOptions: e.boolean().optional(),
380
- optionExplanations: e.array(e.union([m, e.null()])).optional()
492
+ optionExplanations: e.array(e.union([T, e.null()])).optional()
381
493
  }).refine((e) => e.correctIndices.every((t) => t < e.options.length), {
382
- message: "N��kter+� z index+� spr+�vn+�ch odpov��d+� odkazuje na neexistuj+�c+� mo+�nost",
494
+ message: "Some of correct answer indices reference non-existent option",
383
495
  path: ["correctIndices"]
384
496
  }).refine((e) => new Set(e.correctIndices).size === e.correctIndices.length, {
385
- message: "Indexy spr+�vn+�ch odpov��d+� obsahuj+� duplicity",
497
+ message: "Correct answer indices contain duplicates",
386
498
  path: ["correctIndices"]
387
499
  }).refine((e) => e.minSelections && e.maxSelections ? e.minSelections <= e.maxSelections : !0, {
388
- message: "Minim+�ln+� po��et v+�b��r+� mus+� b+�t men+�+� nebo roven maxim+�ln+�mu po��tu",
500
+ message: "Minimum number of selections must be less than or equal to maximum",
389
501
  path: ["maxSelections"]
390
502
  }).refine((e) => e.maxSelections ? e.maxSelections <= e.options.length : !0, {
391
- message: "Maxim+�ln+� po��et v+�b��r+� nesm+� p+�es+�hnout po��et mo+�nost+�",
503
+ message: "Maximum number of selections must not exceed number of options",
392
504
  path: ["maxSelections"]
505
+ }).refine((e) => e.minSelections ? e.minSelections <= e.options.length : !0, {
506
+ message: "minSelections cannot be greater than the number of options",
507
+ path: ["minSelections"]
393
508
  }).refine((e) => e.optionExplanations ? e.optionExplanations.length === e.options.length : !0, {
394
- message: "Po��et vysv��tlen+� mo+�nost+� mus+� b+�t stejn+� jako po��et mo+�nost+�",
509
+ message: "Number of option explanations must match number of options",
395
510
  path: ["optionExplanations"]
396
- }), W = F.extend({
511
+ }).refine((e) => new Set(e.options.map((e) => e.toLowerCase())).size === e.options.length, {
512
+ message: "Options must not contain duplicates (case-insensitive)",
513
+ path: ["options"]
514
+ }), Ze = R.extend({
397
515
  type: e.literal("short-answer"),
398
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
399
- answers: e.array(p).min(1, "Mus+� b+�t alespo+� 1 spr+�vn+� odpov����"),
516
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
517
+ answers: e.array(w).min(1, "Must have at least 1 correct answer"),
400
518
  caseSensitive: e.boolean().optional(),
401
519
  trimWhitespace: e.boolean().optional(),
402
520
  acceptPartial: e.boolean().optional(),
403
521
  ignoreDiacritics: e.boolean().optional()
404
- }), Me = F.extend({
522
+ }), Qe = R.extend({
405
523
  type: e.literal("fill-in-blanks"),
406
- question: h,
407
- text: m.max(1e4, "Text nesm+� b+�t del+�+� ne+� 10000 znak+�"),
408
- blanks: e.record(e.string(), e.array(p).min(1, "Ka+�d+� mezera mus+� m+�t alespo+� 1 spr+�vnou odpov����")),
524
+ question: E,
525
+ text: T.max(1e4, "Text must not be longer than 10000 characters"),
526
+ blanks: e.record(D, e.array(w).min(1, "Each blank must have at least 1 correct answer")),
409
527
  caseSensitive: e.boolean().optional(),
410
528
  trimWhitespace: e.boolean().optional()
411
529
  }).refine((e) => Object.keys(e.blanks).length > 0, {
412
- message: "Text mus+� obsahovat alespo+� 1 mezeru k dopln��n+�",
530
+ message: "Text must contain at least 1 blank to fill",
413
531
  path: ["blanks"]
414
532
  }).refine((e) => {
415
533
  let t = /<blank:([a-zA-Z0-9_-]{1,64})\s*\/>/g, n = /* @__PURE__ */ new Set(), r;
@@ -419,15 +537,15 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
419
537
  for (let e of i) if (!n.has(e)) return !1;
420
538
  return !0;
421
539
  }, {
422
- message: "V+�echny tokeny v textu mus+� m+�t definici v blanks a v+�echny definice mus+� b+�t pou+�ity v textu",
540
+ message: "All tokens in text must have definition in blanks and all definitions must be used in text",
423
541
  path: ["blanks"]
424
- }), Ne = F.extend({
542
+ }), $e = R.extend({
425
543
  type: e.literal("fill-in-select"),
426
- question: h,
427
- text: m.max(1e4, "Text nesm+� b+�t del+�+� ne+� 10000 znak+�"),
428
- blanks: e.record(e.string(), I)
544
+ question: E,
545
+ text: T.max(1e4, "Text must not be longer than 10000 characters"),
546
+ blanks: e.record(D, Ie)
429
547
  }).refine((e) => Object.keys(e.blanks).length > 0, {
430
- message: "Text mus+� obsahovat alespo+� 1 mezeru k v+�b��ru",
548
+ message: "Text must contain at least 1 blank to select",
431
549
  path: ["blanks"]
432
550
  }).refine((e) => {
433
551
  let t = /<blank:([a-zA-Z0-9_-]{1,64})\s*\/>/g, n = /* @__PURE__ */ new Set(), r;
@@ -437,95 +555,98 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
437
555
  for (let e of i) if (!n.has(e)) return !1;
438
556
  return !0;
439
557
  }, {
440
- message: "V+�echny tokeny v textu mus+� m+�t definici v blanks a v+�echny definice mus+� b+�t pou+�ity v textu",
558
+ message: "All tokens in text must have definition in blanks and all definitions must be used in text",
441
559
  path: ["blanks"]
442
- }), Pe = F.extend({
560
+ }), et = R.extend({
443
561
  type: e.literal("match-pairs"),
444
- question: h,
445
- prompts: e.array(m).min(2, "Mus+� b+�t alespo+� 2 p+�ry k p+�i+�azen+�"),
446
- matches: e.array(m).min(2, "Mus+� b+�t alespo+� 2 p+�ry k p+�i+�azen+�")
562
+ question: E,
563
+ prompts: e.array(T).min(2, "Must have at least 2 pairs to match").max(100, "Maximum 100 pairs"),
564
+ matches: e.array(T).min(2, "Must have at least 2 pairs to match").max(100, "Maximum 100 pairs")
447
565
  }).refine((e) => e.prompts.length === e.matches.length, {
448
- message: "Po��et ot+�zek (prompts) mus+� b+�t stejn+� jako po��et odpov��d+� (matches)",
566
+ message: "Number of prompts must match number of matches",
449
567
  path: ["matches"]
450
- }), Fe = F.extend({
568
+ }), tt = R.extend({
451
569
  type: e.literal("match-complex"),
452
- question: h,
453
- leftItems: e.array(m).min(1, "Mus+� b+�t alespo+� 1 polo+�ka vlevo"),
454
- rightItems: e.array(m).min(1, "Mus+� b+�t alespo+� 1 polo+�ka vpravo"),
455
- connections: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "Mus+� b+�t alespo+� 1 propojen+�"),
570
+ question: E,
571
+ leftItems: e.array(T).min(1, "Must have at least 1 item on left"),
572
+ rightItems: e.array(T).min(1, "Must have at least 1 item on right"),
573
+ connections: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "Must have at least 1 connection"),
456
574
  minCorrect: e.number().int().positive().optional()
457
575
  }).refine((e) => e.connections.every(([t, n]) => t < e.leftItems.length && n < e.rightItems.length), {
458
- message: "N��kter+� propojen+� odkazuje na neexistuj+�c+� polo+�ku",
576
+ message: "Some connection references non-existent item",
459
577
  path: ["connections"]
460
578
  }).refine((e) => new Set(e.connections.map((e) => `${e[0]}-${e[1]}`)).size === e.connections.length, {
461
- message: "Propojen+� obsahuj+� duplicity",
579
+ message: "Connections contain duplicates",
462
580
  path: ["connections"]
463
581
  }).refine((e) => e.minCorrect ? e.minCorrect <= e.connections.length : !0, {
464
- message: "Minim+�ln+� po��et spr+�vn+�ch odpov��d+� nesm+� p+�es+�hnout celkov+� po��et propojen+�",
582
+ message: "Minimum correct answers must not exceed total number of connections",
465
583
  path: ["minCorrect"]
466
- }), Ie = F.extend({
584
+ }), nt = R.extend({
467
585
  type: e.literal("sort-items"),
468
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
469
- items: e.array(m).min(2, "Mus+� b+�t alespo+� 2 polo+�ky k se+�azen+�")
470
- }), Le = F.extend({
586
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
587
+ items: e.array(T).min(2, "Must have at least 2 items to sort")
588
+ }), rt = R.extend({
471
589
  type: e.literal("slider"),
472
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
590
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
473
591
  min: e.number(),
474
592
  max: e.number(),
475
- step: e.number().positive("Krok mus+� b+�t kladn+� ��+�slo"),
593
+ step: e.number().positive("Step must be a positive number"),
476
594
  correctAnswer: e.number(),
477
- tolerance: e.number().nonnegative("Tolerance mus+� b+�t nez+�porn+�"),
595
+ tolerance: e.number().nonnegative("Tolerance must be non-negative"),
478
596
  unit: e.string().optional()
479
597
  }).refine((e) => e.min < e.max, {
480
- message: "Minim+�ln+� hodnota mus+� b+�t men+�+� ne+� maxim+�ln+� hodnota",
598
+ message: "Minimum value must be less than maximum value",
481
599
  path: ["max"]
482
600
  }).refine((e) => e.correctAnswer >= e.min && e.correctAnswer <= e.max, {
483
- message: "Spr+�vn+� odpov���� mus+� b+�t v rozmez+� min-max",
601
+ message: "Correct answer must be in range min-max",
484
602
  path: ["correctAnswer"]
485
603
  }).refine((e) => {
486
604
  let t = (e.correctAnswer - e.min) / e.step;
487
605
  return Math.abs(t - Math.round(t)) < 1e-4;
488
606
  }, {
489
- message: "Spr+�vn+� odpov���� mus+� b+�t dosa+�iteln+� pomoc+� definovan+�ho kroku",
607
+ message: "Correct answer must be reachable by defined step",
490
608
  path: ["correctAnswer"]
491
609
  }).refine((e) => e.tolerance <= (e.max - e.min) / 2, {
492
- message: "Tolerance nesm+� b+�t v��t+�+� ne+� polovina rozsahu hodnot",
610
+ message: "Tolerance must not be greater than half of value range",
493
611
  path: ["tolerance"]
494
- }), Re = F.extend({
612
+ }), it = R.extend({
495
613
  type: e.literal("pin-on-image"),
496
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
497
- targetAsset: f,
498
- hotspots: e.array(V).min(1, "Mus+� b+�t alespo+� 1 hotspot"),
614
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
615
+ targetAsset: C,
616
+ hotspots: e.array(Re).min(1, "Must have at least 1 hotspot"),
499
617
  multipleCorrect: e.boolean().optional(),
500
618
  minCorrect: e.number().int().positive().optional()
501
619
  }).refine((e) => e.minCorrect ? e.minCorrect <= e.hotspots.length : !0, {
502
- message: "Minim+�ln+� po��et spr+�vn+�ch odpov��d+� nesm+� p+�es+�hnout po��et hotspot+�",
620
+ message: "Minimum correct answers must not exceed number of hotspots",
503
621
  path: ["minCorrect"]
504
- }), ze = F.extend({
622
+ }), at = R.extend({
505
623
  type: e.literal("categorize"),
506
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
507
- categories: e.array(p).min(2, "Mus+� b+�t alespo+� 2 kategorie"),
508
- items: e.array(be).min(1, "Mus+� b+�t alespo+� 1 polo+�ka ke kategorizaci")
624
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
625
+ categories: e.array(w).min(2, "Must have at least 2 categories"),
626
+ items: e.array(Be).min(1, "Must have at least 1 item to categorize")
627
+ }).refine((e) => new Set(e.categories.map((e) => e.toLowerCase())).size === e.categories.length, {
628
+ message: "Categories must not contain duplicates (case-insensitive)",
629
+ path: ["categories"]
509
630
  }).refine((e) => e.items.every((t) => t.correctCategoryIndex < e.categories.length), {
510
- message: "N��kter+� polo+�ka odkazuje na neexistuj+�c+� kategorii",
631
+ message: "Some item references non-existent category",
511
632
  path: ["items"]
512
- }), Be = F.extend({
633
+ }), ot = R.extend({
513
634
  type: e.literal("timeline"),
514
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
515
- events: e.array(Se).min(2, "Mus+� b+�t alespo+� 2 ud+�losti"),
635
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
636
+ events: e.array(He).min(2, "Must have at least 2 events"),
516
637
  randomize: e.boolean().optional()
517
- }), Ve = F.extend({
638
+ }), st = R.extend({
518
639
  type: e.literal("matrix"),
519
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
520
- rows: e.array(p).min(1, "Mus+� b+�t alespo+� 1 +�+�dek"),
521
- columns: e.array(p).min(1, "Mus+� b+�t alespo+� 1 sloupec"),
522
- correctCells: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "Mus+� b+�t alespo+� 1 spr+�vn+� bu+�ka"),
640
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
641
+ rows: e.array(w).min(1, "Must have at least 1 row"),
642
+ columns: e.array(w).min(1, "Must have at least 1 column"),
643
+ correctCells: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "Must have at least 1 correct cell"),
523
644
  multiplePerRow: e.boolean().optional()
524
645
  }).refine((e) => e.correctCells.every(([t, n]) => t < e.rows.length && n < e.columns.length), {
525
- message: "N��kter+� sou+�adnice bu+�ky odkazuj+� na neexistuj+�c+� +�+�dek nebo sloupec",
646
+ message: "Some cell coordinates reference non-existent row or column",
526
647
  path: ["correctCells"]
527
648
  }).refine((e) => new Set(e.correctCells.map((e) => `${e[0]}-${e[1]}`)).size === e.correctCells.length, {
528
- message: "Spr+�vn+� bu+�ky obsahuj+� duplicity",
649
+ message: "Correct cells contain duplicates",
529
650
  path: ["correctCells"]
530
651
  }).refine((e) => {
531
652
  if (!e.multiplePerRow) {
@@ -535,103 +656,103 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
535
656
  }
536
657
  return !0;
537
658
  }, {
538
- message: "Pokud multiplePerRow je false, ka+�d+� +�+�dek sm+� m+�t maxim+�ln�� 1 spr+�vnou bu+�ku",
659
+ message: "If multiplePerRow is false, each row may have maximum 1 correct cell",
539
660
  path: ["correctCells"]
540
- }), He = F.extend({
661
+ }), ct = R.extend({
541
662
  type: e.literal("math-input"),
542
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
543
- correctAnswer: p,
544
- alternativeAnswers: e.array(p).optional(),
663
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
664
+ correctAnswer: w,
665
+ alternativeAnswers: e.array(w).optional(),
545
666
  tolerance: e.number().nonnegative().optional()
546
- }), Ue = F.extend({
667
+ }), lt = R.extend({
547
668
  type: e.literal("diagram-label"),
548
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
549
- targetAsset: f,
550
- labels: e.array(m).min(1, "Mus+� b+�t alespo+� 1 +�t+�tek"),
669
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
670
+ targetAsset: C,
671
+ labels: e.array(T).min(1, "Must have at least 1 label"),
551
672
  caseSensitive: e.boolean().optional(),
552
673
  requireTyping: e.boolean().optional(),
553
- zones: e.array(Ce).min(1, "Mus+� b+�t alespo+� 1 z+-na")
674
+ zones: e.array(Ue).min(1, "Must have at least 1 zone")
554
675
  }).refine((e) => e.zones.every((t) => t.correctLabelIndex < e.labels.length), {
555
- message: "N��kter+� z+-na odkazuje na neexistuj+�c+� +�t+�tek",
676
+ message: "Some zone references non-existent label",
556
677
  path: ["zones"]
557
- }), We = F.extend({
678
+ }), ut = R.extend({
558
679
  type: e.literal("open-ended"),
559
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
680
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
560
681
  minWords: e.number().int().positive().optional(),
561
682
  maxWords: e.number().int().positive().optional(),
562
- sampleAnswer: m.optional(),
563
- rubric: Te.optional()
683
+ sampleAnswer: T.optional(),
684
+ rubric: We.optional()
564
685
  }).refine((e) => e.minWords && e.maxWords ? e.minWords <= e.maxWords : !0, {
565
- message: "Minim+�ln+� po��et slov mus+� b+�t men+�+� nebo roven maxim+�ln+�mu po��tu",
686
+ message: "Minimum word count must be less than or equal to maximum",
566
687
  path: ["maxWords"]
567
- }), Ge = F.extend({
688
+ }), dt = R.extend({
568
689
  type: e.literal("numeric-input"),
569
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
690
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
570
691
  value: e.number(),
571
692
  tolerance: e.number().nonnegative().optional(),
572
- range: Ee.optional(),
693
+ range: Ge.optional(),
573
694
  unit: e.string().optional()
574
- }), Ke = F.extend({
695
+ }), ft = R.extend({
575
696
  type: e.literal("pin-on-model"),
576
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
577
- targetAsset: f,
578
- hotspots: e.array(B).min(1, "Mus+� b+�t alespo+� 1 hotspot"),
697
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
698
+ targetAsset: C,
699
+ hotspots: e.array(H).min(1, "Must have at least 1 hotspot"),
579
700
  multipleCorrect: e.boolean().optional(),
580
701
  minCorrect: e.number().int().positive().optional(),
581
- camera: U.optional()
702
+ camera: ze.optional()
582
703
  }).refine((e) => e.minCorrect ? e.minCorrect <= e.hotspots.length : !0, {
583
- message: "Minim+�ln+� po��et spr+�vn+�ch odpov��d+� nesm+� p+�es+�hnout po��et hotspot+�",
704
+ message: "Minimum correct answers must not exceed number of hotspots",
584
705
  path: ["minCorrect"]
585
- }), qe = F.extend({
706
+ }), pt = R.extend({
586
707
  type: e.literal("chess-puzzle"),
587
- question: m.max(1e4, "Ot+�zka nesm+� b+�t del+�+� ne+� 10000 znak+�"),
588
- fen: p,
589
- answers: e.array(e.array(p).min(1, "Sekvence tah+� nesm+� b+�t pr+�zdn+�")).min(1, "Mus+� b+�t alespo+� 1 spr+�vn+� sekvence tah+�"),
590
- elo: e.number().int().nonnegative("ELO mus+� b+�t nez+�porn+� cel+� ��+�slo").optional()
708
+ question: T.max(1e4, "Question must not be longer than 10000 characters"),
709
+ fen: w,
710
+ answers: e.array(e.array(w).min(1, "Move sequence must not be empty")).min(1, "Must have at least 1 correct move sequence"),
711
+ elo: e.number().int().nonnegative("ELO must be non-negative integer").optional()
591
712
  }), G = e.discriminatedUnion("type", [
592
- De,
593
- Oe,
594
- ke,
595
- Ae,
596
- je,
597
- W,
598
- Me,
599
- Ne,
600
- Pe,
601
- Fe,
602
- Ie,
603
- Le,
604
- Re,
605
- ze,
606
- Be,
607
- Ve,
608
- He,
609
- Ue,
610
- We,
611
- Ge,
612
713
  Ke,
613
- qe
714
+ qe,
715
+ Je,
716
+ Ye,
717
+ Xe,
718
+ Ze,
719
+ Qe,
720
+ $e,
721
+ et,
722
+ tt,
723
+ nt,
724
+ rt,
725
+ it,
726
+ at,
727
+ ot,
728
+ st,
729
+ ct,
730
+ lt,
731
+ ut,
732
+ dt,
733
+ ft,
734
+ pt
614
735
  ]), K = e.object({
615
736
  $schema: e.string().url().optional(),
616
- version: e.string().regex(/^\d+\.\d+$/, "Verze musi byt ve formatu \"X.Y\" (napr. \"0.1\")"),
617
- meta: P,
618
- items: e.array(G).max(1e4, "Maxim+�ln�� 10000 polo+�ek na sadu")
737
+ version: e.string().regex(/^\d+\.\d+$/, "Version must be in MAJOR.MINOR format (e.g. \"0.1\")"),
738
+ meta: L,
739
+ items: e.array(G).max(1e4, "Maximum 10000 items per set")
619
740
  }).refine((e) => {
620
741
  let t = new Set(e.items.map((e) => e.id));
621
742
  for (let n of e.items) {
622
743
  if (n.relatedItems) {
623
- for (let e of n.relatedItems) if (!t.has(e)) return !1;
744
+ for (let e of n.relatedItems) if (!t.has(e) || n.id === e) return !1;
624
745
  }
625
746
  if (n.dependencyItems) {
626
- for (let e of n.dependencyItems) if (!t.has(e)) return !1;
747
+ for (let e of n.dependencyItems) if (!t.has(e) || n.id === e) return !1;
627
748
  }
628
749
  }
629
750
  return !0;
630
751
  }, {
631
- message: "N��kter+� polo+�ka odkazuje na neexistuj+�c+� relatedItems nebo dependencyItems",
752
+ message: "Some item references non-existent relatedItems, dependencyItems, or references itself",
632
753
  path: ["items"]
633
754
  }).refine((e) => e.meta.thumbnail && e.meta.assets ? e.meta.thumbnail in e.meta.assets : !0, {
634
- message: "Thumbnail odkazuje na neexistuj+�c+� asset",
755
+ message: "Thumbnail references non-existent asset",
635
756
  path: ["meta", "thumbnail"]
636
757
  }).refine((e) => {
637
758
  let t = new Set((e.meta.sourceMaterials || []).map((e) => e.id));
@@ -640,13 +761,29 @@ var s = e.string().uuid({ message: "Neplatn+� form+�t UUID" }), c = e.string
640
761
  }
641
762
  return !0;
642
763
  }, {
643
- message: "N��kter+� polo+�ka odkazuje na neexistuj+�c+� source material",
764
+ message: "Some item references non-existent source material",
644
765
  path: ["items"]
645
766
  });
646
- function Je(e) {
647
- return K.parse(e);
767
+ function mt(t) {
768
+ let n = t instanceof Error ? t.message : "OQSE Security Error: Maximum nesting depth exceeded limit of 10 levels.";
769
+ return new e.ZodError([{
770
+ code: e.ZodIssueCode.custom,
771
+ message: n,
772
+ path: []
773
+ }]);
774
+ }
775
+ function ht(e) {
776
+ return _(e, 10), K.parse(e);
648
777
  }
649
- function Ye(e) {
778
+ function gt(e) {
779
+ try {
780
+ _(e, 10);
781
+ } catch (e) {
782
+ return {
783
+ success: !1,
784
+ error: mt(e)
785
+ };
786
+ }
650
787
  let t = K.safeParse(e);
651
788
  return t.success ? {
652
789
  success: !0,
@@ -656,10 +793,18 @@ function Ye(e) {
656
793
  error: t.error
657
794
  };
658
795
  }
659
- function Xe(e) {
660
- return G.parse(e);
796
+ function _t(e) {
797
+ return _(e, 10), G.parse(e);
661
798
  }
662
- function Ze(e) {
799
+ function vt(e) {
800
+ try {
801
+ _(e, 10);
802
+ } catch (e) {
803
+ return {
804
+ success: !1,
805
+ error: mt(e)
806
+ };
807
+ }
663
808
  let t = G.safeParse(e);
664
809
  return t.success ? {
665
810
  success: !0,
@@ -669,128 +814,127 @@ function Ze(e) {
669
814
  error: t.error
670
815
  };
671
816
  }
672
- function Qe(e) {
817
+ function yt(e) {
673
818
  return e.issues.map((e) => {
674
- let t = e.path.join(" ��� ");
675
- return t ? `${t}: ${e.message}` : e.message;
819
+ let t = "";
820
+ return e.path.forEach((n, r) => {
821
+ if (typeof n == "number") t += `[${n}]`;
822
+ else {
823
+ let i = r === 0 || typeof e.path[r - 1] == "number";
824
+ t += (i ? "" : ".") + String(n);
825
+ }
826
+ }), t ? `${t}: ${e.message}` : e.message;
676
827
  });
677
828
  }
678
829
  //#endregion
679
- //#region src/manifest.ts
680
- function q(e) {
681
- return /^\d+\.\d+$/.test(e);
682
- }
683
- function $e(e) {
684
- return /^\d+\.\d+\.\d+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$/.test(e);
685
- }
686
- function J(e, t) {
687
- let [n = 0, r = 0] = e.split(".").map(Number), [i = 0, a = 0] = t.split(".").map(Number);
688
- return n === i ? r - a : n - i;
689
- }
690
- function et(e) {
691
- if (typeof e != "object" || !e) return !1;
692
- let t = e;
693
- return typeof t.version == "string" && typeof t.id == "string" && typeof t.appName == "string" && t.appName !== "" && typeof t.capabilities == "object" && t.capabilities !== null && Array.isArray(t.capabilities.actions);
694
- }
695
- function tt(e, t) {
696
- return !(!q(e) || t.minOqseVersion && q(t.minOqseVersion) && J(e, t.minOqseVersion) < 0 || t.maxOqseVersion && q(t.maxOqseVersion) && J(e, t.maxOqseVersion) > 0);
697
- }
698
- //#endregion
699
830
  //#region src/manifestValidation.ts
700
- var Y = e.string().min(1, "Hodnota nesm+� b+�t pr+�zdn+�").trim(), nt = e.string().url("Mus+� b+�t platn+� absolutn+� URL").refine((e) => e.startsWith("http://") || e.startsWith("https://"), { message: "URL mus+� pou+�+�vat sch+�ma http nebo https" }), X = e.string().regex(/^\d+\.\d+$/, "Verze mus+� b+�t ve form+�tu MAJOR.MINOR (nap+�. \"0.1\")"), rt = e.string().regex(/^\d+\.\d+\.\d+$/, "Verze mus+� b+�t ve form+�tu MAJOR.MINOR.PATCH (nap+�. \"1.2.3\")"), it = [
701
- "render",
702
- "edit",
703
- "validate",
704
- "import",
705
- "export"
706
- ], at = e.string().refine((e) => it.includes(e) || e.startsWith("x-"), { message: "Akce mus+� b+�t jedna z [render, edit, validate, import, export] nebo m+�t prefix \"x-\"" }), ot = e.enum([
831
+ var q = e.string().min(1, "Value must not be empty").trim(), bt = e.string().regex(/^[a-zA-Z]{2,8}(-[a-zA-Z0-9\-]+)*$/, "Must be a valid BCP 47 locale (e.g., \"en\", \"cs\")"), J = e.string().url("Must be a valid absolute URL").refine((e) => e.startsWith("http://") || e.startsWith("https://"), { message: "URL must use http or https scheme" }), xt = e.string().regex(/^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "Must be a valid URN UUID (urn:uuid:...)"), Y = e.string().regex(/^\d+\.\d+$/, "Version must be in MAJOR.MINOR format (e.g. \"0.1\")"), St = [
832
+ "note",
833
+ "flashcard",
834
+ "true-false",
835
+ "mcq-single",
836
+ "mcq-multi",
837
+ "short-answer",
838
+ "fill-in-blanks",
839
+ "fill-in-select",
840
+ "match-pairs",
841
+ "match-complex",
842
+ "sort-items",
843
+ "slider",
844
+ "pin-on-image",
845
+ "categorize",
846
+ "timeline",
847
+ "matrix",
848
+ "math-input",
849
+ "diagram-label",
850
+ "open-ended",
851
+ "numeric-input",
852
+ "pin-on-model",
853
+ "chess-puzzle"
854
+ ], Ct = q.refine((e) => St.includes(e) || e.startsWith("x-"), { message: "Custom types MUST use the \"x-\" prefix" }), wt = e.string().refine((e) => c.includes(e) || e.startsWith("x-"), { message: "Action must be one of [render, edit, validate, import, export] or have prefix \"x-\"" }), Tt = e.enum([
707
855
  "game",
708
856
  "fun",
709
857
  "drill"
710
- ]), st = e.enum([
858
+ ]), Et = e.enum([
711
859
  "low",
712
860
  "medium",
713
861
  "high"
714
- ]), ct = [
715
- "math",
716
- "media",
717
- "media-image",
718
- "media-audio",
719
- "media-video",
720
- "media-model",
721
- "hotspots",
722
- "hotspots-3d",
723
- "complex-pairing",
724
- "open-text",
725
- "chess"
726
- ], lt = e.string().refine((e) => ct.includes(e) || e.startsWith("x-"), { message: "Funkce mus+� b+�t jedna z ofici+�ln+�ch kl+���+� nebo m+�t prefix \"x-\"" }), ut = e.string().min(1, "Kl+��� vlastnosti polo+�ky nesm+� b+�t pr+�zdn+�"), dt = e.string().min(1, "Kl+��� vlastnosti metadat nesm+� b+�t pr+�zdn+�");
727
- function Z(t) {
862
+ ]), Dt = e.string().refine((e) => l.includes(e) || e.startsWith("x-"), { message: "Feature must be one of official keys or have prefix \"x-\"" }), Ot = e.string().refine((e) => u.includes(e) || e.startsWith("x-"), { message: "Item property must be an official key or have prefix \"x-\"" }), kt = e.string().refine((e) => d.includes(e) || e.startsWith("x-"), { message: "Meta property must be an official key or have prefix \"x-\"" });
863
+ function X(t) {
728
864
  return e.union([
729
865
  e.tuple([e.literal("*")]),
730
866
  e.array(t),
731
867
  e.null()
732
868
  ]);
733
869
  }
734
- var ft = e.string().refine((e) => e.startsWith("image/"), { message: "Mus+� b+�t platn+� MIME typ obr+�zku (image/...)" }), pt = e.string().refine((e) => e.startsWith("audio/"), { message: "Mus+� b+�t platn+� MIME typ zvuku (audio/...)" }), mt = e.string().refine((e) => e.startsWith("video/"), { message: "Mus+� b+�t platn+� MIME typ videa (video/...)" }), ht = e.string().refine((e) => e.startsWith("model/") || e === "application/octet-stream", { message: "Mus+� b+�t platn+� MIME typ 3D modelu (model/... nebo application/octet-stream)" }), gt = e.object({
735
- image: Z(ft).optional(),
736
- audio: Z(pt).optional(),
737
- video: Z(mt).optional(),
738
- model: Z(ht).optional()
739
- }), _t = w.extend({
740
- actions: e.array(at).min(1, "Aplikace mus+� podporovat alespo+� jednu akci"),
741
- types: Z(Y).optional(),
742
- assets: gt.optional()
743
- }), Q = e.object({
744
- $schema: nt.optional(),
745
- id: Y.regex(/^[a-z0-9-]+(\.[a-z0-9-]+)*$/, "ID mus+� b+�t ve tvaru reverse-domain (nap+�. \"com.acme.app\")"),
746
- appName: Y.max(100, "N+�zev nesm+� b+�t del+�+� ne+� 100 znak+�"),
747
- version: X,
748
- pluginVersion: rt.optional(),
749
- minOqseVersion: X.optional(),
750
- maxOqseVersion: X.optional(),
751
- capabilities: _t,
752
- author: e.string().max(100, "N+�zev autora nesm+� b+�t del+�+� ne+� 100 znak+�").optional(),
753
- authorUrl: nt.optional(),
754
- locales: e.array(Y).optional(),
755
- description: e.string().max(500, "Popis nesm+� b+�t del+�+� ne+� 500 znak+�").optional(),
870
+ var At = e.string().refine((e) => e.startsWith("image/"), { message: "Must be a valid image MIME type (image/...)" }), jt = e.string().refine((e) => e.startsWith("audio/"), { message: "Must be a valid audio MIME type (audio/...)" }), Mt = e.string().refine((e) => e.startsWith("video/"), { message: "Must be a valid video MIME type (video/...)" }), Nt = e.string().refine((e) => e.startsWith("model/") || e === "application/octet-stream", { message: "Must be a valid 3D model MIME type (model/... or application/octet-stream)" }), Pt = e.object({
871
+ image: X(At).optional(),
872
+ audio: X(jt).optional(),
873
+ video: X(Mt).optional(),
874
+ model: X(Nt).optional()
875
+ }), Ft = F.extend({
876
+ actions: e.array(wt).min(1, "Application must support at least one action"),
877
+ types: X(Ct).optional(),
878
+ assets: Pt.optional()
879
+ }).superRefine((t, n) => {
880
+ (t.actions.includes("render") || t.actions.includes("edit")) && (!Array.isArray(t.types) || t.types.length === 0) && n.addIssue({
881
+ code: e.ZodIssueCode.custom,
882
+ message: "types array MUST be declared and non-empty when actions include \"render\" or \"edit\"",
883
+ path: ["types"]
884
+ });
885
+ }), Z = e.object({
886
+ $schema: J.optional(),
887
+ id: e.union([J, xt]),
888
+ appName: q.max(200, "Name must not be longer than 200 characters"),
889
+ version: Y,
890
+ pluginVersion: e.string().optional(),
891
+ minOqseVersion: Y.optional(),
892
+ maxOqseVersion: Y.optional(),
893
+ capabilities: Ft,
894
+ author: j.optional(),
895
+ contributors: e.array(j).optional(),
896
+ license: e.string().max(100, "License must not be longer than 100 characters").optional(),
897
+ licenseUrl: J.optional(),
898
+ locales: e.array(bt).optional(),
899
+ description: e.string().max(5e3, "Description must not be longer than 5000 characters").optional(),
756
900
  emoji: e.string().optional(),
757
- tags: e.array(Y).optional(),
758
- studyMode: ot.optional(),
759
- questionDensity: st.optional(),
901
+ tags: e.array(q).optional(),
902
+ studyMode: Tt.optional(),
903
+ questionDensity: Et.optional(),
760
904
  appSpecific: e.record(e.string(), e.unknown()).optional()
761
905
  }).refine((e) => {
762
906
  if (!e.minOqseVersion || !e.maxOqseVersion) return !0;
763
- let t = e.minOqseVersion.split("."), n = e.maxOqseVersion.split("."), r = Number(t[0]), i = Number(t[1]), a = Number(n[0]), ee = Number(n[1]);
764
- return r === a ? i <= ee : r < a;
907
+ let t = e.minOqseVersion.split("."), n = e.maxOqseVersion.split("."), r = Number(t[0]), i = Number(t[1]), a = Number(n[0]), o = Number(n[1]);
908
+ return r === a ? i <= o : r < a;
765
909
  }, {
766
- message: "minOqseVersion nesm+� b+�t vy+�+�+� ne+� maxOqseVersion",
910
+ message: "minOqseVersion must not be greater than maxOqseVersion",
767
911
  path: ["minOqseVersion"]
768
912
  });
769
- function vt(e) {
770
- return Q.parse(e);
913
+ function It(e) {
914
+ return Z.parse(e);
771
915
  }
772
- function yt(e) {
773
- return Q.safeParse(e);
916
+ function Lt(e) {
917
+ return Z.safeParse(e);
774
918
  }
775
- function bt(e) {
919
+ function Rt(e) {
776
920
  return e.issues.map((e) => {
777
921
  let t = e.path.map((e) => String(e)).join(".");
778
922
  return t ? `${t}: ${e.message}` : e.message;
779
923
  }).join("\n");
780
924
  }
781
- function xt(e) {
782
- return Q.safeParse(e).success;
925
+ function zt(e) {
926
+ return Z.safeParse(e).success;
783
927
  }
784
928
  //#endregion
785
929
  //#region src/progressValidation.ts
786
- var St = e.object({
930
+ var Bt = e.object({
787
931
  attempts: e.number().int().nonnegative(),
788
932
  incorrect: e.number().int().nonnegative(),
789
933
  streak: e.number().int().nonnegative()
790
934
  }).refine((e) => e.incorrect <= e.attempts, {
791
- message: "incorrect nesmí být vyšší než attempts",
935
+ message: "incorrect cannot be higher than attempts",
792
936
  path: ["incorrect"]
793
- }), Ct = e.object({
937
+ }), Vt = e.object({
794
938
  isCorrect: e.boolean(),
795
939
  confidence: e.union([
796
940
  e.literal(1),
@@ -798,14 +942,11 @@ var St = e.object({
798
942
  e.literal(3),
799
943
  e.literal(4)
800
944
  ]).optional(),
801
- answeredAt: u,
945
+ answeredAt: x,
802
946
  timeSpent: e.number().int().nonnegative().optional(),
803
- hintsUsed: e.number().int().nonnegative().optional(),
947
+ hintsUsed: e.number().int().nonnegative().default(0),
804
948
  isSkipped: e.boolean().optional()
805
- }).refine((e) => !e.isSkipped || e.isCorrect === !1, {
806
- message: "Pokud je isSkipped=true, isCorrect má být false",
807
- path: ["isCorrect"]
808
- }), wt = e.object({
949
+ }), Ht = e.object({
809
950
  bucket: e.union([
810
951
  e.literal(0),
811
952
  e.literal(1),
@@ -813,34 +954,86 @@ var St = e.object({
813
954
  e.literal(3),
814
955
  e.literal(4)
815
956
  ]),
816
- nextReviewAt: u.optional(),
817
- stats: St,
818
- lastAnswer: Ct.optional(),
957
+ nextReviewAt: x.optional(),
958
+ stats: Bt,
959
+ lastAnswer: Vt.optional(),
819
960
  appSpecific: e.record(e.string(), e.unknown()).optional()
820
- }), Tt = e.object({
821
- setId: s,
822
- exportedAt: u,
823
- algorithm: e.string().regex(/^[a-z0-9-]+$/, "algorithm musí mít formát lowercase alphanumeric s pomlčkami").optional()
824
- }), $ = e.object({
825
- $schema: e.string().url().optional(),
826
- version: e.string().regex(/^\d+\.\d+$/, "Verze musí být ve formátu MAJOR.MINOR"),
827
- meta: Tt,
828
- records: e.record(s, wt)
961
+ }), Ut = e.object({
962
+ setId: v,
963
+ exportedAt: x,
964
+ algorithm: e.string().regex(/^[a-z0-9-]+$/, "algorithm must be lowercase alphanumeric with hyphens format").optional()
965
+ }), Q = e.object({
966
+ $schema: e.url().optional(),
967
+ version: e.string().regex(/^\d+\.\d+$/, "Version must be in MAJOR.MINOR format"),
968
+ meta: Ut,
969
+ records: e.record(v, Ht)
970
+ });
971
+ function Wt(e) {
972
+ return Q.parse(e);
973
+ }
974
+ function Gt(e) {
975
+ return Q.safeParse(e);
976
+ }
977
+ var $ = e.object(L.shape).pick({
978
+ id: !0,
979
+ title: !0,
980
+ language: !0,
981
+ updatedAt: !0,
982
+ description: !0,
983
+ author: !0,
984
+ subject: !0,
985
+ tags: !0,
986
+ createdAt: !0,
987
+ estimatedTime: !0,
988
+ requirements: !0
989
+ }).partial({ createdAt: !0 }).extend({
990
+ url: S,
991
+ $schema: e.string().url().optional()
829
992
  });
830
- function Et(e) {
993
+ function Kt(e) {
831
994
  return $.parse(e);
832
995
  }
833
- function Dt(e) {
996
+ function qt(e) {
834
997
  return $.safeParse(e);
835
998
  }
836
999
  //#endregion
837
- //#region src/utils.ts
838
- var Ot = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
839
- function kt(e) {
840
- return Ot.test(e);
1000
+ //#region src/richTextProcessor.ts
1001
+ function Jt(e) {
1002
+ let t = {};
1003
+ return {
1004
+ text: e.replace(/<(asset|blank):([a-zA-Z0-9_-]+)\s*\/>/gi, (e, n, r) => {
1005
+ let i = `[[__OQSE_TOKEN_${Math.random().toString(36).substring(2, 15)}__]]`;
1006
+ return t[i] = {
1007
+ type: n.toLowerCase(),
1008
+ key: r.toLowerCase(),
1009
+ originalTag: e
1010
+ }, i;
1011
+ }),
1012
+ tokens: t
1013
+ };
841
1014
  }
842
- function At() {
843
- return crypto.randomUUID();
1015
+ function Yt(e) {
1016
+ let t = e.replace(/`{1,}[^`]*`{1,}/g, "").replace(/~{3,}[\s\S]*?~{3,}/g, "");
1017
+ if (/<[a-zA-Z\/][^>]*>/.test(t)) throw Error("OQSE Security Error: Raw HTML tags are not allowed in Tier 1 (Pure Markdown). If the set requires HTML formatting, it MUST declare the 'html' feature in meta.requirements.");
1018
+ }
1019
+ function Xt(e, t, n) {
1020
+ let r = e;
1021
+ for (let [e, i] of Object.entries(t)) {
1022
+ let t = i.originalTag;
1023
+ i.type === "asset" && n.assetReplacer ? t = n.assetReplacer(i.key) : i.type === "blank" && n.blankReplacer && (t = n.blankReplacer(i.key)), r = r.replace(e, t);
1024
+ }
1025
+ return r;
1026
+ }
1027
+ function Zt(e, t, n) {
1028
+ if (!e) return "";
1029
+ let r = t?.features?.includes("html") ?? !1, { text: i, tokens: a } = Jt(e);
1030
+ r || Yt(i);
1031
+ let o = n.markdownParser(i);
1032
+ if (r) {
1033
+ if (!n.htmlSanitizer) throw Error("OQSE Security Error: Set requires 'html' (Tier 2), but no htmlSanitizer (like DOMPurify) was provided to the options.");
1034
+ o = n.htmlSanitizer(o);
1035
+ }
1036
+ return Xt(o, a, n);
844
1037
  }
845
1038
  //#endregion
846
- export { d as AbsoluteURLSchema, _ as AssetDictionarySchema, f as AssetKeySchema, pt as AudioMimeTypeSchema, F as BaseItemSchema, k as BloomLevelSchema, U as CameraSetupSchema, be as CategorizeItemSchema, ze as CategorizeItemTypeSchema, qe as ChessPuzzleItemSchema, R as CircleHotspotSchema, A as CognitiveLoadSchema, Ue as DiagramLabelItemSchema, Ce as DiagramZoneSchema, lt as FeatureFlagSchema, w as FeatureProfileSchema, E as FeatureRequirementSchema, T as FeatureTypeSchema, Me as FillInBlanksItemSchema, Ne as FillInSelectItemSchema, Oe as FlashcardItemSchema, V as HotspotObjectSchema, u as ISO8601DateTimeSchema, ft as ImageMimeTypeSchema, ut as ItemPropertyKeySchema, c as LanguageCodeSchema, Ct as LastAnswerObjectSchema, O as LinkedSetObjectSchema, je as MCQMultiItemSchema, Ae as MCQSingleItemSchema, gt as ManifestAssetsSchema, _t as ManifestCapabilitiesSchema, Fe as MatchComplexItemSchema, Pe as MatchPairsItemSchema, He as MathInputItemSchema, M as MathRendererSchema, N as MathSettingsSchema, Ve as MatrixItemSchema, ve as MediaObjectSchema, B as MeshHotspotSchema, dt as MetaPropertyKeySchema, ht as ModelMimeTypeSchema, De as NoteItemSchema, Ge as NumericInputItemSchema, Ee as NumericRangeSchema, at as OQSEActionSchema, K as OQSEFileSchema, G as OQSEItemSchema, Q as OQSEManifestSchema, P as OQSEMetaSchema, $ as OQSEPFileSchema, st as OQSEQuestionDensitySchema, ot as OQSEStudyModeSchema, ye as OfficialFeatureNameSchema, We as OpenEndedItemSchema, h as OptionalRichContentSchema, j as PedagogySchema, v as PersonObjectSchema, Re as PinOnImageItemSchema, Ke as PinOnModelItemSchema, p as PlainTextSchema, z as PolygonHotspotSchema, Tt as ProgressMetaSchema, wt as ProgressRecordSchema, L as RectHotspotSchema, m as RichContentSchema, we as RubricCriterionSchema, Te as RubricSchema, l as SPDXLicenseSchema, I as SelectBlankObjectSchema, W as ShortAnswerItemSchema, Le as SliderItemSchema, Ie as SortItemsItemSchema, b as SourceMaterialSchema, y as SourceMaterialTypeSchema, x as SourceReferenceSchema, St as StatsObjectSchema, g as SubtitleTrackSchema, C as TagDefinitionDictionarySchema, S as TagDefinitionSchema, Se as TimelineEventSchema, Be as TimelineItemSchema, xe as TimelinePrecisionSchema, D as TranslationObjectSchema, ke as TrueFalseItemSchema, s as UUIDSchema, H as Vector3Schema, mt as VideoMimeTypeSchema, J as compareManifestVersions, bt as formatManifestErrors, Qe as formatValidationErrors, At as generateUUID, ce as isCategorize, ge as isChessPuzzle, o as isCoreItem, fe as isDiagramLabel, _e as isExtendedItem, te as isFillInBlanks, ne as isFillInSelect, n as isFlashcard, a as isMCQMulti, i as isMCQSingle, ie as isMatchComplex, re as isMatchPairs, de as isMathInput, ue as isMatrix, t as isNote, me as isNumericInput, et as isOQSEManifest, pe as isOpenEnded, se as isPinOnImage, he as isPinOnModel, ee as isShortAnswer, oe as isSlider, ae as isSortItems, le as isTimeline, r as isTrueFalse, q as isValidManifestVersion, xt as isValidOQSEManifest, $e as isValidSemVer, kt as isValidUUID, tt as isVersionCompatible, yt as safeValidateManifest, Ye as safeValidateOQSEFile, Ze as safeValidateOQSEItem, Dt as safeValidateOQSEPFile, vt as validateManifest, Je as validateOQSEFile, Xe as validateOQSEItem, Et as validateOQSEPFile };
1039
+ export { S as AbsoluteURLSchema, A as AssetDictionarySchema, C as AssetKeySchema, jt as AudioMimeTypeSchema, R as BaseItemSchema, D as BlankTokenSchema, Ne as BloomLevelSchema, ze as CameraSetupSchema, Be as CategorizeItemObjectSchema, at as CategorizeItemSchema, pt as ChessPuzzleItemSchema, B as CircleHotspotSchema, Pe as CognitiveLoadSchema, lt as DiagramLabelItemSchema, Ue as DiagramZoneSchema, Dt as FeatureFlagSchema, F as FeatureProfileSchema, Qe as FillInBlanksItemSchema, $e as FillInSelectItemSchema, qe as FlashcardItemSchema, Re as Hotspot2DSchema, Le as HotspotObjectSchema, x as ISO8601DateTimeSchema, At as ImageMimeTypeSchema, Ot as ItemPropertyKeySchema, y as LanguageCodeSchema, Vt as LastAnswerObjectSchema, I as LinkedSetObjectSchema, Xe as MCQMultiItemSchema, Ye as MCQSingleItemSchema, Pt as ManifestAssetsSchema, Ft as ManifestCapabilitiesSchema, tt as MatchComplexItemSchema, et as MatchPairsItemSchema, ct as MathInputItemSchema, st as MatrixItemSchema, k as MediaObjectSchema, H as MeshHotspotSchema, kt as MetaPropertyKeySchema, Nt as ModelMimeTypeSchema, Ke as NoteItemSchema, dt as NumericInputItemSchema, Ge as NumericRangeSchema, c as OFFICIAL_ACTIONS, l as OFFICIAL_FEATURE_KEYS, u as OFFICIAL_ITEM_PROPERTIES, d as OFFICIAL_META_PROPERTIES, wt as OQSEActionSchema, K as OQSEFileSchema, $ as OQSEHeaderSchema, G as OQSEItemSchema, Z as OQSEManifestSchema, L as OQSEMetaSchema, Q as OQSEPFileSchema, Et as OQSEQuestionDensitySchema, Tt as OQSEStudyModeSchema, ut as OpenEndedItemSchema, E as OptionalRichContentSchema, Fe as PedagogySchema, j as PersonObjectSchema, it as PinOnImageItemSchema, ft as PinOnModelItemSchema, w as PlainTextSchema, V as PolygonHotspotSchema, Ut as ProgressMetaSchema, Ht as ProgressRecordSchema, z as RectHotspotSchema, T as RichContentSchema, W as RubricCriterionSchema, We as RubricSchema, b as SPDXLicenseSchema, Ie as SelectBlankObjectSchema, Ze as ShortAnswerItemSchema, rt as SliderItemSchema, nt as SortItemsItemSchema, N as SourceMaterialSchema, M as SourceMaterialTypeSchema, P as SourceReferenceSchema, Bt as StatsObjectSchema, O as SubtitleTrackSchema, je as TagDefinitionDictionarySchema, Ae as TagDefinitionSchema, He as TimelineEventSchema, ot as TimelineItemSchema, Ve as TimelinePrecisionSchema, Me as TranslationObjectSchema, Je as TrueFalseItemSchema, v as UUIDSchema, U as Vector3Schema, Mt as VideoMimeTypeSchema, p as compareManifestVersions, Xt as detokenizeOqseTags, Rt as formatManifestErrors, yt as formatValidationErrors, ke as generateUUID, se as isCategorize, he as isChessPuzzle, s as isCoreItem, de as isDiagramLabel, ge as isExtendedItem, ee as isFillInBlanks, te as isFillInSelect, n as isFlashcard, a as isMCQMulti, i as isMCQSingle, re as isMatchComplex, ne as isMatchPairs, ue as isMathInput, le as isMatrix, t as isNote, pe as isNumericInput, ve as isOQSEManifest, fe as isOpenEnded, oe as isPinOnImage, me as isPinOnModel, o as isShortAnswer, ae as isSlider, ie as isSortItems, ce as isTimeline, r as isTrueFalse, f as isValidManifestVersion, zt as isValidOQSEManifest, _e as isValidSemVer, Oe as isValidUUID, ye as isVersionCompatible, Zt as prepareRichTextForDisplay, Lt as safeValidateManifest, gt as safeValidateOQSEFile, qt as safeValidateOQSEHeader, vt as safeValidateOQSEItem, Gt as safeValidateOQSEPFile, Jt as tokenizeOqseTags, _ as validateJsonDepth, It as validateManifest, ht as validateOQSEFile, Kt as validateOQSEHeader, _t as validateOQSEItem, Wt as validateOQSEPFile, Yt as validateTier1Markdown };