@epic-web/workshop-app 5.4.1 → 5.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.
Files changed (145) hide show
  1. package/build/client/assets/{_-BrkpfnBb.js → _-hAKYb2AG.js} +2 -2
  2. package/build/client/assets/{_-BrkpfnBb.js.map → _-hAKYb2AG.js.map} +1 -1
  3. package/build/client/assets/_exerciseNumber-PTdG9GGB.js +2 -0
  4. package/build/client/assets/_exerciseNumber-PTdG9GGB.js.map +1 -0
  5. package/build/client/assets/{_exerciseNumber_._stepNumber-BIMJh_sg.js → _exerciseNumber_._stepNumber-03erOIGo.js} +2 -2
  6. package/build/client/assets/{_exerciseNumber_._stepNumber-BIMJh_sg.js.map → _exerciseNumber_._stepNumber-03erOIGo.js.map} +1 -1
  7. package/build/client/assets/_exerciseNumber_.finished-DQg4F1NL.js +2 -0
  8. package/build/client/assets/_exerciseNumber_.finished-DQg4F1NL.js.map +1 -0
  9. package/build/client/assets/{_layout-88n0To1b.js → _layout-B4JGpA3A.js} +2 -2
  10. package/build/client/assets/{_layout-88n0To1b.js.map → _layout-B4JGpA3A.js.map} +1 -1
  11. package/build/client/assets/{_layout-Dfmv2zcn.js → _layout-BJbMl6SJ.js} +2 -2
  12. package/build/client/assets/{_layout-Dfmv2zcn.js.map → _layout-BJbMl6SJ.js.map} +1 -1
  13. package/build/client/assets/_layout-Bu0lel3p.js +6 -0
  14. package/build/client/assets/_layout-Bu0lel3p.js.map +1 -0
  15. package/build/client/assets/_layout-DHoH74NH.js +2 -0
  16. package/build/client/assets/_layout-DHoH74NH.js.map +1 -0
  17. package/build/client/assets/{accordion-D9-D-n9p.js → accordion-DLg7gJkp.js} +2 -2
  18. package/build/client/assets/{accordion-D9-D-n9p.js.map → accordion-DLg7gJkp.js.map} +1 -1
  19. package/build/client/assets/account-DLDPrc9J.js +2 -0
  20. package/build/client/assets/account-DLDPrc9J.js.map +1 -0
  21. package/build/client/assets/app-DJDjmdlu.js +2 -0
  22. package/build/client/assets/{app-DgTXXO8s.js.map → app-DJDjmdlu.js.map} +1 -1
  23. package/build/client/assets/{button-_qPvcoqR.js → button-39zQyNX6.js} +2 -2
  24. package/build/client/assets/{button-_qPvcoqR.js.map → button-39zQyNX6.js.map} +1 -1
  25. package/build/client/assets/{components-Be92gVxW.js → components-DUNtf72c.js} +2 -2
  26. package/build/client/assets/{components-Be92gVxW.js.map → components-DUNtf72c.js.map} +1 -1
  27. package/build/client/assets/diff-B3oaU_KB.js +2 -0
  28. package/build/client/assets/{diff-BhRAIPKc.js.map → diff-B3oaU_KB.js.map} +1 -1
  29. package/build/client/assets/{diff-8nlDkmpc.js → diff-BNCREJvf.js} +2 -2
  30. package/build/client/assets/{diff-8nlDkmpc.js.map → diff-BNCREJvf.js.map} +1 -1
  31. package/build/client/assets/{discord-BUWZUTEC.js → discord-CEOqKs_c.js} +2 -2
  32. package/build/client/assets/{discord-BUWZUTEC.js.map → discord-CEOqKs_c.js.map} +1 -1
  33. package/build/client/assets/discord-CpIgvYus.js +2 -0
  34. package/build/client/assets/discord-CpIgvYus.js.map +1 -0
  35. package/build/client/assets/{entry.client-DqIWuxf8.js → entry.client-CrlHhRMR.js} +2 -2
  36. package/build/client/assets/{entry.client-DqIWuxf8.js.map → entry.client-CrlHhRMR.js.map} +1 -1
  37. package/build/client/assets/epic-video-D8ex9vao.js +2 -0
  38. package/build/client/assets/epic-video-D8ex9vao.js.map +1 -0
  39. package/build/client/assets/error-boundary-3zItlMUO.js +2 -0
  40. package/build/client/assets/{error-boundary-BZA-ffa8.js.map → error-boundary-3zItlMUO.js.map} +1 -1
  41. package/build/client/assets/finished-rUzUjnEm.js +2 -0
  42. package/build/client/assets/finished-rUzUjnEm.js.map +1 -0
  43. package/build/client/assets/index-BajUQsFT.js +3053 -0
  44. package/build/client/assets/index-BajUQsFT.js.map +1 -0
  45. package/build/client/assets/{index-BCxBKsqT.js → index-CLNXC84j.js} +2 -2
  46. package/build/client/assets/{index-BCxBKsqT.js.map → index-CLNXC84j.js.map} +1 -1
  47. package/build/client/assets/{index-BCTr8uu6.js → index-CV3nxGFp.js} +2 -2
  48. package/build/client/assets/{index-BCTr8uu6.js.map → index-CV3nxGFp.js.map} +1 -1
  49. package/build/client/assets/{index-BFGhCX_U.js → index-C_B1-9rF.js} +2 -2
  50. package/build/client/assets/{index-BFGhCX_U.js.map → index-C_B1-9rF.js.map} +1 -1
  51. package/build/client/assets/{index-pkiQppkK.js → index-DDqzbGM2.js} +2 -2
  52. package/build/client/assets/{index-pkiQppkK.js.map → index-DDqzbGM2.js.map} +1 -1
  53. package/build/client/assets/index-DE-jwnOP.js +2 -0
  54. package/build/client/assets/index-DE-jwnOP.js.map +1 -0
  55. package/build/client/assets/{index-DZDhtMuq.js → index-DFqQCjCw.js} +2 -2
  56. package/build/client/assets/{index-DZDhtMuq.js.map → index-DFqQCjCw.js.map} +1 -1
  57. package/build/client/assets/{index-Bdg3v8tC.js → index-DH1w3QmP.js} +2 -2
  58. package/build/client/assets/{index-Bdg3v8tC.js.map → index-DH1w3QmP.js.map} +1 -1
  59. package/build/client/assets/{index-C9Hx0Dey.js → index-LjRZeU7x.js} +2 -2
  60. package/build/client/assets/{index-C9Hx0Dey.js.map → index-LjRZeU7x.js.map} +1 -1
  61. package/build/client/assets/index-mivnjq36.js +2 -0
  62. package/build/client/assets/index-mivnjq36.js.map +1 -0
  63. package/build/client/assets/{loading-XhMtj4mp.js → loading-DW_I206H.js} +2 -2
  64. package/build/client/assets/{loading-XhMtj4mp.js.map → loading-DW_I206H.js.map} +1 -1
  65. package/build/client/assets/{login-C1oOgi98.js → login-CdNej0Z7.js} +2 -2
  66. package/build/client/assets/{login-C1oOgi98.js.map → login-CdNej0Z7.js.map} +1 -1
  67. package/build/client/assets/manifest-62ea49c4.js +1 -0
  68. package/build/client/assets/{mdx-CEjzXoEx.js → mdx-C9dqA6IZ.js} +2 -2
  69. package/build/client/assets/{mdx-CEjzXoEx.js.map → mdx-C9dqA6IZ.js.map} +1 -1
  70. package/build/client/assets/{misc-DUy_whwE.js → misc-DIdEn_jt.js} +2 -2
  71. package/build/client/assets/{misc-DUy_whwE.js.map → misc-DIdEn_jt.js.map} +1 -1
  72. package/build/client/assets/{nav-chevrons-DnR25VLp.js → nav-chevrons-B3SvZV8B.js} +2 -2
  73. package/build/client/assets/{nav-chevrons-DnR25VLp.js.map → nav-chevrons-B3SvZV8B.js.map} +1 -1
  74. package/build/client/assets/onboarding-CC9zz4rl.js +2 -0
  75. package/build/client/assets/onboarding-CC9zz4rl.js.map +1 -0
  76. package/build/client/assets/{pe-ChIwTk8v.js → pe-D5h19vSo.js} +2 -2
  77. package/build/client/assets/{pe-ChIwTk8v.js.map → pe-D5h19vSo.js.map} +1 -1
  78. package/build/client/assets/presence-D1DPz__2.js +28 -0
  79. package/build/client/assets/presence-D1DPz__2.js.map +1 -0
  80. package/build/client/assets/{preview-DaZd0wMb.js → preview-BEtmdi0E.js} +2 -2
  81. package/build/client/assets/{preview-DaZd0wMb.js.map → preview-BEtmdi0E.js.map} +1 -1
  82. package/build/client/assets/{product-DIAmCwmZ.js → product-CYOFfeJM.js} +2 -2
  83. package/build/client/assets/{product-DIAmCwmZ.js.map → product-CYOFfeJM.js.map} +1 -1
  84. package/build/client/assets/{progress-DQt_Bn9o.js → progress-D6SP0Gec.js} +2 -2
  85. package/build/client/assets/{progress-DQt_Bn9o.js.map → progress-D6SP0Gec.js.map} +1 -1
  86. package/build/client/assets/{progress-bar-BaTU3Yx_.js → progress-bar-CBDBzRQ2.js} +2 -2
  87. package/build/client/assets/{progress-bar-BaTU3Yx_.js.map → progress-bar-CBDBzRQ2.js.map} +1 -1
  88. package/build/client/assets/{request-info-ByUEfOil.js → request-info-vBkaf3Rk.js} +2 -2
  89. package/build/client/assets/{request-info-ByUEfOil.js.map → request-info-vBkaf3Rk.js.map} +1 -1
  90. package/build/client/assets/{revalidation-ws-dUa9CAqr.js → revalidation-ws-DK5QOPlL.js} +2 -2
  91. package/build/client/assets/{revalidation-ws-dUa9CAqr.js.map → revalidation-ws-DK5QOPlL.js.map} +1 -1
  92. package/build/client/assets/{root-a3d3Qwip.js → root-Bg-hxaOK.js} +2 -2
  93. package/build/client/assets/{root-a3d3Qwip.js.map → root-Bg-hxaOK.js.map} +1 -1
  94. package/build/client/assets/{set-playground-CBHBA46B.js → set-playground-CMoUFgkO.js} +2 -2
  95. package/build/client/assets/{set-playground-CBHBA46B.js.map → set-playground-CMoUFgkO.js.map} +1 -1
  96. package/build/client/assets/{support-CIz02V_r.js → support-CPzYlWkd.js} +2 -2
  97. package/build/client/assets/{support-CIz02V_r.js.map → support-CPzYlWkd.js.map} +1 -1
  98. package/build/client/assets/test-C8wkLh9a.js +2 -0
  99. package/build/client/assets/{test-DoKJvNug.js.map → test-C8wkLh9a.js.map} +1 -1
  100. package/build/client/assets/{tests-DbuyD2cI.js → tests-CiM4RPOf.js} +2 -2
  101. package/build/client/assets/tests-CiM4RPOf.js.map +1 -0
  102. package/build/client/assets/{tooltip-DO9uwurQ.js → tooltip-BoVikCa-.js} +2 -2
  103. package/build/client/assets/{tooltip-DO9uwurQ.js.map → tooltip-BoVikCa-.js.map} +1 -1
  104. package/build/client/assets/{use-event-source-x59d4R2Z.js → use-event-source-M87p8Tme.js} +2 -2
  105. package/build/client/assets/{use-event-source-x59d4R2Z.js.map → use-event-source-M87p8Tme.js.map} +1 -1
  106. package/build/client/assets/{user-Bv6wYhQP.js → user-CbbIYEs8.js} +2 -2
  107. package/build/client/assets/{user-Bv6wYhQP.js.map → user-CbbIYEs8.js.map} +1 -1
  108. package/build/client/assets/{version-lxUUxt3s.js → version-CIF3cX3N.js} +2 -2
  109. package/build/client/assets/{version-lxUUxt3s.js.map → version-CIF3cX3N.js.map} +1 -1
  110. package/build/client/assets/{workshop-config-WVltG_BV.js → workshop-config-C5sYl312.js} +2 -2
  111. package/build/client/assets/{workshop-config-WVltG_BV.js.map → workshop-config-C5sYl312.js.map} +1 -1
  112. package/build/server/index.js +456 -782
  113. package/build/server/index.js.map +1 -1
  114. package/package.json +8 -8
  115. package/build/client/assets/_exerciseNumber-DCSM0NCG.js +0 -2
  116. package/build/client/assets/_exerciseNumber-DCSM0NCG.js.map +0 -1
  117. package/build/client/assets/_exerciseNumber_.finished-nbpk1ToO.js +0 -2
  118. package/build/client/assets/_exerciseNumber_.finished-nbpk1ToO.js.map +0 -1
  119. package/build/client/assets/_layout-Cbz7Qt-S.js +0 -6
  120. package/build/client/assets/_layout-Cbz7Qt-S.js.map +0 -1
  121. package/build/client/assets/_layout-DTAM9xh5.js +0 -2
  122. package/build/client/assets/_layout-DTAM9xh5.js.map +0 -1
  123. package/build/client/assets/account-C4Piztoz.js +0 -2
  124. package/build/client/assets/account-C4Piztoz.js.map +0 -1
  125. package/build/client/assets/app-DgTXXO8s.js +0 -2
  126. package/build/client/assets/diff-BhRAIPKc.js +0 -2
  127. package/build/client/assets/discord-Bdnx7fu-.js +0 -2
  128. package/build/client/assets/discord-Bdnx7fu-.js.map +0 -1
  129. package/build/client/assets/epic-video-Bp4BOD2R.js +0 -3053
  130. package/build/client/assets/epic-video-Bp4BOD2R.js.map +0 -1
  131. package/build/client/assets/error-boundary-BZA-ffa8.js +0 -2
  132. package/build/client/assets/finished-C0cpfAFL.js +0 -2
  133. package/build/client/assets/finished-C0cpfAFL.js.map +0 -1
  134. package/build/client/assets/index-Bi1TbRTj.js +0 -2
  135. package/build/client/assets/index-Bi1TbRTj.js.map +0 -1
  136. package/build/client/assets/index-Ca4vBON4.js +0 -2
  137. package/build/client/assets/index-Ca4vBON4.js.map +0 -1
  138. package/build/client/assets/manifest-e5b2a6e1.js +0 -1
  139. package/build/client/assets/onboarding-C2YNq60k.js +0 -2
  140. package/build/client/assets/onboarding-C2YNq60k.js.map +0 -1
  141. package/build/client/assets/presence-DJGFvdDh.js +0 -28
  142. package/build/client/assets/presence-DJGFvdDh.js.map +0 -1
  143. package/build/client/assets/test-DoKJvNug.js +0 -2
  144. package/build/client/assets/tests-DbuyD2cI.js.map +0 -1
  145. /package/build/client/assets/{epic-video-DUnRvy1A.css → index-DUnRvy1A.css} +0 -0
@@ -5,11 +5,13 @@ import { RemixServer, Link, useRouteError, useParams, isRouteErrorResponse, useN
5
5
  import { isbot } from "isbot";
6
6
  import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
7
7
  import path from "node:path";
8
- import { makeSingletonCache, cachified, fsCache, deleteCache, compiledCodeCache, diffFilesCache, diffCodeCache, shouldForceFresh, getAllFileCacheEntries, ogCache } from "@epic-web/workshop-utils/cache.server";
9
- import { getPreferences, getAuthInfo, readOnboardingData, requireAuthInfo, deleteDb, setPresencePreferences, PlayerPreferencesSchema, setPlayerPreferences, setAuthInfo, markOnboardingVideoWatched } from "@epic-web/workshop-utils/db.server";
10
- import { z } from "zod";
11
- import { getWorkshopInstructions, getWorkshopFinished, getExercises, getApps, getPlaygroundAppName, extractNumbersAndTypeFromAppNameOrPath, getAppFromFile, getAppByName, isProblemApp, isPlaygroundApp, isExerciseStepApp, getExercise, isSolutionApp, isExampleApp, getRelativePath as getRelativePath$1, workshopRoot, modifiedTimes, getForceFreshForDir, setPlayground, requireExerciseApp, requireExercise, getExerciseApp, getAppDisplayName, getNextExerciseApp, getPrevExerciseApp, getAppPageRoute } from "@epic-web/workshop-utils/apps.server";
8
+ import { makeSingletonCache, cachified, deleteCache, compiledCodeCache, diffFilesCache, diffCodeCache, shouldForceFresh, fsCache, getAllFileCacheEntries, ogCache } from "@epic-web/workshop-utils/cache.server";
12
9
  import { getWorkshopConfig } from "@epic-web/workshop-utils/config.server";
10
+ import { getAuthInfo, getPreferences, readOnboardingData, requireAuthInfo, PlayerPreferencesSchema, setPlayerPreferences, deleteDb, setPresencePreferences, setAuthInfo, markOnboardingVideoWatched } from "@epic-web/workshop-utils/db.server";
11
+ import { getUserInfo, userHasAccessToWorkshop, getProgress, updateProgress, getEpicVideoInfos } from "@epic-web/workshop-utils/epic-api.server";
12
+ import { getUserId, getSetClientIdCookieHeader } from "@epic-web/workshop-utils/user.server";
13
+ import { z } from "zod";
14
+ import { getApps, getExercises, getPlaygroundAppName, extractNumbersAndTypeFromAppNameOrPath, getAppFromFile, getAppByName, isProblemApp, isPlaygroundApp, isExerciseStepApp, getExercise, isSolutionApp, isExampleApp, getRelativePath as getRelativePath$1, workshopRoot, modifiedTimes, getForceFreshForDir, setPlayground, requireExerciseApp, requireExercise, getExerciseApp, getAppDisplayName, getNextExerciseApp, getPrevExerciseApp, getAppPageRoute, getWorkshopFinished, getWorkshopInstructions } from "@epic-web/workshop-utils/apps.server";
13
15
  import { getEnv } from "@epic-web/workshop-utils/env.server";
14
16
  import { makeTimings, getServerTimeHeader, combineServerTimings, time } from "@epic-web/workshop-utils/timing.server";
15
17
  import { cssBundleHref } from "@remix-run/css-bundle";
@@ -20,7 +22,7 @@ import { ClientOnly } from "remix-utils/client-only";
20
22
  import slugify from "@sindresorhus/slugify";
21
23
  import clsx$1, { clsx } from "clsx";
22
24
  import * as React from "react";
23
- import React__default, { useRef, useEffect, useMemo, useState, createContext, useContext, useSyncExternalStore, forwardRef, useImperativeHandle, Suspense, useReducer } from "react";
25
+ import React__default, { useRef, useEffect, useMemo, useState, createContext, useContext, useCallback, useSyncExternalStore, forwardRef, useImperativeHandle, Suspense, useReducer } from "react";
24
26
  import { extendTailwindMerge } from "tailwind-merge";
25
27
  import { Toaster, toast } from "sonner";
26
28
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
@@ -33,14 +35,13 @@ import { clientHint as clientHint$1 } from "@epic-web/client-hints/time-zone";
33
35
  import { safeRedirect } from "remix-utils/safe-redirect";
34
36
  import { ServerOnly } from "remix-utils/server-only";
35
37
  import * as cookie from "cookie";
36
- import cookie__default from "cookie";
37
- import md5 from "md5-hex";
38
- import { createId } from "@paralleldrive/cuid2";
39
38
  import { usePartySocket } from "partysocket/react";
39
+ import { createId } from "@paralleldrive/cuid2";
40
40
  import { motion, useAnimationControls } from "framer-motion";
41
41
  import { useHydrated } from "remix-utils/use-hydrated";
42
42
  import * as DialogPrimitive from "@radix-ui/react-dialog";
43
43
  import { invariantResponse, invariant } from "@epic-web/invariant";
44
+ import RealMuxPlayer, { MinResolution, MaxResolution } from "@mux/mux-player-react";
44
45
  import path$1 from "path";
45
46
  import etag from "etag";
46
47
  import fsExtra from "fs-extra";
@@ -49,7 +50,6 @@ import fs from "fs";
49
50
  import { getDirModifiedTime, modifiedMoreRecentlyThan } from "@epic-web/workshop-utils/modified-time.server";
50
51
  import * as esbuild from "esbuild";
51
52
  import { ElementScrollRestoration } from "@epic-web/restore-scroll";
52
- import RealMuxPlayer from "@mux/mux-player-react";
53
53
  import child_process from "child_process";
54
54
  import os from "os";
55
55
  import shellQuote from "shell-quote";
@@ -74,6 +74,7 @@ import { useEventSource } from "remix-utils/sse/react";
74
74
  import { eventStream } from "remix-utils/sse/server";
75
75
  import { EventEmitter } from "events";
76
76
  import { remember } from "@epic-web/remember";
77
+ import md5 from "md5-hex";
77
78
  import { Issuer } from "openid-client";
78
79
  import inspector from "node:inspector";
79
80
  import { getCommitInfo, getLatestWorkshopAppVersion } from "@epic-web/workshop-utils/git.server";
@@ -148,15 +149,18 @@ const MessageSchema = z.object({
148
149
  );
149
150
  const PresenceSchema = z.object({ users: z.array(UserSchema) });
150
151
  const presenceCache = makeSingletonCache("PresenceCache");
151
- async function getPresentUsers(user, { timings, request } = {}) {
152
- return cachified({
152
+ async function getPresentUsers({
153
+ timings,
154
+ request
155
+ } = {}) {
156
+ const presence = await cachified({
153
157
  key: "presence",
154
158
  cache: presenceCache,
155
159
  timings,
156
160
  request,
157
- ttl: 1e3 * 60 * 5,
161
+ ttl: 1e3 * 2,
158
162
  swr: 1e3 * 60 * 60 * 24,
159
- checkValue: z.array(UserSchema),
163
+ checkValue: PresenceSchema,
160
164
  async getFreshValue(context) {
161
165
  try {
162
166
  const response = await Promise.race([
@@ -175,20 +179,53 @@ async function getPresentUsers(user, { timings, request } = {}) {
175
179
  `Unexpected response from partykit: ${response.status} ${response.statusText}`
176
180
  );
177
181
  }
178
- const presence = PresenceSchema.parse(await response.json());
179
- const preferences = await getPreferences();
180
- const users = presence.users;
181
- if ((preferences == null ? void 0 : preferences.presence.optOut) ?? !user) {
182
- return uniqueUsers(users.filter((u) => u.id !== (user == null ? void 0 : user.id)));
183
- } else {
184
- return uniqueUsers([...users, user]);
185
- }
182
+ const presence2 = PresenceSchema.parse(await response.json());
183
+ return presence2;
186
184
  } catch {
187
185
  context.metadata.ttl = 300;
188
- return [];
186
+ return { users: [] };
189
187
  }
190
188
  }
191
189
  });
190
+ const { users } = presence;
191
+ const authInfo = await getAuthInfo();
192
+ const userId = request ? (await getUserId({ request })).id : (authInfo == null ? void 0 : authInfo.id) ?? null;
193
+ const preferences = await getPreferences();
194
+ if ((preferences == null ? void 0 : preferences.presence.optOut) || !userId) {
195
+ return uniqueUsers(users.filter((u) => u.id !== userId));
196
+ } else {
197
+ const user = { id: userId };
198
+ const config = getWorkshopConfig();
199
+ const url = request ? new URL(request.url) : void 0;
200
+ user.location = {
201
+ workshopTitle: config.title,
202
+ origin: url ? url.origin : void 0
203
+ };
204
+ if (url) {
205
+ if (url.pathname.startsWith("/exercise/")) {
206
+ const [exerciseNumber, stepNumber, type] = url.pathname.split("/").slice(2);
207
+ user.location.exercise = {
208
+ exerciseNumber: isNaN(Number(exerciseNumber)) ? null : Number(exerciseNumber),
209
+ stepNumber: isNaN(Number(stepNumber)) ? null : Number(stepNumber),
210
+ type: type === "problem" ? "problem" : type === "solution" ? "solution" : null
211
+ };
212
+ }
213
+ }
214
+ if (authInfo) {
215
+ const [userInfo, hasAccess] = await Promise.all([
216
+ getUserInfo({ request, timings }),
217
+ userHasAccessToWorkshop({ request, timings })
218
+ ]);
219
+ Object.assign(user, {
220
+ name: userInfo == null ? void 0 : userInfo.name,
221
+ avatarUrl: userInfo == null ? void 0 : userInfo.imageUrlLarge,
222
+ imageUrlSmall: userInfo == null ? void 0 : userInfo.imageUrlSmall,
223
+ imageUrlLarge: userInfo == null ? void 0 : userInfo.imageUrlLarge,
224
+ hasAccess
225
+ });
226
+ }
227
+ return uniqueUsers([...users, user]);
228
+ }
192
229
  }
193
230
  function uniqueUsers(users) {
194
231
  const seen = /* @__PURE__ */ new Set();
@@ -903,495 +940,6 @@ function createConfettiHeaders(value = String(Date.now())) {
903
940
  })
904
941
  });
905
942
  }
906
- const Transcript = z.string().nullable().optional().transform((s) => s ?? "Transcripts not available");
907
- const EpicVideoInfoSchema = z.object({
908
- transcript: Transcript,
909
- muxPlaybackId: z.string()
910
- });
911
- const EpicVideoRegionRestrictedErrorSchema = z.object({
912
- requestCountry: z.string(),
913
- restrictedCountry: z.string(),
914
- isRegionRestricted: z.literal(true)
915
- });
916
- const CachedEpicVideoInfoSchema = z.object({
917
- transcript: Transcript,
918
- muxPlaybackId: z.string(),
919
- status: z.literal("success"),
920
- statusCode: z.number(),
921
- statusText: z.string()
922
- }).or(
923
- z.object({
924
- status: z.literal("error"),
925
- statusCode: z.number(),
926
- statusText: z.string(),
927
- type: z.literal("unknown")
928
- })
929
- ).or(
930
- z.object({
931
- status: z.literal("error"),
932
- statusCode: z.number(),
933
- statusText: z.string(),
934
- type: z.literal("region-restricted"),
935
- requestCountry: z.string(),
936
- restrictedCountry: z.string()
937
- })
938
- ).or(z.null());
939
- async function getEpicVideoInfos(epicWebUrls, { request, timings } = {}) {
940
- if (!epicWebUrls) return {};
941
- const authInfo = await getAuthInfo();
942
- if (ENV.EPICSHOP_DEPLOYED) return {};
943
- const epicVideoInfos = {};
944
- for (const epicVideoEmbed of epicWebUrls) {
945
- const epicVideoInfo = await getEpicVideoInfo({
946
- epicVideoEmbed,
947
- accessToken: authInfo == null ? void 0 : authInfo.tokenSet.access_token,
948
- request,
949
- timings
950
- });
951
- if (epicVideoInfo) {
952
- epicVideoInfos[epicVideoEmbed] = epicVideoInfo;
953
- }
954
- }
955
- return epicVideoInfos;
956
- }
957
- async function getEpicVideoInfo({
958
- epicVideoEmbed,
959
- accessToken,
960
- request,
961
- timings
962
- }) {
963
- const tokenPortion = accessToken ? md5(accessToken) : "unauthenticated";
964
- const key = `epic-video-info:${tokenPortion}:${epicVideoEmbed}`;
965
- return cachified({
966
- key,
967
- request,
968
- cache: fsCache,
969
- timings,
970
- ttl: 1e3 * 60 * 60,
971
- swr: 1e3 * 60 * 60 * 24 * 30,
972
- checkValue: CachedEpicVideoInfoSchema,
973
- async getFreshValue(context) {
974
- const epicUrl = new URL(epicVideoEmbed);
975
- if (epicUrl.host !== "www.epicweb.dev" && epicUrl.host !== "www.epicreact.dev") {
976
- return null;
977
- }
978
- if (epicUrl.pathname.startsWith("/tutorials/")) {
979
- epicUrl.pathname = epicUrl.pathname.replace(
980
- /^\/tutorials\//,
981
- "/workshops/"
982
- );
983
- }
984
- const infoResponse = await fetch(
985
- `https://${epicUrl.host}/api${epicUrl.pathname}`,
986
- accessToken ? { headers: { authorization: `Bearer ${accessToken}` } } : void 0
987
- );
988
- const { status, statusText } = infoResponse;
989
- if (infoResponse.status >= 200 && infoResponse.status < 300) {
990
- const rawInfo = await infoResponse.json();
991
- const infoResult = EpicVideoInfoSchema.safeParse(rawInfo);
992
- if (infoResult.success) {
993
- return {
994
- status: "success",
995
- statusCode: status,
996
- statusText,
997
- ...infoResult.data
998
- };
999
- } else {
1000
- context.metadata.ttl = 1e3 * 2;
1001
- context.metadata.swr = 0;
1002
- const restrictedResult = EpicVideoRegionRestrictedErrorSchema.safeParse(rawInfo);
1003
- if (restrictedResult.success) {
1004
- return {
1005
- status: "error",
1006
- statusCode: status,
1007
- statusText,
1008
- type: "region-restricted",
1009
- ...restrictedResult.data
1010
- };
1011
- } else {
1012
- console.warn(
1013
- `Response from EpicWeb for "${epicUrl.pathname}" does not match expectation`,
1014
- infoResult.error
1015
- );
1016
- return {
1017
- status: "error",
1018
- statusCode: 500,
1019
- statusText: "API Data Type Mismatch",
1020
- type: "unknown"
1021
- };
1022
- }
1023
- }
1024
- } else {
1025
- context.metadata.ttl = 1e3 * 2;
1026
- context.metadata.swr = 0;
1027
- return {
1028
- status: "error",
1029
- statusCode: status,
1030
- statusText,
1031
- type: "unknown"
1032
- };
1033
- }
1034
- }
1035
- }).catch((e) => {
1036
- console.error(`Failed to fetch epic video info for ${epicVideoEmbed}`, e);
1037
- throw e;
1038
- });
1039
- }
1040
- async function getEpicProgress({
1041
- timings,
1042
- request,
1043
- forceFresh
1044
- } = {}) {
1045
- if (ENV.EPICSHOP_DEPLOYED) return [];
1046
- const authInfo = await getAuthInfo();
1047
- const {
1048
- product: { host }
1049
- } = getWorkshopConfig();
1050
- if (!authInfo) return [];
1051
- const tokenPart = md5(authInfo.tokenSet.access_token);
1052
- const EpicProgressSchema = z.array(
1053
- z.object({
1054
- lessonId: z.string(),
1055
- completedAt: z.string().nullable()
1056
- })
1057
- );
1058
- return cachified({
1059
- key: `epic-progress:${host}:${tokenPart}`,
1060
- cache: fsCache,
1061
- request,
1062
- timings,
1063
- forceFresh,
1064
- ttl: 1e3 * 2,
1065
- swr: 1e3 * 60 * 60 * 24 * 30,
1066
- checkValue: EpicProgressSchema,
1067
- async getFreshValue(context) {
1068
- const response = await fetch(`https://${host}/api/progress`, {
1069
- headers: {
1070
- authorization: `Bearer ${authInfo.tokenSet.access_token}`
1071
- }
1072
- }).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
1073
- if (response.status < 200 || response.status >= 300) {
1074
- console.error(
1075
- `Failed to fetch progress from EpicWeb: ${response.status} ${response.statusText}`
1076
- );
1077
- context.metadata.ttl = 1e3 * 2;
1078
- context.metadata.swr = 0;
1079
- return [];
1080
- }
1081
- return EpicProgressSchema.parse(await response.json());
1082
- }
1083
- });
1084
- }
1085
- async function getProgress({
1086
- timings,
1087
- request
1088
- } = {}) {
1089
- if (ENV.EPICSHOP_DEPLOYED) return [];
1090
- const authInfo = await getAuthInfo();
1091
- if (!authInfo) return [];
1092
- const {
1093
- product: { slug, host }
1094
- } = getWorkshopConfig();
1095
- if (!slug) return [];
1096
- const [
1097
- workshopData,
1098
- epicProgress,
1099
- workshopInstructions,
1100
- workshopFinished,
1101
- exercises
1102
- ] = await Promise.all([
1103
- getWorkshopData(slug, { request, timings }),
1104
- getEpicProgress({ request, timings }),
1105
- getWorkshopInstructions({ request }),
1106
- getWorkshopFinished({ request }),
1107
- getExercises({ request, timings })
1108
- ]);
1109
- const progress = [];
1110
- for (const resource of workshopData.resources) {
1111
- const lessons = resource._type === "section" ? resource.lessons : [resource];
1112
- for (const lesson of lessons) {
1113
- const epicLessonSlug = lesson.slug;
1114
- const lessonProgress = epicProgress.find(
1115
- ({ lessonId }) => lessonId === lesson._id
1116
- );
1117
- const epicCompletedAt = lessonProgress ? lessonProgress.completedAt : null;
1118
- const progressForLesson = getProgressForLesson(epicLessonSlug, {
1119
- workshopInstructions,
1120
- workshopFinished,
1121
- exercises
1122
- });
1123
- const epicLessonUrl = `https://${host}/workshops/${slug}/${epicLessonSlug}`;
1124
- if (progressForLesson) {
1125
- progress.push({
1126
- ...progressForLesson,
1127
- epicLessonUrl,
1128
- epicLessonSlug,
1129
- epicCompletedAt
1130
- });
1131
- } else {
1132
- progress.push({
1133
- type: "unknown",
1134
- epicLessonUrl,
1135
- epicLessonSlug,
1136
- epicCompletedAt
1137
- });
1138
- }
1139
- }
1140
- }
1141
- return progress;
1142
- }
1143
- function getProgressForLesson(epicLessonSlug, {
1144
- workshopInstructions,
1145
- workshopFinished,
1146
- exercises
1147
- }) {
1148
- var _a;
1149
- const hasEmbed = (embed) => embed == null ? void 0 : embed.some((e) => e.split("/").at(-1) === epicLessonSlug);
1150
- if (workshopInstructions.compiled.status === "success" && hasEmbed(workshopInstructions.compiled.epicVideoEmbeds)) {
1151
- return { type: "workshop-instructions" };
1152
- }
1153
- if (workshopFinished.compiled.status === "success" && hasEmbed(workshopFinished.compiled.epicVideoEmbeds)) {
1154
- return { type: "workshop-finished" };
1155
- }
1156
- for (const exercise of exercises) {
1157
- if (hasEmbed(exercise.instructionsEpicVideoEmbeds)) {
1158
- return {
1159
- type: "instructions",
1160
- exerciseNumber: exercise.exerciseNumber
1161
- };
1162
- }
1163
- if (hasEmbed(exercise.finishedEpicVideoEmbeds)) {
1164
- return {
1165
- type: "finished",
1166
- exerciseNumber: exercise.exerciseNumber
1167
- };
1168
- }
1169
- for (const step of exercise.steps.filter(Boolean)) {
1170
- if (hasEmbed((_a = step.problem) == null ? void 0 : _a.epicVideoEmbeds)) {
1171
- return {
1172
- type: "step",
1173
- exerciseNumber: exercise.exerciseNumber,
1174
- stepNumber: step.stepNumber
1175
- };
1176
- }
1177
- }
1178
- }
1179
- }
1180
- async function updateProgress({ lessonSlug, complete }, {
1181
- timings,
1182
- request
1183
- } = {}) {
1184
- if (ENV.EPICSHOP_DEPLOYED) {
1185
- return {
1186
- status: "error",
1187
- error: "cannot update progress when deployed"
1188
- };
1189
- }
1190
- const authInfo = await getAuthInfo();
1191
- if (!authInfo) {
1192
- return { status: "error", error: "not authenticated" };
1193
- }
1194
- const {
1195
- product: { host }
1196
- } = getWorkshopConfig();
1197
- const response = await fetch(`https://${host}/api/progress`, {
1198
- method: "POST",
1199
- headers: {
1200
- authorization: `Bearer ${authInfo.tokenSet.access_token}`,
1201
- "content-type": "application/json"
1202
- },
1203
- body: JSON.stringify(
1204
- complete ? { lessonSlug } : { lessonSlug, remove: true }
1205
- )
1206
- }).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
1207
- await getEpicProgress({ forceFresh: true, request, timings });
1208
- if (response.status < 200 || response.status >= 300) {
1209
- return {
1210
- status: "error",
1211
- error: `${response.status} ${response.statusText}`
1212
- };
1213
- }
1214
- return { status: "success" };
1215
- }
1216
- const ModuleSchema = z.object({
1217
- resources: z.array(
1218
- z.union([
1219
- z.object({
1220
- _type: z.literal("lesson"),
1221
- _id: z.string(),
1222
- slug: z.string()
1223
- }),
1224
- z.object({
1225
- _type: z.literal("section"),
1226
- lessons: z.array(z.object({ _id: z.string(), slug: z.string() }))
1227
- })
1228
- ])
1229
- )
1230
- });
1231
- async function getWorkshopData(slug, {
1232
- timings,
1233
- request,
1234
- forceFresh
1235
- } = {}) {
1236
- if (ENV.EPICSHOP_DEPLOYED) return { resources: [] };
1237
- const authInfo = await getAuthInfo();
1238
- if (!authInfo) return { resources: [] };
1239
- const {
1240
- product: { host }
1241
- } = getWorkshopConfig();
1242
- return cachified({
1243
- key: `epic-workshop-data:${host}:${slug}`,
1244
- cache: fsCache,
1245
- request,
1246
- forceFresh,
1247
- timings,
1248
- checkValue: ModuleSchema,
1249
- async getFreshValue() {
1250
- const response = await fetch(
1251
- `https://${host}/api/workshops/${encodeURIComponent(slug)}`
1252
- ).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
1253
- if (response.status < 200 || response.status >= 300) {
1254
- console.error(
1255
- `Failed to fetch workshop data from EpicWeb for ${slug}: ${response.status} ${response.statusText}`
1256
- );
1257
- return { resources: [] };
1258
- }
1259
- return ModuleSchema.parse(await response.json());
1260
- }
1261
- });
1262
- }
1263
- async function userHasAccessToWorkshop({
1264
- timings,
1265
- request,
1266
- forceFresh
1267
- }) {
1268
- var _a;
1269
- const config = getWorkshopConfig();
1270
- const {
1271
- product: { host, slug }
1272
- } = config;
1273
- if (!slug) return true;
1274
- if (ENV.EPICSHOP_DEPLOYED) {
1275
- const cookieHeader = request.headers.get("Cookie");
1276
- if (!cookieHeader) return false;
1277
- const cookies = cookie__default.parse(cookieHeader);
1278
- return ((_a = cookies.skill) == null ? void 0 : _a.split(",").includes(slug)) ?? false;
1279
- }
1280
- const authInfo = await getAuthInfo();
1281
- if (!authInfo) return false;
1282
- return cachified({
1283
- key: `user-has-access-to-workshop:${host}:${slug}`,
1284
- cache: fsCache,
1285
- request,
1286
- forceFresh,
1287
- timings,
1288
- ttl: 1e3 * 5,
1289
- checkValue: z.boolean(),
1290
- async getFreshValue(context) {
1291
- const response = await fetch(
1292
- `https://${host}/api/workshops/${encodeURIComponent(slug)}/access`,
1293
- {
1294
- headers: {
1295
- authorization: `Bearer ${authInfo.tokenSet.access_token}`
1296
- }
1297
- }
1298
- ).catch((e) => new Response(getErrorMessage(e), { status: 500 }));
1299
- const hasAccess = response.ok ? await response.json() === true : false;
1300
- if (hasAccess) {
1301
- context.metadata.ttl = 1e3 * 60 * 60 * 24 * 30;
1302
- context.metadata.swr = 1e3 * 60 * 60 * 24 * 30;
1303
- }
1304
- return hasAccess;
1305
- }
1306
- });
1307
- }
1308
- const UserInfoSchema = z.object({
1309
- id: z.string(),
1310
- name: z.string().nullable(),
1311
- email: z.string().email(),
1312
- image: z.string().nullable(),
1313
- discordProfile: z.object({
1314
- nick: z.string().nullable(),
1315
- user: z.object({
1316
- id: z.string(),
1317
- username: z.string(),
1318
- avatar: z.string().nullable().optional(),
1319
- global_name: z.string().nullable().optional()
1320
- })
1321
- }).nullable().optional()
1322
- }).transform((data) => {
1323
- var _a, _b;
1324
- return {
1325
- ...data,
1326
- imageUrlSmall: resizeImageUrl(data.image, { size: 64 }) ?? resolveDiscordAvatar((_a = data.discordProfile) == null ? void 0 : _a.user, {
1327
- size: 64
1328
- }) ?? resolveGravatarUrl(data.email, { size: 64 }),
1329
- imageUrlLarge: resizeImageUrl(data.image, { size: 512 }) ?? resolveDiscordAvatar((_b = data.discordProfile) == null ? void 0 : _b.user, {
1330
- size: 512
1331
- }) ?? resolveGravatarUrl(data.email, { size: 512 })
1332
- };
1333
- });
1334
- function resizeImageUrl(url, { size }) {
1335
- if (!url) return null;
1336
- const urlObj = new URL(url);
1337
- urlObj.searchParams.set("size", size.toString());
1338
- return urlObj.toString();
1339
- }
1340
- function resolveGravatarUrl(email, { size }) {
1341
- if (!email) return null;
1342
- const hash = md5(email.toLowerCase());
1343
- const gravatarOptions = new URLSearchParams({
1344
- size: size.toString(),
1345
- default: "identicon"
1346
- });
1347
- return `https://www.gravatar.com/avatar/${hash}?${gravatarOptions.toString()}`;
1348
- }
1349
- function resolveDiscordAvatar(user, { size }) {
1350
- if (!user) return null;
1351
- const { avatar, id: userId } = user;
1352
- if (!avatar) return null;
1353
- const isGif = avatar.startsWith("a_");
1354
- const url = new URL(
1355
- `/avatars/${userId}/${avatar}.${isGif ? "gif" : "png"}`,
1356
- "https://cdn.discordapp.com"
1357
- );
1358
- url.searchParams.set("size", size.toString());
1359
- return url.toString();
1360
- }
1361
- async function getUserInfo({
1362
- timings,
1363
- request,
1364
- forceFresh
1365
- } = {}) {
1366
- const authInfo = await getAuthInfo();
1367
- if (!authInfo) return null;
1368
- const { tokenSet } = authInfo;
1369
- const {
1370
- product: { host }
1371
- } = getWorkshopConfig();
1372
- const accessToken = tokenSet.access_token;
1373
- const url = `https://${host}/oauth/userinfo`;
1374
- return cachified({
1375
- key: `${url}:${md5(accessToken)}`,
1376
- cache: fsCache,
1377
- request,
1378
- forceFresh,
1379
- timings,
1380
- ttl: 1e3 * 60 * 1,
1381
- swr: 1e3 * 60 * 60 * 24 * 365,
1382
- checkValue: UserInfoSchema,
1383
- async getFreshValue() {
1384
- const response = await fetch(url, {
1385
- headers: { authorization: `Bearer ${accessToken}` }
1386
- });
1387
- if (!response.ok) {
1388
- throw new Error(`Failed to fetch user info: ${response.statusText}`);
1389
- }
1390
- const data = await response.json();
1391
- return UserInfoSchema.parse(data);
1392
- }
1393
- });
1394
- }
1395
943
  const PresenceContext = createContext(null);
1396
944
  function usePresencePreferences() {
1397
945
  var _a;
@@ -1407,35 +955,51 @@ const ExerciseAppParamsSchema = z.object({
1407
955
  exerciseNumber: z.coerce.number().finite(),
1408
956
  stepNumber: z.coerce.number().finite().optional()
1409
957
  });
958
+ function useFirstCallDelayedCallback(cb, delay) {
959
+ const [timedPromise] = useState(
960
+ () => new Promise((resolve) => setTimeout(resolve, delay))
961
+ );
962
+ const mounted = useRef(true);
963
+ const currentCallRef = useRef(null);
964
+ const lastCbRef = useRef(cb);
965
+ useEffect(() => {
966
+ lastCbRef.current = cb;
967
+ }, [cb]);
968
+ const delayedCb = useCallback(
969
+ (...args) => {
970
+ const thisOne = Symbol();
971
+ currentCallRef.current = thisOne;
972
+ void timedPromise.then(() => {
973
+ if (!mounted.current) return;
974
+ if (currentCallRef.current !== thisOne) {
975
+ return;
976
+ }
977
+ lastCbRef.current(...args);
978
+ });
979
+ },
980
+ [timedPromise]
981
+ );
982
+ return delayedCb;
983
+ }
1410
984
  function usePresenceSocket(user) {
1411
985
  const workshopTitle = useOptionalWorkshopTitle();
1412
- const { userHasAccess = false } = useRouteLoaderData("root") ?? {};
986
+ const { userHasAccess = false, userId } = useRouteLoaderData("root") ?? {};
1413
987
  const requestInfo = useRequestInfo();
1414
988
  const rawParams = useParams();
1415
989
  const prefs = usePresencePreferences();
1416
990
  const data = useRouteLoaderData("root");
1417
991
  const [users, setUsers] = useState((data == null ? void 0 : data.presence.users) ?? []);
1418
- const [clientId] = useState(() => {
1419
- if (typeof document === "undefined") return null;
1420
- if (user) return user.id;
1421
- const clientId2 = sessionStorage.getItem("clientId");
1422
- if (clientId2) return clientId2;
1423
- const newClientId = createId();
1424
- sessionStorage.setItem("clientId", newClientId);
1425
- return newClientId;
1426
- });
992
+ const handleMessage = useFirstCallDelayedCallback((evt) => {
993
+ const messageResult = MessageSchema.safeParse(JSON.parse(String(evt.data)));
994
+ if (!messageResult.success) return;
995
+ if (messageResult.data.type === "presence") {
996
+ setUsers(messageResult.data.payload.users);
997
+ }
998
+ }, 2e3);
1427
999
  const socket = usePartySocket({
1428
1000
  host: new URL(partykitBaseUrl).host,
1429
1001
  room: partykitRoom,
1430
- onMessage(evt) {
1431
- const messageResult = MessageSchema.safeParse(
1432
- JSON.parse(String(evt.data))
1433
- );
1434
- if (!messageResult.success) return;
1435
- if (messageResult.data.type === "presence") {
1436
- setUsers(messageResult.data.payload.users);
1437
- }
1438
- }
1002
+ onMessage: handleMessage
1439
1003
  });
1440
1004
  const paramsResult = ExerciseAppParamsSchema.safeParse(rawParams);
1441
1005
  const params = paramsResult.success ? paramsResult.data : null;
@@ -1451,54 +1015,58 @@ function usePresenceSocket(user) {
1451
1015
  } : null
1452
1016
  };
1453
1017
  let message = null;
1454
- if ((!user || (prefs == null ? void 0 : prefs.optOut)) && clientId) {
1455
- if (user) {
1018
+ if (user) {
1019
+ if (prefs == null ? void 0 : prefs.optOut) {
1456
1020
  message = { type: "remove-user", payload: { id: user.id } };
1021
+ } else {
1022
+ message = {
1023
+ type: "add-user",
1024
+ payload: {
1025
+ id: user.id,
1026
+ name: user.name,
1027
+ hasAccess: userHasAccess,
1028
+ imageUrlSmall: user.imageUrlSmall,
1029
+ imageUrlLarge: user.imageUrlLarge,
1030
+ location
1031
+ }
1032
+ };
1457
1033
  }
1458
- message = { type: "add-user", payload: { id: clientId, location } };
1459
- } else if (user) {
1460
- message = {
1461
- type: "add-user",
1462
- payload: {
1463
- id: user.id,
1464
- name: user.name,
1465
- hasAccess: userHasAccess,
1466
- imageUrlSmall: user.imageUrlSmall,
1467
- imageUrlLarge: user.imageUrlLarge,
1468
- location
1469
- }
1470
- };
1034
+ } else if (userId == null ? void 0 : userId.id) {
1035
+ message = { type: "add-user", payload: { id: userId.id, location } };
1471
1036
  }
1472
1037
  const messageJson = message ? JSON.stringify(message) : null;
1473
1038
  useEffect(() => {
1474
1039
  if (messageJson) socket.send(messageJson);
1475
1040
  }, [messageJson, socket]);
1476
- const scoredUsers = scoreUsers(location, users);
1041
+ const scoredUsers = scoreUsers({ id: userId == null ? void 0 : userId.id, location }, users);
1477
1042
  return { users: scoredUsers };
1478
1043
  }
1479
- function scoreUsers(location, users) {
1480
- const scoredUsers = users.map((user) => {
1044
+ function scoreUsers(user, users) {
1045
+ const { location } = user;
1046
+ const scoredUsers = users.map((user2) => {
1481
1047
  var _a, _b, _c, _d;
1482
1048
  let score = 0;
1483
1049
  const available = 5;
1484
- if (user.hasAccess) {
1050
+ if (user2.hasAccess) {
1485
1051
  score += 1;
1486
1052
  }
1487
- if ((location == null ? void 0 : location.workshopTitle) === ((_a = user.location) == null ? void 0 : _a.workshopTitle)) {
1053
+ if ((location == null ? void 0 : location.workshopTitle) === ((_a = user2.location) == null ? void 0 : _a.workshopTitle)) {
1488
1054
  score += 1;
1489
- if (((_b = location == null ? void 0 : location.exercise) == null ? void 0 : _b.exerciseNumber) && location.exercise.exerciseNumber === ((_d = (_c = user.location) == null ? void 0 : _c.exercise) == null ? void 0 : _d.exerciseNumber)) {
1055
+ if (((_b = location == null ? void 0 : location.exercise) == null ? void 0 : _b.exerciseNumber) && location.exercise.exerciseNumber === ((_d = (_c = user2.location) == null ? void 0 : _c.exercise) == null ? void 0 : _d.exerciseNumber)) {
1490
1056
  score += 1;
1491
- if (location.exercise.stepNumber && location.exercise.stepNumber === user.location.exercise.stepNumber) {
1057
+ if (location.exercise.stepNumber && location.exercise.stepNumber === user2.location.exercise.stepNumber) {
1492
1058
  score += 1;
1493
- if (location.exercise.type && location.exercise.type === user.location.exercise.type) {
1059
+ if (location.exercise.type && location.exercise.type === user2.location.exercise.type) {
1494
1060
  score += 1;
1495
1061
  }
1496
1062
  }
1497
1063
  }
1498
1064
  }
1499
- return { user, score: Math.floor(score / available * 10) / 10 };
1065
+ return { user: user2, score: Math.floor(score / available * 10) / 10 };
1500
1066
  });
1501
1067
  return scoredUsers.sort((a, b) => {
1068
+ if (a.user.id === (user == null ? void 0 : user.id)) return -1;
1069
+ if (b.user.id === (user == null ? void 0 : user.id)) return 1;
1502
1070
  if (a.score === b.score) return 0;
1503
1071
  return a.score > b.score ? -1 : 1;
1504
1072
  });
@@ -1622,6 +1190,7 @@ const meta$6 = ({ data: data2 }) => {
1622
1190
  });
1623
1191
  };
1624
1192
  async function loader$x({ request }) {
1193
+ var _a;
1625
1194
  const timings = makeTimings("rootLoader");
1626
1195
  const workshopConfig = getWorkshopConfig();
1627
1196
  const {
@@ -1640,6 +1209,7 @@ async function loader$x({ request }) {
1640
1209
  const { confettiId, headers: confettiHeaders } = getConfetti(request);
1641
1210
  const { toast: toast2, headers: toastHeaders } = await getToast(request);
1642
1211
  const asyncStuff = await promiseHash({
1212
+ userId: getUserId({ request }),
1643
1213
  preferences: getPreferences(),
1644
1214
  progress: getProgress({ timings }).catch((e) => {
1645
1215
  console.error("Failed to get progress", e);
@@ -1650,7 +1220,7 @@ async function loader$x({ request }) {
1650
1220
  userHasAccess: userHasAccessToWorkshop({ request, timings }),
1651
1221
  apps: getApps({ request, timings })
1652
1222
  });
1653
- const presentUsers = await getPresentUsers(asyncStuff.user, {
1223
+ const presentUsers = await getPresentUsers({
1654
1224
  request,
1655
1225
  timings
1656
1226
  });
@@ -1685,19 +1255,15 @@ async function loader$x({ request }) {
1685
1255
  }
1686
1256
  },
1687
1257
  {
1688
- headers: combineHeaders(toastHeaders, confettiHeaders, {
1689
- "Server-Timing": timings.toString()
1690
- })
1258
+ headers: combineHeaders(
1259
+ toastHeaders,
1260
+ confettiHeaders,
1261
+ { "Server-Timing": timings.toString() },
1262
+ ((_a = asyncStuff.userId) == null ? void 0 : _a.type) === "cookie.randomId" ? { "Set-Cookie": getSetClientIdCookieHeader(asyncStuff.userId.id) } : void 0
1263
+ )
1691
1264
  }
1692
1265
  );
1693
1266
  }
1694
- const headers$c = ({ loaderHeaders }) => {
1695
- const headers2 = {
1696
- "Cache-Control": loaderHeaders.get("Cache-Control") ?? "",
1697
- "Server-Timing": loaderHeaders.get("Server-Timing") ?? ""
1698
- };
1699
- return headers2;
1700
- };
1701
1267
  function Document({
1702
1268
  children,
1703
1269
  env = {},
@@ -1765,7 +1331,6 @@ const route0 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
1765
1331
  __proto__: null,
1766
1332
  ErrorBoundary: ErrorBoundary$6,
1767
1333
  default: AppWithProviders,
1768
- headers: headers$c,
1769
1334
  links,
1770
1335
  loader: loader$x,
1771
1336
  meta: meta$6
@@ -2359,7 +1924,7 @@ const route38 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
2359
1924
  useRequireEpicProgress
2360
1925
  }, Symbol.toStringTag, { value: "Module" }));
2361
1926
  async function loader$v({ request }) {
2362
- var _a;
1927
+ var _a, _b;
2363
1928
  const timings = makeTimings("appLayoutLoader");
2364
1929
  const { title: workshopTitle } = getWorkshopConfig();
2365
1930
  const [exercises, playgroundAppName] = await Promise.all([
@@ -2394,7 +1959,8 @@ async function loader$v({ request }) {
2394
1959
  name: (problem == null ? void 0 : problem.name) ?? (solution == null ? void 0 : solution.name) ?? "Unknown"
2395
1960
  }))
2396
1961
  })),
2397
- playground
1962
+ playground,
1963
+ isMenuOpened: ((_b = request.headers.get("cookie")) == null ? void 0 : _b.includes("es_menu_open=true")) ?? false
2398
1964
  },
2399
1965
  {
2400
1966
  headers: {
@@ -2540,11 +2106,16 @@ function FacePile({ isMenuOpened }) {
2540
2106
  }
2541
2107
  const useIsWide = makeMediaQueryStore("(min-width: 640px)", true);
2542
2108
  function App() {
2109
+ const data2 = useLoaderData();
2543
2110
  const user = useOptionalUser();
2544
2111
  const isWide = useIsWide();
2545
2112
  const isHydrated = useHydrated();
2546
- const [isMenuOpened, setMenuOpened] = React.useState(false);
2113
+ const [isMenuOpened, setMenuOpenedState] = React.useState(data2.isMenuOpened);
2547
2114
  useRevalidationWS({ watchPaths: ["./exercises/README.mdx"] });
2115
+ function setMenuOpened(value) {
2116
+ setMenuOpenedState(value);
2117
+ document.cookie = `es_menu_open=${value.toString()}; path=/; SameSite=Lax;`;
2118
+ }
2548
2119
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2549
2120
  user ? null : /* @__PURE__ */ jsx(NoUserBanner, {}),
2550
2121
  isHydrated && isWide ? null : /* @__PURE__ */ jsx(
@@ -2599,6 +2170,7 @@ function NoUserBanner() {
2599
2170
  const {
2600
2171
  product: { host, displayName }
2601
2172
  } = useWorkshopConfig();
2173
+ const userHasAccess = useUserHasAccess();
2602
2174
  const details = /* @__PURE__ */ jsx("div", { children: ENV.EPICSHOP_DEPLOYED ? /* @__PURE__ */ jsxs("div", { children: [
2603
2175
  `This is the deployed version. `,
2604
2176
  /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2646,7 +2218,7 @@ function NoUserBanner() {
2646
2218
  details
2647
2219
  ] })
2648
2220
  ] }),
2649
- /* @__PURE__ */ jsxs("div", { className: "hidden h-full flex-col items-center sm:flex md:flex-row", children: [
2221
+ userHasAccess ? null : /* @__PURE__ */ jsxs("div", { className: "hidden h-full flex-col items-center sm:flex md:flex-row", children: [
2650
2222
  /* @__PURE__ */ jsxs(
2651
2223
  Link,
2652
2224
  {
@@ -3353,6 +2925,7 @@ function NavToggle({
3353
2925
  setMenuOpened,
3354
2926
  menuControls
3355
2927
  }) {
2928
+ const initialOpenRef = React.useRef(isMenuOpened);
3356
2929
  const menuButtonRef = React.useRef(null);
3357
2930
  const path01Variants = {
3358
2931
  open: { d: "M3.06061 2.99999L21.0606 21" },
@@ -3410,7 +2983,7 @@ function NavToggle({
3410
2983
  /* @__PURE__ */ jsx(
3411
2984
  motion.path,
3412
2985
  {
3413
- ...path01Variants.closed,
2986
+ ...path01Variants[initialOpenRef.current ? "open" : "closed"],
3414
2987
  animate: path01Controls,
3415
2988
  transition: { duration: 0.2 },
3416
2989
  stroke: "currentColor",
@@ -3420,7 +2993,7 @@ function NavToggle({
3420
2993
  /* @__PURE__ */ jsx(
3421
2994
  motion.path,
3422
2995
  {
3423
- ...path02Variants.closed,
2996
+ ...path02Variants[initialOpenRef.current ? "open" : "closed"],
3424
2997
  animate: path02Controls,
3425
2998
  transition: { duration: 0.2 },
3426
2999
  stroke: "currentColor",
@@ -3514,6 +3087,228 @@ function ButtonLink({
3514
3087
  }
3515
3088
  ) });
3516
3089
  }
3090
+ const PlaybackTimeSchema = z.object({
3091
+ time: z.number(),
3092
+ expiresAt: z.string()
3093
+ }).transform((data2) => {
3094
+ return { time: Number(data2.time), expiresAt: new Date(data2.expiresAt) };
3095
+ });
3096
+ function usePlayerPreferences() {
3097
+ var _a;
3098
+ const data2 = useRouteLoaderData("root");
3099
+ return ((_a = data2 == null ? void 0 : data2.preferences) == null ? void 0 : _a.player) ?? null;
3100
+ }
3101
+ const ignoredInputs = [
3102
+ "INPUT",
3103
+ "SELECT",
3104
+ "BUTTON",
3105
+ "TEXTAREA",
3106
+ "MUX-PLAYER",
3107
+ "SUMMARY"
3108
+ ];
3109
+ const ignoredRoles = ["button", "option", "combobox", "tab", "tablist"];
3110
+ function shouldIgnoreHotkey(el) {
3111
+ let current = el;
3112
+ while (current) {
3113
+ if (!(current instanceof HTMLElement)) return false;
3114
+ const isIgnored = ignoredInputs.includes(current.tagName) || ignoredRoles.includes(current.getAttribute("role") || "") || current.isContentEditable;
3115
+ if (isIgnored) return true;
3116
+ current = current.parentElement;
3117
+ }
3118
+ return false;
3119
+ }
3120
+ async function action$9({ request }) {
3121
+ const result = PlayerPreferencesSchema.safeParse(await request.json());
3122
+ if (!result.success) {
3123
+ return unstable_data$1({ status: "error", error: result.error.flatten() }, {
3124
+ status: 400
3125
+ });
3126
+ }
3127
+ await setPlayerPreferences(result.data);
3128
+ return { status: "success" };
3129
+ }
3130
+ function useLatest(value) {
3131
+ const ref = React.useRef(value);
3132
+ React.useEffect(() => {
3133
+ ref.current = value;
3134
+ }, [value]);
3135
+ return ref;
3136
+ }
3137
+ function MuxPlayer({
3138
+ muxPlayerRef,
3139
+ ...props
3140
+ }) {
3141
+ const playerPreferences = usePlayerPreferences();
3142
+ const playerPreferencesFetcher = useFetcher();
3143
+ const [metadataLoaded, setMetadataLoaded] = React.useState(false);
3144
+ const currentTimeSessionKey = `${props.playbackId}:currentTime`;
3145
+ const [currentTime, setCurrentTime] = React.useState(0);
3146
+ const fetcherRef = useLatest(playerPreferencesFetcher);
3147
+ const playerPreferencesRef = useLatest(playerPreferences);
3148
+ React.useEffect(() => {
3149
+ if (typeof document === "undefined") return;
3150
+ const stored = sessionStorage.getItem(currentTimeSessionKey);
3151
+ if (!stored) return;
3152
+ try {
3153
+ const { time: time2, expiresAt } = PlaybackTimeSchema.parse(JSON.parse(stored));
3154
+ if (expiresAt.getTime() < Date.now()) throw new Error("Time expired");
3155
+ setCurrentTime(time2);
3156
+ } catch {
3157
+ sessionStorage.removeItem(currentTimeSessionKey);
3158
+ }
3159
+ }, [currentTimeSessionKey]);
3160
+ React.useEffect(() => {
3161
+ function handleUserKeyPress(e) {
3162
+ if (!muxPlayerRef.current) return;
3163
+ const activeElement = document.activeElement;
3164
+ if (shouldIgnoreHotkey(activeElement)) return;
3165
+ if (shouldIgnoreHotkey(e.target)) return;
3166
+ if (e.key === " ") {
3167
+ e.preventDefault();
3168
+ void (muxPlayerRef.current.paused ? muxPlayerRef.current.play() : muxPlayerRef.current.pause());
3169
+ }
3170
+ if (e.key === "ArrowRight") {
3171
+ e.preventDefault();
3172
+ muxPlayerRef.current.currentTime = muxPlayerRef.current.currentTime + (muxPlayerRef.current.forwardSeekOffset || 10);
3173
+ }
3174
+ if (e.key === "ArrowLeft") {
3175
+ e.preventDefault();
3176
+ muxPlayerRef.current.currentTime = muxPlayerRef.current.currentTime - (muxPlayerRef.current.forwardSeekOffset || 10);
3177
+ }
3178
+ if (e.key === "f" && !e.metaKey && !e.ctrlKey) {
3179
+ e.preventDefault();
3180
+ void (document.fullscreenElement ? document.exitFullscreen() : muxPlayerRef.current.requestFullscreen());
3181
+ }
3182
+ }
3183
+ window.document.addEventListener("keydown", handleUserKeyPress);
3184
+ return () => {
3185
+ window.document.removeEventListener("keydown", handleUserKeyPress);
3186
+ };
3187
+ }, [muxPlayerRef]);
3188
+ const updatePreferences = useDebounce(() => {
3189
+ const player = muxPlayerRef.current;
3190
+ if (!player) return;
3191
+ const subs = Array.from(player.textTracks ?? []).find(
3192
+ (t) => t.kind === "subtitles"
3193
+ );
3194
+ const newPrefs = {
3195
+ playbackRate: player.playbackRate,
3196
+ volumeRate: player.volume,
3197
+ subtitle: subs ? { id: subs.id, mode: subs.mode } : { id: null, mode: "disabled" }
3198
+ };
3199
+ if (isDeepEqual(newPrefs, playerPreferencesRef.current)) return;
3200
+ fetcherRef.current.submit(newPrefs, {
3201
+ method: "POST",
3202
+ action: "/video-player",
3203
+ encType: "application/json"
3204
+ });
3205
+ }, 300);
3206
+ React.useEffect(() => {
3207
+ var _a, _b;
3208
+ if (!metadataLoaded) return;
3209
+ const textTracks = (_a = muxPlayerRef.current) == null ? void 0 : _a.textTracks;
3210
+ if (!textTracks) return;
3211
+ const subtitlePref = (_b = playerPreferencesRef.current) == null ? void 0 : _b.subtitle;
3212
+ if (subtitlePref == null ? void 0 : subtitlePref.id) {
3213
+ const preferredTextTrack = textTracks.getTrackById(subtitlePref.id);
3214
+ if (preferredTextTrack) {
3215
+ preferredTextTrack.mode = subtitlePref.mode ?? "hidden";
3216
+ }
3217
+ }
3218
+ textTracks.addEventListener("change", updatePreferences);
3219
+ return () => {
3220
+ textTracks.removeEventListener("change", updatePreferences);
3221
+ };
3222
+ }, [metadataLoaded, muxPlayerRef, playerPreferencesRef, updatePreferences]);
3223
+ return /* @__PURE__ */ jsx("div", { className: "flex aspect-video flex-col", children: /* @__PURE__ */ jsx(
3224
+ RealMuxPlayer,
3225
+ {
3226
+ ref: muxPlayerRef,
3227
+ playbackRates: [
3228
+ 0.5,
3229
+ 0.75,
3230
+ 1,
3231
+ 1.25,
3232
+ 1.5,
3233
+ 1.75,
3234
+ 2,
3235
+ 2.5,
3236
+ 3,
3237
+ 3.5,
3238
+ // lol, someone really asked for this and I think it's funny so let's do it
3239
+ // https://twitter.com/zackerydev/status/1710840197879918840
3240
+ 4
3241
+ ],
3242
+ volume: (playerPreferences == null ? void 0 : playerPreferences.volumeRate) ?? 1,
3243
+ playbackRate: (playerPreferences == null ? void 0 : playerPreferences.playbackRate) ?? 1,
3244
+ thumbnailTime: currentTime,
3245
+ onRateChange: updatePreferences,
3246
+ onVolumeChange: updatePreferences,
3247
+ streamType: "on-demand",
3248
+ defaultHiddenCaptions: true,
3249
+ currentTime,
3250
+ onTimeUpdate: () => {
3251
+ var _a;
3252
+ return sessionStorage.setItem(
3253
+ currentTimeSessionKey,
3254
+ JSON.stringify({
3255
+ time: (_a = muxPlayerRef.current) == null ? void 0 : _a.currentTime,
3256
+ expiresAt: new Date(Date.now() + 1e3 * 60 * 30).toISOString()
3257
+ })
3258
+ );
3259
+ },
3260
+ accentColor: "#427cf0",
3261
+ targetLiveWindow: NaN,
3262
+ onLoadedMetadata: () => setMetadataLoaded(true),
3263
+ minResolution: getMinResolutionValue(playerPreferences == null ? void 0 : playerPreferences.minResolution),
3264
+ maxResolution: getMaxResolutionValue(playerPreferences == null ? void 0 : playerPreferences.maxResolution),
3265
+ ...props
3266
+ }
3267
+ ) });
3268
+ }
3269
+ function isDeepEqual(obj1, obj2) {
3270
+ if (obj1 === obj2) return true;
3271
+ if (typeof obj1 !== typeof obj2) return false;
3272
+ if (typeof obj1 !== "object" || typeof obj2 !== "object") return false;
3273
+ if (obj1 === null || obj2 === null) return false;
3274
+ if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
3275
+ if (Array.isArray(obj1) && Array.isArray(obj2)) {
3276
+ if (obj1.length !== obj2.length) return false;
3277
+ for (let i = 0; i < obj1.length; i++) {
3278
+ if (!isDeepEqual(obj1[i], obj2[i])) return false;
3279
+ }
3280
+ return true;
3281
+ }
3282
+ const keys1 = Object.keys(obj1);
3283
+ const keys2 = Object.keys(obj2);
3284
+ if (keys1.length !== keys2.length) return false;
3285
+ for (const key of keys1) {
3286
+ if (!isDeepEqual(obj1[key], obj2[key])) return false;
3287
+ }
3288
+ return true;
3289
+ }
3290
+ function getMinResolutionValue(resolution) {
3291
+ if (!resolution) return void 0;
3292
+ if (resolution <= 480) return MinResolution.noLessThan480p;
3293
+ if (resolution <= 540) return MinResolution.noLessThan540p;
3294
+ if (resolution <= 720) return MinResolution.noLessThan720p;
3295
+ if (resolution <= 1080) return MinResolution.noLessThan1080p;
3296
+ if (resolution <= 1440) return MinResolution.noLessThan1440p;
3297
+ return MinResolution.noLessThan2160p;
3298
+ }
3299
+ function getMaxResolutionValue(resolution) {
3300
+ if (!resolution) return void 0;
3301
+ if (resolution <= 720) return MaxResolution.upTo720p;
3302
+ if (resolution <= 1080) return MaxResolution.upTo1080p;
3303
+ if (resolution <= 1440) return MaxResolution.upTo1440p;
3304
+ return MaxResolution.upTo2160p;
3305
+ }
3306
+ const route45 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3307
+ __proto__: null,
3308
+ MuxPlayer,
3309
+ action: action$9,
3310
+ usePlayerPreferences
3311
+ }, Symbol.toStringTag, { value: "Module" }));
3517
3312
  const handle$9 = {
3518
3313
  getSitemapEntries: () => null
3519
3314
  };
@@ -3522,7 +3317,7 @@ async function loader$u({ request }) {
3522
3317
  await requireAuthInfo({ request });
3523
3318
  return {};
3524
3319
  }
3525
- async function action$9({ request }) {
3320
+ async function action$8({ request }) {
3526
3321
  ensureUndeployed();
3527
3322
  const formData = await request.formData();
3528
3323
  const intent = formData.get("intent");
@@ -3542,6 +3337,18 @@ async function action$9({ request }) {
3542
3337
  description: `You are now ${optOut ? "invisible" : "visible"}.`,
3543
3338
  type: "success"
3544
3339
  });
3340
+ } else if (intent === "update-player-preferences") {
3341
+ const minResolution = formData.get("minResolution");
3342
+ const maxResolution = formData.get("maxResolution");
3343
+ await setPlayerPreferences({
3344
+ minResolution: minResolution ? Number(minResolution) : void 0,
3345
+ maxResolution: maxResolution ? Number(maxResolution) : void 0
3346
+ });
3347
+ return redirectWithToast("/account", {
3348
+ title: "Preferences updated",
3349
+ description: "Your video player preferences have been updated.",
3350
+ type: "success"
3351
+ });
3545
3352
  }
3546
3353
  return redirect$1("/account");
3547
3354
  }
@@ -3555,7 +3362,10 @@ function Account() {
3555
3362
  const user = useUser();
3556
3363
  const discordMember = useOptionalDiscordMember();
3557
3364
  const presencePreferences = usePresencePreferences();
3365
+ const playerPreferences = usePlayerPreferences();
3558
3366
  const connectDiscordURL = useConnectDiscordURL$1();
3367
+ const navigation = useNavigation();
3368
+ const isSubmitting = navigation.state === "submitting";
3559
3369
  return /* @__PURE__ */ jsxs("main", { className: "container flex h-full w-full max-w-3xl flex-grow flex-col items-center justify-center gap-4", children: [
3560
3370
  user.imageUrlLarge ? /* @__PURE__ */ jsx(
3561
3371
  "img",
@@ -3628,6 +3438,61 @@ function Account() {
3628
3438
  }
3629
3439
  )
3630
3440
  ] }),
3441
+ /* @__PURE__ */ jsx("hr", { className: "w-full" }),
3442
+ /* @__PURE__ */ jsxs("div", { children: [
3443
+ /* @__PURE__ */ jsx("h2", { className: "mb-2 text-xl", children: "Video Player Preferences" }),
3444
+ /* @__PURE__ */ jsxs(Form, { method: "post", className: "flex flex-col gap-4", children: [
3445
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3446
+ /* @__PURE__ */ jsx("label", { htmlFor: "minResolution", children: "Minimum Resolution:" }),
3447
+ /* @__PURE__ */ jsxs(
3448
+ "select",
3449
+ {
3450
+ id: "minResolution",
3451
+ name: "minResolution",
3452
+ defaultValue: playerPreferences == null ? void 0 : playerPreferences.minResolution,
3453
+ children: [
3454
+ /* @__PURE__ */ jsx("option", { value: "", children: "Auto" }),
3455
+ /* @__PURE__ */ jsx("option", { value: "480", children: "480p" }),
3456
+ /* @__PURE__ */ jsx("option", { value: "720", children: "720p" }),
3457
+ /* @__PURE__ */ jsx("option", { value: "1080", children: "1080p" }),
3458
+ /* @__PURE__ */ jsx("option", { value: "1440", children: "1440p" }),
3459
+ /* @__PURE__ */ jsx("option", { value: "2160", children: "2160p (4K)" })
3460
+ ]
3461
+ }
3462
+ )
3463
+ ] }),
3464
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3465
+ /* @__PURE__ */ jsx("label", { htmlFor: "maxResolution", children: "Maximum Resolution:" }),
3466
+ /* @__PURE__ */ jsxs(
3467
+ "select",
3468
+ {
3469
+ id: "maxResolution",
3470
+ name: "maxResolution",
3471
+ defaultValue: playerPreferences == null ? void 0 : playerPreferences.maxResolution,
3472
+ children: [
3473
+ /* @__PURE__ */ jsx("option", { value: "", children: "Auto" }),
3474
+ /* @__PURE__ */ jsx("option", { value: "720", children: "720p" }),
3475
+ /* @__PURE__ */ jsx("option", { value: "1080", children: "1080p" }),
3476
+ /* @__PURE__ */ jsx("option", { value: "1440", children: "1440p" }),
3477
+ /* @__PURE__ */ jsx("option", { value: "2160", children: "2160p (4K)" })
3478
+ ]
3479
+ }
3480
+ )
3481
+ ] }),
3482
+ /* @__PURE__ */ jsx(
3483
+ Button,
3484
+ {
3485
+ varient: "mono",
3486
+ type: "submit",
3487
+ name: "intent",
3488
+ value: "update-player-preferences",
3489
+ disabled: isSubmitting,
3490
+ children: isSubmitting ? "Updating..." : "Update Player Preferences"
3491
+ }
3492
+ )
3493
+ ] })
3494
+ ] }),
3495
+ /* @__PURE__ */ jsx("hr", { className: "w-full" }),
3631
3496
  /* @__PURE__ */ jsxs("p", { children: [
3632
3497
  "Check",
3633
3498
  " ",
@@ -3646,7 +3511,7 @@ function Account() {
3646
3511
  }
3647
3512
  const route3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3648
3513
  __proto__: null,
3649
- action: action$9,
3514
+ action: action$8,
3650
3515
  default: Account,
3651
3516
  handle: handle$9,
3652
3517
  loader: loader$u
@@ -3813,7 +3678,7 @@ async function loader$s(args) {
3813
3678
  api.cleanupError(error);
3814
3679
  }
3815
3680
  }
3816
- async function action$8(args) {
3681
+ async function action$7(args) {
3817
3682
  const api = await getApiModule(args);
3818
3683
  invariantResponse(
3819
3684
  api.mod.action,
@@ -3901,7 +3766,7 @@ async function getApiModule({ request, params }) {
3901
3766
  }
3902
3767
  const route5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3903
3768
  __proto__: null,
3904
- action: action$8,
3769
+ action: action$7,
3905
3770
  loader: loader$s
3906
3771
  }, Symbol.toStringTag, { value: "Module" }));
3907
3772
  async function loader$r({ request, params }) {
@@ -4353,210 +4218,6 @@ const route12 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
4353
4218
  default: ExercisesLayout,
4354
4219
  handle: handle$7
4355
4220
  }, Symbol.toStringTag, { value: "Module" }));
4356
- const PlaybackTimeSchema = z.object({
4357
- time: z.number(),
4358
- expiresAt: z.string()
4359
- }).transform((data2) => {
4360
- return { time: Number(data2.time), expiresAt: new Date(data2.expiresAt) };
4361
- });
4362
- function usePlayerPreferences() {
4363
- var _a;
4364
- const data2 = useRouteLoaderData("root");
4365
- return ((_a = data2 == null ? void 0 : data2.preferences) == null ? void 0 : _a.player) ?? null;
4366
- }
4367
- const ignoredInputs = [
4368
- "INPUT",
4369
- "SELECT",
4370
- "BUTTON",
4371
- "TEXTAREA",
4372
- "MUX-PLAYER",
4373
- "SUMMARY"
4374
- ];
4375
- const ignoredRoles = ["button", "option", "combobox", "tab", "tablist"];
4376
- function shouldIgnoreHotkey(el) {
4377
- let current = el;
4378
- while (current) {
4379
- if (!(current instanceof HTMLElement)) return false;
4380
- const isIgnored = ignoredInputs.includes(current.tagName) || ignoredRoles.includes(current.getAttribute("role") || "") || current.isContentEditable;
4381
- if (isIgnored) return true;
4382
- current = current.parentElement;
4383
- }
4384
- return false;
4385
- }
4386
- async function action$7({ request }) {
4387
- const result = PlayerPreferencesSchema.safeParse(await request.json());
4388
- if (!result.success) {
4389
- return unstable_data$1({ status: "error", error: result.error.flatten() }, {
4390
- status: 400
4391
- });
4392
- }
4393
- await setPlayerPreferences(result.data);
4394
- return { status: "success" };
4395
- }
4396
- function useLatest(value) {
4397
- const ref = React.useRef(value);
4398
- React.useEffect(() => {
4399
- ref.current = value;
4400
- }, [value]);
4401
- return ref;
4402
- }
4403
- function MuxPlayer({
4404
- muxPlayerRef,
4405
- ...props
4406
- }) {
4407
- const playerPreferences = usePlayerPreferences();
4408
- const playerPreferencesFetcher = useFetcher();
4409
- const [metadataLoaded, setMetadataLoaded] = React.useState(false);
4410
- const currentTimeSessionKey = `${props.playbackId}:currentTime`;
4411
- const [currentTime, setCurrentTime] = React.useState(0);
4412
- const fetcherRef = useLatest(playerPreferencesFetcher);
4413
- const playerPreferencesRef = useLatest(playerPreferences);
4414
- React.useEffect(() => {
4415
- if (typeof document === "undefined") return;
4416
- const stored = sessionStorage.getItem(currentTimeSessionKey);
4417
- if (!stored) return;
4418
- try {
4419
- const { time: time2, expiresAt } = PlaybackTimeSchema.parse(JSON.parse(stored));
4420
- if (expiresAt.getTime() < Date.now()) throw new Error("Time expired");
4421
- setCurrentTime(time2);
4422
- } catch {
4423
- sessionStorage.removeItem(currentTimeSessionKey);
4424
- }
4425
- }, [currentTimeSessionKey]);
4426
- React.useEffect(() => {
4427
- function handleUserKeyPress(e) {
4428
- if (!muxPlayerRef.current) return;
4429
- const activeElement = document.activeElement;
4430
- if (shouldIgnoreHotkey(activeElement)) return;
4431
- if (shouldIgnoreHotkey(e.target)) return;
4432
- if (e.key === " ") {
4433
- e.preventDefault();
4434
- void (muxPlayerRef.current.paused ? muxPlayerRef.current.play() : muxPlayerRef.current.pause());
4435
- }
4436
- if (e.key === "ArrowRight") {
4437
- e.preventDefault();
4438
- muxPlayerRef.current.currentTime = muxPlayerRef.current.currentTime + (muxPlayerRef.current.forwardSeekOffset || 10);
4439
- }
4440
- if (e.key === "ArrowLeft") {
4441
- e.preventDefault();
4442
- muxPlayerRef.current.currentTime = muxPlayerRef.current.currentTime - (muxPlayerRef.current.forwardSeekOffset || 10);
4443
- }
4444
- if (e.key === "f" && !e.metaKey && !e.ctrlKey) {
4445
- e.preventDefault();
4446
- void (document.fullscreenElement ? document.exitFullscreen() : muxPlayerRef.current.requestFullscreen());
4447
- }
4448
- }
4449
- window.document.addEventListener("keydown", handleUserKeyPress);
4450
- return () => {
4451
- window.document.removeEventListener("keydown", handleUserKeyPress);
4452
- };
4453
- }, [muxPlayerRef]);
4454
- const updatePreferences = useDebounce(() => {
4455
- const player = muxPlayerRef.current;
4456
- if (!player) return;
4457
- const subs = Array.from(player.textTracks ?? []).find(
4458
- (t) => t.kind === "subtitles"
4459
- );
4460
- const newPrefs = {
4461
- playbackRate: player.playbackRate,
4462
- volumeRate: player.volume,
4463
- subtitle: subs ? { id: subs.id, mode: subs.mode } : { id: null, mode: "disabled" }
4464
- };
4465
- if (isDeepEqual(newPrefs, playerPreferencesRef.current)) return;
4466
- fetcherRef.current.submit(newPrefs, {
4467
- method: "POST",
4468
- action: "/video-player",
4469
- encType: "application/json"
4470
- });
4471
- }, 300);
4472
- React.useEffect(() => {
4473
- var _a, _b;
4474
- if (!metadataLoaded) return;
4475
- const textTracks = (_a = muxPlayerRef.current) == null ? void 0 : _a.textTracks;
4476
- if (!textTracks) return;
4477
- const subtitlePref = (_b = playerPreferencesRef.current) == null ? void 0 : _b.subtitle;
4478
- if (subtitlePref == null ? void 0 : subtitlePref.id) {
4479
- const preferredTextTrack = textTracks.getTrackById(subtitlePref.id);
4480
- if (preferredTextTrack) {
4481
- preferredTextTrack.mode = subtitlePref.mode ?? "hidden";
4482
- }
4483
- }
4484
- textTracks.addEventListener("change", updatePreferences);
4485
- return () => {
4486
- textTracks.removeEventListener("change", updatePreferences);
4487
- };
4488
- }, [metadataLoaded, muxPlayerRef, playerPreferencesRef, updatePreferences]);
4489
- return /* @__PURE__ */ jsx("div", { className: "flex aspect-video flex-col", children: /* @__PURE__ */ jsx(
4490
- RealMuxPlayer,
4491
- {
4492
- ref: muxPlayerRef,
4493
- playbackRates: [
4494
- 0.5,
4495
- 0.75,
4496
- 1,
4497
- 1.25,
4498
- 1.5,
4499
- 1.75,
4500
- 2,
4501
- 2.5,
4502
- 3,
4503
- 3.5,
4504
- // lol, someone really asked for this and I think it's funny so let's do it
4505
- // https://twitter.com/zackerydev/status/1710840197879918840
4506
- 4
4507
- ],
4508
- volume: (playerPreferences == null ? void 0 : playerPreferences.volumeRate) ?? 1,
4509
- playbackRate: (playerPreferences == null ? void 0 : playerPreferences.playbackRate) ?? 1,
4510
- thumbnailTime: currentTime,
4511
- onRateChange: updatePreferences,
4512
- onVolumeChange: updatePreferences,
4513
- streamType: "on-demand",
4514
- defaultHiddenCaptions: true,
4515
- currentTime,
4516
- onTimeUpdate: () => {
4517
- var _a;
4518
- return sessionStorage.setItem(
4519
- currentTimeSessionKey,
4520
- JSON.stringify({
4521
- time: (_a = muxPlayerRef.current) == null ? void 0 : _a.currentTime,
4522
- expiresAt: new Date(Date.now() + 1e3 * 60 * 30).toISOString()
4523
- })
4524
- );
4525
- },
4526
- accentColor: "#427cf0",
4527
- targetLiveWindow: NaN,
4528
- onLoadedMetadata: () => setMetadataLoaded(true),
4529
- ...props
4530
- }
4531
- ) });
4532
- }
4533
- function isDeepEqual(obj1, obj2) {
4534
- if (obj1 === obj2) return true;
4535
- if (typeof obj1 !== typeof obj2) return false;
4536
- if (typeof obj1 !== "object" || typeof obj2 !== "object") return false;
4537
- if (obj1 === null || obj2 === null) return false;
4538
- if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
4539
- if (Array.isArray(obj1) && Array.isArray(obj2)) {
4540
- if (obj1.length !== obj2.length) return false;
4541
- for (let i = 0; i < obj1.length; i++) {
4542
- if (!isDeepEqual(obj1[i], obj2[i])) return false;
4543
- }
4544
- return true;
4545
- }
4546
- const keys1 = Object.keys(obj1);
4547
- const keys2 = Object.keys(obj2);
4548
- if (keys1.length !== keys2.length) return false;
4549
- for (const key of keys1) {
4550
- if (!isDeepEqual(obj1[key], obj2[key])) return false;
4551
- }
4552
- return true;
4553
- }
4554
- const route45 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4555
- __proto__: null,
4556
- MuxPlayer,
4557
- action: action$7,
4558
- usePlayerPreferences
4559
- }, Symbol.toStringTag, { value: "Module" }));
4560
4221
  function Loading({
4561
4222
  className,
4562
4223
  children = "Loading"
@@ -10110,7 +9771,20 @@ async function registerDevice() {
10110
9771
  return;
10111
9772
  }
10112
9773
  const userinfo = await client.userinfo(tokenSet);
10113
- await setAuthInfo({ tokenSet, email: userinfo.email, name: userinfo.name });
9774
+ let id;
9775
+ if (typeof userinfo.id === "string") {
9776
+ id = userinfo.id;
9777
+ } else {
9778
+ console.warn("[UNEXPECTED] User ID is not a string:", userinfo.id);
9779
+ id = userinfo.email ? md5(userinfo.email) : createId();
9780
+ }
9781
+ await setAuthInfo({
9782
+ id,
9783
+ tokenSet,
9784
+ email: userinfo.email,
9785
+ name: userinfo.name
9786
+ });
9787
+ await getUserInfo({ forceFresh: true });
10114
9788
  authEmitter.emit(EVENTS.AUTH_RESOLVED);
10115
9789
  } catch (error) {
10116
9790
  authEmitter.emit(EVENTS.AUTH_REJECTED, {
@@ -11299,7 +10973,7 @@ const route41 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
11299
10973
  __proto__: null,
11300
10974
  loader
11301
10975
  }, Symbol.toStringTag, { value: "Module" }));
11302
- const serverManifest = { "entry": { "module": "/assets/entry.client-DqIWuxf8.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes": { "root": { "id": "root", "parentId": void 0, "path": "", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/root-a3d3Qwip.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/components-Be92gVxW.js", "/assets/misc-DUy_whwE.js", "/assets/pe-ChIwTk8v.js", "/assets/error-boundary-BZA-ffa8.js", "/assets/progress-bar-BaTU3Yx_.js", "/assets/index-C9Hx0Dey.js", "/assets/tooltip-DO9uwurQ.js", "/assets/index-Bdg3v8tC.js", "/assets/presence-DJGFvdDh.js", "/assets/seo-pBpFCWsy.js", "/assets/request-info-ByUEfOil.js"], "css": [] }, "routes/$": { "id": "routes/$", "parentId": "root", "path": "*", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_-BrkpfnBb.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/error-boundary-BZA-ffa8.js", "/assets/misc-DUy_whwE.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes/_app+/_layout": { "id": "routes/_app+/_layout", "parentId": "root", "path": void 0, "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-Cbz7Qt-S.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/misc-DUy_whwE.js", "/assets/pe-ChIwTk8v.js", "/assets/product-DIAmCwmZ.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/tooltip-DO9uwurQ.js", "/assets/index-BCTr8uu6.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/presence-DJGFvdDh.js", "/assets/progress-DQt_Bn9o.js", "/assets/index-Bdg3v8tC.js", "/assets/components-Be92gVxW.js", "/assets/request-info-ByUEfOil.js"], "css": [] }, "routes/_app+/account": { "id": "routes/_app+/account", "parentId": "routes/_app+/_layout", "path": "account", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/account-C4Piztoz.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/button-_qPvcoqR.js", "/assets/misc-DUy_whwE.js", "/assets/tooltip-DO9uwurQ.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/presence-DJGFvdDh.js", "/assets/components-Be92gVxW.js", "/assets/request-info-ByUEfOil.js"], "css": [] }, "routes/_app+/app.$appName+/$": { "id": "routes/_app+/app.$appName+/$", "parentId": "routes/_app+/_layout", "path": "app/:appName/*", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/api.$": { "id": "routes/_app+/app.$appName+/api.$", "parentId": "routes/_app+/_layout", "path": "app/:appName/api/*", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/api._-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/epic_ws[.js]": { "id": "routes/_app+/app.$appName+/epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/:appName/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/index": { "id": "routes/_app+/app.$appName+/index", "parentId": "routes/_app+/_layout", "path": "app/:appName/", "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/test.$testName": { "id": "routes/_app+/app.$appName+/test.$testName", "parentId": "routes/_app+/_layout", "path": "app/:appName/test/:testName", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test._testName-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/test.epic_ws[.js]": { "id": "routes/_app+/app.$appName+/test.epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/:appName/test/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test.epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.epic_ws[.js]": { "id": "routes/_app+/app.epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/app.epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/discord": { "id": "routes/_app+/discord", "parentId": "routes/_app+/_layout", "path": "discord", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord-Bdnx7fu-.js", "imports": ["/assets/discord-BUWZUTEC.js", "/assets/index-BFGhCX_U.js", "/assets/misc-DUy_whwE.js", "/assets/components-Be92gVxW.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js"], "css": [] }, "routes/_app+/exercise+/_layout": { "id": "routes/_app+/exercise+/_layout", "parentId": "routes/_app+/_layout", "path": "exercise", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-88n0To1b.js", "imports": ["/assets/index-BFGhCX_U.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber": { "id": "routes/_app+/exercise+/$exerciseNumber", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber-DCSM0NCG.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/index-BCxBKsqT.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/mdx-CEjzXoEx.js", "/assets/progress-DQt_Bn9o.js", "/assets/misc-DUy_whwE.js", "/assets/seo-pBpFCWsy.js", "/assets/components-Be92gVxW.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber/:stepNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber_._stepNumber-BIMJh_sg.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/misc-DUy_whwE.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "path": ":type", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_layout-DTAM9xh5.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/index-BCxBKsqT.js", "/assets/error-boundary-BZA-ffa8.js", "/assets/nav-chevrons-DnR25VLp.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/mdx-CEjzXoEx.js", "/assets/progress-DQt_Bn9o.js", "/assets/set-playground-CBHBA46B.js", "/assets/seo-pBpFCWsy.js", "/assets/misc-DUy_whwE.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/tooltip-DO9uwurQ.js", "/assets/request-info-ByUEfOil.js", "/assets/components-Be92gVxW.js", "/assets/index-BCTr8uu6.js", "/assets/progress-bar-BaTU3Yx_.js", "/assets/pe-ChIwTk8v.js", "/assets/index-DZDhtMuq.js", "/assets/index-Bdg3v8tC.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/app": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/app", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": "app", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/app-DgTXXO8s.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/preview-DaZd0wMb.js", "/assets/components-Be92gVxW.js", "/assets/misc-DUy_whwE.js", "/assets/request-info-ByUEfOil.js", "/assets/button-_qPvcoqR.js", "/assets/loading-XhMtj4mp.js", "/assets/index-Bdg3v8tC.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/index-Bi1TbRTj.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/tooltip-DO9uwurQ.js", "/assets/index-DZDhtMuq.js", "/assets/misc-DUy_whwE.js", "/assets/diff-8nlDkmpc.js", "/assets/error-boundary-BZA-ffa8.js", "/assets/loading-XhMtj4mp.js", "/assets/discord-BUWZUTEC.js", "/assets/components-Be92gVxW.js", "/assets/index-C9Hx0Dey.js", "/assets/set-playground-CBHBA46B.js", "/assets/tests-DbuyD2cI.js", "/assets/preview-DaZd0wMb.js", "/assets/index-BCTr8uu6.js", "/assets/accordion-D9-D-n9p.js", "/assets/mdx-CEjzXoEx.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/pe-ChIwTk8v.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/use-event-source-x59d4R2Z.js", "/assets/button-_qPvcoqR.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/test": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/test", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": "test", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test-DoKJvNug.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/tests-DbuyD2cI.js", "/assets/components-Be92gVxW.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/misc-DUy_whwE.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/accordion-D9-D-n9p.js", "/assets/index-DZDhtMuq.js", "/assets/index-BCTr8uu6.js", "/assets/use-event-source-x59d4R2Z.js", "/assets/set-playground-CBHBA46B.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_._stepNumber.index-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.finished": { "id": "routes/_app+/exercise+/$exerciseNumber_.finished", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber/finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_.finished-nbpk1ToO.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/index-BCxBKsqT.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/loading-XhMtj4mp.js", "/assets/nav-chevrons-DnR25VLp.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/mdx-CEjzXoEx.js", "/assets/progress-DQt_Bn9o.js", "/assets/misc-DUy_whwE.js", "/assets/seo-pBpFCWsy.js", "/assets/components-Be92gVxW.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/finished": { "id": "routes/_app+/finished", "parentId": "routes/_app+/_layout", "path": "finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/finished-C0cpfAFL.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/index-BCxBKsqT.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/loading-XhMtj4mp.js", "/assets/nav-chevrons-DnR25VLp.js", "/assets/revalidation-ws-dUa9CAqr.js", "/assets/mdx-CEjzXoEx.js", "/assets/misc-DUy_whwE.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-DQt_Bn9o.js", "/assets/components-Be92gVxW.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/index": { "id": "routes/_app+/index", "parentId": "routes/_app+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/index-Ca4vBON4.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/index-BCxBKsqT.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/error-boundary-BZA-ffa8.js", "/assets/mdx-CEjzXoEx.js", "/assets/misc-DUy_whwE.js", "/assets/progress-DQt_Bn9o.js", "/assets/components-Be92gVxW.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/login": { "id": "routes/_app+/login", "parentId": "routes/_app+/_layout", "path": "login", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/login-C1oOgi98.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/use-event-source-x59d4R2Z.js", "/assets/button-_qPvcoqR.js", "/assets/loading-XhMtj4mp.js", "/assets/product-DIAmCwmZ.js", "/assets/workshop-config-WVltG_BV.js", "/assets/request-info-ByUEfOil.js", "/assets/components-Be92gVxW.js", "/assets/misc-DUy_whwE.js", "/assets/index-Bdg3v8tC.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js"], "css": [] }, "routes/_app+/support": { "id": "routes/_app+/support", "parentId": "routes/_app+/_layout", "path": "support", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/support-CIz02V_r.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes/admin+/_layout": { "id": "routes/admin+/_layout", "parentId": "root", "path": "admin", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-Dfmv2zcn.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes/admin+/apps": { "id": "routes/admin+/apps", "parentId": "routes/admin+/_layout", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/admin+/cache": { "id": "routes/admin+/cache", "parentId": "routes/admin+/_layout", "path": "cache", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/cache-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/admin+/index": { "id": "routes/admin+/index", "parentId": "routes/admin+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-pkiQppkK.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/misc-DUy_whwE.js", "/assets/tooltip-DO9uwurQ.js", "/assets/progress-DQt_Bn9o.js", "/assets/components-Be92gVxW.js", "/assets/pe-ChIwTk8v.js"], "css": [] }, "routes/admin+/version": { "id": "routes/admin+/version", "parentId": "routes/admin+/_layout", "path": "version", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/version-lxUUxt3s.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/workshop-config-WVltG_BV.js", "/assets/components-Be92gVxW.js"], "css": [] }, "routes/apps": { "id": "routes/apps", "parentId": "root", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-DP2rzg_V.js", "imports": [], "css": [] }, "routes/diff": { "id": "routes/diff", "parentId": "root", "path": "diff", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/diff-BhRAIPKc.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/misc-DUy_whwE.js", "/assets/diff-8nlDkmpc.js", "/assets/nav-chevrons-DnR25VLp.js", "/assets/components-Be92gVxW.js", "/assets/accordion-D9-D-n9p.js", "/assets/tooltip-DO9uwurQ.js", "/assets/index-DZDhtMuq.js", "/assets/index-BCTr8uu6.js", "/assets/mdx-CEjzXoEx.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/pe-ChIwTk8v.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js", "/assets/progress-bar-BaTU3Yx_.js", "/assets/revalidation-ws-dUa9CAqr.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/exercises": { "id": "routes/exercises", "parentId": "root", "path": "exercises", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/exercises-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/launch-editor": { "id": "routes/launch-editor", "parentId": "root", "path": "launch-editor", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/launch-editor-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/login-sse": { "id": "routes/login-sse", "parentId": "root", "path": "login-sse", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/login-sse-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/og": { "id": "routes/og", "parentId": "root", "path": "og", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/og-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/onboarding": { "id": "routes/onboarding", "parentId": "root", "path": "onboarding", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/onboarding-C2YNq60k.js", "imports": ["/assets/index-BFGhCX_U.js", "/assets/button-_qPvcoqR.js", "/assets/epic-video-Bp4BOD2R.js", "/assets/components-Be92gVxW.js", "/assets/misc-DUy_whwE.js", "/assets/index-Bdg3v8tC.js", "/assets/request-info-ByUEfOil.js", "/assets/tooltip-DO9uwurQ.js", "/assets/pe-ChIwTk8v.js", "/assets/loading-XhMtj4mp.js", "/assets/user-Bv6wYhQP.js", "/assets/workshop-config-WVltG_BV.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/processes": { "id": "routes/processes", "parentId": "root", "path": "processes", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/processes-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/progress": { "id": "routes/progress", "parentId": "root", "path": "progress", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/progress-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/robots[.]txt": { "id": "routes/robots[.]txt", "parentId": "root", "path": "robots.txt", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/robots_._txt-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/set-playground": { "id": "routes/set-playground", "parentId": "root", "path": "set-playground", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/set-playground-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/sitemap[.]xml": { "id": "routes/sitemap[.]xml", "parentId": "root", "path": "sitemap.xml", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/sitemap_._xml-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/start": { "id": "routes/start", "parentId": "root", "path": "start", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/start-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/test": { "id": "routes/test", "parentId": "root", "path": "test", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/theme/index": { "id": "routes/theme/index", "parentId": "root", "path": "theme", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-DP2rzg_V.js", "imports": [], "css": [] }, "routes/video-player/index": { "id": "routes/video-player/index", "parentId": "root", "path": "video-player", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-K6Dvbx-E.js", "imports": [], "css": [] } }, "url": "/assets/manifest-e5b2a6e1.js", "version": "e5b2a6e1" };
10976
+ const serverManifest = { "entry": { "module": "/assets/entry.client-CrlHhRMR.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes": { "root": { "id": "root", "parentId": void 0, "path": "", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/root-Bg-hxaOK.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/components-DUNtf72c.js", "/assets/misc-DIdEn_jt.js", "/assets/pe-D5h19vSo.js", "/assets/error-boundary-3zItlMUO.js", "/assets/progress-bar-CBDBzRQ2.js", "/assets/index-LjRZeU7x.js", "/assets/tooltip-BoVikCa-.js", "/assets/index-DH1w3QmP.js", "/assets/presence-D1DPz__2.js", "/assets/seo-pBpFCWsy.js", "/assets/request-info-vBkaf3Rk.js"], "css": [] }, "routes/$": { "id": "routes/$", "parentId": "root", "path": "*", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_-hAKYb2AG.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/error-boundary-3zItlMUO.js", "/assets/misc-DIdEn_jt.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes/_app+/_layout": { "id": "routes/_app+/_layout", "parentId": "root", "path": void 0, "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-Bu0lel3p.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/misc-DIdEn_jt.js", "/assets/pe-D5h19vSo.js", "/assets/product-CYOFfeJM.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/tooltip-BoVikCa-.js", "/assets/index-CV3nxGFp.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/presence-D1DPz__2.js", "/assets/progress-D6SP0Gec.js", "/assets/index-DH1w3QmP.js", "/assets/components-DUNtf72c.js", "/assets/request-info-vBkaf3Rk.js"], "css": [] }, "routes/_app+/account": { "id": "routes/_app+/account", "parentId": "routes/_app+/_layout", "path": "account", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/account-DLDPrc9J.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/button-39zQyNX6.js", "/assets/misc-DIdEn_jt.js", "/assets/tooltip-BoVikCa-.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/presence-D1DPz__2.js", "/assets/index-BajUQsFT.js", "/assets/components-DUNtf72c.js", "/assets/request-info-vBkaf3Rk.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/app.$appName+/$": { "id": "routes/_app+/app.$appName+/$", "parentId": "routes/_app+/_layout", "path": "app/:appName/*", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/api.$": { "id": "routes/_app+/app.$appName+/api.$", "parentId": "routes/_app+/_layout", "path": "app/:appName/api/*", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/api._-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/epic_ws[.js]": { "id": "routes/_app+/app.$appName+/epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/:appName/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/index": { "id": "routes/_app+/app.$appName+/index", "parentId": "routes/_app+/_layout", "path": "app/:appName/", "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/test.$testName": { "id": "routes/_app+/app.$appName+/test.$testName", "parentId": "routes/_app+/_layout", "path": "app/:appName/test/:testName", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test._testName-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/test.epic_ws[.js]": { "id": "routes/_app+/app.$appName+/test.epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/:appName/test/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test.epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.epic_ws[.js]": { "id": "routes/_app+/app.epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/app.epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/discord": { "id": "routes/_app+/discord", "parentId": "routes/_app+/_layout", "path": "discord", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord-CpIgvYus.js", "imports": ["/assets/discord-CEOqKs_c.js", "/assets/index-C_B1-9rF.js", "/assets/misc-DIdEn_jt.js", "/assets/components-DUNtf72c.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js"], "css": [] }, "routes/_app+/exercise+/_layout": { "id": "routes/_app+/exercise+/_layout", "parentId": "routes/_app+/_layout", "path": "exercise", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-B4JGpA3A.js", "imports": ["/assets/index-C_B1-9rF.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber": { "id": "routes/_app+/exercise+/$exerciseNumber", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber-PTdG9GGB.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/index-CLNXC84j.js", "/assets/epic-video-D8ex9vao.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/mdx-C9dqA6IZ.js", "/assets/progress-D6SP0Gec.js", "/assets/misc-DIdEn_jt.js", "/assets/seo-pBpFCWsy.js", "/assets/components-DUNtf72c.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber/:stepNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber_._stepNumber-03erOIGo.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/misc-DIdEn_jt.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "path": ":type", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_layout-DHoH74NH.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/index-CLNXC84j.js", "/assets/error-boundary-3zItlMUO.js", "/assets/nav-chevrons-B3SvZV8B.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/mdx-C9dqA6IZ.js", "/assets/progress-D6SP0Gec.js", "/assets/set-playground-CMoUFgkO.js", "/assets/seo-pBpFCWsy.js", "/assets/misc-DIdEn_jt.js", "/assets/epic-video-D8ex9vao.js", "/assets/tooltip-BoVikCa-.js", "/assets/request-info-vBkaf3Rk.js", "/assets/components-DUNtf72c.js", "/assets/index-CV3nxGFp.js", "/assets/progress-bar-CBDBzRQ2.js", "/assets/pe-D5h19vSo.js", "/assets/index-DFqQCjCw.js", "/assets/index-DH1w3QmP.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/app": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/app", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": "app", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/app-DJDjmdlu.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/preview-BEtmdi0E.js", "/assets/components-DUNtf72c.js", "/assets/misc-DIdEn_jt.js", "/assets/request-info-vBkaf3Rk.js", "/assets/button-39zQyNX6.js", "/assets/loading-DW_I206H.js", "/assets/index-DH1w3QmP.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/index", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/index-DE-jwnOP.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/tooltip-BoVikCa-.js", "/assets/index-DFqQCjCw.js", "/assets/misc-DIdEn_jt.js", "/assets/diff-BNCREJvf.js", "/assets/error-boundary-3zItlMUO.js", "/assets/loading-DW_I206H.js", "/assets/discord-CEOqKs_c.js", "/assets/components-DUNtf72c.js", "/assets/index-LjRZeU7x.js", "/assets/set-playground-CMoUFgkO.js", "/assets/tests-CiM4RPOf.js", "/assets/preview-BEtmdi0E.js", "/assets/index-CV3nxGFp.js", "/assets/accordion-DLg7gJkp.js", "/assets/mdx-C9dqA6IZ.js", "/assets/epic-video-D8ex9vao.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/use-event-source-M87p8Tme.js", "/assets/button-39zQyNX6.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/test": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/test", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.$type+/_layout", "path": "test", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test-C8wkLh9a.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/tests-CiM4RPOf.js", "/assets/components-DUNtf72c.js", "/assets/epic-video-D8ex9vao.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/misc-DIdEn_jt.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/accordion-DLg7gJkp.js", "/assets/index-DFqQCjCw.js", "/assets/index-CV3nxGFp.js", "/assets/use-event-source-M87p8Tme.js", "/assets/set-playground-CMoUFgkO.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index": { "id": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber.index", "parentId": "routes/_app+/exercise+/$exerciseNumber_.$stepNumber", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_._stepNumber.index-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/exercise+/$exerciseNumber_.finished": { "id": "routes/_app+/exercise+/$exerciseNumber_.finished", "parentId": "routes/_app+/exercise+/_layout", "path": ":exerciseNumber/finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_.finished-DQg4F1NL.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/index-CLNXC84j.js", "/assets/epic-video-D8ex9vao.js", "/assets/loading-DW_I206H.js", "/assets/nav-chevrons-B3SvZV8B.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/mdx-C9dqA6IZ.js", "/assets/progress-D6SP0Gec.js", "/assets/misc-DIdEn_jt.js", "/assets/seo-pBpFCWsy.js", "/assets/components-DUNtf72c.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/finished": { "id": "routes/_app+/finished", "parentId": "routes/_app+/_layout", "path": "finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/finished-rUzUjnEm.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/index-CLNXC84j.js", "/assets/epic-video-D8ex9vao.js", "/assets/loading-DW_I206H.js", "/assets/nav-chevrons-B3SvZV8B.js", "/assets/revalidation-ws-DK5QOPlL.js", "/assets/mdx-C9dqA6IZ.js", "/assets/misc-DIdEn_jt.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-D6SP0Gec.js", "/assets/components-DUNtf72c.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/index": { "id": "routes/_app+/index", "parentId": "routes/_app+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/index-mivnjq36.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/index-CLNXC84j.js", "/assets/epic-video-D8ex9vao.js", "/assets/error-boundary-3zItlMUO.js", "/assets/mdx-C9dqA6IZ.js", "/assets/misc-DIdEn_jt.js", "/assets/progress-D6SP0Gec.js", "/assets/components-DUNtf72c.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/_app+/login": { "id": "routes/_app+/login", "parentId": "routes/_app+/_layout", "path": "login", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/login-CdNej0Z7.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/use-event-source-M87p8Tme.js", "/assets/button-39zQyNX6.js", "/assets/loading-DW_I206H.js", "/assets/product-CYOFfeJM.js", "/assets/workshop-config-C5sYl312.js", "/assets/request-info-vBkaf3Rk.js", "/assets/components-DUNtf72c.js", "/assets/misc-DIdEn_jt.js", "/assets/index-DH1w3QmP.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js"], "css": [] }, "routes/_app+/support": { "id": "routes/_app+/support", "parentId": "routes/_app+/_layout", "path": "support", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/support-CPzYlWkd.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes/admin+/_layout": { "id": "routes/admin+/_layout", "parentId": "root", "path": "admin", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-BJbMl6SJ.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes/admin+/apps": { "id": "routes/admin+/apps", "parentId": "routes/admin+/_layout", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/admin+/cache": { "id": "routes/admin+/cache", "parentId": "routes/admin+/_layout", "path": "cache", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/cache-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/admin+/index": { "id": "routes/admin+/index", "parentId": "routes/admin+/_layout", "path": void 0, "index": true, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-DDqzbGM2.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/misc-DIdEn_jt.js", "/assets/tooltip-BoVikCa-.js", "/assets/progress-D6SP0Gec.js", "/assets/components-DUNtf72c.js", "/assets/pe-D5h19vSo.js"], "css": [] }, "routes/admin+/version": { "id": "routes/admin+/version", "parentId": "routes/admin+/_layout", "path": "version", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/version-CIF3cX3N.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/workshop-config-C5sYl312.js", "/assets/components-DUNtf72c.js"], "css": [] }, "routes/apps": { "id": "routes/apps", "parentId": "root", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-DP2rzg_V.js", "imports": [], "css": [] }, "routes/diff": { "id": "routes/diff", "parentId": "root", "path": "diff", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/diff-B3oaU_KB.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/misc-DIdEn_jt.js", "/assets/diff-BNCREJvf.js", "/assets/nav-chevrons-B3SvZV8B.js", "/assets/components-DUNtf72c.js", "/assets/accordion-DLg7gJkp.js", "/assets/tooltip-BoVikCa-.js", "/assets/index-DFqQCjCw.js", "/assets/index-CV3nxGFp.js", "/assets/mdx-C9dqA6IZ.js", "/assets/epic-video-D8ex9vao.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js", "/assets/progress-bar-CBDBzRQ2.js", "/assets/revalidation-ws-DK5QOPlL.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/exercises": { "id": "routes/exercises", "parentId": "root", "path": "exercises", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/exercises-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/launch-editor": { "id": "routes/launch-editor", "parentId": "root", "path": "launch-editor", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/launch-editor-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/login-sse": { "id": "routes/login-sse", "parentId": "root", "path": "login-sse", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/login-sse-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/og": { "id": "routes/og", "parentId": "root", "path": "og", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/og-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/onboarding": { "id": "routes/onboarding", "parentId": "root", "path": "onboarding", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/onboarding-CC9zz4rl.js", "imports": ["/assets/index-C_B1-9rF.js", "/assets/button-39zQyNX6.js", "/assets/epic-video-D8ex9vao.js", "/assets/components-DUNtf72c.js", "/assets/misc-DIdEn_jt.js", "/assets/index-DH1w3QmP.js", "/assets/request-info-vBkaf3Rk.js", "/assets/tooltip-BoVikCa-.js", "/assets/pe-D5h19vSo.js", "/assets/index-BajUQsFT.js", "/assets/loading-DW_I206H.js", "/assets/user-CbbIYEs8.js", "/assets/workshop-config-C5sYl312.js"], "css": ["/assets/index-DUnRvy1A.css"] }, "routes/processes": { "id": "routes/processes", "parentId": "root", "path": "processes", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/processes-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/progress": { "id": "routes/progress", "parentId": "root", "path": "progress", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/progress-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/robots[.]txt": { "id": "routes/robots[.]txt", "parentId": "root", "path": "robots.txt", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/robots_._txt-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/set-playground": { "id": "routes/set-playground", "parentId": "root", "path": "set-playground", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/set-playground-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/sitemap[.]xml": { "id": "routes/sitemap[.]xml", "parentId": "root", "path": "sitemap.xml", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/sitemap_._xml-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/start": { "id": "routes/start", "parentId": "root", "path": "start", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/start-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/test": { "id": "routes/test", "parentId": "root", "path": "test", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/theme/index": { "id": "routes/theme/index", "parentId": "root", "path": "theme", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-DP2rzg_V.js", "imports": [], "css": [] }, "routes/video-player/index": { "id": "routes/video-player/index", "parentId": "root", "path": "video-player", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-K6Dvbx-E.js", "imports": [], "css": [] } }, "url": "/assets/manifest-62ea49c4.js", "version": "62ea49c4" };
11303
10977
  const mode = "production";
11304
10978
  const assetsBuildDirectory = "build/client";
11305
10979
  const basename = "/";