@quizpot/quizcore 0.0.1 → 0.0.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.
Files changed (132) hide show
  1. package/README.md +1 -7
  2. package/dist/index.cjs +582 -0
  3. package/dist/index.d.cts +472 -0
  4. package/dist/index.d.cts.map +1 -0
  5. package/dist/index.d.mts +472 -0
  6. package/dist/index.d.mts.map +1 -0
  7. package/dist/index.mjs +522 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +4 -4
  10. package/dist/events/client/host/kick-player.d.ts +0 -8
  11. package/dist/events/client/host/kick-player.d.ts.map +0 -1
  12. package/dist/events/client/host/kick-player.js +0 -4
  13. package/dist/events/client/host/next-step.d.ts +0 -5
  14. package/dist/events/client/host/next-step.d.ts.map +0 -1
  15. package/dist/events/client/host/next-step.js +0 -3
  16. package/dist/events/client/host/start-lobby.d.ts +0 -6
  17. package/dist/events/client/host/start-lobby.d.ts.map +0 -1
  18. package/dist/events/client/host/start-lobby.js +0 -4
  19. package/dist/events/client/player/submit-answer.d.ts +0 -9
  20. package/dist/events/client/player/submit-answer.d.ts.map +0 -1
  21. package/dist/events/client/player/submit-answer.js +0 -4
  22. package/dist/events/server/lobby-deleted.d.ts +0 -8
  23. package/dist/events/server/lobby-deleted.d.ts.map +0 -1
  24. package/dist/events/server/lobby-deleted.js +0 -4
  25. package/dist/events/server/lobby-joined.d.ts +0 -14
  26. package/dist/events/server/lobby-joined.d.ts.map +0 -1
  27. package/dist/events/server/lobby-joined.js +0 -18
  28. package/dist/events/server/lobby-status-update.d.ts +0 -32
  29. package/dist/events/server/lobby-status-update.d.ts.map +0 -1
  30. package/dist/events/server/lobby-status-update.js +0 -4
  31. package/dist/events/server/player-answer-result.d.ts +0 -11
  32. package/dist/events/server/player-answer-result.d.ts.map +0 -1
  33. package/dist/events/server/player-answer-result.js +0 -4
  34. package/dist/events/server/player-joined.d.ts +0 -9
  35. package/dist/events/server/player-joined.d.ts.map +0 -1
  36. package/dist/events/server/player-joined.js +0 -4
  37. package/dist/events/server/player-kicked.d.ts +0 -6
  38. package/dist/events/server/player-kicked.d.ts.map +0 -1
  39. package/dist/events/server/player-kicked.js +0 -4
  40. package/dist/events/server/player-left.d.ts +0 -9
  41. package/dist/events/server/player-left.d.ts.map +0 -1
  42. package/dist/events/server/player-left.js +0 -4
  43. package/dist/events/server/player-update.d.ts +0 -9
  44. package/dist/events/server/player-update.d.ts.map +0 -1
  45. package/dist/events/server/player-update.js +0 -4
  46. package/dist/events/server/update-lobby-answers.d.ts +0 -8
  47. package/dist/events/server/update-lobby-answers.d.ts.map +0 -1
  48. package/dist/events/server/update-lobby-answers.js +0 -4
  49. package/dist/index.d.ts +0 -26
  50. package/dist/index.d.ts.map +0 -1
  51. package/dist/index.js +0 -25
  52. package/dist/managers/lobby-manager.d.ts +0 -12
  53. package/dist/managers/lobby-manager.d.ts.map +0 -1
  54. package/dist/managers/lobby-manager.js +0 -112
  55. package/dist/questions/multiple-choice.d.ts +0 -22
  56. package/dist/questions/multiple-choice.d.ts.map +0 -1
  57. package/dist/questions/multiple-choice.js +0 -1
  58. package/dist/questions/short-answer.d.ts +0 -12
  59. package/dist/questions/short-answer.d.ts.map +0 -1
  60. package/dist/questions/short-answer.js +0 -1
  61. package/dist/questions/true-false.d.ts +0 -12
  62. package/dist/questions/true-false.d.ts.map +0 -1
  63. package/dist/questions/true-false.js +0 -1
  64. package/dist/schemas/quizfile.d.ts +0 -87
  65. package/dist/schemas/quizfile.d.ts.map +0 -1
  66. package/dist/schemas/quizfile.js +0 -55
  67. package/dist/slides/comparison.d.ts +0 -9
  68. package/dist/slides/comparison.d.ts.map +0 -1
  69. package/dist/slides/comparison.js +0 -7
  70. package/dist/slides/titleImageTextSlide.d.ts +0 -9
  71. package/dist/slides/titleImageTextSlide.d.ts.map +0 -1
  72. package/dist/slides/titleImageTextSlide.js +0 -7
  73. package/dist/slides/titleSlide.d.ts +0 -8
  74. package/dist/slides/titleSlide.d.ts.map +0 -1
  75. package/dist/slides/titleSlide.js +0 -6
  76. package/dist/types/events.d.ts +0 -16
  77. package/dist/types/events.d.ts.map +0 -1
  78. package/dist/types/events.js +0 -1
  79. package/dist/types/lobby.d.ts +0 -42
  80. package/dist/types/lobby.d.ts.map +0 -1
  81. package/dist/types/lobby.js +0 -10
  82. package/dist/types/question.d.ts +0 -106
  83. package/dist/types/question.d.ts.map +0 -1
  84. package/dist/types/question.js +0 -22
  85. package/dist/types/questions/multiple-choice.d.ts +0 -51
  86. package/dist/types/questions/multiple-choice.d.ts.map +0 -1
  87. package/dist/types/questions/multiple-choice.js +0 -21
  88. package/dist/types/questions/short-answer.d.ts +0 -34
  89. package/dist/types/questions/short-answer.d.ts.map +0 -1
  90. package/dist/types/questions/short-answer.js +0 -11
  91. package/dist/types/questions/true-false.d.ts +0 -36
  92. package/dist/types/questions/true-false.d.ts.map +0 -1
  93. package/dist/types/questions/true-false.js +0 -12
  94. package/dist/types/quizfile.d.ts +0 -79
  95. package/dist/types/quizfile.d.ts.map +0 -1
  96. package/dist/types/quizfile.js +0 -23
  97. package/dist/types/quizstep.d.ts +0 -73
  98. package/dist/types/quizstep.d.ts.map +0 -1
  99. package/dist/types/quizstep.js +0 -7
  100. package/dist/types/quiztheme.d.ts +0 -7
  101. package/dist/types/quiztheme.d.ts.map +0 -1
  102. package/dist/types/quiztheme.js +0 -5
  103. package/dist/types/slide.d.ts +0 -18
  104. package/dist/types/slide.d.ts.map +0 -1
  105. package/dist/types/slide.js +0 -9
  106. package/dist/types/slides/comparison.d.ts +0 -9
  107. package/dist/types/slides/comparison.d.ts.map +0 -1
  108. package/dist/types/slides/comparison.js +0 -7
  109. package/dist/types/slides/titleImageTextSlide.d.ts +0 -9
  110. package/dist/types/slides/titleImageTextSlide.d.ts.map +0 -1
  111. package/dist/types/slides/titleImageTextSlide.js +0 -7
  112. package/dist/types/slides/titleSlide.d.ts +0 -8
  113. package/dist/types/slides/titleSlide.d.ts.map +0 -1
  114. package/dist/types/slides/titleSlide.js +0 -6
  115. package/dist/util/guards.d.ts +0 -18
  116. package/dist/util/guards.d.ts.map +0 -1
  117. package/dist/util/guards.js +0 -15
  118. package/dist/util/names/additives.json +0 -23
  119. package/dist/util/names/animals.json +0 -23
  120. package/dist/util/names/colors.json +0 -14
  121. package/dist/util/names/names.d.ts +0 -4
  122. package/dist/util/names/names.d.ts.map +0 -1
  123. package/dist/util/names/names.js +0 -31
  124. package/dist/util/sanitizer.d.ts +0 -3
  125. package/dist/util/sanitizer.d.ts.map +0 -1
  126. package/dist/util/sanitizer.js +0 -18
  127. package/dist/util/score.d.ts +0 -11
  128. package/dist/util/score.d.ts.map +0 -1
  129. package/dist/util/score.js +0 -33
  130. package/dist/util/validator.d.ts +0 -14
  131. package/dist/util/validator.d.ts.map +0 -1
  132. package/dist/util/validator.js +0 -27
package/README.md CHANGED
@@ -1,9 +1,3 @@
1
1
  # Quizcore
2
2
 
3
- Core library for Quizpot
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install git+https://github.com/quizpot/quizcore.git
9
- ```
3
+ Core library for Quizpot
package/dist/index.cjs ADDED
@@ -0,0 +1,582 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let zod = require("zod");
25
+ zod = __toESM(zod);
26
+ //#region src/events/client/host/kick-player.ts
27
+ const createKickPlayerEvent = (playerId) => ({
28
+ type: "KICK_PLAYER",
29
+ payload: { playerId }
30
+ });
31
+ //#endregion
32
+ //#region src/events/client/host/next-step.ts
33
+ const createNextStepEvent = () => ({ type: "NEXT_STEP" });
34
+ //#endregion
35
+ //#region src/events/client/host/start-lobby.ts
36
+ const createStartLobbyEvent = () => ({
37
+ type: "START_LOBBY",
38
+ payload: {}
39
+ });
40
+ //#endregion
41
+ //#region src/events/client/player/submit-answer.ts
42
+ const createSubmitAnswerEvent = (submission) => ({
43
+ type: "SUBMIT_ANSWER",
44
+ payload: { submission }
45
+ });
46
+ //#endregion
47
+ //#region src/events/server/lobby-deleted.ts
48
+ const createLobbyDeletedEvent = (reason) => ({
49
+ type: "LOBBY_DELETED",
50
+ payload: { reason }
51
+ });
52
+ //#endregion
53
+ //#region src/events/server/lobby-joined.ts
54
+ const createLobbyJoinedEvent = (lobby, me, isHost) => ({
55
+ type: "LOBBY_JOINED",
56
+ payload: {
57
+ lobby: {
58
+ code: lobby.code,
59
+ quizInfo: lobby.quizInfo,
60
+ status: lobby.status,
61
+ timeoutStartedAt: lobby.timeoutStartedAt,
62
+ duration: lobby.duration,
63
+ currentStep: lobby.currentStep,
64
+ settings: lobby.settings
65
+ },
66
+ me,
67
+ players: isHost ? lobby.players : void 0,
68
+ currentAnswers: isHost ? lobby.currentAnswers : void 0,
69
+ answers: isHost ? lobby.answers : void 0
70
+ }
71
+ });
72
+ //#endregion
73
+ //#region src/events/server/lobby-status-update.ts
74
+ const createLobbyStatusUpdateEvent = (payload) => ({
75
+ type: "LOBBY_STATUS_UPDATE",
76
+ payload
77
+ });
78
+ //#endregion
79
+ //#region src/events/server/player-answer-result.ts
80
+ const createPlayerAnswerResultEvent = (isCorrect, pointsAwarded, score, streak) => ({
81
+ type: "PLAYER_ANSWER_RESULT",
82
+ payload: {
83
+ isCorrect,
84
+ pointsAwarded,
85
+ score,
86
+ streak
87
+ }
88
+ });
89
+ //#endregion
90
+ //#region src/events/server/player-joined.ts
91
+ const createPlayerJoinedEvent = (player) => ({
92
+ type: "PLAYER_JOINED",
93
+ payload: { player }
94
+ });
95
+ //#endregion
96
+ //#region src/events/server/player-kicked.ts
97
+ const createPlayerKickedEvent = () => ({
98
+ type: "PLAYER_KICKED",
99
+ payload: {}
100
+ });
101
+ //#endregion
102
+ //#region src/events/server/player-left.ts
103
+ const createPlayerLeftEvent = (player) => ({
104
+ type: "PLAYER_LEFT",
105
+ payload: { player }
106
+ });
107
+ //#endregion
108
+ //#region src/events/server/player-update.ts
109
+ const createPlayerUpdateEvent = (player) => ({
110
+ type: "PLAYER_UPDATE",
111
+ payload: { player }
112
+ });
113
+ //#endregion
114
+ //#region src/events/server/update-lobby-answers.ts
115
+ const createUpdateLobbyAnswersEvent = (count) => ({
116
+ type: "UPDATE_LOBBY_ANSWERS",
117
+ payload: { count }
118
+ });
119
+ //#endregion
120
+ //#region src/types/lobby.ts
121
+ let LobbyStatus = /* @__PURE__ */ function(LobbyStatus) {
122
+ LobbyStatus["waiting"] = "waiting";
123
+ LobbyStatus["slide"] = "slide";
124
+ LobbyStatus["question"] = "question";
125
+ LobbyStatus["answer"] = "answer";
126
+ LobbyStatus["answers"] = "answers";
127
+ LobbyStatus["score"] = "score";
128
+ LobbyStatus["end"] = "end";
129
+ return LobbyStatus;
130
+ }({});
131
+ //#endregion
132
+ //#region src/util/guards.ts
133
+ const isQuestion = (step) => {
134
+ return step.type === "question";
135
+ };
136
+ const isSlide = (step) => {
137
+ return step.type === "slide";
138
+ };
139
+ const isMultipleChoice = (data) => {
140
+ return data.type === "multiple-choice";
141
+ };
142
+ const isTrueFalse = (data) => {
143
+ return data.type === "true-false";
144
+ };
145
+ const isShortAnswer = (data) => {
146
+ return data.type === "short-answer";
147
+ };
148
+ //#endregion
149
+ //#region src/util/score.ts
150
+ const BASE_SCORE = 500;
151
+ const TIME_BONUS_MAX = 500;
152
+ const calculateScore = (player, question, answer, quiz) => {
153
+ if (!answer.isCorrect) return {
154
+ newScore: player.score,
155
+ pointsAwarded: 0
156
+ };
157
+ const pointMultiplier = {
158
+ noPoints: 0,
159
+ normalPoints: 1,
160
+ doublePoints: 2
161
+ }[question.points] ?? 1;
162
+ if (pointMultiplier === 0) return {
163
+ newScore: player.score,
164
+ pointsAwarded: 0
165
+ };
166
+ let timeBonus = 0;
167
+ if (question.timeLimit > 0) {
168
+ const timeLimitMs = question.timeLimit * 1e3;
169
+ timeBonus = TIME_BONUS_MAX * (1 - Math.max(0, Math.min(answer.timeTaken, timeLimitMs)) / timeLimitMs);
170
+ }
171
+ let questionScore = (BASE_SCORE + timeBonus) * pointMultiplier;
172
+ if (player.streak >= 2) {
173
+ const totalQuestions = quiz.steps.filter((s) => s.type === "question").length;
174
+ const dynamicCap = Math.min(1.2 + Math.max(0, totalQuestions - 5) * .02, 1.5);
175
+ const streakMultiplier = Math.min(1 + (player.streak - 1) * .05, dynamicCap);
176
+ questionScore *= streakMultiplier;
177
+ }
178
+ const finalPoints = Math.trunc(questionScore);
179
+ return {
180
+ newScore: player.score + finalPoints,
181
+ pointsAwarded: finalPoints
182
+ };
183
+ };
184
+ //#endregion
185
+ //#region src/util/validator.ts
186
+ const isCorrect = (question, submission) => {
187
+ if (isMultipleChoice(question) && submission.type === "multiple-choice") {
188
+ if (question.matchAll) {
189
+ const correctIndices = question.choices.map((c, i) => c.correct ? i : -1).filter((i) => i !== -1);
190
+ return submission.choices.length === correctIndices.length && submission.choices.every((index) => correctIndices.includes(index));
191
+ }
192
+ if (submission.choices.length === 0) return false;
193
+ return submission.choices.every((index) => {
194
+ const choice = question.choices[index];
195
+ return choice ? choice.correct : false;
196
+ });
197
+ }
198
+ if (isTrueFalse(question) && submission.type === "true-false") return question.answer === submission.answer;
199
+ if (isShortAnswer(question) && submission.type === "short-answer") {
200
+ const playerAns = submission.answer.trim().toLowerCase();
201
+ return question.answers.some((ans) => ans.trim().toLowerCase() === playerAns);
202
+ }
203
+ return false;
204
+ };
205
+ //#endregion
206
+ //#region src/managers/lobby-manager.ts
207
+ const createLobby = (code, hostId, quiz) => ({
208
+ code,
209
+ host: hostId,
210
+ quiz,
211
+ quizInfo: {
212
+ title: quiz.title,
213
+ stepCount: quiz.steps.length,
214
+ theme: quiz.theme
215
+ },
216
+ players: [],
217
+ status: LobbyStatus.waiting,
218
+ currentStep: 0,
219
+ timeoutStartedAt: null,
220
+ duration: null,
221
+ currentAnswers: [],
222
+ answers: [],
223
+ settings: {
224
+ customNames: true,
225
+ displayOnDevice: true
226
+ }
227
+ });
228
+ const advanceLobby = (lobby) => {
229
+ const isLastStep = lobby.currentStep >= lobby.quiz.steps.length - 1;
230
+ switch (lobby.status) {
231
+ case LobbyStatus.waiting: return prepareStep(lobby, 0);
232
+ case LobbyStatus.slide:
233
+ case LobbyStatus.score: return isLastStep ? {
234
+ ...lobby,
235
+ status: LobbyStatus.end
236
+ } : prepareStep(lobby, lobby.currentStep + 1);
237
+ case LobbyStatus.question: {
238
+ const step = lobby.quiz.steps[lobby.currentStep];
239
+ if (step.type !== "question") return lobby;
240
+ return {
241
+ ...lobby,
242
+ status: LobbyStatus.answer,
243
+ timeoutStartedAt: Date.now(),
244
+ duration: step.data.timeLimit * 1e3,
245
+ currentAnswers: []
246
+ };
247
+ }
248
+ case LobbyStatus.answer: return {
249
+ ...lobby,
250
+ status: LobbyStatus.answers
251
+ };
252
+ case LobbyStatus.answers: return {
253
+ ...lobby,
254
+ status: LobbyStatus.score
255
+ };
256
+ default: return lobby;
257
+ }
258
+ };
259
+ const prepareStep = (lobby, index) => {
260
+ const step = lobby.quiz.steps[index];
261
+ if (!step) return lobby;
262
+ if (step.type === "slide") return {
263
+ ...lobby,
264
+ status: LobbyStatus.slide,
265
+ currentStep: index
266
+ };
267
+ return {
268
+ ...lobby,
269
+ status: LobbyStatus.question,
270
+ currentStep: index,
271
+ timeoutStartedAt: null,
272
+ duration: null,
273
+ currentAnswers: []
274
+ };
275
+ };
276
+ const handleSubmission = (lobby, playerId, submission) => {
277
+ if (lobby.status !== LobbyStatus.answer) return null;
278
+ const step = lobby.quiz.steps[lobby.currentStep];
279
+ if (!isQuestion(step)) return null;
280
+ const player = lobby.players.find((p) => p.id === playerId);
281
+ if (!player) return null;
282
+ if (lobby.currentAnswers.some((a) => a.playerId === playerId)) return null;
283
+ const correct = isCorrect(step.data, submission);
284
+ const answerObj = {
285
+ playerId,
286
+ submission,
287
+ timeTaken: Date.now() - (lobby.timeoutStartedAt ?? 0),
288
+ isCorrect: correct,
289
+ pointsAwarded: 0
290
+ };
291
+ const { newScore, pointsAwarded } = calculateScore(player, step.data, answerObj, lobby.quiz);
292
+ answerObj.pointsAwarded = pointsAwarded;
293
+ return {
294
+ nextLobby: {
295
+ ...lobby,
296
+ players: lobby.players.map((p) => p.id === playerId ? {
297
+ ...p,
298
+ score: newScore,
299
+ streak: correct ? p.streak + 1 : 0
300
+ } : p),
301
+ currentAnswers: [...lobby.currentAnswers, answerObj]
302
+ },
303
+ result: {
304
+ type: "PLAYER_ANSWER_RESULT",
305
+ payload: {
306
+ isCorrect: correct,
307
+ pointsAwarded,
308
+ score: newScore,
309
+ streak: correct ? player.streak + 1 : 0
310
+ }
311
+ }
312
+ };
313
+ };
314
+ //#endregion
315
+ //#region src/types/questions/true-false.ts
316
+ const TrueFalseQuestionSchema = BaseQuestionSchema.extend({
317
+ type: zod.default.literal("true-false"),
318
+ answer: zod.default.boolean(),
319
+ labels: zod.default.array(zod.default.string()).min(2).max(2)
320
+ });
321
+ const SafeTrueFalseQuestionSchema = TrueFalseQuestionSchema.omit({ answer: true });
322
+ const TrueFalseQuestionAnswerSchema = zod.default.object({
323
+ type: zod.default.literal("true-false"),
324
+ answer: zod.default.boolean()
325
+ });
326
+ //#endregion
327
+ //#region src/types/questions/short-answer.ts
328
+ const ShortAnswerQuestionSchema = BaseQuestionSchema.extend({
329
+ type: zod.default.literal("short-answer"),
330
+ answers: zod.default.array(zod.default.string()).min(1)
331
+ });
332
+ const SafeShortAnswerQuestionSchema = ShortAnswerQuestionSchema.omit({ answers: true });
333
+ const ShortAnswerQuestionAnswerSchema = zod.default.object({
334
+ type: zod.default.literal("short-answer"),
335
+ answer: zod.default.string()
336
+ });
337
+ //#endregion
338
+ //#region src/types/question.ts
339
+ const QuestionSchema = zod.default.discriminatedUnion("type", [
340
+ MultipleChoiceQuestionSchema,
341
+ TrueFalseQuestionSchema,
342
+ ShortAnswerQuestionSchema
343
+ ]);
344
+ zod.default.discriminatedUnion("type", [
345
+ SafeMultipleChoiceQuestionSchema,
346
+ SafeTrueFalseQuestionSchema,
347
+ SafeShortAnswerQuestionSchema
348
+ ]);
349
+ const QuestionPointsSchema = zod.default.enum([
350
+ "normalPoints",
351
+ "doublePoints",
352
+ "noPoints"
353
+ ]);
354
+ const BaseQuestionSchema = zod.default.object({
355
+ question: zod.default.string().min(1),
356
+ imageHash: zod.default.hash("sha256", { error: "Invalid image hash" }).optional(),
357
+ displayTime: zod.default.number().min(1).max(60),
358
+ timeLimit: zod.default.number().min(1).max(180),
359
+ points: QuestionPointsSchema
360
+ });
361
+ //#endregion
362
+ //#region src/types/questions/multiple-choice.ts
363
+ const ChoiceSchema = zod.default.object({
364
+ text: zod.default.string(),
365
+ correct: zod.default.boolean()
366
+ });
367
+ const SafeChoiceSchema = ChoiceSchema.omit({ correct: true });
368
+ const MultipleChoiceQuestionSchema = BaseQuestionSchema.extend({
369
+ type: zod.default.literal("multiple-choice"),
370
+ choices: zod.default.array(ChoiceSchema).min(2),
371
+ matchAll: zod.default.boolean()
372
+ });
373
+ const SafeMultipleChoiceQuestionSchema = MultipleChoiceQuestionSchema.omit({ choices: true }).extend({ choices: zod.default.array(SafeChoiceSchema).min(2) });
374
+ const MultipleChoiceQuestionAnswerSchema = zod.default.object({
375
+ type: zod.default.literal("multiple-choice"),
376
+ choices: zod.default.array(zod.default.number()).min(1)
377
+ });
378
+ //#endregion
379
+ //#region src/types/quiztheme.ts
380
+ const QuizThemeSchema = zod.default.object({
381
+ color: zod.default.string().regex(/^#[0-9a-fA-F]{6}$/, { message: "Invalid color format. Must be a 7-character hex code (e.g., #RRGGBB)." }),
382
+ background: zod.default.hash("sha256", { error: "Invalid background hash" }).optional()
383
+ });
384
+ //#endregion
385
+ //#region src/types/slides/titleSlide.ts
386
+ const TitleSlideLayoutSchema = zod.default.object({
387
+ slideType: zod.default.literal("title"),
388
+ title: zod.default.string(),
389
+ subtitle: zod.default.string().optional()
390
+ });
391
+ //#endregion
392
+ //#region src/types/slides/titleImageTextSlide.ts
393
+ const TitleImageTextSlideLayoutSchema = zod.default.object({
394
+ slideType: zod.default.literal("titleImageText"),
395
+ title: zod.default.string(),
396
+ imageHash: zod.default.hash("sha256", { error: "Invalid image hash" }).optional(),
397
+ text: zod.default.string()
398
+ });
399
+ //#endregion
400
+ //#region src/types/slides/comparison.ts
401
+ const ComparisonSlideLayoutSchema = zod.default.object({
402
+ slideType: zod.default.literal("comparison"),
403
+ title: zod.default.string(),
404
+ left: zod.default.string(),
405
+ right: zod.default.string()
406
+ });
407
+ //#endregion
408
+ //#region src/types/slide.ts
409
+ const SlideSchema = zod.default.discriminatedUnion("slideType", [
410
+ TitleSlideLayoutSchema,
411
+ TitleImageTextSlideLayoutSchema,
412
+ ComparisonSlideLayoutSchema
413
+ ]);
414
+ //#endregion
415
+ //#region src/types/quizstep.ts
416
+ const QuizStepSchema = zod.default.discriminatedUnion("type", [zod.default.object({
417
+ type: zod.default.literal("question"),
418
+ data: QuestionSchema
419
+ }), zod.default.object({
420
+ type: zod.default.literal("slide"),
421
+ data: SlideSchema
422
+ })]);
423
+ //#endregion
424
+ //#region src/types/quizfile.ts
425
+ const QuizFileSchema = zod.default.object({
426
+ id: zod.default.uuid(),
427
+ version: zod.default.literal(2),
428
+ title: zod.default.string().min(1, "Title must be atleast 1 character long").max(64, "Title can't be longer than 64 characters"),
429
+ description: zod.default.string().max(255, "Description can't be longer than 256 characters").optional(),
430
+ theme: QuizThemeSchema,
431
+ language: zod.default.string().length(2, "Language must be a 2-letter ISO 639-1 code"),
432
+ steps: zod.default.array(QuizStepSchema).min(1, "Quiz must have at least 1 step"),
433
+ images: zod.default.record(zod.default.hash("sha256", { error: "Invalid image hash" }), zod.default.string().refine((val) => {
434
+ return val.startsWith("http") || val.startsWith("data:image/");
435
+ }, "Image must be a valid URL or Base64 data string")),
436
+ updatedAt: zod.default.iso.datetime(),
437
+ createdAt: zod.default.iso.datetime()
438
+ });
439
+ //#endregion
440
+ //#region src/util/names/animals.json
441
+ var animals_default = [
442
+ "Dog",
443
+ "Cat",
444
+ "Rabbit",
445
+ "Lion",
446
+ "Tiger",
447
+ "Bear",
448
+ "Wolf",
449
+ "Fox",
450
+ "Panda",
451
+ "Elephant",
452
+ "Gorilla",
453
+ "Giraffe",
454
+ "Hippo",
455
+ "Rhino",
456
+ "Zebra",
457
+ "Crocodile",
458
+ "Alligator",
459
+ "Octopus",
460
+ "Butterfly",
461
+ "Bee",
462
+ "Ant"
463
+ ];
464
+ //#endregion
465
+ //#region src/util/names/additives.json
466
+ var additives_default = [
467
+ "Funny",
468
+ "Happy",
469
+ "Sad",
470
+ "Angry",
471
+ "Scary",
472
+ "Serious",
473
+ "Cautious",
474
+ "Joyful",
475
+ "Broken",
476
+ "Smart",
477
+ "Shy",
478
+ "Silly",
479
+ "Lazy",
480
+ "Cheerful",
481
+ "Calm",
482
+ "Sleepy",
483
+ "Tired",
484
+ "Lonely",
485
+ "Crazy",
486
+ "Mad",
487
+ "Gentle"
488
+ ];
489
+ //#endregion
490
+ //#region src/util/names/colors.json
491
+ var colors_default = [
492
+ "Red",
493
+ "Blue",
494
+ "Green",
495
+ "Yellow",
496
+ "Purple",
497
+ "Orange",
498
+ "Pink",
499
+ "Brown",
500
+ "Black",
501
+ "White",
502
+ "Gray",
503
+ "Cyan"
504
+ ];
505
+ //#endregion
506
+ //#region src/util/names/names.ts
507
+ const generateName = () => {
508
+ const animal = animals_default[Math.floor(Math.random() * animals_default.length)];
509
+ const additive = additives_default[Math.floor(Math.random() * additives_default.length)];
510
+ return `${colors_default[Math.floor(Math.random() * colors_default.length)]}${additive}${animal}`;
511
+ };
512
+ const generateUniqueName = (players) => {
513
+ const maxPossible = animals_default.length * additives_default.length * colors_default.length;
514
+ if (players.length >= maxPossible) throw new Error("No unique names available");
515
+ const existingNames = new Set(players.map((p) => p.name));
516
+ for (let i = 0; i < 100; i++) {
517
+ const candidate = generateName();
518
+ if (!existingNames.has(candidate)) return candidate;
519
+ }
520
+ for (const c of colors_default) for (const ad of additives_default) for (const an of animals_default) {
521
+ const candidate = `${c}${ad}${an}`;
522
+ if (!existingNames.has(candidate)) return candidate;
523
+ }
524
+ throw new Error("No unique names available");
525
+ };
526
+ //#endregion
527
+ //#region src/util/sanitizer.ts
528
+ const sanitizeQuestion = (question) => {
529
+ if (isMultipleChoice(question)) return {
530
+ ...question,
531
+ choices: question.choices.map(({ text }) => ({ text }))
532
+ };
533
+ if (isTrueFalse(question)) {
534
+ const { answer, ...sanitized } = question;
535
+ return sanitized;
536
+ }
537
+ if (isShortAnswer(question)) {
538
+ const { answers, ...sanitized } = question;
539
+ return sanitized;
540
+ }
541
+ throw new Error("Invalid question type");
542
+ };
543
+ //#endregion
544
+ exports.ChoiceSchema = ChoiceSchema;
545
+ exports.LobbyStatus = LobbyStatus;
546
+ exports.MultipleChoiceQuestionAnswerSchema = MultipleChoiceQuestionAnswerSchema;
547
+ exports.MultipleChoiceQuestionSchema = MultipleChoiceQuestionSchema;
548
+ exports.QuizFileSchema = QuizFileSchema;
549
+ exports.SafeChoiceSchema = SafeChoiceSchema;
550
+ exports.SafeMultipleChoiceQuestionSchema = SafeMultipleChoiceQuestionSchema;
551
+ exports.SafeShortAnswerQuestionSchema = SafeShortAnswerQuestionSchema;
552
+ exports.SafeTrueFalseQuestionSchema = SafeTrueFalseQuestionSchema;
553
+ exports.ShortAnswerQuestionAnswerSchema = ShortAnswerQuestionAnswerSchema;
554
+ exports.ShortAnswerQuestionSchema = ShortAnswerQuestionSchema;
555
+ exports.TrueFalseQuestionAnswerSchema = TrueFalseQuestionAnswerSchema;
556
+ exports.TrueFalseQuestionSchema = TrueFalseQuestionSchema;
557
+ exports.advanceLobby = advanceLobby;
558
+ exports.calculateScore = calculateScore;
559
+ exports.createKickPlayerEvent = createKickPlayerEvent;
560
+ exports.createLobby = createLobby;
561
+ exports.createLobbyDeletedEvent = createLobbyDeletedEvent;
562
+ exports.createLobbyJoinedEvent = createLobbyJoinedEvent;
563
+ exports.createLobbyStatusUpdateEvent = createLobbyStatusUpdateEvent;
564
+ exports.createNextStepEvent = createNextStepEvent;
565
+ exports.createPlayerAnswerResultEvent = createPlayerAnswerResultEvent;
566
+ exports.createPlayerJoinedEvent = createPlayerJoinedEvent;
567
+ exports.createPlayerKickedEvent = createPlayerKickedEvent;
568
+ exports.createPlayerLeftEvent = createPlayerLeftEvent;
569
+ exports.createPlayerUpdateEvent = createPlayerUpdateEvent;
570
+ exports.createStartLobbyEvent = createStartLobbyEvent;
571
+ exports.createSubmitAnswerEvent = createSubmitAnswerEvent;
572
+ exports.createUpdateLobbyAnswersEvent = createUpdateLobbyAnswersEvent;
573
+ exports.generateName = generateName;
574
+ exports.generateUniqueName = generateUniqueName;
575
+ exports.handleSubmission = handleSubmission;
576
+ exports.isCorrect = isCorrect;
577
+ exports.isMultipleChoice = isMultipleChoice;
578
+ exports.isQuestion = isQuestion;
579
+ exports.isShortAnswer = isShortAnswer;
580
+ exports.isSlide = isSlide;
581
+ exports.isTrueFalse = isTrueFalse;
582
+ exports.sanitizeQuestion = sanitizeQuestion;