@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/README.md +25 -8
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +625 -432
- package/dist/src/header.d.ts +7 -0
- package/dist/src/headerValidation.d.ts +5 -0
- package/dist/src/headerValidation.test.d.ts +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/manifest.d.ts +24 -21
- package/dist/src/manifestValidation.d.ts +21 -8
- package/dist/src/oqse.d.ts +11 -52
- package/dist/src/oqseValidation.d.ts +98 -81
- package/dist/src/progress.d.ts +2 -2
- package/dist/src/richTextProcessor.d.ts +46 -0
- package/dist/src/richTextProcessor.test.d.ts +1 -0
- package/dist/src/utils.d.ts +14 -0
- package/dist/src/utils.test.d.ts +1 -0
- package/package.json +3 -1
- package/schemas/oqse-header-v0.1.json +120 -0
- package/schemas/oqse-manifest-v0.1.json +71 -15
- package/schemas/{oqsep-v0.1.json → oqse-progress-v0.1.json} +3 -1
- package/schemas/oqse-v0.1.json +156 -103
- package/SPECIFICATION.md +0 -2444
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
|
|
18
|
+
function o(e) {
|
|
19
19
|
return e.type === "short-answer";
|
|
20
20
|
}
|
|
21
|
-
function
|
|
21
|
+
function ee(e) {
|
|
22
22
|
return e.type === "fill-in-blanks";
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function te(e) {
|
|
25
25
|
return e.type === "fill-in-select";
|
|
26
26
|
}
|
|
27
|
-
function
|
|
27
|
+
function ne(e) {
|
|
28
28
|
return e.type === "match-pairs";
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function re(e) {
|
|
31
31
|
return e.type === "match-complex";
|
|
32
32
|
}
|
|
33
|
-
function
|
|
33
|
+
function ie(e) {
|
|
34
34
|
return e.type === "sort-items";
|
|
35
35
|
}
|
|
36
|
-
function
|
|
36
|
+
function ae(e) {
|
|
37
37
|
return e.type === "slider";
|
|
38
38
|
}
|
|
39
|
-
function
|
|
39
|
+
function oe(e) {
|
|
40
40
|
return e.type === "pin-on-image";
|
|
41
41
|
}
|
|
42
|
-
function
|
|
42
|
+
function se(e) {
|
|
43
43
|
return e.type === "categorize";
|
|
44
44
|
}
|
|
45
|
-
function
|
|
45
|
+
function ce(e) {
|
|
46
46
|
return e.type === "timeline";
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function le(e) {
|
|
49
49
|
return e.type === "matrix";
|
|
50
50
|
}
|
|
51
|
-
function
|
|
51
|
+
function ue(e) {
|
|
52
52
|
return e.type === "math-input";
|
|
53
53
|
}
|
|
54
|
-
function
|
|
54
|
+
function de(e) {
|
|
55
55
|
return e.type === "diagram-label";
|
|
56
56
|
}
|
|
57
|
-
function
|
|
57
|
+
function fe(e) {
|
|
58
58
|
return e.type === "open-ended";
|
|
59
59
|
}
|
|
60
|
-
function
|
|
60
|
+
function pe(e) {
|
|
61
61
|
return e.type === "numeric-input";
|
|
62
62
|
}
|
|
63
|
-
function
|
|
63
|
+
function me(e) {
|
|
64
64
|
return e.type === "pin-on-model";
|
|
65
65
|
}
|
|
66
|
-
function
|
|
66
|
+
function he(e) {
|
|
67
67
|
return e.type === "chess-puzzle";
|
|
68
68
|
}
|
|
69
|
-
function
|
|
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
|
|
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
|
|
83
|
-
lang:
|
|
84
|
-
value: e.string().min(1, "URI
|
|
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
|
-
}),
|
|
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
|
|
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:
|
|
102
|
-
caption:
|
|
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(
|
|
109
|
-
license:
|
|
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: "
|
|
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: "
|
|
243
|
+
message: "Images must have alternative text (altText) defined for accessibility",
|
|
117
244
|
path: ["altText"]
|
|
118
|
-
}),
|
|
119
|
-
name:
|
|
245
|
+
}), A = e.record(C, k), j = e.object({
|
|
246
|
+
name: w,
|
|
120
247
|
role: e.string().optional(),
|
|
121
|
-
email: e.string().email("
|
|
122
|
-
url:
|
|
123
|
-
}),
|
|
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
|
-
]),
|
|
135
|
-
id:
|
|
136
|
-
type:
|
|
137
|
-
value:
|
|
138
|
-
title:
|
|
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(
|
|
141
|
-
publishedDate:
|
|
142
|
-
retrievedAt:
|
|
143
|
-
license:
|
|
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: "
|
|
285
|
+
message: "For url, pdf, video, audio and image types, value must be a valid URL",
|
|
159
286
|
path: ["value"]
|
|
160
|
-
}),
|
|
161
|
-
id:
|
|
287
|
+
}), P = e.object({
|
|
288
|
+
id: w,
|
|
162
289
|
location: e.string().optional(),
|
|
163
|
-
quote:
|
|
164
|
-
}),
|
|
165
|
-
wikidataId: e.string().regex(/^Q\d+$/, "Wikidata ID
|
|
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
|
-
}),
|
|
168
|
-
features: e.array(e.string().
|
|
169
|
-
latexPackages: e.array(e.string().min(1, "
|
|
170
|
-
itemProperties: e.array(e.string().
|
|
171
|
-
metaProperties: e.array(e.string().
|
|
172
|
-
}),
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
]),
|
|
315
|
+
]), Pe = e.enum([
|
|
208
316
|
"low",
|
|
209
317
|
"medium",
|
|
210
318
|
"high"
|
|
211
|
-
]),
|
|
212
|
-
bloomLevel:
|
|
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
|
|
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:
|
|
325
|
+
cognitiveLoad: Pe.optional(),
|
|
218
326
|
partialCredit: e.boolean().optional(),
|
|
219
|
-
penaltyPerWrong: e.number().min(0).max(1, "
|
|
220
|
-
}),
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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:
|
|
234
|
-
updatedAt:
|
|
235
|
-
author:
|
|
236
|
-
contributors: e.array(
|
|
237
|
-
license:
|
|
238
|
-
licenseUrl:
|
|
239
|
-
requirements:
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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(
|
|
247
|
-
relatedSets: e.array(
|
|
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: "
|
|
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: "
|
|
358
|
+
message: "Creation date (createdAt) must be before or equal to update date (updatedAt)",
|
|
256
359
|
path: ["updatedAt"]
|
|
257
|
-
}),
|
|
258
|
-
id:
|
|
360
|
+
}), R = e.object({
|
|
361
|
+
id: v,
|
|
259
362
|
type: e.string(),
|
|
260
|
-
assets:
|
|
261
|
-
lang:
|
|
262
|
-
tags: e.array(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
}),
|
|
275
|
-
options: e.array(
|
|
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: "
|
|
382
|
+
message: "Correct answer index references non-existent option",
|
|
279
383
|
path: ["correctIndex"]
|
|
280
|
-
}),
|
|
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
|
|
284
|
-
y: e.number().min(0).max(100, "Y
|
|
285
|
-
width: e.number().min(0).max(100, "
|
|
286
|
-
height: e.number().min(0).max(100, "
|
|
287
|
-
}),
|
|
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
|
|
291
|
-
y: e.number().min(0).max(100, "Y
|
|
292
|
-
radius: e.number().min(0).max(100, "
|
|
293
|
-
}),
|
|
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
|
|
300
|
-
}),
|
|
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:
|
|
304
|
-
}),
|
|
305
|
-
L,
|
|
306
|
-
R,
|
|
407
|
+
targetName: w
|
|
408
|
+
}), Le = e.discriminatedUnion("type", [
|
|
307
409
|
z,
|
|
308
|
-
B
|
|
309
|
-
|
|
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
|
-
}),
|
|
314
|
-
position:
|
|
315
|
-
target:
|
|
316
|
-
}),
|
|
317
|
-
id:
|
|
318
|
-
text:
|
|
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
|
-
}),
|
|
428
|
+
}), Ve = e.enum([
|
|
321
429
|
"year",
|
|
322
430
|
"month",
|
|
323
431
|
"day",
|
|
324
432
|
"datetime"
|
|
325
|
-
]),
|
|
326
|
-
id:
|
|
327
|
-
text:
|
|
328
|
-
date:
|
|
329
|
-
precision:
|
|
330
|
-
}),
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
]),
|
|
335
|
-
label:
|
|
336
|
-
percentage: e.number().min(0, "
|
|
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
|
-
}),
|
|
339
|
-
message: "
|
|
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
|
-
}),
|
|
449
|
+
}), Ge = e.object({
|
|
342
450
|
min: e.number(),
|
|
343
451
|
max: e.number()
|
|
344
452
|
}).refine((e) => e.min <= e.max, {
|
|
345
|
-
message: "
|
|
453
|
+
message: "Minimum value must be less than or equal to maximum value",
|
|
346
454
|
path: ["max"]
|
|
347
|
-
}),
|
|
455
|
+
}), Ke = R.extend({
|
|
348
456
|
type: e.literal("note"),
|
|
349
457
|
title: e.string().optional(),
|
|
350
|
-
content:
|
|
351
|
-
|
|
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:
|
|
354
|
-
back:
|
|
355
|
-
}),
|
|
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:
|
|
466
|
+
question: T.max(1e4, "Question must not be longer than 10000 characters"),
|
|
358
467
|
answer: e.boolean()
|
|
359
|
-
}),
|
|
468
|
+
}), Ye = R.extend({
|
|
360
469
|
type: e.literal("mcq-single"),
|
|
361
|
-
question:
|
|
362
|
-
options: e.array(
|
|
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([
|
|
474
|
+
optionExplanations: e.array(e.union([T, e.null()])).optional()
|
|
366
475
|
}).refine((e) => e.correctIndex < e.options.length, {
|
|
367
|
-
message: "
|
|
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: "
|
|
479
|
+
message: "Number of option explanations must match number of options",
|
|
371
480
|
path: ["optionExplanations"]
|
|
372
|
-
})
|
|
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:
|
|
375
|
-
options: e.array(
|
|
376
|
-
correctIndices: e.array(e.number().int().nonnegative()).min(1, "
|
|
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([
|
|
492
|
+
optionExplanations: e.array(e.union([T, e.null()])).optional()
|
|
381
493
|
}).refine((e) => e.correctIndices.every((t) => t < e.options.length), {
|
|
382
|
-
message: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
509
|
+
message: "Number of option explanations must match number of options",
|
|
395
510
|
path: ["optionExplanations"]
|
|
396
|
-
})
|
|
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:
|
|
399
|
-
answers: e.array(
|
|
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
|
-
}),
|
|
522
|
+
}), Qe = R.extend({
|
|
405
523
|
type: e.literal("fill-in-blanks"),
|
|
406
|
-
question:
|
|
407
|
-
text:
|
|
408
|
-
blanks: e.record(
|
|
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
|
|
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: "
|
|
540
|
+
message: "All tokens in text must have definition in blanks and all definitions must be used in text",
|
|
423
541
|
path: ["blanks"]
|
|
424
|
-
}),
|
|
542
|
+
}), $e = R.extend({
|
|
425
543
|
type: e.literal("fill-in-select"),
|
|
426
|
-
question:
|
|
427
|
-
text:
|
|
428
|
-
blanks: e.record(
|
|
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
|
|
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: "
|
|
558
|
+
message: "All tokens in text must have definition in blanks and all definitions must be used in text",
|
|
441
559
|
path: ["blanks"]
|
|
442
|
-
}),
|
|
560
|
+
}), et = R.extend({
|
|
443
561
|
type: e.literal("match-pairs"),
|
|
444
|
-
question:
|
|
445
|
-
prompts: e.array(
|
|
446
|
-
matches: e.array(
|
|
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: "
|
|
566
|
+
message: "Number of prompts must match number of matches",
|
|
449
567
|
path: ["matches"]
|
|
450
|
-
}),
|
|
568
|
+
}), tt = R.extend({
|
|
451
569
|
type: e.literal("match-complex"),
|
|
452
|
-
question:
|
|
453
|
-
leftItems: e.array(
|
|
454
|
-
rightItems: e.array(
|
|
455
|
-
connections: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "
|
|
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: "
|
|
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: "
|
|
579
|
+
message: "Connections contain duplicates",
|
|
462
580
|
path: ["connections"]
|
|
463
581
|
}).refine((e) => e.minCorrect ? e.minCorrect <= e.connections.length : !0, {
|
|
464
|
-
message: "
|
|
582
|
+
message: "Minimum correct answers must not exceed total number of connections",
|
|
465
583
|
path: ["minCorrect"]
|
|
466
|
-
}),
|
|
584
|
+
}), nt = R.extend({
|
|
467
585
|
type: e.literal("sort-items"),
|
|
468
|
-
question:
|
|
469
|
-
items: e.array(
|
|
470
|
-
}),
|
|
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:
|
|
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("
|
|
593
|
+
step: e.number().positive("Step must be a positive number"),
|
|
476
594
|
correctAnswer: e.number(),
|
|
477
|
-
tolerance: e.number().nonnegative("Tolerance
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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
|
|
610
|
+
message: "Tolerance must not be greater than half of value range",
|
|
493
611
|
path: ["tolerance"]
|
|
494
|
-
}),
|
|
612
|
+
}), it = R.extend({
|
|
495
613
|
type: e.literal("pin-on-image"),
|
|
496
|
-
question:
|
|
497
|
-
targetAsset:
|
|
498
|
-
hotspots: e.array(
|
|
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: "
|
|
620
|
+
message: "Minimum correct answers must not exceed number of hotspots",
|
|
503
621
|
path: ["minCorrect"]
|
|
504
|
-
}),
|
|
622
|
+
}), at = R.extend({
|
|
505
623
|
type: e.literal("categorize"),
|
|
506
|
-
question:
|
|
507
|
-
categories: e.array(
|
|
508
|
-
items: e.array(
|
|
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: "
|
|
631
|
+
message: "Some item references non-existent category",
|
|
511
632
|
path: ["items"]
|
|
512
|
-
}),
|
|
633
|
+
}), ot = R.extend({
|
|
513
634
|
type: e.literal("timeline"),
|
|
514
|
-
question:
|
|
515
|
-
events: e.array(
|
|
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
|
-
}),
|
|
638
|
+
}), st = R.extend({
|
|
518
639
|
type: e.literal("matrix"),
|
|
519
|
-
question:
|
|
520
|
-
rows: e.array(
|
|
521
|
-
columns: e.array(
|
|
522
|
-
correctCells: e.array(e.tuple([e.number().int().nonnegative(), e.number().int().nonnegative()])).min(1, "
|
|
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: "
|
|
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: "
|
|
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: "
|
|
659
|
+
message: "If multiplePerRow is false, each row may have maximum 1 correct cell",
|
|
539
660
|
path: ["correctCells"]
|
|
540
|
-
}),
|
|
661
|
+
}), ct = R.extend({
|
|
541
662
|
type: e.literal("math-input"),
|
|
542
|
-
question:
|
|
543
|
-
correctAnswer:
|
|
544
|
-
alternativeAnswers: e.array(
|
|
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
|
-
}),
|
|
667
|
+
}), lt = R.extend({
|
|
547
668
|
type: e.literal("diagram-label"),
|
|
548
|
-
question:
|
|
549
|
-
targetAsset:
|
|
550
|
-
labels: e.array(
|
|
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(
|
|
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: "
|
|
676
|
+
message: "Some zone references non-existent label",
|
|
556
677
|
path: ["zones"]
|
|
557
|
-
}),
|
|
678
|
+
}), ut = R.extend({
|
|
558
679
|
type: e.literal("open-ended"),
|
|
559
|
-
question:
|
|
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:
|
|
563
|
-
rubric:
|
|
683
|
+
sampleAnswer: T.optional(),
|
|
684
|
+
rubric: We.optional()
|
|
564
685
|
}).refine((e) => e.minWords && e.maxWords ? e.minWords <= e.maxWords : !0, {
|
|
565
|
-
message: "
|
|
686
|
+
message: "Minimum word count must be less than or equal to maximum",
|
|
566
687
|
path: ["maxWords"]
|
|
567
|
-
}),
|
|
688
|
+
}), dt = R.extend({
|
|
568
689
|
type: e.literal("numeric-input"),
|
|
569
|
-
question:
|
|
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:
|
|
693
|
+
range: Ge.optional(),
|
|
573
694
|
unit: e.string().optional()
|
|
574
|
-
}),
|
|
695
|
+
}), ft = R.extend({
|
|
575
696
|
type: e.literal("pin-on-model"),
|
|
576
|
-
question:
|
|
577
|
-
targetAsset:
|
|
578
|
-
hotspots: e.array(
|
|
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:
|
|
702
|
+
camera: ze.optional()
|
|
582
703
|
}).refine((e) => e.minCorrect ? e.minCorrect <= e.hotspots.length : !0, {
|
|
583
|
-
message: "
|
|
704
|
+
message: "Minimum correct answers must not exceed number of hotspots",
|
|
584
705
|
path: ["minCorrect"]
|
|
585
|
-
}),
|
|
706
|
+
}), pt = R.extend({
|
|
586
707
|
type: e.literal("chess-puzzle"),
|
|
587
|
-
question:
|
|
588
|
-
fen:
|
|
589
|
-
answers: e.array(e.array(
|
|
590
|
-
elo: e.number().int().nonnegative("ELO
|
|
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+$/, "
|
|
617
|
-
meta:
|
|
618
|
-
items: e.array(G).max(1e4, "
|
|
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: "
|
|
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
|
|
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: "
|
|
764
|
+
message: "Some item references non-existent source material",
|
|
644
765
|
path: ["items"]
|
|
645
766
|
});
|
|
646
|
-
function
|
|
647
|
-
|
|
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
|
|
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
|
|
660
|
-
return G.parse(e);
|
|
796
|
+
function _t(e) {
|
|
797
|
+
return _(e, 10), G.parse(e);
|
|
661
798
|
}
|
|
662
|
-
function
|
|
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
|
|
817
|
+
function yt(e) {
|
|
673
818
|
return e.issues.map((e) => {
|
|
674
|
-
let t =
|
|
675
|
-
return
|
|
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
|
|
701
|
-
"
|
|
702
|
-
"
|
|
703
|
-
"
|
|
704
|
-
"
|
|
705
|
-
"
|
|
706
|
-
|
|
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
|
-
]),
|
|
858
|
+
]), Et = e.enum([
|
|
711
859
|
"low",
|
|
712
860
|
"medium",
|
|
713
861
|
"high"
|
|
714
|
-
]),
|
|
715
|
-
|
|
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
|
|
735
|
-
image:
|
|
736
|
-
audio:
|
|
737
|
-
video:
|
|
738
|
-
model:
|
|
739
|
-
}),
|
|
740
|
-
actions: e.array(
|
|
741
|
-
types:
|
|
742
|
-
assets:
|
|
743
|
-
}),
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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(
|
|
758
|
-
studyMode:
|
|
759
|
-
questionDensity:
|
|
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]),
|
|
764
|
-
return r === a ? i <=
|
|
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
|
|
910
|
+
message: "minOqseVersion must not be greater than maxOqseVersion",
|
|
767
911
|
path: ["minOqseVersion"]
|
|
768
912
|
});
|
|
769
|
-
function
|
|
770
|
-
return
|
|
913
|
+
function It(e) {
|
|
914
|
+
return Z.parse(e);
|
|
771
915
|
}
|
|
772
|
-
function
|
|
773
|
-
return
|
|
916
|
+
function Lt(e) {
|
|
917
|
+
return Z.safeParse(e);
|
|
774
918
|
}
|
|
775
|
-
function
|
|
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
|
|
782
|
-
return
|
|
925
|
+
function zt(e) {
|
|
926
|
+
return Z.safeParse(e).success;
|
|
783
927
|
}
|
|
784
928
|
//#endregion
|
|
785
929
|
//#region src/progressValidation.ts
|
|
786
|
-
var
|
|
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
|
|
935
|
+
message: "incorrect cannot be higher than attempts",
|
|
792
936
|
path: ["incorrect"]
|
|
793
|
-
}),
|
|
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:
|
|
945
|
+
answeredAt: x,
|
|
802
946
|
timeSpent: e.number().int().nonnegative().optional(),
|
|
803
|
-
hintsUsed: e.number().int().nonnegative().
|
|
947
|
+
hintsUsed: e.number().int().nonnegative().default(0),
|
|
804
948
|
isSkipped: e.boolean().optional()
|
|
805
|
-
})
|
|
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:
|
|
817
|
-
stats:
|
|
818
|
-
lastAnswer:
|
|
957
|
+
nextReviewAt: x.optional(),
|
|
958
|
+
stats: Bt,
|
|
959
|
+
lastAnswer: Vt.optional(),
|
|
819
960
|
appSpecific: e.record(e.string(), e.unknown()).optional()
|
|
820
|
-
}),
|
|
821
|
-
setId:
|
|
822
|
-
exportedAt:
|
|
823
|
-
algorithm: e.string().regex(/^[a-z0-9-]+$/, "algorithm
|
|
824
|
-
}),
|
|
825
|
-
$schema: e.
|
|
826
|
-
version: e.string().regex(/^\d+\.\d+$/, "
|
|
827
|
-
meta:
|
|
828
|
-
records: e.record(
|
|
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
|
|
993
|
+
function Kt(e) {
|
|
831
994
|
return $.parse(e);
|
|
832
995
|
}
|
|
833
|
-
function
|
|
996
|
+
function qt(e) {
|
|
834
997
|
return $.safeParse(e);
|
|
835
998
|
}
|
|
836
999
|
//#endregion
|
|
837
|
-
//#region src/
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
return
|
|
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
|
|
843
|
-
|
|
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 {
|
|
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 };
|