@orakl/protocol 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- export type ErrorCode = "no_active_lobby" | "no_active_game" | "device_blocked" | "nickname_taken" | "device_in_lobby" | "player_not_found" | "player_not_pending" | "intermission_invalid_phase" | "not_in_intermission" | "category_name_exists" | "category_not_found" | "category_has_questions" | "user_not_found" | "curation_request_pending" | "curator_self_join" | "already_flagged" | "question_not_found" | "invalid_answer" | "game_in_progress" | "already_answered" | "observer_forbidden" | "no_questions" | "no_categories" | "too_few_players" | "hosting_closed" | "not_answered" | "flag_not_toggleable" | "solo_invalid_phase" | "solo_invalid_answer" | "solo_nonce_mismatch" | "solo_answer_expired" | "solo_guest_limit" | "solo_no_questions" | "solo_resume_denied" | "solo_auth_failed";
1
+ export type ErrorCode = "no_active_lobby" | "no_active_game" | "device_blocked" | "nickname_taken" | "device_in_lobby" | "player_not_found" | "player_not_pending" | "intermission_invalid_phase" | "not_in_intermission" | "category_name_exists" | "category_not_found" | "category_has_questions" | "tag_name_exists" | "tag_not_found" | "tag_has_questions" | "tag_in_season" | "category_in_season" | "season_name_exists" | "season_not_found" | "user_not_found" | "curation_request_pending" | "curator_self_join" | "already_flagged" | "question_not_found" | "invalid_answer" | "game_in_progress" | "already_answered" | "observer_forbidden" | "no_questions" | "season_out_of_season" | "no_categories" | "too_few_players" | "hosting_closed" | "not_answered" | "flag_not_toggleable" | "solo_invalid_phase" | "solo_invalid_answer" | "solo_nonce_mismatch" | "solo_answer_expired" | "solo_guest_limit" | "solo_no_questions" | "solo_season_out_of_season" | "solo_resume_denied" | "solo_auth_failed";
package/dist/index.d.ts CHANGED
@@ -19,6 +19,6 @@ export type { StreakFlourish } from "./streak-flourish.ts";
19
19
  export type { LeaderboardBoard, LeaderboardDifficulty, LeaderboardScope, } from "./wire/leaderboard.ts";
20
20
  export type { OperatorQuestionRevision, PublishManyResult, QuestionRevision, } from "./wire/question-bank.ts";
21
21
  export type { FlagDetail, FlagReporter, FlagStatus, QuestionFlagAnswerSnapshot, QuestionSnapshot, } from "./wire/question-flags.ts";
22
- export type { ClientMessage, CuratorAdvanceMessage, CuratorIntermissionMessage, CuratorPlayerActionMessage, CuratorResetMessage, CuratorStartMessage, CuratorStartRoleSelectionMessage, CuratorStopMessage, GameErrorMessage, GameFinalScoresMessage, GameIntermissionMessage, GamePingMessage, GameQuestionMessage, GameResultsReadyMessage, GameResumeMessage, GameRoundResultMessage, GameStartMessage, GameTickMessage, JoinAcceptedMessage, JoinRejectedMessage, LobbyEndedMessage, LobbyResetMessage, LobbyUpdateMessage, PlayerAnsweredMessage, PlayerAnswerMessage, PlayerEmoteBroadcastMessage, PlayerEmoteMessage, PlayerLeaveMessage, PlayerNicknameChangedMessage, PlayerQuestionAnswer, PlayerRateAckMessage, PlayerRateMessage, PlayerRemovedMessage, PlayerRoleMessage, RoleSelectionLockedMessage, RoleSelectionStartMessage, RoleSelectionUpdateMessage, ServerMessage, WsAuthMessage, WsPongMessage, } from "./ws.ts";
22
+ export type { ClientMessage, CuratorAdvanceMessage, CuratorIntermissionMessage, CuratorPlayerActionMessage, CuratorResetMessage, CuratorStartMessage, CuratorStartRoleSelectionMessage, CuratorStopMessage, GameErrorMessage, GameFinalScoresMessage, GameIntermissionMessage, GamePingMessage, GameQuestionMessage, GameResultsReadyMessage, GameResumeMessage, GameRoundResultMessage, GameStartMessage, GameTickMessage, JoinAcceptedMessage, JoinRejectedMessage, LobbyEndedMessage, LobbyResetMessage, LobbyUpdateMessage, PlayerAnsweredMessage, PlayerAnswerMessage, PlayerEmoteBroadcastMessage, PlayerEmoteMessage, PlayerLeaveMessage, PlayerNicknameChangedMessage, PlayerQuestionAnswer, PlayerRateAckMessage, PlayerRateMessage, PlayerRemovedMessage, PlayerRoleMessage, RateAckReason, RoleSelectionLockedMessage, RoleSelectionStartMessage, RoleSelectionUpdateMessage, ServerMessage, WsAuthMessage, WsPongMessage, } from "./ws.ts";
23
23
  export { ClientMessageSchema } from "./ws-game.schema.ts";
24
24
  export { SoloClientMessageSchema } from "./ws-solo.schema.ts";
@@ -125,6 +125,45 @@ declare const CategorySchema: Schema.Struct<{
125
125
  question_count: typeof Schema.Number;
126
126
  }>;
127
127
  export type Category = typeof CategorySchema.Type;
128
+ declare const TagSchema: Schema.Struct<{
129
+ id: typeof Schema.String;
130
+ name: typeof Schema.String;
131
+ /** Questions whose `metadata.tags` carries this name. */
132
+ question_count: typeof Schema.Number;
133
+ }>;
134
+ export type Tag = typeof TagSchema.Type;
135
+ declare const SeasonSchema: Schema.Struct<{
136
+ id: typeof Schema.String;
137
+ name: typeof Schema.String;
138
+ description: Schema.NullOr<typeof Schema.String>;
139
+ icon: Schema.NullOr<typeof Schema.String>;
140
+ color: Schema.NullOr<typeof Schema.String>;
141
+ /** `window_start`/`window_end` are `MM-DD` when true, `YYYY-MM-DD` when false. */
142
+ repeats_yearly: typeof Schema.Boolean;
143
+ window_start: typeof Schema.String;
144
+ window_end: typeof Schema.String;
145
+ /** Member `categories.id` values. */
146
+ category_ids: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
147
+ /** Member tag names. */
148
+ tag_names: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
149
+ /** Distinct questions reachable through the member categories and tags. */
150
+ pool_size: typeof Schema.Number;
151
+ }>;
152
+ export type Season = typeof SeasonSchema.Type;
153
+ /** A Season as the play pickers offer it: selectable in its window, locked with its opening day outside. */
154
+ export declare const SeasonOptionSchema: Schema.Struct<{
155
+ id: typeof Schema.String;
156
+ name: typeof Schema.String;
157
+ icon: Schema.NullOr<typeof Schema.String>;
158
+ color: Schema.NullOr<typeof Schema.String>;
159
+ /** Distinct questions reachable through the Season's categories and Tags. */
160
+ count: typeof Schema.Number;
161
+ /** True outside the window; a locked Season cannot be picked. */
162
+ locked: typeof Schema.Boolean;
163
+ /** Next day the window opens (`YYYY-MM-DD`), only while locked. */
164
+ opensOn: Schema.NullOr<typeof Schema.String>;
165
+ }>;
166
+ export type SeasonOption = typeof SeasonOptionSchema.Type;
128
167
  /** Body of `GET /api/pages/manage/categories`. */
129
168
  export declare const ManageCategoriesPageSchema: Schema.Struct<{
130
169
  initial: Schema.mutable<Schema.Array$<Schema.Schema<{
@@ -142,6 +181,40 @@ export declare const ManageCategoriesPageSchema: Schema.Struct<{
142
181
  readonly icon: string | null;
143
182
  readonly question_count: number;
144
183
  }, never>>>;
184
+ tags: Schema.mutable<Schema.Array$<Schema.Schema<{
185
+ readonly name: string;
186
+ readonly id: string;
187
+ readonly question_count: number;
188
+ }, {
189
+ readonly name: string;
190
+ readonly id: string;
191
+ readonly question_count: number;
192
+ }, never>>>;
193
+ seasons: Schema.mutable<Schema.Array$<Schema.Schema<{
194
+ readonly name: string;
195
+ readonly id: string;
196
+ readonly color: string | null;
197
+ readonly description: string | null;
198
+ readonly icon: string | null;
199
+ readonly repeats_yearly: boolean;
200
+ readonly window_start: string;
201
+ readonly window_end: string;
202
+ readonly category_ids: string[];
203
+ readonly tag_names: string[];
204
+ readonly pool_size: number;
205
+ }, {
206
+ readonly name: string;
207
+ readonly id: string;
208
+ readonly color: string | null;
209
+ readonly description: string | null;
210
+ readonly icon: string | null;
211
+ readonly repeats_yearly: boolean;
212
+ readonly window_start: string;
213
+ readonly window_end: string;
214
+ readonly category_ids: string[];
215
+ readonly tag_names: string[];
216
+ readonly pool_size: number;
217
+ }, never>>>;
145
218
  }>;
146
219
  declare const AdminFlagRowSchema: Schema.Struct<{
147
220
  questionId: typeof Schema.String;
@@ -307,6 +380,7 @@ declare const QuizPresetSchema: Schema.Struct<{
307
380
  name: typeof Schema.String;
308
381
  description: Schema.NullOr<typeof Schema.String>;
309
382
  category_ids: typeof Schema.String;
383
+ season_ids: typeof Schema.String;
310
384
  timer_duration: typeof Schema.Number;
311
385
  questions_per_round: typeof Schema.Number;
312
386
  difficulty: typeof Schema.String;
@@ -317,8 +391,40 @@ declare const QuizPresetSchema: Schema.Struct<{
317
391
  created_at: typeof Schema.String;
318
392
  }>;
319
393
  export type QuizPreset = typeof QuizPresetSchema.Type;
394
+ /** What stopped a saved Quiz's members from drawing: closed or gone Seasons, gone categories. */
395
+ declare const PresetMarksSchema: Schema.Struct<{
396
+ closedSeasons: Schema.mutable<Schema.Array$<Schema.Schema<{
397
+ readonly name: string;
398
+ readonly id: string;
399
+ readonly opensOn: string | null;
400
+ }, {
401
+ readonly name: string;
402
+ readonly id: string;
403
+ readonly opensOn: string | null;
404
+ }, never>>>;
405
+ missingSeasons: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
406
+ missingCategories: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
407
+ }>;
408
+ export type PresetMarks = typeof PresetMarksSchema.Type;
320
409
  /** Body of `GET /api/pages/curator/create`. */
321
410
  export declare const CuratorCreatePageSchema: Schema.Struct<{
411
+ seasons: Schema.mutable<Schema.Array$<Schema.Schema<{
412
+ readonly name: string;
413
+ readonly count: number;
414
+ readonly id: string;
415
+ readonly color: string | null;
416
+ readonly icon: string | null;
417
+ readonly locked: boolean;
418
+ readonly opensOn: string | null;
419
+ }, {
420
+ readonly name: string;
421
+ readonly count: number;
422
+ readonly id: string;
423
+ readonly color: string | null;
424
+ readonly icon: string | null;
425
+ readonly locked: boolean;
426
+ readonly opensOn: string | null;
427
+ }, never>>>;
322
428
  categories: Schema.mutable<Schema.Array$<Schema.Schema<{
323
429
  readonly name: string;
324
430
  readonly count: number;
@@ -339,8 +445,9 @@ export declare const CuratorCreatePageSchema: Schema.Struct<{
339
445
  readonly description: string | null;
340
446
  readonly difficulty: string;
341
447
  readonly created_at: string;
342
- readonly curator_id: string;
343
448
  readonly category_ids: string;
449
+ readonly curator_id: string;
450
+ readonly season_ids: string;
344
451
  readonly timer_duration: number;
345
452
  readonly questions_per_round: number;
346
453
  readonly advance_mode: string;
@@ -353,8 +460,9 @@ export declare const CuratorCreatePageSchema: Schema.Struct<{
353
460
  readonly description: string | null;
354
461
  readonly difficulty: string;
355
462
  readonly created_at: string;
356
- readonly curator_id: string;
357
463
  readonly category_ids: string;
464
+ readonly curator_id: string;
465
+ readonly season_ids: string;
358
466
  readonly timer_duration: number;
359
467
  readonly questions_per_round: number;
360
468
  readonly advance_mode: string;
@@ -362,6 +470,19 @@ export declare const CuratorCreatePageSchema: Schema.Struct<{
362
470
  readonly access_mode: string;
363
471
  readonly max_players: number | null;
364
472
  }, never>>>;
473
+ presetMarks: Schema.Record$<typeof Schema.String, Schema.Struct<{
474
+ closedSeasons: Schema.mutable<Schema.Array$<Schema.Schema<{
475
+ readonly name: string;
476
+ readonly id: string;
477
+ readonly opensOn: string | null;
478
+ }, {
479
+ readonly name: string;
480
+ readonly id: string;
481
+ readonly opensOn: string | null;
482
+ }, never>>>;
483
+ missingSeasons: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
484
+ missingCategories: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
485
+ }>>;
365
486
  presetLimit: typeof Schema.Number;
366
487
  subscription: Schema.NullOr<Schema.Struct<{
367
488
  user_id: typeof Schema.String;
@@ -389,6 +510,7 @@ export declare const CuratorHistoryPageSchema: Schema.Struct<{
389
510
  readonly started_at: string;
390
511
  readonly player_count: number;
391
512
  readonly total_questions: number;
513
+ readonly seasonNames: string[];
392
514
  readonly winners: string[];
393
515
  readonly top_score: number | null;
394
516
  }, {
@@ -397,6 +519,7 @@ export declare const CuratorHistoryPageSchema: Schema.Struct<{
397
519
  readonly started_at: string;
398
520
  readonly player_count: number;
399
521
  readonly total_questions: number;
522
+ readonly seasonNames: string[];
400
523
  readonly winners: string[];
401
524
  readonly top_score: number | null;
402
525
  }, never>>>;
@@ -419,6 +542,8 @@ export declare const CuratorHistoryEntryPageSchema: Schema.Struct<{
419
542
  started_at: typeof Schema.String;
420
543
  ended_at: Schema.NullOr<typeof Schema.String>;
421
544
  categoryIds: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
545
+ seasonIds: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
546
+ seasonNames: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
422
547
  timer_duration: typeof Schema.Number;
423
548
  total_questions: typeof Schema.Number;
424
549
  player_count: typeof Schema.Number;
@@ -530,6 +655,8 @@ export declare const CuratorQuestionsPageSchema: Schema.Struct<{
530
655
  } | undefined;
531
656
  }, never>>>;
532
657
  questionsCursor: Schema.NullOr<typeof Schema.String>;
658
+ /** Admin-defined tag names, sorted. */
659
+ tags: Schema.mutable<Schema.Array$<Schema.Schema<string, string, never>>>;
533
660
  }>;
534
661
  /** Body of `GET /api/pages/solo/layout`. */
535
662
  export declare const SoloLayoutPageSchema: Schema.Struct<{
@@ -539,6 +666,7 @@ export declare const SoloLayoutPageSchema: Schema.Struct<{
539
666
  nickname: typeof Schema.String;
540
667
  profileAvatarSrc: typeof Schema.String;
541
668
  categoryDataJson: typeof Schema.String;
669
+ seasonDataJson: typeof Schema.String;
542
670
  }>;
543
671
  declare const LeaderboardEntrySchema: Schema.Struct<{
544
672
  rank: typeof Schema.Number;
@@ -576,8 +704,10 @@ export declare const SoloLeaderboardPageSchema: Schema.Struct<{
576
704
  readonly completed_at: number;
577
705
  }, never>>>;
578
706
  board: Schema.Literal<["score", "streak", "accuracy", "speed", "survival"]>;
579
- scope: Schema.Literal<["all-time", "daily", "weekly", "monthly"]>;
707
+ scope: Schema.Literal<["all-time", "daily", "weekly", "monthly", "season"]>;
580
708
  categoryId: Schema.NullOr<typeof Schema.String>;
709
+ seasonId: Schema.NullOr<typeof Schema.String>;
710
+ opening: Schema.NullOr<typeof Schema.String>;
581
711
  difficulty: Schema.NullOr<Schema.Literal<["easy", "medium", "hard"]>>;
582
712
  categories: Schema.mutable<Schema.Array$<Schema.Schema<{
583
713
  readonly name: string;
@@ -586,6 +716,19 @@ export declare const SoloLeaderboardPageSchema: Schema.Struct<{
586
716
  readonly name: string;
587
717
  readonly id: string;
588
718
  }, never>>>;
719
+ seasonBoards: Schema.mutable<Schema.Array$<Schema.Schema<{
720
+ readonly name: string;
721
+ readonly label: string;
722
+ readonly current: boolean;
723
+ readonly seasonId: string;
724
+ readonly opening: string;
725
+ }, {
726
+ readonly name: string;
727
+ readonly label: string;
728
+ readonly current: boolean;
729
+ readonly seasonId: string;
730
+ readonly opening: string;
731
+ }, never>>>;
589
732
  userRank: Schema.NullOr<typeof Schema.Number>;
590
733
  eligibleCount: typeof Schema.Number;
591
734
  percentile: Schema.NullOr<typeof Schema.Number>;
@@ -119,9 +119,47 @@ const CategorySchema = Schema.Struct({
119
119
  color: Schema.NullOr(Schema.String),
120
120
  question_count: Schema.Number,
121
121
  });
122
+ const TagSchema = Schema.Struct({
123
+ id: Schema.String,
124
+ name: Schema.String,
125
+ /** Questions whose `metadata.tags` carries this name. */
126
+ question_count: Schema.Number,
127
+ });
128
+ const SeasonSchema = Schema.Struct({
129
+ id: Schema.String,
130
+ name: Schema.String,
131
+ description: Schema.NullOr(Schema.String),
132
+ icon: Schema.NullOr(Schema.String),
133
+ color: Schema.NullOr(Schema.String),
134
+ /** `window_start`/`window_end` are `MM-DD` when true, `YYYY-MM-DD` when false. */
135
+ repeats_yearly: Schema.Boolean,
136
+ window_start: Schema.String,
137
+ window_end: Schema.String,
138
+ /** Member `categories.id` values. */
139
+ category_ids: Arr(Schema.String),
140
+ /** Member tag names. */
141
+ tag_names: Arr(Schema.String),
142
+ /** Distinct questions reachable through the member categories and tags. */
143
+ pool_size: Schema.Number,
144
+ });
145
+ /** A Season as the play pickers offer it: selectable in its window, locked with its opening day outside. */
146
+ export const SeasonOptionSchema = Schema.Struct({
147
+ id: Schema.String,
148
+ name: Schema.String,
149
+ icon: Schema.NullOr(Schema.String),
150
+ color: Schema.NullOr(Schema.String),
151
+ /** Distinct questions reachable through the Season's categories and Tags. */
152
+ count: Schema.Number,
153
+ /** True outside the window; a locked Season cannot be picked. */
154
+ locked: Schema.Boolean,
155
+ /** Next day the window opens (`YYYY-MM-DD`), only while locked. */
156
+ opensOn: Schema.NullOr(Schema.String),
157
+ });
122
158
  /** Body of `GET /api/pages/manage/categories`. */
123
159
  export const ManageCategoriesPageSchema = Schema.Struct({
124
160
  initial: Arr(CategorySchema),
161
+ tags: Arr(TagSchema),
162
+ seasons: Arr(SeasonSchema),
125
163
  });
126
164
  const AdminFlagRowSchema = Schema.Struct({
127
165
  questionId: Schema.String,
@@ -210,6 +248,7 @@ const QuizPresetSchema = Schema.Struct({
210
248
  name: Schema.String,
211
249
  description: Schema.NullOr(Schema.String),
212
250
  category_ids: Schema.String,
251
+ season_ids: Schema.String,
213
252
  timer_duration: Schema.Number,
214
253
  questions_per_round: Schema.Number,
215
254
  difficulty: Schema.String,
@@ -219,11 +258,23 @@ const QuizPresetSchema = Schema.Struct({
219
258
  max_players: Schema.NullOr(Schema.Number),
220
259
  created_at: Schema.String,
221
260
  });
261
+ /** What stopped a saved Quiz's members from drawing: closed or gone Seasons, gone categories. */
262
+ const PresetMarksSchema = Schema.Struct({
263
+ closedSeasons: Arr(Schema.Struct({
264
+ id: Schema.String,
265
+ name: Schema.String,
266
+ opensOn: Schema.NullOr(Schema.String),
267
+ })),
268
+ missingSeasons: Arr(Schema.String),
269
+ missingCategories: Arr(Schema.String),
270
+ });
222
271
  /** Body of `GET /api/pages/curator/create`. */
223
272
  export const CuratorCreatePageSchema = Schema.Struct({
273
+ seasons: Arr(SeasonOptionSchema),
224
274
  categories: Arr(CategoryOptionSchema),
225
275
  categoryColors: Schema.Record({ key: Schema.String, value: Schema.String }),
226
276
  presets: Arr(QuizPresetSchema),
277
+ presetMarks: Schema.Record({ key: Schema.String, value: PresetMarksSchema }),
227
278
  presetLimit: Schema.Number,
228
279
  subscription: Schema.NullOr(SubscriptionRowSchema),
229
280
  allowance: HostingAllowanceSchema,
@@ -234,6 +285,7 @@ const GameHistoryListItemSchema = Schema.Struct({
234
285
  started_at: Schema.String,
235
286
  player_count: Schema.Number,
236
287
  total_questions: Schema.Number,
288
+ seasonNames: Arr(Schema.String),
237
289
  winners: Arr(Schema.String),
238
290
  top_score: Schema.NullOr(Schema.Number),
239
291
  });
@@ -248,6 +300,8 @@ const GameHistoryEntrySchema = Schema.Struct({
248
300
  started_at: Schema.String,
249
301
  ended_at: Schema.NullOr(Schema.String),
250
302
  categoryIds: Arr(Schema.String),
303
+ seasonIds: Arr(Schema.String),
304
+ seasonNames: Arr(Schema.String),
251
305
  timer_duration: Schema.Number,
252
306
  total_questions: Schema.Number,
253
307
  player_count: Schema.Number,
@@ -324,6 +378,8 @@ export const CuratorQuestionsPageSchema = Schema.Struct({
324
378
  categories: Arr(CuratorQuestionsCategoryOptionSchema),
325
379
  ownQuestions: Arr(OwnQuestionSchema),
326
380
  questionsCursor: Schema.NullOr(Schema.String),
381
+ /** Admin-defined tag names, sorted. */
382
+ tags: Arr(Schema.String),
327
383
  });
328
384
  /** Body of `GET /api/pages/solo/layout`. */
329
385
  export const SoloLayoutPageSchema = Schema.Struct({
@@ -333,6 +389,7 @@ export const SoloLayoutPageSchema = Schema.Struct({
333
389
  nickname: Schema.String,
334
390
  profileAvatarSrc: Schema.String,
335
391
  categoryDataJson: Schema.String,
392
+ seasonDataJson: Schema.String,
336
393
  });
337
394
  const LeaderboardEntrySchema = Schema.Struct({
338
395
  rank: Schema.Number,
@@ -349,10 +406,19 @@ const LeaderboardEntrySchema = Schema.Struct({
349
406
  export const SoloLeaderboardPageSchema = Schema.Struct({
350
407
  entries: Arr(LeaderboardEntrySchema),
351
408
  board: Schema.Literal("score", "streak", "accuracy", "speed", "survival"),
352
- scope: Schema.Literal("all-time", "daily", "weekly", "monthly"),
409
+ scope: Schema.Literal("all-time", "daily", "weekly", "monthly", "season"),
353
410
  categoryId: Schema.NullOr(Schema.String),
411
+ seasonId: Schema.NullOr(Schema.String),
412
+ opening: Schema.NullOr(Schema.String),
354
413
  difficulty: Schema.NullOr(Schema.Literal("easy", "medium", "hard")),
355
414
  categories: Arr(Schema.Struct({ id: Schema.String, name: Schema.String })),
415
+ seasonBoards: Arr(Schema.Struct({
416
+ seasonId: Schema.String,
417
+ name: Schema.String,
418
+ opening: Schema.String,
419
+ label: Schema.String,
420
+ current: Schema.Boolean,
421
+ })),
356
422
  userRank: Schema.NullOr(Schema.Number),
357
423
  eligibleCount: Schema.Number,
358
424
  percentile: Schema.NullOr(Schema.Number),
@@ -72,6 +72,7 @@ declare const SoloHistoryEntrySchema: Schema.Struct<{
72
72
  mode: typeof Schema.String;
73
73
  difficulty: typeof Schema.String;
74
74
  category_ids: typeof Schema.String;
75
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
75
76
  completed_at: typeof Schema.Number;
76
77
  }>;
77
78
  export type SoloHistoryEntry = typeof SoloHistoryEntrySchema.Type;
@@ -89,6 +90,7 @@ declare const PlayerGamePlacementSchema: Schema.Struct<{
89
90
  final_score: typeof Schema.Number;
90
91
  rank: typeof Schema.Number;
91
92
  player_count: typeof Schema.Number;
93
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
92
94
  badges: Schema.mutable<Schema.Array$<Schema.Literal<["victor", "flawless", "sharpshooter", "podium", "unstoppable", "on-a-roll", "survivor", "giant-slayer", "pantheon", "specialist", "polymath", "phoenix", "strong-finish", "master-of-none", "faster-than-most", "clutch", "quick-draw", "decisive", "sure-handed", "unbroken-focus", "devotee", "herculean", "undaunted", "measured", "strong-start", "new-heights", "far-horizons", "cassandra", "lone-torch", "ascendant", "undisputed"]>>>;
93
95
  }>;
94
96
  export type PlayerGamePlacement = typeof PlayerGamePlacementSchema.Type;
@@ -105,6 +107,7 @@ export declare const HistoryPageSchema: Schema.Struct<{
105
107
  mode: typeof Schema.String;
106
108
  difficulty: typeof Schema.String;
107
109
  category_ids: typeof Schema.String;
110
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
108
111
  completed_at: typeof Schema.Number;
109
112
  }>>>;
110
113
  trendRuns: Schema.mutable<Schema.Array$<Schema.Struct<{
@@ -117,6 +120,7 @@ export declare const HistoryPageSchema: Schema.Struct<{
117
120
  mode: typeof Schema.String;
118
121
  difficulty: typeof Schema.String;
119
122
  category_ids: typeof Schema.String;
123
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
120
124
  completed_at: typeof Schema.Number;
121
125
  }>>>;
122
126
  latestRunAt: Schema.NullOr<typeof Schema.Number>;
@@ -164,6 +168,7 @@ export declare const HistoryPageSchema: Schema.Struct<{
164
168
  final_score: typeof Schema.Number;
165
169
  rank: typeof Schema.Number;
166
170
  player_count: typeof Schema.Number;
171
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
167
172
  badges: Schema.mutable<Schema.Array$<Schema.Literal<["victor", "flawless", "sharpshooter", "podium", "unstoppable", "on-a-roll", "survivor", "giant-slayer", "pantheon", "specialist", "polymath", "phoenix", "strong-finish", "master-of-none", "faster-than-most", "clutch", "quick-draw", "decisive", "sure-handed", "unbroken-focus", "devotee", "herculean", "undaunted", "measured", "strong-start", "new-heights", "far-horizons", "cassandra", "lone-torch", "ascendant", "undisputed"]>>>;
168
173
  }>>>;
169
174
  runsNextCursor: Schema.NullOr<typeof Schema.String>;
@@ -178,6 +183,7 @@ export declare const GameReviewPageSchema: Schema.Struct<{
178
183
  finalScore: typeof Schema.Number;
179
184
  rank: typeof Schema.Number;
180
185
  playerCount: typeof Schema.Number;
186
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
181
187
  }>;
182
188
  standings: Schema.mutable<Schema.Array$<Schema.Struct<{
183
189
  id: typeof Schema.String;
@@ -238,6 +244,7 @@ export declare const SoloReviewPageSchema: Schema.Struct<{
238
244
  difficulty: typeof Schema.String;
239
245
  completedAt: typeof Schema.Number;
240
246
  categories: Schema.mutable<Schema.Array$<typeof Schema.String>>;
247
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
241
248
  }>;
242
249
  badges: Schema.mutable<Schema.Array$<Schema.Struct<{
243
250
  id: Schema.Literal<["victor", "flawless", "sharpshooter", "podium", "unstoppable", "on-a-roll", "survivor", "giant-slayer", "pantheon", "specialist", "polymath", "phoenix", "strong-finish", "master-of-none", "faster-than-most", "clutch", "quick-draw", "decisive", "sure-handed", "unbroken-focus", "devotee", "herculean", "undaunted", "measured", "strong-start", "new-heights", "far-horizons", "cassandra", "lone-torch", "ascendant", "undisputed"]>;
@@ -75,6 +75,7 @@ const SoloHistoryEntrySchema = Schema.Struct({
75
75
  mode: Schema.String,
76
76
  difficulty: Schema.String,
77
77
  category_ids: Schema.String,
78
+ seasons: Schema.mutable(Schema.Array(Schema.String)),
78
79
  completed_at: Schema.Number,
79
80
  });
80
81
  const SoloStatsSchema = Schema.Struct({
@@ -107,6 +108,7 @@ const PlayerGamePlacementSchema = Schema.Struct({
107
108
  final_score: Schema.Number,
108
109
  rank: Schema.Number,
109
110
  player_count: Schema.Number,
111
+ seasons: Schema.mutable(Schema.Array(Schema.String)),
110
112
  badges: Schema.mutable(Schema.Array(BadgeIdSchema)),
111
113
  });
112
114
  /** Body of `GET /api/pages/history`. */
@@ -150,6 +152,7 @@ export const GameReviewPageSchema = Schema.Struct({
150
152
  finalScore: Schema.Number,
151
153
  rank: Schema.Number,
152
154
  playerCount: Schema.Number,
155
+ seasons: Schema.mutable(Schema.Array(Schema.String)),
153
156
  }),
154
157
  standings: Schema.mutable(Schema.Array(GameStandingEntrySchema)),
155
158
  badges: Schema.mutable(Schema.Array(Schema.Struct({ id: BadgeIdSchema, rarity: RaritySchema }))),
@@ -193,6 +196,7 @@ export const SoloReviewPageSchema = Schema.Struct({
193
196
  difficulty: Schema.String,
194
197
  completedAt: Schema.Number,
195
198
  categories: Schema.mutable(Schema.Array(Schema.String)),
199
+ seasons: Schema.mutable(Schema.Array(Schema.String)),
196
200
  }),
197
201
  badges: Schema.mutable(Schema.Array(Schema.Struct({ id: BadgeIdSchema, rarity: RaritySchema }))),
198
202
  categoryStats: Schema.mutable(Schema.Array(Schema.Struct({
@@ -96,6 +96,7 @@ export declare const QuizContextPageSchema: Schema.Struct<{
96
96
  quizName: typeof Schema.String;
97
97
  description: typeof Schema.String;
98
98
  categories: Schema.mutable<Schema.Array$<typeof Schema.String>>;
99
+ seasons: Schema.mutable<Schema.Array$<typeof Schema.String>>;
99
100
  difficulty: Schema.NullOr<Schema.Literal<["all", "easy", "medium", "hard"]>>;
100
101
  accessMode: Schema.Literal<["open", "invite-only"]>;
101
102
  maxPlayers: Schema.NullOr<typeof Schema.Number>;
@@ -85,6 +85,7 @@ const LobbySchema = Schema.Struct({
85
85
  quizName: Schema.String,
86
86
  description: Schema.String,
87
87
  categories: Schema.mutable(Schema.Array(Schema.String)),
88
+ seasons: Schema.mutable(Schema.Array(Schema.String)),
88
89
  difficulty: Schema.NullOr(DifficultyFilterSchema),
89
90
  accessMode: AccessModeSchema,
90
91
  maxPlayers: Schema.NullOr(Schema.Number),
package/dist/solo-ws.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { BadgeAward } from "./badges.ts";
2
2
  import type { PlayerSafeQuestion } from "./player-safe-question.ts";
3
3
  import type { MediaType, QuestionSource } from "./question.ts";
4
4
  import type { QuestionRating } from "./question-rating.ts";
5
+ import type { RateAckReason } from "./ws.ts";
5
6
  export type SoloAuthMessage = {
6
7
  type: "auth";
7
8
  token: string;
@@ -9,6 +10,8 @@ export type SoloAuthMessage = {
9
10
  export type SoloStartMessage = {
10
11
  type: "solo:start";
11
12
  categoryIds: string[];
13
+ /** Seasons drawn from beside the categories. */
14
+ seasonIds?: string[];
12
15
  timerDuration: number;
13
16
  questionCount: number;
14
17
  mode: "normal" | "endless";
@@ -49,7 +52,8 @@ export type SoloResumeMessage = {
49
52
  resumeToken?: string;
50
53
  };
51
54
  /** Rate a question revealed in this run (signed-in runs only). The server
52
- * answers with `solo:rate-ack` echoing `questionId`. */
55
+ * answers with `solo:rate-ack` echoing `questionId`; `reason: "disabled"`
56
+ * when the rating flag is off. */
53
57
  export type SoloRateMessage = {
54
58
  type: "solo:rate";
55
59
  /** Bank question id, as on `solo:question`. */
@@ -198,7 +202,7 @@ export type SoloPingMessage = {
198
202
  };
199
203
  export type SoloErrorMessage = {
200
204
  type: "solo:error";
201
- code: "auth_failed" | "entitlement_exceeded" | "invalid_message" | "session_expired" | "internal";
205
+ code: "auth_failed" | "entitlement_exceeded" | "invalid_message" | "session_expired" | "season_out_of_season" | "no_questions" | "internal";
202
206
  message: string;
203
207
  };
204
208
  /** Build a `solo:error` frame — the one shape every failure site sends. */
@@ -214,5 +218,7 @@ export type SoloRateAckMessage = {
214
218
  type: "solo:rate-ack";
215
219
  questionId: string;
216
220
  ok: boolean;
221
+ /** Set when the server refuses every rating because the flag is off. */
222
+ reason?: RateAckReason;
217
223
  };
218
224
  export type SoloServerMessage = SoloReadyMessage | SoloQuestionMessage | SoloTickMessage | SoloRoundResultMessage | SoloStreakMessage | SoloStreakLostMessage | SoloStreakNearMissMessage | SoloStreakBestMessage | SoloLifeSpentMessage | SoloFinalMessage | SoloPingMessage | SoloErrorMessage | SoloGuestLimitMessage | SoloRateAckMessage;
@@ -1,6 +1,6 @@
1
1
  /** Which ranking a leaderboard sorts by. */
2
2
  export type LeaderboardBoard = "score" | "streak" | "accuracy" | "speed" | "survival";
3
- /** Rolling window a leaderboard is scoped to. */
4
- export type LeaderboardScope = "all-time" | "daily" | "weekly" | "monthly";
3
+ /** Rolling window a leaderboard is scoped to, or one Season opening. */
4
+ export type LeaderboardScope = "all-time" | "daily" | "weekly" | "monthly" | "season";
5
5
  /** Difficulty filter a leaderboard is narrowed to. */
6
6
  export type LeaderboardDifficulty = "easy" | "medium" | "hard";
@@ -23,6 +23,7 @@ export declare const ClientMessageSchema: Schema.Union<[Schema.Struct<{
23
23
  }>, Schema.Struct<{
24
24
  type: Schema.Literal<["curator:start"]>;
25
25
  categoryIds: Schema.optional<Schema.filter<Schema.Array$<typeof Schema.NonEmptyString>>>;
26
+ seasonIds: Schema.optional<Schema.filter<Schema.Array$<typeof Schema.NonEmptyString>>>;
26
27
  timerDuration: Schema.optional<Schema.Literal<[15, 30, 45, 60]>>;
27
28
  questionsPerRound: Schema.optional<Schema.filter<Schema.filter<Schema.filter<typeof Schema.Number>>>>;
28
29
  difficulty: Schema.optional<Schema.Literal<["all", "easy", "medium", "hard"]>>;
@@ -94,6 +94,7 @@ const CuratorStartMessageSchema = Schema.Struct({
94
94
  // preloadedQuestions carries the round — game-ws-server.ts's makeStartGame
95
95
  // surfaces the friendly "select a category" error itself.
96
96
  categoryIds: Schema.optional(Schema.Array(Schema.NonEmptyString).pipe(Schema.maxItems(50))),
97
+ seasonIds: Schema.optional(Schema.Array(Schema.NonEmptyString).pipe(Schema.maxItems(50))),
97
98
  timerDuration: Schema.optional(Schema.Literal(...GAME.roundConfig.timerOptions)),
98
99
  // Not the QuestionsPerRound literal on purpose (ws.ts types this as a plain
99
100
  // number) — bounded generously instead, mirroring DrawQuestionsSchema's cap.
@@ -5,6 +5,7 @@ export declare const SoloClientMessageSchema: Schema.Union<[Schema.Struct<{
5
5
  }>, Schema.Struct<{
6
6
  type: Schema.Literal<["solo:start"]>;
7
7
  categoryIds: Schema.filter<Schema.Array$<typeof Schema.NonEmptyString>>;
8
+ seasonIds: Schema.optional<Schema.filter<Schema.Array$<typeof Schema.NonEmptyString>>>;
8
9
  timerDuration: Schema.filter<Schema.filter<Schema.filter<typeof Schema.Number>>>;
9
10
  questionCount: Schema.filter<Schema.filter<Schema.filter<typeof Schema.Number>>>;
10
11
  mode: Schema.Literal<["normal", "endless"]>;
@@ -17,6 +17,7 @@ const SoloAuthMessageSchema = Schema.Struct({
17
17
  const SoloStartMessageSchema = Schema.Struct({
18
18
  type: Schema.Literal("solo:start"),
19
19
  categoryIds: Schema.Array(Schema.NonEmptyString).pipe(Schema.maxItems(50)),
20
+ seasonIds: Schema.optional(Schema.Array(Schema.NonEmptyString).pipe(Schema.maxItems(50))),
20
21
  // Plain `number` on the hand-written type (clamped to [5,120] downstream) —
21
22
  // bounded generously here against absurd/non-finite values.
22
23
  timerDuration: Schema.Number.pipe(Schema.finite(), Schema.greaterThan(0), Schema.lessThanOrEqualTo(600)),
package/dist/ws.d.ts CHANGED
@@ -37,7 +37,7 @@ export interface PlayerLeaveMessage {
37
37
  }
38
38
  /** Rate a question revealed in this game — from a signed-in player who played
39
39
  * its round, or the curator. The server answers with `player:rate-ack`
40
- * echoing `questionId`. */
40
+ * echoing `questionId`; `reason: "disabled"` when the rating flag is off. */
41
41
  export interface PlayerRateMessage {
42
42
  type: "player:rate";
43
43
  /** Bank question id, as on `game:question`. */
@@ -49,6 +49,9 @@ export interface PlayerRateMessage {
49
49
  export interface CuratorStartMessage {
50
50
  type: "curator:start";
51
51
  categoryIds?: string[];
52
+ /** Season ids; the server keeps only those the Lobby fixed at create or
53
+ * re-host. */
54
+ seasonIds?: string[];
52
55
  timerDuration?: TimerDuration;
53
56
  questionsPerRound?: number;
54
57
  difficulty?: DifficultyFilter;
@@ -108,6 +111,8 @@ export interface LobbyUpdateMessage {
108
111
  description?: string;
109
112
  /** Curator's live-edited category list. */
110
113
  categories?: readonly string[];
114
+ /** Names of the Seasons the Lobby draws from. */
115
+ seasons?: readonly string[];
111
116
  /** Curator's live-edited difficulty filter. */
112
117
  difficulty?: DifficultyFilter;
113
118
  }
@@ -289,6 +294,8 @@ export interface GameErrorMessage {
289
294
  /** Human-readable fallback when the UI has no copy for `code`. */
290
295
  message: string;
291
296
  }
297
+ /** Why a rate ack failed for every rating alike: the flag is off. */
298
+ export type RateAckReason = "disabled";
292
299
  /** Outcome of one `player:rate`, to the rater only: `ok` is false when the
293
300
  * rating was refused or not stored. Failures ride here, never on
294
301
  * `game:error`, which carries no request id. */
@@ -296,4 +303,6 @@ export interface PlayerRateAckMessage {
296
303
  type: "player:rate-ack";
297
304
  questionId: string;
298
305
  ok: boolean;
306
+ /** Set when the server refuses every rating because the flag is off. */
307
+ reason?: RateAckReason;
299
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orakl/protocol",
3
- "version": "0.3.0",
3
+ "version": "0.6.0",
4
4
  "description": "Orakl wire contract: WebSocket frames and API response schemas.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "repository": {