@juki-team/commons 0.1.41 → 0.1.42

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.
@@ -600,6 +600,9 @@ exports.WebSocketSubscriptionEvent = void 0;
600
600
  WebSocketSubscriptionEvent["UNSUBSCRIBE_CLIENT_TRACK"] = "UNSUBSCRIBE_CLIENT_TRACK";
601
601
  WebSocketSubscriptionEvent["SUBSCRIBE_USER_NOTIFICATION"] = "SUBSCRIBE_USER_NOTIFICATION";
602
602
  WebSocketSubscriptionEvent["UNSUBSCRIBE_USER_NOTIFICATION"] = "UNSUBSCRIBE_USER_NOTIFICATION";
603
+ // an administrator watching the data (screenshots, location, device) sent by another client
604
+ WebSocketSubscriptionEvent["SUBSCRIBE_CLIENT_TRACK_DATA"] = "SUBSCRIBE_CLIENT_TRACK_DATA";
605
+ WebSocketSubscriptionEvent["UNSUBSCRIBE_CLIENT_TRACK_DATA"] = "UNSUBSCRIBE_CLIENT_TRACK_DATA";
603
606
  })(exports.WebSocketSubscriptionEvent || (exports.WebSocketSubscriptionEvent = {}));
604
607
  exports.WebSocketResponseEvent = void 0;
605
608
  (function (WebSocketResponseEvent) {
@@ -623,6 +626,20 @@ exports.WebSocketResponseEvent = void 0;
623
626
  WebSocketResponseEvent["USER_NOTIFICATION_SUBMISSION"] = "USER_NOTIFICATION_SUBMISSION";
624
627
  WebSocketResponseEvent["USER_NOTIFICATION_CONTEST_CLARIFICATION"] = "USER_NOTIFICATION_CONTEST_CLARIFICATION";
625
628
  })(exports.WebSocketResponseEvent || (exports.WebSocketResponseEvent = {}));
629
+ /**
630
+ * Roles of a contest, with the same names as the contest members in the DB.
631
+ * Each role has its own Ably channel: `contest:{contestKey}:{role}` (see `CHANNEL_CONTEST`).
632
+ * The inclusions follow `getContestAccessPermissions`:
633
+ * spectators ⊇ managers ⊇ administrators, spectators ⊇ participants, spectators ⊇ guests
634
+ */
635
+ exports.ContestChannelRole = void 0;
636
+ (function (ContestChannelRole) {
637
+ ContestChannelRole["ADMINISTRATORS"] = "administrators";
638
+ ContestChannelRole["MANAGERS"] = "managers";
639
+ ContestChannelRole["PARTICIPANTS"] = "participants";
640
+ ContestChannelRole["GUESTS"] = "guests";
641
+ ContestChannelRole["SPECTATORS"] = "spectators";
642
+ })(exports.ContestChannelRole || (exports.ContestChannelRole = {}));
626
643
 
627
644
  exports.DataViewMode = void 0;
628
645
  (function (DataViewMode) {
@@ -2223,11 +2240,43 @@ const EMPTY_USER_PERMISSIONS = {
2223
2240
  },
2224
2241
  };
2225
2242
 
2243
+ // LEGACY channels (fan-out per client through the subscriptions registry in Redis)
2226
2244
  const CHANNEL_PUBLISH_SUBSCRIPTIONS = 'pub:subscriptions';
2227
2245
  const CHANNEL_SUBSCRIBE_NOTIFICATIONS = 'sub:notifications';
2228
2246
  const CHANNEL_SUBSCRIBE_CLIENT = (clientId) => `sub:client:${clientId}`;
2229
2247
  const CHANNEL_PUBLISH_MESSAGES = 'pub:messages';
2230
2248
  const CHANNEL_PRESENCE_CLIENT = 'pre:client';
2249
+ // TOPIC channels
2250
+ // The backend publishes each event once on the channel of its topic and Ably does the fan-out.
2251
+ // Who can subscribe to each channel is decided by the API in the Ably token (capability), never by the client.
2252
+ // Dynamic segments are encoded so a key can never inject a `:` (the Ably segment separator used by the wildcards).
2253
+ const segment = (value) => encodeURIComponent(value);
2254
+ /** private: events of one session (code runs, requested data, client track commands) */
2255
+ const CHANNEL_SESSION = (sessionId) => `session:${segment(sessionId)}`;
2256
+ /** private: events of one user (own submissions while the contest is frozen, notifications) */
2257
+ const CHANNEL_USER = (userId) => `user:${segment(userId)}`;
2258
+ /** public: status of a submission when it is visible to everyone */
2259
+ const CHANNEL_SUBMISSION = (submitId) => `submission:${segment(submitId)}`;
2260
+ /** public: changes of a problem */
2261
+ const CHANNEL_PROBLEM = (problemKey) => `problem:${segment(problemKey)}`;
2262
+ /** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
2263
+ const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${role}`;
2264
+ /** system administrators only: data sent by a tracked client (screenshots, location, device) */
2265
+ const CHANNEL_CLIENT_TRACK = (trackedClientId) => `client-track:${segment(trackedClientId)}`;
2266
+ // capability resources (wildcards) of the public channels
2267
+ const CHANNEL_SUBMISSION_ALL = 'submission:*';
2268
+ const CHANNEL_PROBLEM_ALL = 'problem:*';
2269
+ const CHANNEL_CLIENT_TRACK_ALL = 'client-track:*';
2270
+ /**
2271
+ * Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
2272
+ */
2273
+ const getContestChannelParams = (channel) => {
2274
+ const [namespace, contestKey, role, ...rest] = channel.split(':');
2275
+ if (namespace !== 'contest' || !contestKey || rest.length || !Object.values(exports.ContestChannelRole).includes(role)) {
2276
+ return null;
2277
+ }
2278
+ return { contestKey: decodeURIComponent(contestKey), role: role };
2279
+ };
2231
2280
 
2232
2281
  const NEW_PAGE_SHEET = () => ({
2233
2282
  id: uuid.v4(),
@@ -3010,6 +3059,16 @@ const isUnsubscribeUserNotificationWebSocketEventDTO = (event) => {
3010
3059
  return isWebsocketSubscription(event)
3011
3060
  && event?.event === exports.WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
3012
3061
  };
3062
+ const isSubscribeClientTrackDataWebSocketEventDTO = (event) => {
3063
+ return isWebsocketSubscription(event)
3064
+ && event?.event === exports.WebSocketSubscriptionEvent.SUBSCRIBE_CLIENT_TRACK_DATA
3065
+ && typeof event?.trackedClientId === 'string' && !!event.trackedClientId;
3066
+ };
3067
+ const isUnsubscribeClientTrackDataWebSocketEventDTO = (event) => {
3068
+ return isWebsocketSubscription(event)
3069
+ && event?.event === exports.WebSocketSubscriptionEvent.UNSUBSCRIBE_CLIENT_TRACK_DATA
3070
+ && typeof event?.trackedClientId === 'string' && !!event.trackedClientId;
3071
+ };
3013
3072
  // is WebSocketResponseEventDTO, CHANNEL_SUBSCRIBE_CLIENT
3014
3073
  const isWebSocketResponseEventDTO = (event) => {
3015
3074
  return Object.values(exports.WebSocketResponseEvent).includes(event?.event)
@@ -3123,6 +3182,73 @@ const isUserNotificationContestClarificationWebSocketResponseEventDTO = (event)
3123
3182
  const getWebSocketResponseEventKey = (event, clientId, id) => {
3124
3183
  return `${event}-${clientId}-${id}`;
3125
3184
  };
3185
+ const isWebSocketChannelsMessageDTO = (message) => {
3186
+ return Array.isArray(message?.channels)
3187
+ && message.channels.every((channel) => typeof channel === 'string' && !!channel)
3188
+ && Object.values(exports.WebSocketResponseEvent).includes(message?.event?.event);
3189
+ };
3190
+ /**
3191
+ * Key used by the client to dispatch a response received on a TOPIC channel to its subscribers.
3192
+ * It is the same key that `getKeyWebSocketEventDTO` (base-ui) computes for the subscription.
3193
+ * Returns null when the response is not addressed to this client.
3194
+ */
3195
+ const getWebSocketResponseEventKeyOfResponse = (response, clientId) => {
3196
+ if (isCodeRunStatusMessageWebSocketResponseEventDTO(response)) {
3197
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.CODE_RUN_STATUS, clientId, response.runId);
3198
+ }
3199
+ if (isSubmissionRunStatusMessageWebSocketResponseEventDTO(response)) {
3200
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.SUBMISSION_RUN_STATUS, clientId, response.submitId);
3201
+ }
3202
+ if (isSendDataWebSocketResponseEventDTO(response)) {
3203
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.SEND_DATA_, clientId, response.dataId);
3204
+ }
3205
+ if (isContestChangesWebSocketResponseEventDTO(response)) {
3206
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.CONTEST_CHANGES, clientId, response.content.contestKey);
3207
+ }
3208
+ if (isProblemCrawledWebSocketResponseEventDTO(response)) {
3209
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.PROBLEM_CRAWLED, clientId, response.content.problemKey);
3210
+ }
3211
+ if (isSubmissionsCrawlWebSocketResponseEventDTO(response)) {
3212
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.SUBMISSIONS_CRAWL, clientId, join([response.content.contestKey, response.content.problemKeys]));
3213
+ }
3214
+ if (isClientTrackWebSocketResponseEventDTO(response)) {
3215
+ // the command is sent to the session channel, `key` holds the targeted client
3216
+ return response.key === clientId ? getWebSocketResponseEventKey(exports.WebSocketResponseEvent.CLIENT_TRACK, clientId, '*') : null;
3217
+ }
3218
+ if (isUserNotificationWebSocketResponseEventDTO(response)) {
3219
+ return getWebSocketResponseEventKey(exports.WebSocketResponseEvent.USER_NOTIFICATION_, clientId, '*');
3220
+ }
3221
+ return null;
3222
+ };
3223
+ /**
3224
+ * TOPIC channel that the client has to attach to receive the events of a subscription,
3225
+ * or null when the events arrive by the private channels (session / user) the client is always attached to.
3226
+ * Ably rejects the attach if the token has no capability for the channel.
3227
+ */
3228
+ const getChannelOfWebSocketSubscribeEvent = (event) => {
3229
+ if (isSubscribeSubmissionRunStatusWebSocketEventDTO(event)) {
3230
+ return CHANNEL_SUBMISSION(event.submitId);
3231
+ }
3232
+ if (isSubscribeProblemCrawledWebSocketEventDTO(event)) {
3233
+ return CHANNEL_PROBLEM(event.problemKey);
3234
+ }
3235
+ if (isSubscribeContestChangesWebSocketEventDTO(event) || isSubscribeSubmissionsCrawlWebSocketEventDTO(event)) {
3236
+ return CHANNEL_CONTEST(event.contestKey, exports.ContestChannelRole.SPECTATORS);
3237
+ }
3238
+ if (isSubscribeClientTrackDataWebSocketEventDTO(event)) {
3239
+ return CHANNEL_CLIENT_TRACK(event.trackedClientId);
3240
+ }
3241
+ return null;
3242
+ };
3243
+ /**
3244
+ * Contest whose role channels the client needs (the API grants them when the token is requested with this contest).
3245
+ */
3246
+ const getContestKeyOfWebSocketSubscribeEvent = (event) => {
3247
+ if (isSubscribeContestChangesWebSocketEventDTO(event) || isSubscribeSubmissionsCrawlWebSocketEventDTO(event)) {
3248
+ return event.contestKey;
3249
+ }
3250
+ return null;
3251
+ };
3126
3252
 
3127
3253
  function splitTime(timeRemaining) {
3128
3254
  const remaining = [];
@@ -3350,11 +3476,20 @@ exports.ACCEPTED_PROGRAMMING_LANGUAGES = ACCEPTED_PROGRAMMING_LANGUAGES;
3350
3476
  exports.ASPECT_RATIO = ASPECT_RATIO;
3351
3477
  exports.AssignmentState = AssignmentState;
3352
3478
  exports.AssignmentType = AssignmentType;
3479
+ exports.CHANNEL_CLIENT_TRACK = CHANNEL_CLIENT_TRACK;
3480
+ exports.CHANNEL_CLIENT_TRACK_ALL = CHANNEL_CLIENT_TRACK_ALL;
3481
+ exports.CHANNEL_CONTEST = CHANNEL_CONTEST;
3353
3482
  exports.CHANNEL_PRESENCE_CLIENT = CHANNEL_PRESENCE_CLIENT;
3483
+ exports.CHANNEL_PROBLEM = CHANNEL_PROBLEM;
3484
+ exports.CHANNEL_PROBLEM_ALL = CHANNEL_PROBLEM_ALL;
3354
3485
  exports.CHANNEL_PUBLISH_MESSAGES = CHANNEL_PUBLISH_MESSAGES;
3355
3486
  exports.CHANNEL_PUBLISH_SUBSCRIPTIONS = CHANNEL_PUBLISH_SUBSCRIPTIONS;
3487
+ exports.CHANNEL_SESSION = CHANNEL_SESSION;
3488
+ exports.CHANNEL_SUBMISSION = CHANNEL_SUBMISSION;
3489
+ exports.CHANNEL_SUBMISSION_ALL = CHANNEL_SUBMISSION_ALL;
3356
3490
  exports.CHANNEL_SUBSCRIBE_CLIENT = CHANNEL_SUBSCRIBE_CLIENT;
3357
3491
  exports.CHANNEL_SUBSCRIBE_NOTIFICATIONS = CHANNEL_SUBSCRIBE_NOTIFICATIONS;
3492
+ exports.CHANNEL_USER = CHANNEL_USER;
3358
3493
  exports.CODE_LANGUAGE = CODE_LANGUAGE;
3359
3494
  exports.COMPANY_PLAN = COMPANY_PLAN;
3360
3495
  exports.CONTEST_ROLE = CONTEST_ROLE;
@@ -3459,8 +3594,11 @@ exports.contentsResponse = contentsResponse;
3459
3594
  exports.errorsResponse = errorsResponse;
3460
3595
  exports.getAssignmentCommentKey = getAssignmentCommentKey;
3461
3596
  exports.getAssignmentUserCommentKey = getAssignmentUserCommentKey;
3597
+ exports.getChannelOfWebSocketSubscribeEvent = getChannelOfWebSocketSubscribeEvent;
3462
3598
  exports.getClientId = getClientId;
3599
+ exports.getContestChannelParams = getContestChannelParams;
3463
3600
  exports.getContestCommentKey = getContestCommentKey;
3601
+ exports.getContestKeyOfWebSocketSubscribeEvent = getContestKeyOfWebSocketSubscribeEvent;
3464
3602
  exports.getDataLog = getDataLog;
3465
3603
  exports.getDataOfTestCase = getDataOfTestCase;
3466
3604
  exports.getDays = getDays;
@@ -3482,6 +3620,7 @@ exports.getTotalExercisesOfSummaryWorksheetsInPages = getTotalExercisesOfSummary
3482
3620
  exports.getUserKey = getUserKey;
3483
3621
  exports.getVerdictFromTestCase = getVerdictFromTestCase;
3484
3622
  exports.getWebSocketResponseEventKey = getWebSocketResponseEventKey;
3623
+ exports.getWebSocketResponseEventKeyOfResponse = getWebSocketResponseEventKeyOfResponse;
3485
3624
  exports.getWorksheetsInPages = getWorksheetsInPages;
3486
3625
  exports.getYears = getYears;
3487
3626
  exports.humanFileSize = humanFileSize;
@@ -3520,6 +3659,7 @@ exports.isSendDataWebSocketResponseEventDTO = isSendDataWebSocketResponseEventDT
3520
3659
  exports.isStringJson = isStringJson;
3521
3660
  exports.isSubmissionRunStatusMessageWebSocketResponseEventDTO = isSubmissionRunStatusMessageWebSocketResponseEventDTO;
3522
3661
  exports.isSubmissionsCrawlWebSocketResponseEventDTO = isSubmissionsCrawlWebSocketResponseEventDTO;
3662
+ exports.isSubscribeClientTrackDataWebSocketEventDTO = isSubscribeClientTrackDataWebSocketEventDTO;
3523
3663
  exports.isSubscribeClientTrackWebSocketEventDTO = isSubscribeClientTrackWebSocketEventDTO;
3524
3664
  exports.isSubscribeCodeRunStatusWebSocketEventDTO = isSubscribeCodeRunStatusWebSocketEventDTO;
3525
3665
  exports.isSubscribeContestChangesWebSocketEventDTO = isSubscribeContestChangesWebSocketEventDTO;
@@ -3529,6 +3669,7 @@ exports.isSubscribeSubmissionRunStatusWebSocketEventDTO = isSubscribeSubmissionR
3529
3669
  exports.isSubscribeSubmissionsCrawlWebSocketEventDTO = isSubscribeSubmissionsCrawlWebSocketEventDTO;
3530
3670
  exports.isSubscribeUserNotificationWebSocketEventDTO = isSubscribeUserNotificationWebSocketEventDTO;
3531
3671
  exports.isTeamMember = isTeamMember;
3672
+ exports.isUnsubscribeClientTrackDataWebSocketEventDTO = isUnsubscribeClientTrackDataWebSocketEventDTO;
3532
3673
  exports.isUnsubscribeClientTrackWebSocketEventDTO = isUnsubscribeClientTrackWebSocketEventDTO;
3533
3674
  exports.isUnsubscribeCodeRunStatusWebSocketEventDTO = isUnsubscribeCodeRunStatusWebSocketEventDTO;
3534
3675
  exports.isUnsubscribeContestChangesWebSocketEventDTO = isUnsubscribeContestChangesWebSocketEventDTO;
@@ -3542,6 +3683,7 @@ exports.isUserMessageWebSocketResponseEventDTO = isUserMessageWebSocketResponseE
3542
3683
  exports.isUserNotificationContestClarificationWebSocketResponseEventDTO = isUserNotificationContestClarificationWebSocketResponseEventDTO;
3543
3684
  exports.isUserNotificationSubmissionWebSocketResponseEventDTO = isUserNotificationSubmissionWebSocketResponseEventDTO;
3544
3685
  exports.isUserNotificationWebSocketResponseEventDTO = isUserNotificationWebSocketResponseEventDTO;
3686
+ exports.isWebSocketChannelsMessageDTO = isWebSocketChannelsMessageDTO;
3545
3687
  exports.isWebSocketResponseEventDTO = isWebSocketResponseEventDTO;
3546
3688
  exports.isWebsocketSubscription = isWebsocketSubscription;
3547
3689
  exports.isWorksheetAssignment = isWorksheetAssignment;
package/dist/esm/index.js CHANGED
@@ -598,6 +598,9 @@ var WebSocketSubscriptionEvent;
598
598
  WebSocketSubscriptionEvent["UNSUBSCRIBE_CLIENT_TRACK"] = "UNSUBSCRIBE_CLIENT_TRACK";
599
599
  WebSocketSubscriptionEvent["SUBSCRIBE_USER_NOTIFICATION"] = "SUBSCRIBE_USER_NOTIFICATION";
600
600
  WebSocketSubscriptionEvent["UNSUBSCRIBE_USER_NOTIFICATION"] = "UNSUBSCRIBE_USER_NOTIFICATION";
601
+ // an administrator watching the data (screenshots, location, device) sent by another client
602
+ WebSocketSubscriptionEvent["SUBSCRIBE_CLIENT_TRACK_DATA"] = "SUBSCRIBE_CLIENT_TRACK_DATA";
603
+ WebSocketSubscriptionEvent["UNSUBSCRIBE_CLIENT_TRACK_DATA"] = "UNSUBSCRIBE_CLIENT_TRACK_DATA";
601
604
  })(WebSocketSubscriptionEvent || (WebSocketSubscriptionEvent = {}));
602
605
  var WebSocketResponseEvent;
603
606
  (function (WebSocketResponseEvent) {
@@ -621,6 +624,20 @@ var WebSocketResponseEvent;
621
624
  WebSocketResponseEvent["USER_NOTIFICATION_SUBMISSION"] = "USER_NOTIFICATION_SUBMISSION";
622
625
  WebSocketResponseEvent["USER_NOTIFICATION_CONTEST_CLARIFICATION"] = "USER_NOTIFICATION_CONTEST_CLARIFICATION";
623
626
  })(WebSocketResponseEvent || (WebSocketResponseEvent = {}));
627
+ /**
628
+ * Roles of a contest, with the same names as the contest members in the DB.
629
+ * Each role has its own Ably channel: `contest:{contestKey}:{role}` (see `CHANNEL_CONTEST`).
630
+ * The inclusions follow `getContestAccessPermissions`:
631
+ * spectators ⊇ managers ⊇ administrators, spectators ⊇ participants, spectators ⊇ guests
632
+ */
633
+ var ContestChannelRole;
634
+ (function (ContestChannelRole) {
635
+ ContestChannelRole["ADMINISTRATORS"] = "administrators";
636
+ ContestChannelRole["MANAGERS"] = "managers";
637
+ ContestChannelRole["PARTICIPANTS"] = "participants";
638
+ ContestChannelRole["GUESTS"] = "guests";
639
+ ContestChannelRole["SPECTATORS"] = "spectators";
640
+ })(ContestChannelRole || (ContestChannelRole = {}));
624
641
 
625
642
  var DataViewMode;
626
643
  (function (DataViewMode) {
@@ -2221,11 +2238,43 @@ const EMPTY_USER_PERMISSIONS = {
2221
2238
  },
2222
2239
  };
2223
2240
 
2241
+ // LEGACY channels (fan-out per client through the subscriptions registry in Redis)
2224
2242
  const CHANNEL_PUBLISH_SUBSCRIPTIONS = 'pub:subscriptions';
2225
2243
  const CHANNEL_SUBSCRIBE_NOTIFICATIONS = 'sub:notifications';
2226
2244
  const CHANNEL_SUBSCRIBE_CLIENT = (clientId) => `sub:client:${clientId}`;
2227
2245
  const CHANNEL_PUBLISH_MESSAGES = 'pub:messages';
2228
2246
  const CHANNEL_PRESENCE_CLIENT = 'pre:client';
2247
+ // TOPIC channels
2248
+ // The backend publishes each event once on the channel of its topic and Ably does the fan-out.
2249
+ // Who can subscribe to each channel is decided by the API in the Ably token (capability), never by the client.
2250
+ // Dynamic segments are encoded so a key can never inject a `:` (the Ably segment separator used by the wildcards).
2251
+ const segment = (value) => encodeURIComponent(value);
2252
+ /** private: events of one session (code runs, requested data, client track commands) */
2253
+ const CHANNEL_SESSION = (sessionId) => `session:${segment(sessionId)}`;
2254
+ /** private: events of one user (own submissions while the contest is frozen, notifications) */
2255
+ const CHANNEL_USER = (userId) => `user:${segment(userId)}`;
2256
+ /** public: status of a submission when it is visible to everyone */
2257
+ const CHANNEL_SUBMISSION = (submitId) => `submission:${segment(submitId)}`;
2258
+ /** public: changes of a problem */
2259
+ const CHANNEL_PROBLEM = (problemKey) => `problem:${segment(problemKey)}`;
2260
+ /** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
2261
+ const CHANNEL_CONTEST = (contestKey, role) => `contest:${segment(contestKey)}:${role}`;
2262
+ /** system administrators only: data sent by a tracked client (screenshots, location, device) */
2263
+ const CHANNEL_CLIENT_TRACK = (trackedClientId) => `client-track:${segment(trackedClientId)}`;
2264
+ // capability resources (wildcards) of the public channels
2265
+ const CHANNEL_SUBMISSION_ALL = 'submission:*';
2266
+ const CHANNEL_PROBLEM_ALL = 'problem:*';
2267
+ const CHANNEL_CLIENT_TRACK_ALL = 'client-track:*';
2268
+ /**
2269
+ * Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
2270
+ */
2271
+ const getContestChannelParams = (channel) => {
2272
+ const [namespace, contestKey, role, ...rest] = channel.split(':');
2273
+ if (namespace !== 'contest' || !contestKey || rest.length || !Object.values(ContestChannelRole).includes(role)) {
2274
+ return null;
2275
+ }
2276
+ return { contestKey: decodeURIComponent(contestKey), role: role };
2277
+ };
2229
2278
 
2230
2279
  const NEW_PAGE_SHEET = () => ({
2231
2280
  id: v4(),
@@ -3008,6 +3057,16 @@ const isUnsubscribeUserNotificationWebSocketEventDTO = (event) => {
3008
3057
  return isWebsocketSubscription(event)
3009
3058
  && event?.event === WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
3010
3059
  };
3060
+ const isSubscribeClientTrackDataWebSocketEventDTO = (event) => {
3061
+ return isWebsocketSubscription(event)
3062
+ && event?.event === WebSocketSubscriptionEvent.SUBSCRIBE_CLIENT_TRACK_DATA
3063
+ && typeof event?.trackedClientId === 'string' && !!event.trackedClientId;
3064
+ };
3065
+ const isUnsubscribeClientTrackDataWebSocketEventDTO = (event) => {
3066
+ return isWebsocketSubscription(event)
3067
+ && event?.event === WebSocketSubscriptionEvent.UNSUBSCRIBE_CLIENT_TRACK_DATA
3068
+ && typeof event?.trackedClientId === 'string' && !!event.trackedClientId;
3069
+ };
3011
3070
  // is WebSocketResponseEventDTO, CHANNEL_SUBSCRIBE_CLIENT
3012
3071
  const isWebSocketResponseEventDTO = (event) => {
3013
3072
  return Object.values(WebSocketResponseEvent).includes(event?.event)
@@ -3121,6 +3180,73 @@ const isUserNotificationContestClarificationWebSocketResponseEventDTO = (event)
3121
3180
  const getWebSocketResponseEventKey = (event, clientId, id) => {
3122
3181
  return `${event}-${clientId}-${id}`;
3123
3182
  };
3183
+ const isWebSocketChannelsMessageDTO = (message) => {
3184
+ return Array.isArray(message?.channels)
3185
+ && message.channels.every((channel) => typeof channel === 'string' && !!channel)
3186
+ && Object.values(WebSocketResponseEvent).includes(message?.event?.event);
3187
+ };
3188
+ /**
3189
+ * Key used by the client to dispatch a response received on a TOPIC channel to its subscribers.
3190
+ * It is the same key that `getKeyWebSocketEventDTO` (base-ui) computes for the subscription.
3191
+ * Returns null when the response is not addressed to this client.
3192
+ */
3193
+ const getWebSocketResponseEventKeyOfResponse = (response, clientId) => {
3194
+ if (isCodeRunStatusMessageWebSocketResponseEventDTO(response)) {
3195
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.CODE_RUN_STATUS, clientId, response.runId);
3196
+ }
3197
+ if (isSubmissionRunStatusMessageWebSocketResponseEventDTO(response)) {
3198
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.SUBMISSION_RUN_STATUS, clientId, response.submitId);
3199
+ }
3200
+ if (isSendDataWebSocketResponseEventDTO(response)) {
3201
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.SEND_DATA_, clientId, response.dataId);
3202
+ }
3203
+ if (isContestChangesWebSocketResponseEventDTO(response)) {
3204
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.CONTEST_CHANGES, clientId, response.content.contestKey);
3205
+ }
3206
+ if (isProblemCrawledWebSocketResponseEventDTO(response)) {
3207
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.PROBLEM_CRAWLED, clientId, response.content.problemKey);
3208
+ }
3209
+ if (isSubmissionsCrawlWebSocketResponseEventDTO(response)) {
3210
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.SUBMISSIONS_CRAWL, clientId, join([response.content.contestKey, response.content.problemKeys]));
3211
+ }
3212
+ if (isClientTrackWebSocketResponseEventDTO(response)) {
3213
+ // the command is sent to the session channel, `key` holds the targeted client
3214
+ return response.key === clientId ? getWebSocketResponseEventKey(WebSocketResponseEvent.CLIENT_TRACK, clientId, '*') : null;
3215
+ }
3216
+ if (isUserNotificationWebSocketResponseEventDTO(response)) {
3217
+ return getWebSocketResponseEventKey(WebSocketResponseEvent.USER_NOTIFICATION_, clientId, '*');
3218
+ }
3219
+ return null;
3220
+ };
3221
+ /**
3222
+ * TOPIC channel that the client has to attach to receive the events of a subscription,
3223
+ * or null when the events arrive by the private channels (session / user) the client is always attached to.
3224
+ * Ably rejects the attach if the token has no capability for the channel.
3225
+ */
3226
+ const getChannelOfWebSocketSubscribeEvent = (event) => {
3227
+ if (isSubscribeSubmissionRunStatusWebSocketEventDTO(event)) {
3228
+ return CHANNEL_SUBMISSION(event.submitId);
3229
+ }
3230
+ if (isSubscribeProblemCrawledWebSocketEventDTO(event)) {
3231
+ return CHANNEL_PROBLEM(event.problemKey);
3232
+ }
3233
+ if (isSubscribeContestChangesWebSocketEventDTO(event) || isSubscribeSubmissionsCrawlWebSocketEventDTO(event)) {
3234
+ return CHANNEL_CONTEST(event.contestKey, ContestChannelRole.SPECTATORS);
3235
+ }
3236
+ if (isSubscribeClientTrackDataWebSocketEventDTO(event)) {
3237
+ return CHANNEL_CLIENT_TRACK(event.trackedClientId);
3238
+ }
3239
+ return null;
3240
+ };
3241
+ /**
3242
+ * Contest whose role channels the client needs (the API grants them when the token is requested with this contest).
3243
+ */
3244
+ const getContestKeyOfWebSocketSubscribeEvent = (event) => {
3245
+ if (isSubscribeContestChangesWebSocketEventDTO(event) || isSubscribeSubmissionsCrawlWebSocketEventDTO(event)) {
3246
+ return event.contestKey;
3247
+ }
3248
+ return null;
3249
+ };
3124
3250
 
3125
3251
  function splitTime(timeRemaining) {
3126
3252
  const remaining = [];
@@ -3344,4 +3470,4 @@ const isQuizTextSheet = (value) => {
3344
3470
  && ['text', 'number', 'textarea'].includes(value?.inputType);
3345
3471
  };
3346
3472
 
3347
- export { ACCEPTED_PROGRAMMING_LANGUAGES, ASPECT_RATIO, AspectRatio, AssignmentClass, AssignmentState, AssignmentType, CHANNEL_PRESENCE_CLIENT, CHANNEL_PUBLISH_MESSAGES, CHANNEL_PUBLISH_SUBSCRIPTIONS, CHANNEL_SUBSCRIBE_CLIENT, CHANNEL_SUBSCRIBE_NOTIFICATIONS, CODE_LANGUAGE, COMPANY_PLAN, CONTEST_ROLE, CONTEST_STATUS, COOKIE_JUKI_SESSION_ID, COURSE_ROLE, CodeLanguage, CollectionKey, CompanyLogoType, CompanyPlan, ContestEventAction, ContestProblemBlockedByType, ContestProblemPrerequisiteType, ContestStatus, ContestTimeStatus, CourseRole, CourseStatus, CourseType, DAY_NAMES, DEFAULT_JUKI_PROFILE_IMAGE, DataViewMode, EMPTY_DOCUMENT_MEMBERS, EMPTY_ENTITY_MEMBERS, EMPTY_USER_PERMISSIONS, ENTITY_ACCESS, ENTITY_STATE, ERROR, EmailSubscription, EmailValidationState, EntityAccess, EntityCollection, EntityMembersRank, EntityState, EntityStatus, ErrorCode, FILE_ROLE, FILE_TYPE, FileMemberRole, FileType, FilesJukiPub, GroupType, HEADER_JUKI_FORWARDED_HOST, HEADER_JUKI_INTERNAL_API_KEY, HEADER_JUKI_METADATA, HEADER_JUKI_SECRET_TOKEN, HEADER_JUKI_VISITOR_SESSION_ID, HOURS, HTTPMethod, JUDGE, JUKI_APP_COMPANY_KEY, JUKI_APP_EMAIL_TEMPLATE, JkError, Judge, LANGUAGE, LOWERCASE_LETTERS, Language, LogLevel, LogOperation, MAX_DATE, MEMBER_ROLE, MILLISECONDS, MINUTES, MIN_DATE, MONTH_NAMES, MemberRole, MemberType, MenuViewMode, NEW_PAGE_SHEET, NUMBERS, NotificationType, ONE_DAY, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_WEEK, PAGE, PAGE_SIZE, PAGE_SIZES, PALLETE, PROBLEM_INPUT, PROBLEM_MODE, PROBLEM_MODES, PROBLEM_OUTPUT, PROBLEM_ROLE, PROBLEM_TYPE, PROBLEM_TYPES, PROBLEM_VERDICT, PROBLEM_VERDICTS, ProblemInput, ProblemOutput, ProblemScoringMode, ProblemType, ProblemVerdict, ProfileSetting, QuizScoringMode, RUNNER_ACCEPTED_PROBLEM_MODES, RUNNER_ACCEPTED_PROBLEM_TYPES, RUNNER_ACCEPTED_PROGRAMMING_LANGUAGES, RUNNER_TYPE, Role, RunnerType, SECONDS, SEC_120, SEC_30, SEC_60, SEC_90, SEPARATOR_TOKEN, SUBMISSION_RUN_STATUS, SUPPORT_EMAIL, SYSTEM_ROLE, ScopeData, Status, SubmissionRunStatus, SystemRole, TEAM_ROLE, THEME, TeamRole, TeamStatus, Theme, TypeTestCase, UPPERCASE_LETTERS, USER_ROLE, USER_STATUS, UserRole, UserStatus, UserTeamStatus, WebSocketMessageEvent, WebSocketResponseEvent, WebSocketSubscriptionEvent, WorkingIn, WorksheetType, YEARS, _KEY, _SUB_SUB_TAB, _SUB_TAB, _TAB, _TYPE, areArraysDifferent, chunkString, cleanRequest, clearAsyncInterval, consoleError, consoleInfo, consoleWarn, contentResponse, contentsResponse, errorsResponse, getAssignmentCommentKey, getAssignmentUserCommentKey, getClientId, getContestCommentKey, getDataLog, getDataOfTestCase, getDays, getDefaultMeta, getDocumentAccess, getHours, getMilliseconds, getMinutes, getMonths, getParamsOfClientId, getParamsOfUserKey, getPlainText, getProblemCommentKey, getProblemJudgeKey, getRandomString, getSeconds, getSummaryWorksheetsInPages, getTotalExercisesOfSummaryWorksheetsInPages, getUserKey, getVerdictFromTestCase, getWebSocketResponseEventKey, getWorksheetsInPages, getYears, humanFileSize, indexToLetters, isBasicWorksheet, isChatCompletionsWebSocketEventDTO, isClientTrackDeviceWebSocketEventDTO, isClientTrackLocationWebSocketEventDTO, isClientTrackScreenshotWebSocketEventDTO, isClientTrackWebSocketResponseEventDTO, isCodeEditorSheet, isCodeRunStatusMessageWebSocketResponseEventDTO, isContestAssignment, isContestChangesWebSocketResponseEventDTO, isCourseAssignment, isEndlessContest, isFutureContest, isGlobalContest, isJkmdSheet, isObjectJson, isPastContest, isPingWebSocketEventDTO, isPongWebSocketResponseEventDTO, isProblemCrawledWebSocketResponseEventDTO, isQuizOptionsSheet, isQuizProblemSheet, isQuizTextSheet, isSenDataChatCompletionsWebSocketResponseEventDTO, isSendDataClientTrackWebSocketResponseEventDTO, isSendDataEc2InstancesListWebSocketResponseEventDTO, isSendDataEcsTaskDefinitionListWebSocketResponseEventDTO, isSendDataEcsTasksListWebSocketResponseEventDTO, isSendDataRunCommandWebSocketResponseEventDTO, isSendDataSsmSessionsListWebSocketResponseEventDTO, isSendDataWebSocketResponseEventDTO, isStringJson, isSubmissionRunStatusMessageWebSocketResponseEventDTO, isSubmissionsCrawlWebSocketResponseEventDTO, isSubscribeClientTrackWebSocketEventDTO, isSubscribeCodeRunStatusWebSocketEventDTO, isSubscribeContestChangesWebSocketEventDTO, isSubscribeGetDataWebSocketEventDTO, isSubscribeProblemCrawledWebSocketEventDTO, isSubscribeSubmissionRunStatusWebSocketEventDTO, isSubscribeSubmissionsCrawlWebSocketEventDTO, isSubscribeUserNotificationWebSocketEventDTO, isTeamMember, isUnsubscribeClientTrackWebSocketEventDTO, isUnsubscribeCodeRunStatusWebSocketEventDTO, isUnsubscribeContestChangesWebSocketEventDTO, isUnsubscribeGetDataWebSocketEventDTO, isUnsubscribeProblemCrawledWebSocketEventDTO, isUnsubscribeSubmissionRunStatusWebSocketEventDTO, isUnsubscribeSubmissionsCrawlWebSocketEventDTO, isUnsubscribeUserNotificationWebSocketEventDTO, isUserMember, isUserMessageWebSocketResponseEventDTO, isUserNotificationContestClarificationWebSocketResponseEventDTO, isUserNotificationSubmissionWebSocketResponseEventDTO, isUserNotificationWebSocketResponseEventDTO, isWebSocketResponseEventDTO, isWebsocketSubscription, isWorksheetAssignment, join, lettersToIndex, memorySizeOf, mergeVerdicts, mex, normalizeToLocalStartOfDay, normalizeToLocalStartOfHour, normalizeToLocalStartOfMinutes, normalizeToLocalStartOfSeconds, objectUpdate, objectsUpdate, range, removeExtension, safeJsonParse, setAsyncInterval, split, splitTime, stringToArrayBuffer, toEntityMembersDTO, toJkError };
3473
+ export { ACCEPTED_PROGRAMMING_LANGUAGES, ASPECT_RATIO, AspectRatio, AssignmentClass, AssignmentState, AssignmentType, CHANNEL_CLIENT_TRACK, CHANNEL_CLIENT_TRACK_ALL, CHANNEL_CONTEST, CHANNEL_PRESENCE_CLIENT, CHANNEL_PROBLEM, CHANNEL_PROBLEM_ALL, CHANNEL_PUBLISH_MESSAGES, CHANNEL_PUBLISH_SUBSCRIPTIONS, CHANNEL_SESSION, CHANNEL_SUBMISSION, CHANNEL_SUBMISSION_ALL, CHANNEL_SUBSCRIBE_CLIENT, CHANNEL_SUBSCRIBE_NOTIFICATIONS, CHANNEL_USER, CODE_LANGUAGE, COMPANY_PLAN, CONTEST_ROLE, CONTEST_STATUS, COOKIE_JUKI_SESSION_ID, COURSE_ROLE, CodeLanguage, CollectionKey, CompanyLogoType, CompanyPlan, ContestChannelRole, ContestEventAction, ContestProblemBlockedByType, ContestProblemPrerequisiteType, ContestStatus, ContestTimeStatus, CourseRole, CourseStatus, CourseType, DAY_NAMES, DEFAULT_JUKI_PROFILE_IMAGE, DataViewMode, EMPTY_DOCUMENT_MEMBERS, EMPTY_ENTITY_MEMBERS, EMPTY_USER_PERMISSIONS, ENTITY_ACCESS, ENTITY_STATE, ERROR, EmailSubscription, EmailValidationState, EntityAccess, EntityCollection, EntityMembersRank, EntityState, EntityStatus, ErrorCode, FILE_ROLE, FILE_TYPE, FileMemberRole, FileType, FilesJukiPub, GroupType, HEADER_JUKI_FORWARDED_HOST, HEADER_JUKI_INTERNAL_API_KEY, HEADER_JUKI_METADATA, HEADER_JUKI_SECRET_TOKEN, HEADER_JUKI_VISITOR_SESSION_ID, HOURS, HTTPMethod, JUDGE, JUKI_APP_COMPANY_KEY, JUKI_APP_EMAIL_TEMPLATE, JkError, Judge, LANGUAGE, LOWERCASE_LETTERS, Language, LogLevel, LogOperation, MAX_DATE, MEMBER_ROLE, MILLISECONDS, MINUTES, MIN_DATE, MONTH_NAMES, MemberRole, MemberType, MenuViewMode, NEW_PAGE_SHEET, NUMBERS, NotificationType, ONE_DAY, ONE_HOUR, ONE_MINUTE, ONE_SECOND, ONE_WEEK, PAGE, PAGE_SIZE, PAGE_SIZES, PALLETE, PROBLEM_INPUT, PROBLEM_MODE, PROBLEM_MODES, PROBLEM_OUTPUT, PROBLEM_ROLE, PROBLEM_TYPE, PROBLEM_TYPES, PROBLEM_VERDICT, PROBLEM_VERDICTS, ProblemInput, ProblemOutput, ProblemScoringMode, ProblemType, ProblemVerdict, ProfileSetting, QuizScoringMode, RUNNER_ACCEPTED_PROBLEM_MODES, RUNNER_ACCEPTED_PROBLEM_TYPES, RUNNER_ACCEPTED_PROGRAMMING_LANGUAGES, RUNNER_TYPE, Role, RunnerType, SECONDS, SEC_120, SEC_30, SEC_60, SEC_90, SEPARATOR_TOKEN, SUBMISSION_RUN_STATUS, SUPPORT_EMAIL, SYSTEM_ROLE, ScopeData, Status, SubmissionRunStatus, SystemRole, TEAM_ROLE, THEME, TeamRole, TeamStatus, Theme, TypeTestCase, UPPERCASE_LETTERS, USER_ROLE, USER_STATUS, UserRole, UserStatus, UserTeamStatus, WebSocketMessageEvent, WebSocketResponseEvent, WebSocketSubscriptionEvent, WorkingIn, WorksheetType, YEARS, _KEY, _SUB_SUB_TAB, _SUB_TAB, _TAB, _TYPE, areArraysDifferent, chunkString, cleanRequest, clearAsyncInterval, consoleError, consoleInfo, consoleWarn, contentResponse, contentsResponse, errorsResponse, getAssignmentCommentKey, getAssignmentUserCommentKey, getChannelOfWebSocketSubscribeEvent, getClientId, getContestChannelParams, getContestCommentKey, getContestKeyOfWebSocketSubscribeEvent, getDataLog, getDataOfTestCase, getDays, getDefaultMeta, getDocumentAccess, getHours, getMilliseconds, getMinutes, getMonths, getParamsOfClientId, getParamsOfUserKey, getPlainText, getProblemCommentKey, getProblemJudgeKey, getRandomString, getSeconds, getSummaryWorksheetsInPages, getTotalExercisesOfSummaryWorksheetsInPages, getUserKey, getVerdictFromTestCase, getWebSocketResponseEventKey, getWebSocketResponseEventKeyOfResponse, getWorksheetsInPages, getYears, humanFileSize, indexToLetters, isBasicWorksheet, isChatCompletionsWebSocketEventDTO, isClientTrackDeviceWebSocketEventDTO, isClientTrackLocationWebSocketEventDTO, isClientTrackScreenshotWebSocketEventDTO, isClientTrackWebSocketResponseEventDTO, isCodeEditorSheet, isCodeRunStatusMessageWebSocketResponseEventDTO, isContestAssignment, isContestChangesWebSocketResponseEventDTO, isCourseAssignment, isEndlessContest, isFutureContest, isGlobalContest, isJkmdSheet, isObjectJson, isPastContest, isPingWebSocketEventDTO, isPongWebSocketResponseEventDTO, isProblemCrawledWebSocketResponseEventDTO, isQuizOptionsSheet, isQuizProblemSheet, isQuizTextSheet, isSenDataChatCompletionsWebSocketResponseEventDTO, isSendDataClientTrackWebSocketResponseEventDTO, isSendDataEc2InstancesListWebSocketResponseEventDTO, isSendDataEcsTaskDefinitionListWebSocketResponseEventDTO, isSendDataEcsTasksListWebSocketResponseEventDTO, isSendDataRunCommandWebSocketResponseEventDTO, isSendDataSsmSessionsListWebSocketResponseEventDTO, isSendDataWebSocketResponseEventDTO, isStringJson, isSubmissionRunStatusMessageWebSocketResponseEventDTO, isSubmissionsCrawlWebSocketResponseEventDTO, isSubscribeClientTrackDataWebSocketEventDTO, isSubscribeClientTrackWebSocketEventDTO, isSubscribeCodeRunStatusWebSocketEventDTO, isSubscribeContestChangesWebSocketEventDTO, isSubscribeGetDataWebSocketEventDTO, isSubscribeProblemCrawledWebSocketEventDTO, isSubscribeSubmissionRunStatusWebSocketEventDTO, isSubscribeSubmissionsCrawlWebSocketEventDTO, isSubscribeUserNotificationWebSocketEventDTO, isTeamMember, isUnsubscribeClientTrackDataWebSocketEventDTO, isUnsubscribeClientTrackWebSocketEventDTO, isUnsubscribeCodeRunStatusWebSocketEventDTO, isUnsubscribeContestChangesWebSocketEventDTO, isUnsubscribeGetDataWebSocketEventDTO, isUnsubscribeProblemCrawledWebSocketEventDTO, isUnsubscribeSubmissionRunStatusWebSocketEventDTO, isUnsubscribeSubmissionsCrawlWebSocketEventDTO, isUnsubscribeUserNotificationWebSocketEventDTO, isUserMember, isUserMessageWebSocketResponseEventDTO, isUserNotificationContestClarificationWebSocketResponseEventDTO, isUserNotificationSubmissionWebSocketResponseEventDTO, isUserNotificationWebSocketResponseEventDTO, isWebSocketChannelsMessageDTO, isWebSocketResponseEventDTO, isWebsocketSubscription, isWorksheetAssignment, join, lettersToIndex, memorySizeOf, mergeVerdicts, mex, normalizeToLocalStartOfDay, normalizeToLocalStartOfHour, normalizeToLocalStartOfMinutes, normalizeToLocalStartOfSeconds, objectUpdate, objectsUpdate, range, removeExtension, safeJsonParse, setAsyncInterval, split, splitTime, stringToArrayBuffer, toEntityMembersDTO, toJkError };
@@ -1,6 +1,28 @@
1
- import { ClientId } from '../types';
1
+ import { ClientId, ContestChannelRole } from '../types';
2
2
  export declare const CHANNEL_PUBLISH_SUBSCRIPTIONS = "pub:subscriptions";
3
3
  export declare const CHANNEL_SUBSCRIBE_NOTIFICATIONS = "sub:notifications";
4
4
  export declare const CHANNEL_SUBSCRIBE_CLIENT: (clientId: ClientId) => string;
5
5
  export declare const CHANNEL_PUBLISH_MESSAGES = "pub:messages";
6
6
  export declare const CHANNEL_PRESENCE_CLIENT = "pre:client";
7
+ /** private: events of one session (code runs, requested data, client track commands) */
8
+ export declare const CHANNEL_SESSION: (sessionId: string) => string;
9
+ /** private: events of one user (own submissions while the contest is frozen, notifications) */
10
+ export declare const CHANNEL_USER: (userId: string) => string;
11
+ /** public: status of a submission when it is visible to everyone */
12
+ export declare const CHANNEL_SUBMISSION: (submitId: string) => string;
13
+ /** public: changes of a problem */
14
+ export declare const CHANNEL_PROBLEM: (problemKey: string) => string;
15
+ /** per role: events of a contest for one role (and the roles above it, see `ContestChannelRole`) */
16
+ export declare const CHANNEL_CONTEST: (contestKey: string, role: ContestChannelRole) => string;
17
+ /** system administrators only: data sent by a tracked client (screenshots, location, device) */
18
+ export declare const CHANNEL_CLIENT_TRACK: (trackedClientId: ClientId) => string;
19
+ export declare const CHANNEL_SUBMISSION_ALL = "submission:*";
20
+ export declare const CHANNEL_PROBLEM_ALL = "problem:*";
21
+ export declare const CHANNEL_CLIENT_TRACK_ALL = "client-track:*";
22
+ /**
23
+ * Returns the contest and role of a `CHANNEL_CONTEST` channel name, or null if it is not one.
24
+ */
25
+ export declare const getContestChannelParams: (channel: string) => {
26
+ contestKey: string;
27
+ role: ContestChannelRole;
28
+ } | null;
@@ -94,8 +94,16 @@ export interface UnsubscribeUserNotificationWebSocketEventDTO extends WebsocketS
94
94
  event: WebSocketSubscriptionEvent.UNSUBSCRIBE_USER_NOTIFICATION;
95
95
  userKey: string;
96
96
  }
97
- export type WebSocketSubscribeEventDTO = SubscribeCodeRunStatusWebSocketEventDTO | SubscribeSubmissionRunStatusWebSocketEventDTO | SubscribeGetDataWebSocketEventDTO | SubscribeProblemCrawledWebSocketEventDTO | SubscribeSubmissionsCrawlWebSocketEventDTO | SubscribeContestChangesWebSocketEventDTO | SubscribeClientTrackWebSocketEventDTO | SubscribeUserNotificationWebsocketEventDTO;
98
- export type WebSocketUnsubscribeEventDTO = UnsubscribeCodeRunStatusWebSocketEventDTO | UnsubscribeSubmissionRunStatusWebSocketEventDTO | UnsubscribeGetDataWebSocketEventDTO | UnsubscribeProblemCrawledWebSocketEventDTO | UnsubscribeSubmissionsCrawlWebSocketEventDTO | UnsubscribeContestChangesWebSocketEventDTO | UnsubscribeClientTrackWebSocketEventDTO | UnsubscribeUserNotificationWebSocketEventDTO;
97
+ export interface SubscribeClientTrackDataWebSocketEventDTO extends WebsocketSubscription {
98
+ event: WebSocketSubscriptionEvent.SUBSCRIBE_CLIENT_TRACK_DATA;
99
+ trackedClientId: ClientId;
100
+ }
101
+ export interface UnsubscribeClientTrackDataWebSocketEventDTO extends WebsocketSubscription {
102
+ event: WebSocketSubscriptionEvent.UNSUBSCRIBE_CLIENT_TRACK_DATA;
103
+ trackedClientId: ClientId;
104
+ }
105
+ export type WebSocketSubscribeEventDTO = SubscribeCodeRunStatusWebSocketEventDTO | SubscribeSubmissionRunStatusWebSocketEventDTO | SubscribeGetDataWebSocketEventDTO | SubscribeProblemCrawledWebSocketEventDTO | SubscribeSubmissionsCrawlWebSocketEventDTO | SubscribeContestChangesWebSocketEventDTO | SubscribeClientTrackWebSocketEventDTO | SubscribeUserNotificationWebsocketEventDTO | SubscribeClientTrackDataWebSocketEventDTO;
106
+ export type WebSocketUnsubscribeEventDTO = UnsubscribeCodeRunStatusWebSocketEventDTO | UnsubscribeSubmissionRunStatusWebSocketEventDTO | UnsubscribeGetDataWebSocketEventDTO | UnsubscribeProblemCrawledWebSocketEventDTO | UnsubscribeSubmissionsCrawlWebSocketEventDTO | UnsubscribeContestChangesWebSocketEventDTO | UnsubscribeClientTrackWebSocketEventDTO | UnsubscribeUserNotificationWebSocketEventDTO | UnsubscribeClientTrackDataWebSocketEventDTO;
99
107
  export interface WebSocketResponse {
100
108
  event: WebSocketResponseEvent;
101
109
  key: WebSocketResponseEventKey;
@@ -253,4 +261,12 @@ export interface UserNotificationContestClarificationWebSocketResponseEventDTO e
253
261
  };
254
262
  }
255
263
  export type WebSocketResponseEventDTO = PongWebSocketResponseEventDTO | CodeRunStatusWebSocketResponseEventDTO | SubmissionRunStatusWebSocketResponseEventDTO | UserMessageWebSocketResponseEventDTO | SendDataEcsTaskDefinitionListWebSocketResponseEventDTO | SendDataEcsTasksListWebSocketResponseEventDTO | SendDataEc2InstancesListWebSocketResponseEventDTO | SendDataSsmSessionsListWebSocketResponseEventDTO | ProblemCrawledWebSocketResponseEventDTO | SendDataChatCompletionsWebSocketResponseEventDTO | SubmissionsCrawlWebSocketResponseEventDTO | ContestChangesWebSocketResponseEventDTO | SendDataRunCommandWebSocketResponseEventDTO | SendDataClientTrackWebSocketResponseEventDTO | ClientTrackWebSocketResponseEventDTO | UserNotificationSubmissionWebSocketResponseEventDTO | UserNotificationContestClarificationWebSocketResponseEventDTO;
264
+ /**
265
+ * Message sent by the backend producers to the websocket SQS queue:
266
+ * the producer (trusted, with access to the DB) decides the Ably channels where the event is published.
267
+ */
268
+ export interface WebSocketChannelsMessageDTO {
269
+ channels: string[];
270
+ event: WebSocketResponseEventDTO;
271
+ }
256
272
  export {};
@@ -1,4 +1,4 @@
1
- import { ChatCompletionsWebSocketEventDTO, ClientTrackDeviceWebSocketEventDTO, ClientTrackLocationWebSocketEventDTO, ClientTrackScreenshotWebSocketEventDTO, ClientTrackWebSocketResponseEventDTO, CodeRunStatusWebSocketResponseEventDTO, ContestChangesWebSocketResponseEventDTO, PingWebSocketEventDTO, PongWebSocketResponseEventDTO, ProblemCrawledWebSocketResponseEventDTO, SendDataChatCompletionsWebSocketResponseEventDTO, SendDataClientTrackWebSocketResponseEventDTO, SendDataEc2InstancesListWebSocketResponseEventDTO, SendDataEcsTaskDefinitionListWebSocketResponseEventDTO, SendDataEcsTasksListWebSocketResponseEventDTO, SendDataRunCommandWebSocketResponseEventDTO, SendDataSsmSessionsListWebSocketResponseEventDTO, SendDataWebSocketResponseEventDTO, SubmissionRunStatusWebSocketResponseEventDTO, SubmissionsCrawlWebSocketResponseEventDTO, SubscribeClientTrackWebSocketEventDTO, SubscribeCodeRunStatusWebSocketEventDTO, SubscribeContestChangesWebSocketEventDTO, SubscribeGetDataWebSocketEventDTO, SubscribeProblemCrawledWebSocketEventDTO, SubscribeSubmissionRunStatusWebSocketEventDTO, SubscribeSubmissionsCrawlWebSocketEventDTO, SubscribeUserNotificationWebsocketEventDTO, UnsubscribeClientTrackWebSocketEventDTO, UnsubscribeCodeRunStatusWebSocketEventDTO, UnsubscribeContestChangesWebSocketEventDTO, UnsubscribeGetDataWebSocketEventDTO, UnsubscribeProblemCrawledWebSocketEventDTO, UnsubscribeSubmissionRunStatusWebSocketEventDTO, UnsubscribeSubmissionsCrawlWebSocketEventDTO, UnsubscribeUserNotificationWebSocketEventDTO, UserMessageWebSocketResponseEventDTO, UserNotificationContestClarificationWebSocketResponseEventDTO, UserNotificationSubmissionWebSocketResponseEventDTO, UserNotificationWebSocketResponseEventDTO, WebSocketMessageEventDTO, WebSocketResponseEventDTO, WebSocketSubscribeEventDTO, WebSocketUnsubscribeEventDTO } from '../dto';
1
+ import { ChatCompletionsWebSocketEventDTO, ClientTrackDeviceWebSocketEventDTO, ClientTrackLocationWebSocketEventDTO, ClientTrackScreenshotWebSocketEventDTO, ClientTrackWebSocketResponseEventDTO, CodeRunStatusWebSocketResponseEventDTO, ContestChangesWebSocketResponseEventDTO, PingWebSocketEventDTO, PongWebSocketResponseEventDTO, ProblemCrawledWebSocketResponseEventDTO, SendDataChatCompletionsWebSocketResponseEventDTO, SendDataClientTrackWebSocketResponseEventDTO, SendDataEc2InstancesListWebSocketResponseEventDTO, SendDataEcsTaskDefinitionListWebSocketResponseEventDTO, SendDataEcsTasksListWebSocketResponseEventDTO, SendDataRunCommandWebSocketResponseEventDTO, SendDataSsmSessionsListWebSocketResponseEventDTO, SendDataWebSocketResponseEventDTO, SubmissionRunStatusWebSocketResponseEventDTO, SubmissionsCrawlWebSocketResponseEventDTO, SubscribeClientTrackDataWebSocketEventDTO, SubscribeClientTrackWebSocketEventDTO, SubscribeCodeRunStatusWebSocketEventDTO, SubscribeContestChangesWebSocketEventDTO, SubscribeGetDataWebSocketEventDTO, SubscribeProblemCrawledWebSocketEventDTO, SubscribeSubmissionRunStatusWebSocketEventDTO, SubscribeSubmissionsCrawlWebSocketEventDTO, SubscribeUserNotificationWebsocketEventDTO, UnsubscribeClientTrackDataWebSocketEventDTO, UnsubscribeClientTrackWebSocketEventDTO, UnsubscribeCodeRunStatusWebSocketEventDTO, UnsubscribeContestChangesWebSocketEventDTO, UnsubscribeGetDataWebSocketEventDTO, UnsubscribeProblemCrawledWebSocketEventDTO, UnsubscribeSubmissionRunStatusWebSocketEventDTO, UnsubscribeSubmissionsCrawlWebSocketEventDTO, UnsubscribeUserNotificationWebSocketEventDTO, UserMessageWebSocketResponseEventDTO, UserNotificationContestClarificationWebSocketResponseEventDTO, UserNotificationSubmissionWebSocketResponseEventDTO, UserNotificationWebSocketResponseEventDTO, WebSocketChannelsMessageDTO, WebSocketMessageEventDTO, WebSocketResponseEventDTO, WebSocketSubscribeEventDTO, WebSocketUnsubscribeEventDTO } from '../dto';
2
2
  import { ClientId, WebSocketResponseEvent, WebSocketResponseEventKey } from '../types';
3
3
  export declare const isWebsocketSubscription: (event: any) => event is WebSocketSubscribeEventDTO | WebSocketUnsubscribeEventDTO | WebSocketMessageEventDTO;
4
4
  export declare const isPingWebSocketEventDTO: (event: any) => event is PingWebSocketEventDTO;
@@ -22,6 +22,8 @@ export declare const isSubscribeClientTrackWebSocketEventDTO: (event: any) => ev
22
22
  export declare const isUnsubscribeClientTrackWebSocketEventDTO: (event: any) => event is UnsubscribeClientTrackWebSocketEventDTO;
23
23
  export declare const isSubscribeUserNotificationWebSocketEventDTO: (event: any) => event is SubscribeUserNotificationWebsocketEventDTO;
24
24
  export declare const isUnsubscribeUserNotificationWebSocketEventDTO: (event: any) => event is UnsubscribeUserNotificationWebSocketEventDTO;
25
+ export declare const isSubscribeClientTrackDataWebSocketEventDTO: (event: any) => event is SubscribeClientTrackDataWebSocketEventDTO;
26
+ export declare const isUnsubscribeClientTrackDataWebSocketEventDTO: (event: any) => event is UnsubscribeClientTrackDataWebSocketEventDTO;
25
27
  export declare const isWebSocketResponseEventDTO: (event: any) => event is WebSocketResponseEventDTO;
26
28
  export declare const isPongWebSocketResponseEventDTO: (event: any) => event is PongWebSocketResponseEventDTO;
27
29
  export declare const isCodeRunStatusMessageWebSocketResponseEventDTO: (event: any) => event is CodeRunStatusWebSocketResponseEventDTO;
@@ -43,3 +45,20 @@ export declare const isUserNotificationWebSocketResponseEventDTO: (event: any) =
43
45
  export declare const isUserNotificationSubmissionWebSocketResponseEventDTO: (event: any) => event is UserNotificationSubmissionWebSocketResponseEventDTO;
44
46
  export declare const isUserNotificationContestClarificationWebSocketResponseEventDTO: (event: any) => event is UserNotificationContestClarificationWebSocketResponseEventDTO;
45
47
  export declare const getWebSocketResponseEventKey: (event: WebSocketResponseEvent, clientId: ClientId, id: string) => WebSocketResponseEventKey;
48
+ export declare const isWebSocketChannelsMessageDTO: (message: any) => message is WebSocketChannelsMessageDTO;
49
+ /**
50
+ * Key used by the client to dispatch a response received on a TOPIC channel to its subscribers.
51
+ * It is the same key that `getKeyWebSocketEventDTO` (base-ui) computes for the subscription.
52
+ * Returns null when the response is not addressed to this client.
53
+ */
54
+ export declare const getWebSocketResponseEventKeyOfResponse: (response: WebSocketResponseEventDTO, clientId: ClientId) => WebSocketResponseEventKey | null;
55
+ /**
56
+ * TOPIC channel that the client has to attach to receive the events of a subscription,
57
+ * or null when the events arrive by the private channels (session / user) the client is always attached to.
58
+ * Ably rejects the attach if the token has no capability for the channel.
59
+ */
60
+ export declare const getChannelOfWebSocketSubscribeEvent: (event: WebSocketSubscribeEventDTO) => string | null;
61
+ /**
62
+ * Contest whose role channels the client needs (the API grants them when the token is requested with this contest).
63
+ */
64
+ export declare const getContestKeyOfWebSocketSubscribeEvent: (event: WebSocketSubscribeEventDTO) => string | null;
@@ -22,7 +22,9 @@ export declare enum WebSocketSubscriptionEvent {
22
22
  SUBSCRIBE_CLIENT_TRACK = "SUBSCRIBE_CLIENT_TRACK",
23
23
  UNSUBSCRIBE_CLIENT_TRACK = "UNSUBSCRIBE_CLIENT_TRACK",
24
24
  SUBSCRIBE_USER_NOTIFICATION = "SUBSCRIBE_USER_NOTIFICATION",
25
- UNSUBSCRIBE_USER_NOTIFICATION = "UNSUBSCRIBE_USER_NOTIFICATION"
25
+ UNSUBSCRIBE_USER_NOTIFICATION = "UNSUBSCRIBE_USER_NOTIFICATION",
26
+ SUBSCRIBE_CLIENT_TRACK_DATA = "SUBSCRIBE_CLIENT_TRACK_DATA",
27
+ UNSUBSCRIBE_CLIENT_TRACK_DATA = "UNSUBSCRIBE_CLIENT_TRACK_DATA"
26
28
  }
27
29
  export declare enum WebSocketResponseEvent {
28
30
  PONG = "PONG",
@@ -47,3 +49,16 @@ export declare enum WebSocketResponseEvent {
47
49
  }
48
50
  export type ClientId = `${RecordId}|${string}`;
49
51
  export type WebSocketResponseEventKey = `${WebSocketResponseEvent}-${ClientId}-${string}`;
52
+ /**
53
+ * Roles of a contest, with the same names as the contest members in the DB.
54
+ * Each role has its own Ably channel: `contest:{contestKey}:{role}` (see `CHANNEL_CONTEST`).
55
+ * The inclusions follow `getContestAccessPermissions`:
56
+ * spectators ⊇ managers ⊇ administrators, spectators ⊇ participants, spectators ⊇ guests
57
+ */
58
+ export declare enum ContestChannelRole {
59
+ ADMINISTRATORS = "administrators",
60
+ MANAGERS = "managers",
61
+ PARTICIPANTS = "participants",
62
+ GUESTS = "guests",
63
+ SPECTATORS = "spectators"
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juki-team/commons",
3
- "version": "0.1.41",
3
+ "version": "0.1.42",
4
4
  "author": "juki-team",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "rollup": "^4.57.1",
31
31
  "rollup-plugin-typescript2": "^0.36.0",
32
32
  "typedoc": "^0.28.16",
33
- "typescript": "^5.9.3"
33
+ "typescript": "^6.0.3"
34
34
  },
35
35
  "dependencies": {
36
36
  "uuid": "^13.0.0 || ^14.0.0"
@@ -40,6 +40,6 @@
40
40
  "watch": "rollup -cw",
41
41
  "build": "rm -rf docs && rm -rf dist && rollup -c && pnpm run docs",
42
42
  "push-commit": "f() { git add -A && git commit -m \"${1:-chore}\" && pnpm push; }; f",
43
- "push": "f() { npm version patch --no-git-tag-version && pnpm build && pnpm publish --tag release-0.1 --no-git-checks && version=\"v$(node -p \"require('./package.json').version\")\" && git add -A && git commit -m $version && git tag -a $version -m $version && git push origin v0.1.x --follow-tags; }; f"
43
+ "push": "f() { pnpm version patch --no-git-tag-version --no-git-checks && pnpm build && pnpm publish --tag release-0.1 --no-git-checks && version=\"v$(node -p \"require('./package.json').version\")\" && git add -A && git commit -m $version && git tag -a $version -m $version && git push origin v0.1.x --follow-tags; }; f"
44
44
  }
45
45
  }