@epic-web/workshop-app 4.21.1 → 4.22.1

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 (81) hide show
  1. package/build/client/assets/_exerciseNumber-CpcJsfoV.js +2 -0
  2. package/build/client/assets/_exerciseNumber-CpcJsfoV.js.map +1 -0
  3. package/build/client/assets/_exerciseNumber_._stepNumber-B5pmJ29o.js.map +1 -1
  4. package/build/client/assets/_exerciseNumber_.finished-Ddb3J5ls.js +2 -0
  5. package/build/client/assets/_exerciseNumber_.finished-Ddb3J5ls.js.map +1 -0
  6. package/build/client/assets/{_layout-AgYGL72C.js → _layout-BLsKV5zF.js} +2 -2
  7. package/build/client/assets/_layout-BLsKV5zF.js.map +1 -0
  8. package/build/client/assets/_layout-CGLKLU3y.js +2 -0
  9. package/build/client/assets/_layout-CGLKLU3y.js.map +1 -0
  10. package/build/client/assets/_layout-DVR5FOsb.js +2 -0
  11. package/build/client/assets/_layout-DVR5FOsb.js.map +1 -0
  12. package/build/client/assets/app-BhrXo-z3.js +2 -0
  13. package/build/client/assets/{app-BiWJY58g.js.map → app-BhrXo-z3.js.map} +1 -1
  14. package/build/client/assets/diff-BNr0PhFR.js +2 -0
  15. package/build/client/assets/{diff-Cw8ktQ1-.js.map → diff-BNr0PhFR.js.map} +1 -1
  16. package/build/client/assets/{diff-CpFxuKA2.js → diff-Cr4LLBag.js} +2 -2
  17. package/build/client/assets/{diff-CpFxuKA2.js.map → diff-Cr4LLBag.js.map} +1 -1
  18. package/build/client/assets/{epic-video-iluUs1-s.js → epic-video-CxZy3iqK.js} +2 -2
  19. package/build/client/assets/{epic-video-iluUs1-s.js.map → epic-video-CxZy3iqK.js.map} +1 -1
  20. package/build/client/assets/finished-CU8QBU41.js +2 -0
  21. package/build/client/assets/finished-CU8QBU41.js.map +1 -0
  22. package/build/client/assets/index-6GyYWV9G.js +2 -0
  23. package/build/client/assets/{index-BATSX33w.js.map → index-6GyYWV9G.js.map} +1 -1
  24. package/build/client/assets/index-BotyhhDm.js +2 -0
  25. package/build/client/assets/index-BotyhhDm.js.map +1 -0
  26. package/build/client/assets/index-CWVcudJR.js +2 -0
  27. package/build/client/assets/{index-Dm1ll1kT.js.map → index-CWVcudJR.js.map} +1 -1
  28. package/build/client/assets/manifest-42dc4b5a.js +1 -0
  29. package/build/client/assets/mdx-D7ttfz2V.js +2 -0
  30. package/build/client/assets/mdx-D7ttfz2V.js.map +1 -0
  31. package/build/client/assets/onboarding-Co4cldZ0.js +2 -0
  32. package/build/client/assets/onboarding-Co4cldZ0.js.map +1 -0
  33. package/build/client/assets/pe-CUZaIcdt.js +2 -0
  34. package/build/client/assets/pe-CUZaIcdt.js.map +1 -0
  35. package/build/client/assets/preview-CdiZ0d0L.js +2 -0
  36. package/build/client/assets/preview-CdiZ0d0L.js.map +1 -0
  37. package/build/client/assets/progress-Biq-ngNF.js +2 -0
  38. package/build/client/assets/{progress-BsY6hGPp.js.map → progress-Biq-ngNF.js.map} +1 -1
  39. package/build/client/assets/{root-Tcnfz99O.js → root-D_FY953d.js} +2 -2
  40. package/build/client/assets/root-D_FY953d.js.map +1 -0
  41. package/build/client/assets/seo-pBpFCWsy.js.map +1 -1
  42. package/build/client/assets/set-playground-BXHckvUG.js +2 -0
  43. package/build/client/assets/set-playground-BXHckvUG.js.map +1 -0
  44. package/build/client/assets/test-_8PxC8xF.js +2 -0
  45. package/build/client/assets/{test-D9HsTvpe.js.map → test-_8PxC8xF.js.map} +1 -1
  46. package/build/client/assets/{tests-CmNNBElK.js → tests-t3Udab79.js} +2 -2
  47. package/build/client/assets/{tests-CmNNBElK.js.map → tests-t3Udab79.js.map} +1 -1
  48. package/build/server/index.js +371 -311
  49. package/build/server/index.js.map +1 -1
  50. package/package.json +3 -3
  51. package/build/client/assets/_exerciseNumber-Cq0nozYz.js +0 -2
  52. package/build/client/assets/_exerciseNumber-Cq0nozYz.js.map +0 -1
  53. package/build/client/assets/_exerciseNumber_.finished-Q9qnPuZX.js +0 -2
  54. package/build/client/assets/_exerciseNumber_.finished-Q9qnPuZX.js.map +0 -1
  55. package/build/client/assets/_layout-AgYGL72C.js.map +0 -1
  56. package/build/client/assets/_layout-BocZ2xWF.js +0 -2
  57. package/build/client/assets/_layout-BocZ2xWF.js.map +0 -1
  58. package/build/client/assets/_layout-C8uU4Hwj.js +0 -2
  59. package/build/client/assets/_layout-C8uU4Hwj.js.map +0 -1
  60. package/build/client/assets/app-BiWJY58g.js +0 -2
  61. package/build/client/assets/diff-Cw8ktQ1-.js +0 -2
  62. package/build/client/assets/finished-Bm-bCUU2.js +0 -2
  63. package/build/client/assets/finished-Bm-bCUU2.js.map +0 -1
  64. package/build/client/assets/index-BATSX33w.js +0 -2
  65. package/build/client/assets/index-Dm1ll1kT.js +0 -2
  66. package/build/client/assets/index-Tfdnz1AB.js +0 -2
  67. package/build/client/assets/index-Tfdnz1AB.js.map +0 -1
  68. package/build/client/assets/manifest-4bde8719.js +0 -1
  69. package/build/client/assets/mdx-M0kcP-mP.js +0 -2
  70. package/build/client/assets/mdx-M0kcP-mP.js.map +0 -1
  71. package/build/client/assets/onboarding-Bib-wh6d.js +0 -2
  72. package/build/client/assets/onboarding-Bib-wh6d.js.map +0 -1
  73. package/build/client/assets/preview-CqrRe2BV.js +0 -2
  74. package/build/client/assets/preview-CqrRe2BV.js.map +0 -1
  75. package/build/client/assets/progress-BsY6hGPp.js +0 -2
  76. package/build/client/assets/root-Tcnfz99O.js.map +0 -1
  77. package/build/client/assets/set-playground-Cdy8VlVD.js +0 -2
  78. package/build/client/assets/set-playground-Cdy8VlVD.js.map +0 -1
  79. package/build/client/assets/test-D9HsTvpe.js +0 -2
  80. package/build/client/assets/use-hydrated-Citou692.js +0 -2
  81. package/build/client/assets/use-hydrated-Citou692.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  var _a;
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { PassThrough } from "stream";
4
- import { createReadableStreamFromReadable, redirect, json, createCookieSessionStorage, defer } from "@remix-run/node";
5
- import { RemixServer, Link, useRouteError, useParams, isRouteErrorResponse, useNavigation, useFetchers, useRouteLoaderData, useRevalidator, useFetcher, useLoaderData, Outlet, Meta, Links, ScrollRestoration, Scripts, useLocation, NavLink, Form, Await, useSearchParams, useSubmit, useNavigate } from "@remix-run/react";
4
+ import { createReadableStreamFromReadable, redirect, json as json$1, createCookieSessionStorage, defer } from "@remix-run/node";
5
+ import { RemixServer, Link, useRouteError, useParams, isRouteErrorResponse, useNavigation, useFetchers, useRouteLoaderData, useRevalidator, useLocation, json, redirect as redirect$1, useFetcher, useLoaderData, Outlet, Meta, Links, ScrollRestoration, Scripts, NavLink, Form, Await, useSearchParams, useSubmit, useNavigate } from "@remix-run/react";
6
6
  import { isbot } from "isbot";
7
7
  import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
8
8
  import path$1 from "node:path";
@@ -50,11 +50,12 @@ import { Toaster, toast } from "sonner";
50
50
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
51
51
  import { useForm, getFormProps } from "@conform-to/react";
52
52
  import { parseWithZod } from "@conform-to/zod";
53
- import { safeRedirect } from "remix-utils/safe-redirect";
54
53
  import { getHintUtils } from "@epic-web/client-hints";
55
54
  import { clientHint, subscribeToSchemeChange } from "@epic-web/client-hints/color-scheme";
56
55
  import { clientHint as clientHint$2, subscribeToMotionChange } from "@epic-web/client-hints/reduced-motion";
57
56
  import { clientHint as clientHint$1 } from "@epic-web/client-hints/time-zone";
57
+ import { safeRedirect } from "remix-utils/safe-redirect";
58
+ import { ServerOnly } from "remix-utils/server-only";
58
59
  import * as cookie from "cookie";
59
60
  import { createId } from "@paralleldrive/cuid2";
60
61
  import { usePartySocket } from "partysocket/react";
@@ -360,7 +361,9 @@ const AuthInfoSchema = z$1.object({
360
361
  name: z$1.string().nullable().optional()
361
362
  }).transform((d) => ({ ...d, id: md5(d.email) }));
362
363
  const DataSchema = z$1.object({
363
- onboarding: z$1.object({ finishedTourVideo: z$1.boolean() }).optional().default({ finishedTourVideo: false }),
364
+ onboarding: z$1.object({
365
+ tourVideosWatched: z$1.array(z$1.string()).default([])
366
+ }).passthrough().optional().default({ tourVideosWatched: [] }),
364
367
  preferences: z$1.object({
365
368
  player: PlayerPreferencesSchema,
366
369
  presence: PresencePreferencesSchema
@@ -495,11 +498,17 @@ async function readOnboardingData() {
495
498
  const data = await readDb();
496
499
  return (data == null ? void 0 : data.onboarding) ?? null;
497
500
  }
498
- async function updateOnboardingData(onboardingData) {
501
+ async function markOnboardingVideoWatched(videoUrl) {
499
502
  const data = await readDb();
500
503
  const updatedData = {
501
504
  ...data,
502
- onboarding: { ...data == null ? void 0 : data.onboarding, ...onboardingData }
505
+ onboarding: {
506
+ ...data == null ? void 0 : data.onboarding,
507
+ tourVideosWatched: [
508
+ ...(data == null ? void 0 : data.onboarding.tourVideosWatched) ?? [],
509
+ videoUrl
510
+ ].filter(Boolean)
511
+ }
503
512
  };
504
513
  await fsExtra.ensureDir(appDir);
505
514
  await fsExtra.writeJSON(dbPath, updatedData);
@@ -1155,6 +1164,191 @@ async function queuedBundleMDX(...args) {
1155
1164
  const result = await queue.add(() => bundleMDX(...args));
1156
1165
  return result;
1157
1166
  }
1167
+ const workshopRoot$1 = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
1168
+ const StackBlitzConfigSchema = z$1.object({
1169
+ // we default this to `${exerciseTitle} (${type})`
1170
+ title: z$1.string().optional(),
1171
+ // stackblitz defaults this to dev automatically
1172
+ startScript: z$1.string().optional(),
1173
+ // if no value is provided, then stackblitz defaults this to whatever
1174
+ // looks best based on the width of the screen
1175
+ view: z$1.union([z$1.literal("editor"), z$1.literal("preview"), z$1.literal("both")]).optional(),
1176
+ file: z$1.string().optional()
1177
+ });
1178
+ const InstructorSchema = z$1.object({
1179
+ name: z$1.string().optional(),
1180
+ avatar: z$1.string().optional(),
1181
+ "𝕏": z$1.string().optional(),
1182
+ xHandle: z$1.string().optional()
1183
+ });
1184
+ const WorkshopConfigSchema = z$1.object({
1185
+ title: z$1.string(),
1186
+ subtitle: z$1.string().optional(),
1187
+ instructor: InstructorSchema.optional(),
1188
+ epicWorkshopHost: z$1.string().default("www.epicweb.dev"),
1189
+ epicWorkshopSlug: z$1.string().optional(),
1190
+ onboardingVideo: z$1.string().default(
1191
+ "https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app"
1192
+ ),
1193
+ githubRepo: z$1.string(),
1194
+ githubRoot: z$1.string(),
1195
+ stackBlitzConfig: StackBlitzConfigSchema.optional(),
1196
+ forms: z$1.object({
1197
+ workshop: z$1.string().default(
1198
+ "https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}"
1199
+ ),
1200
+ exercise: z$1.string().default(
1201
+ "https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}"
1202
+ )
1203
+ }).default({}),
1204
+ testTab: z$1.object({
1205
+ enabled: z$1.boolean().default(true)
1206
+ }).default({}),
1207
+ scripts: z$1.object({
1208
+ postupdate: z$1.string().optional()
1209
+ }).optional(),
1210
+ initialRoute: z$1.string().optional().default("/")
1211
+ // Add this line
1212
+ });
1213
+ let cachedConfig = null;
1214
+ function bustWorkshopConfigCache() {
1215
+ cachedConfig = null;
1216
+ }
1217
+ function getWorkshopConfig() {
1218
+ if (cachedConfig) return cachedConfig;
1219
+ const packageJsonPath = path$1.join(workshopRoot$1, "package.json");
1220
+ let packageJson;
1221
+ try {
1222
+ const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
1223
+ packageJson = JSON.parse(packageJsonContent);
1224
+ } catch (error) {
1225
+ console.error(`Error reading or parsing package.json:`, error);
1226
+ if (error instanceof Error && error.message.includes("ENOENT")) {
1227
+ throw new Error(
1228
+ `package.json not found at ${packageJsonPath}. Please ensure you're running the command from the correct directory.`
1229
+ );
1230
+ } else if (error instanceof SyntaxError) {
1231
+ throw new Error(
1232
+ `Invalid JSON in package.json at ${packageJsonPath}. Please check the file for syntax errors.`
1233
+ );
1234
+ }
1235
+ throw new Error(
1236
+ `Could not find and parse package.json at ${packageJsonPath}`
1237
+ );
1238
+ }
1239
+ const epicshopConfig = packageJson.epicshop || {};
1240
+ if (epicshopConfig.githubRepo) {
1241
+ epicshopConfig.githubRoot = `${epicshopConfig.githubRepo.replace(/\/$/, "")}/tree/main`;
1242
+ } else if (epicshopConfig.githubRoot) {
1243
+ epicshopConfig.githubRepo = epicshopConfig.githubRoot.replace(
1244
+ /\/(blob|tree)\/.*$/,
1245
+ ""
1246
+ );
1247
+ epicshopConfig.githubRoot = `${epicshopConfig.githubRepo}/tree/main`;
1248
+ } else {
1249
+ throw new Error(
1250
+ "Either githubRepo or githubRoot is required in the epicshop configuration"
1251
+ );
1252
+ }
1253
+ try {
1254
+ const parsedConfig = WorkshopConfigSchema.parse(epicshopConfig);
1255
+ cachedConfig = parsedConfig;
1256
+ return parsedConfig;
1257
+ } catch (error) {
1258
+ if (error instanceof z$1.ZodError) {
1259
+ const flattenedErrors = error.flatten();
1260
+ const errorMessages = Object.entries(flattenedErrors.fieldErrors).map(([field, errors]) => `${field}: ${errors == null ? void 0 : errors.join(", ")}`).concat(flattenedErrors.formErrors);
1261
+ throw new Error(
1262
+ `Invalid epicshop configuration in ${packageJsonPath}:
1263
+ ${errorMessages.join("\n")}`
1264
+ );
1265
+ }
1266
+ throw error;
1267
+ }
1268
+ }
1269
+ async function getStackBlitzUrl({
1270
+ fullPath,
1271
+ title,
1272
+ type
1273
+ }) {
1274
+ var _a2;
1275
+ const workshopConfig = getWorkshopConfig();
1276
+ const appConfig = await getAppConfig(fullPath);
1277
+ if (appConfig.stackBlitzConfig === null) return null;
1278
+ let githubRootUrlString = workshopConfig.githubRepo;
1279
+ const githubRootUrl = new URL(
1280
+ githubRootUrlString.replace(/\/blob\//, "/tree/")
1281
+ );
1282
+ const githubPart = githubRootUrl.pathname;
1283
+ const stackBlitzConfig = {
1284
+ ...appConfig.stackBlitzConfig,
1285
+ title: ((_a2 = appConfig.stackBlitzConfig) == null ? void 0 : _a2.title) ?? `${title} (${type})`
1286
+ };
1287
+ const params = new URLSearchParams(stackBlitzConfig);
1288
+ const relativePath = fullPath.replace(`${workshopRoot$1}${path$1.sep}`, "");
1289
+ const stackBlitzUrl = new URL(
1290
+ `/github${githubPart}/${relativePath}?${params}`,
1291
+ "https://stackblitz.com"
1292
+ );
1293
+ return stackBlitzUrl.toString();
1294
+ }
1295
+ async function getAppConfig(fullPath) {
1296
+ var _a2, _b;
1297
+ const workshopConfig = getWorkshopConfig();
1298
+ let epicshopConfig = {};
1299
+ let scripts = {};
1300
+ const packageJsonPath = path$1.join(fullPath, "package.json");
1301
+ const packageJsonExists = await fs.promises.access(packageJsonPath, fs.constants.F_OK).then(() => true).catch(() => false);
1302
+ if (packageJsonExists) {
1303
+ const pkg = JSON.parse(
1304
+ await fs.promises.readFile(path$1.join(fullPath, "package.json"), "utf8")
1305
+ );
1306
+ epicshopConfig = pkg.epicshop ?? {};
1307
+ scripts = pkg.scripts ?? {};
1308
+ }
1309
+ const AppConfigSchema = z$1.object({
1310
+ stackBlitzConfig: StackBlitzConfigSchema.nullable().optional().transform((appStackBlitzConfig) => {
1311
+ if (!appStackBlitzConfig) return workshopConfig.stackBlitzConfig ?? null;
1312
+ if (!workshopConfig.stackBlitzConfig) return appStackBlitzConfig;
1313
+ return {
1314
+ ...workshopConfig.stackBlitzConfig,
1315
+ ...appStackBlitzConfig
1316
+ };
1317
+ }),
1318
+ testTab: z$1.object({
1319
+ enabled: z$1.boolean().optional().default(((_a2 = workshopConfig.testTab) == null ? void 0 : _a2.enabled) ?? true)
1320
+ }).default({}),
1321
+ scripts: z$1.object({
1322
+ test: z$1.string().optional(),
1323
+ dev: z$1.string().optional()
1324
+ }).default({}),
1325
+ initialRoute: z$1.string().optional().default(workshopConfig.initialRoute)
1326
+ });
1327
+ const appConfig = {
1328
+ stackBlitzConfig: epicshopConfig.stackBlitzConfig,
1329
+ testTab: {
1330
+ enabled: (_b = epicshopConfig.testTab) == null ? void 0 : _b.enabled
1331
+ },
1332
+ scripts: {
1333
+ test: scripts.test,
1334
+ dev: scripts.dev
1335
+ },
1336
+ initialRoute: epicshopConfig.initialRoute
1337
+ };
1338
+ try {
1339
+ return AppConfigSchema.parse(appConfig);
1340
+ } catch (error) {
1341
+ if (error instanceof z$1.ZodError) {
1342
+ const flattenedErrors = error.flatten();
1343
+ const errorMessages = Object.entries(flattenedErrors.fieldErrors).map(([field, errors]) => `${field}: ${errors == null ? void 0 : errors.join(", ")}`).concat(flattenedErrors.formErrors);
1344
+ throw new Error(
1345
+ `Invalid app configuration for ${fullPath}:
1346
+ ${errorMessages.join("\n")}`
1347
+ );
1348
+ }
1349
+ throw error;
1350
+ }
1351
+ }
1158
1352
  const schema = z$1.object({
1159
1353
  NODE_ENV: z$1.enum(["production", "development", "test"]).default("development"),
1160
1354
  EPICSHOP_GITHUB_REPO: z$1.string(),
@@ -1437,43 +1631,8 @@ async function waitForPortToBeAvailable(port2) {
1437
1631
  console.error("Timed out waiting for the port to become available");
1438
1632
  }
1439
1633
  }
1440
- const PkgSchema = z$1.object({}).passthrough();
1441
- async function getPkgProp(fullPath, prop, defaultValue) {
1442
- let pkg;
1443
- try {
1444
- pkg = PkgSchema.parse(
1445
- JSON.parse(
1446
- fs.readFileSync(path$1.join(fullPath, "package.json")).toString()
1447
- )
1448
- );
1449
- } catch (error) {
1450
- throw new Error(`Could not parse package.json of ${fullPath}`, {
1451
- cause: error
1452
- });
1453
- }
1454
- const propPath = prop.split(".");
1455
- let value = pkg;
1456
- for (const p of propPath) {
1457
- value = value[p];
1458
- if (value === void 0) break;
1459
- }
1460
- if (value === void 0 && defaultValue === void 0) {
1461
- throw new Error(
1462
- `Could not find required property ${prop} in package.json of ${fullPath}`
1463
- );
1464
- }
1465
- return value ?? defaultValue;
1466
- }
1467
1634
  let initialized = false;
1468
1635
  const workshopRoot = process.env.EPICSHOP_CONTEXT_CWD = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
1469
- let packageJson;
1470
- try {
1471
- packageJson = JSON.parse(
1472
- fs.readFileSync(path$1.join(workshopRoot, "package.json"), "utf8")
1473
- );
1474
- } catch {
1475
- throw new Error(`Could not find and parse package.json at ${workshopRoot}`);
1476
- }
1477
1636
  const playgroundAppNameInfoPath = path$1.join(
1478
1637
  workshopRoot,
1479
1638
  "node_modules",
@@ -1481,16 +1640,6 @@ const playgroundAppNameInfoPath = path$1.join(
1481
1640
  "epicshop",
1482
1641
  "playground.json"
1483
1642
  );
1484
- const StackBlitzConfigSchema = z$1.object({
1485
- // we default this to `${exerciseTitle} (${type})`
1486
- title: z$1.string().optional(),
1487
- // stackblitz defaults this to dev automatically
1488
- startScript: z$1.string().optional(),
1489
- // if no value is provided, then stackblitz defaults this to whatever
1490
- // looks best based on the width of the screen
1491
- view: z$1.union([z$1.literal("editor"), z$1.literal("preview"), z$1.literal("both")]).optional(),
1492
- file: z$1.string().optional()
1493
- });
1494
1643
  const BaseAppSchema = z$1.object({
1495
1644
  /** a unique identifier for the app */
1496
1645
  name: z$1.string(),
@@ -1618,34 +1767,15 @@ function init() {
1618
1767
  var _a2;
1619
1768
  if (initialized) return;
1620
1769
  initialized = true;
1621
- try {
1622
- const { epicshop } = packageJson;
1623
- let root, repo;
1624
- if (epicshop.githubRepo) {
1625
- repo = epicshop.githubRepo;
1626
- root = `${repo.replace(/\/$/, "")}/tree/main`;
1627
- } else if (epicshop.githubRoot) {
1628
- root = epicshop.githubRoot.replace(/\/$/, "");
1629
- repo = root.replace(/\/(blob|tree)\/.*$/, "");
1630
- } else {
1631
- throw new Error(
1632
- `Please set the URL of your GitHub repo in the "epicshop.githubRepo" property of the package.json.`
1633
- );
1634
- }
1635
- if (!root.includes("/blob/") && !root.includes("/tree/")) {
1636
- root = `${root.replace(/\/$/, "")}/tree/main`;
1637
- }
1638
- process.env.EPICSHOP_GITHUB_REPO = repo;
1639
- process.env.EPICSHOP_GITHUB_ROOT = root;
1640
- } catch (error) {
1641
- throw new Error(
1642
- `Could not set the EPICSHOP_GITHUB_ROOT environment variable. Please set it to the URL of your GitHub repo in the "epicshop.githubRoot" property of the package.json.`,
1643
- { cause: error }
1644
- );
1645
- }
1770
+ const config = getWorkshopConfig();
1771
+ process.env.EPICSHOP_GITHUB_REPO = config.githubRepo;
1772
+ process.env.EPICSHOP_GITHUB_ROOT = config.githubRoot;
1646
1773
  init$1();
1647
1774
  global.ENV = getEnv();
1648
1775
  async function handleFileChanges(event, filePath) {
1776
+ if (filePath === path$1.join(workshopRoot, "package.json")) {
1777
+ bustWorkshopConfigCache();
1778
+ }
1649
1779
  const apps = await getApps();
1650
1780
  for (const app of apps) {
1651
1781
  if (filePath.startsWith(app.fullPath)) {
@@ -1979,16 +2109,11 @@ async function findProblemDir({
1979
2109
  async function getTestInfo({
1980
2110
  fullPath
1981
2111
  }) {
1982
- const hasPkgJson = await exists(path$1.join(fullPath, "package.json"));
1983
- const testsEnabledLocally = hasPkgJson ? await getPkgProp(fullPath, "epicshop.testTab.enabled", true) : true;
1984
- if (testsEnabledLocally === false) return { type: "none" };
1985
- const testsEnabledGlobally = await getPkgProp(
1986
- workshopRoot,
1987
- "epicshop.testTab.enabled",
1988
- true
1989
- );
1990
- if (testsEnabledGlobally === false) return { type: "none" };
1991
- const testScript = hasPkgJson ? await getPkgProp(fullPath, "epicshop.scripts.test", "") : null;
2112
+ const {
2113
+ testTab: { enabled },
2114
+ scripts: { test: testScript }
2115
+ } = await getAppConfig(fullPath);
2116
+ if (enabled === false) return { type: "none" };
1992
2117
  if (testScript) {
1993
2118
  return { type: "script", script: testScript };
1994
2119
  }
@@ -2008,10 +2133,12 @@ async function getDevInfo({
2008
2133
  fullPath,
2009
2134
  portNumber
2010
2135
  }) {
2011
- const hasPkgJson = await exists(path$1.join(fullPath, "package.json"));
2012
- const hasDevScript = hasPkgJson ? Boolean(await getPkgProp(fullPath, "scripts.dev", "")) : false;
2136
+ const {
2137
+ scripts: { dev: devScript },
2138
+ initialRoute
2139
+ } = await getAppConfig(fullPath);
2140
+ const hasDevScript = Boolean(devScript);
2013
2141
  if (hasDevScript) {
2014
- const initialRoute = (hasPkgJson ? await getPkgProp(fullPath, "epicshop.initialRoute", "") : "") || await getPkgProp(workshopRoot, "epicshop.initialRoute", "/");
2015
2142
  return { type: "script", portNumber, initialRoute };
2016
2143
  }
2017
2144
  const indexFiles = (await fsExtra.readdir(fullPath)).filter(
@@ -2023,46 +2150,6 @@ async function getDevInfo({
2023
2150
  return { type: "none" };
2024
2151
  }
2025
2152
  }
2026
- async function getStackBlitzUrl({
2027
- fullPath,
2028
- title,
2029
- type
2030
- }) {
2031
- const Schema = StackBlitzConfigSchema.nullable().optional();
2032
- const appStackBlitzConfig = Schema.parse(
2033
- // if there's no package.json this will throw. If that's the case, we can't use stackblitz
2034
- // https://discord.com/channels/364486390102097930/1260979618240790578
2035
- await getPkgProp(fullPath, "epicshop.stackBlitzConfig", {}).catch(
2036
- () => null
2037
- )
2038
- );
2039
- if (appStackBlitzConfig === null) return null;
2040
- const workshopStackBlitzConfig = Schema.parse(
2041
- await getPkgProp(workshopRoot, "epicshop.stackBlitzConfig", {})
2042
- );
2043
- if (workshopStackBlitzConfig === null) return null;
2044
- let githubRootUrlString = ENV.EPICSHOP_GITHUB_REPO;
2045
- if (!githubRootUrlString) return null;
2046
- if (!githubRootUrlString.includes("/blob/") || !githubRootUrlString.includes("/tree/")) {
2047
- githubRootUrlString = `${githubRootUrlString.replace(/\/$/, "")}/blob/main`;
2048
- }
2049
- const githubRootUrl = new URL(
2050
- githubRootUrlString.replace(/\/blob\//, "/tree/")
2051
- );
2052
- const githubPart = githubRootUrl.pathname;
2053
- const config = {
2054
- ...workshopStackBlitzConfig,
2055
- ...appStackBlitzConfig,
2056
- title: (appStackBlitzConfig == null ? void 0 : appStackBlitzConfig.title) ?? `${title} (${type})`
2057
- };
2058
- const params = new URLSearchParams(config);
2059
- const relativePath = fullPath.replace(`${workshopRoot}${path$1.sep}`, "");
2060
- const stackBlitzUrl = new URL(
2061
- `/github${githubPart}/${relativePath}?${params}`,
2062
- "https://stackblitz.com"
2063
- );
2064
- return stackBlitzUrl.toString();
2065
- }
2066
2153
  async function getPlaygroundApp({
2067
2154
  timings,
2068
2155
  request
@@ -2560,47 +2647,6 @@ function getAppDisplayName(a, allApps) {
2560
2647
  }
2561
2648
  return displayName;
2562
2649
  }
2563
- async function getWorkshopTitle() {
2564
- const title = await getPkgProp(workshopRoot, "epicshop.title");
2565
- if (!title) {
2566
- throw new Error(
2567
- `Workshop title not found. Make sure the root of the workshop has "epicshop" with a "title" property in the package.json. ${workshopRoot}`
2568
- );
2569
- }
2570
- return title;
2571
- }
2572
- async function getWorkshopSubtitle() {
2573
- return await getPkgProp(workshopRoot, "epicshop.subtitle", "");
2574
- }
2575
- async function getWorkshopInstructor() {
2576
- const InstructorSchema = z$1.object({
2577
- name: z$1.string().optional(),
2578
- avatar: z$1.string().optional(),
2579
- "𝕏": z$1.string().optional(),
2580
- // alias because 𝕏 is hard to type 😅
2581
- xHandle: z$1.string().optional()
2582
- }).optional();
2583
- const instructor = InstructorSchema.parse(
2584
- await getPkgProp(workshopRoot, "epicshop.instructor")
2585
- );
2586
- return instructor ? { ...instructor, "𝕏": instructor.xHandle } : void 0;
2587
- }
2588
- async function getEpicWorkshopHost() {
2589
- const epicWorkshopHost = await getPkgProp(
2590
- workshopRoot,
2591
- "epicshop.epicWorkshopHost",
2592
- "www.epicweb.dev"
2593
- );
2594
- return epicWorkshopHost;
2595
- }
2596
- async function getEpicWorkshopSlug() {
2597
- const epicWorkshopSlug = await getPkgProp(
2598
- workshopRoot,
2599
- "epicshop.epicWorkshopSlug",
2600
- ""
2601
- );
2602
- return epicWorkshopSlug || null;
2603
- }
2604
2650
  async function getWorkshopInstructions({
2605
2651
  request
2606
2652
  } = {}) {
@@ -3210,6 +3256,31 @@ function ErrorList({
3210
3256
  if (!(errorsToRender == null ? void 0 : errorsToRender.length)) return null;
3211
3257
  return /* @__PURE__ */ jsx("ul", { id, className: "space-y-1", children: errorsToRender.map((e) => /* @__PURE__ */ jsx("li", { className: "text-danger text-[10px]", children: e }, e)) });
3212
3258
  }
3259
+ const PE_REDIRECT_INPUT_NAME = "__PE_redirectTo";
3260
+ function usePERedirectInput() {
3261
+ const location = useLocation();
3262
+ return /* @__PURE__ */ jsx(ServerOnly, { children: () => /* @__PURE__ */ jsx(
3263
+ "input",
3264
+ {
3265
+ type: "hidden",
3266
+ name: PE_REDIRECT_INPUT_NAME,
3267
+ value: location.pathname
3268
+ }
3269
+ ) });
3270
+ }
3271
+ function ensureProgressiveEnhancement(formData, responseInit) {
3272
+ const redirectTo = formData.get(PE_REDIRECT_INPUT_NAME);
3273
+ if (typeof redirectTo === "string") {
3274
+ throw redirect$1(safeRedirect(redirectTo), responseInit == null ? void 0 : responseInit());
3275
+ }
3276
+ }
3277
+ function jsonWithPE(formData, ...args) {
3278
+ ensureProgressiveEnhancement(formData, () => ({
3279
+ statusText: JSON.stringify(args[0]),
3280
+ ...typeof args[1] === "number" ? { status: args[1] } : args[1]
3281
+ }));
3282
+ return json(...args);
3283
+ }
3213
3284
  const cookieName$1 = "EpicShop_theme";
3214
3285
  function getTheme(request) {
3215
3286
  const cookieHeader = request.headers.get("cookie");
@@ -3225,7 +3296,6 @@ function setTheme(theme) {
3225
3296
  }
3226
3297
  const ROUTE_PATH = "/theme";
3227
3298
  const ThemeFormSchema = z$1.object({
3228
- redirectTo: z$1.string().optional(),
3229
3299
  theme: z$1.enum(["system", "light", "dark"])
3230
3300
  });
3231
3301
  async function action$c({ request }) {
@@ -3234,28 +3304,21 @@ async function action$c({ request }) {
3234
3304
  schema: ThemeFormSchema
3235
3305
  });
3236
3306
  if (submission.status !== "success") {
3237
- return json(submission.reply(), {
3307
+ return json$1(submission.reply(), {
3238
3308
  // You can also use the status to determine the HTTP status code
3239
3309
  status: submission.status === "error" ? 400 : 200
3240
3310
  });
3241
3311
  }
3242
- const { redirectTo, theme } = submission.value;
3312
+ const { theme } = submission.value;
3243
3313
  const responseInit = {
3244
3314
  headers: { "set-cookie": setTheme(theme) }
3245
3315
  };
3246
- if (redirectTo) {
3247
- return redirect(safeRedirect(redirectTo), responseInit);
3248
- } else {
3249
- return json(submission.reply(), responseInit);
3250
- }
3316
+ return jsonWithPE(formData, submission.reply(), responseInit);
3251
3317
  }
3252
3318
  function ThemeSwitch() {
3253
3319
  const requestInfo = useRequestInfo();
3320
+ const peRedirectInput = usePERedirectInput();
3254
3321
  const fetcher = useFetcher();
3255
- const [isHydrated, setIsHydrated] = React.useState(false);
3256
- React.useEffect(() => {
3257
- setIsHydrated(true);
3258
- }, []);
3259
3322
  const [form] = useForm({
3260
3323
  lastResult: fetcher.data,
3261
3324
  onValidate({ formData }) {
@@ -3271,7 +3334,7 @@ function ThemeSwitch() {
3271
3334
  };
3272
3335
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: ROUTE_PATH, ...getFormProps(form), children: [
3273
3336
  /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
3274
- isHydrated ? null : /* @__PURE__ */ jsx("input", { type: "hidden", name: "redirectTo", value: requestInfo.path }),
3337
+ peRedirectInput,
3275
3338
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "theme", value: nextMode }),
3276
3339
  /* @__PURE__ */ jsx(SimpleTooltip, { content: `Change theme from ${mode2} mode`, children: /* @__PURE__ */ jsx(
3277
3340
  "button",
@@ -3471,7 +3534,7 @@ async function getEpicProgress({
3471
3534
  } = {}) {
3472
3535
  if (ENV.EPICSHOP_DEPLOYED) return [];
3473
3536
  const authInfo = await getAuthInfo();
3474
- const epicWorkshopHost = await getEpicWorkshopHost();
3537
+ const { epicWorkshopHost } = getWorkshopConfig();
3475
3538
  if (!authInfo) return [];
3476
3539
  const tokenPart = md5(authInfo.tokenSet.access_token);
3477
3540
  const EpicProgressSchema = z$1.array(
@@ -3514,7 +3577,7 @@ async function getProgress({
3514
3577
  if (ENV.EPICSHOP_DEPLOYED) return [];
3515
3578
  const authInfo = await getAuthInfo();
3516
3579
  if (!authInfo) return [];
3517
- const epicWorkshopSlug = await getEpicWorkshopSlug();
3580
+ const { epicWorkshopSlug } = getWorkshopConfig();
3518
3581
  if (!epicWorkshopSlug) return [];
3519
3582
  const [
3520
3583
  workshopData,
@@ -3613,7 +3676,7 @@ async function updateProgress({ lessonSlug, complete }, {
3613
3676
  if (!authInfo) {
3614
3677
  return { status: "error", error: "not authenticated" };
3615
3678
  }
3616
- const epicWorkshopHost = await getEpicWorkshopHost();
3679
+ const { epicWorkshopHost } = getWorkshopConfig();
3617
3680
  const response = await fetch(`https://${epicWorkshopHost}/api/progress`, {
3618
3681
  method: "POST",
3619
3682
  headers: {
@@ -3651,7 +3714,7 @@ async function getWorkshopData(epicWorkshopSlug, {
3651
3714
  if (ENV.EPICSHOP_DEPLOYED) return { sections: [] };
3652
3715
  const authInfo = await getAuthInfo();
3653
3716
  if (!authInfo) return { sections: [] };
3654
- const epicWorkshopHost = await getEpicWorkshopHost();
3717
+ const { epicWorkshopHost } = getWorkshopConfig();
3655
3718
  return cachified({
3656
3719
  key: `epic-workshop-data:${epicWorkshopHost}:${epicWorkshopSlug}`,
3657
3720
  cache: fsCache,
@@ -3901,27 +3964,18 @@ const meta$5 = ({ data }) => {
3901
3964
  };
3902
3965
  async function loader$y({ request }) {
3903
3966
  const timings = makeTimings("rootLoader");
3967
+ const {
3968
+ title: workshopTitle,
3969
+ subtitle: workshopSubtitle,
3970
+ instructor,
3971
+ onboardingVideo
3972
+ } = getWorkshopConfig();
3904
3973
  const onboarding = await readOnboardingData();
3905
- if (!ENV.EPICSHOP_DEPLOYED && !(onboarding == null ? void 0 : onboarding.finishedTourVideo)) {
3974
+ if (!ENV.EPICSHOP_DEPLOYED && !(onboarding == null ? void 0 : onboarding.tourVideosWatched.includes(onboardingVideo))) {
3906
3975
  if (new URL(request.url).pathname !== "/onboarding") {
3907
3976
  throw redirect("/onboarding");
3908
3977
  }
3909
3978
  }
3910
- const workshopTitle = await time(() => getWorkshopTitle(), {
3911
- type: "getWorkshopTitle",
3912
- desc: "getWorkshopTitle in root",
3913
- timings
3914
- });
3915
- const workshopSubtitle = await time(() => getWorkshopSubtitle(), {
3916
- type: "getWorkshopSubtitle",
3917
- desc: "getWorkshopSubtitle in root",
3918
- timings
3919
- });
3920
- const instructor = await time(() => getWorkshopInstructor(), {
3921
- type: "getInstructor",
3922
- desc: "getInstructor in root",
3923
- timings
3924
- });
3925
3979
  const preferences = await getPreferences();
3926
3980
  const progress = await getProgress({ timings }).catch((e) => {
3927
3981
  console.error("Failed to get progress", e);
@@ -3935,7 +3989,7 @@ async function loader$y({ request }) {
3935
3989
  const user = await getUserInfo();
3936
3990
  const apps = await getApps({ request, timings });
3937
3991
  const presentUsers = await getPresentUsers(user, { request, timings });
3938
- return json(
3992
+ return json$1(
3939
3993
  {
3940
3994
  workshopTitle,
3941
3995
  workshopSubtitle,
@@ -4422,7 +4476,7 @@ async function action$b({ request }) {
4422
4476
  return otherAreFinished ? `You completed exercise ${exerciseNumber}!` : null;
4423
4477
  }
4424
4478
  const announcement = getCompletionAnnouncement();
4425
- return json(result, {
4479
+ return jsonWithPE(formData, result, {
4426
4480
  headers: combineHeaders(
4427
4481
  announcement ? createConfettiHeaders() : null,
4428
4482
  announcement ? await createToastHeaders({
@@ -4439,6 +4493,7 @@ function ProgressToggle({
4439
4493
  }) {
4440
4494
  var _a2, _b, _c, _d;
4441
4495
  const progressFetcher = useFetcher();
4496
+ const peRedirectInput = usePERedirectInput();
4442
4497
  const progressItem = useProgressItem(progressItemSearch);
4443
4498
  const animationRef = React.useRef(null);
4444
4499
  const buttonRef = React.useRef(null);
@@ -4473,6 +4528,7 @@ function ProgressToggle({
4473
4528
  }, [startAnimation]);
4474
4529
  if (ENV.EPICSHOP_DEPLOYED || !progressItem) return null;
4475
4530
  return /* @__PURE__ */ jsxs(progressFetcher.Form, { method: "POST", action: "/progress", children: [
4531
+ peRedirectInput,
4476
4532
  /* @__PURE__ */ jsx(
4477
4533
  "input",
4478
4534
  {
@@ -4555,9 +4611,9 @@ const route36 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
4555
4611
  async function loader$w({ request }) {
4556
4612
  var _a2;
4557
4613
  const timings = makeTimings("appLayoutLoader");
4558
- const [exercises, workshopTitle, playgroundAppName] = await Promise.all([
4614
+ const { title: workshopTitle } = getWorkshopConfig();
4615
+ const [exercises, playgroundAppName] = await Promise.all([
4559
4616
  getExercises({ request, timings }),
4560
- getWorkshopTitle(),
4561
4617
  getPlaygroundAppName()
4562
4618
  ]);
4563
4619
  const playground = {
@@ -4566,7 +4622,7 @@ async function loader$w({ request }) {
4566
4622
  (_a2 = extractNumbersAndTypeFromAppNameOrPath(playgroundAppName ?? "")) == null ? void 0 : _a2.exerciseNumber
4567
4623
  )
4568
4624
  };
4569
- const result = json(
4625
+ const result = json$1(
4570
4626
  {
4571
4627
  workshopTitle,
4572
4628
  exercises: exercises.map((e) => ({
@@ -5742,7 +5798,7 @@ const handle$7 = {
5742
5798
  async function loader$u({ request }) {
5743
5799
  ensureUndeployed();
5744
5800
  await requireAuthInfo({ request });
5745
- return json({ discordAuthUrl: getDiscordAuthURL() });
5801
+ return json$1({ discordAuthUrl: getDiscordAuthURL() });
5746
5802
  }
5747
5803
  async function action$a({ request }) {
5748
5804
  ensureUndeployed();
@@ -6235,7 +6291,7 @@ async function loader$q({ request, params }) {
6235
6291
  );
6236
6292
  }
6237
6293
  const appTitle = app.title;
6238
- const workshopTitle = await getWorkshopTitle();
6294
+ const { title: workshopTitle } = getWorkshopConfig();
6239
6295
  const baseAppTitle = isExerciseStepApp(baseApp) ? [
6240
6296
  `${baseApp.stepNumber.toString().padStart(2, "0")}. ${baseApp.title}`,
6241
6297
  `${baseApp.exerciseNumber.toString().padStart(2, "0")}. ${((_a2 = await getExercise(baseApp.exerciseNumber, { request, timings })) == null ? void 0 : _a2.title) ?? "Unknown"}`,
@@ -6374,7 +6430,7 @@ ${testScriptTag}`;
6374
6430
  );
6375
6431
  const indexCss = indexFiles.find((file) => file.endsWith("index.css"));
6376
6432
  const appTitle = app.title;
6377
- const workshopTitle = await getWorkshopTitle();
6433
+ const { title: workshopTitle } = getWorkshopConfig();
6378
6434
  const title = (isExerciseStepApp(app) ? [
6379
6435
  isProblemApp(app) ? "🧪💪" : isSolutionApp(app) ? "🧪🏁" : null,
6380
6436
  `${app.stepNumber.toString().padStart(2, "0")}. ${app.title}`,
@@ -6421,7 +6477,7 @@ const handle$6 = {
6421
6477
  getSitemapEntries: () => null
6422
6478
  };
6423
6479
  async function loader$o() {
6424
- return json({ discordAuthUrl: getDiscordAuthURL() });
6480
+ return json$1({ discordAuthUrl: getDiscordAuthURL() });
6425
6481
  }
6426
6482
  function useDiscordCTALink({
6427
6483
  discordAuthUrl
@@ -6574,12 +6630,12 @@ const ignoredInputs = [
6574
6630
  async function action$8({ request }) {
6575
6631
  const result = PlayerPreferencesSchema.safeParse(await request.json());
6576
6632
  if (!result.success) {
6577
- return json({ status: "error", error: result.error.flatten() }, {
6633
+ return json$1({ status: "error", error: result.error.flatten() }, {
6578
6634
  status: 400
6579
6635
  });
6580
6636
  }
6581
6637
  await setPlayerPreferences(result.data);
6582
- return json({ status: "success" });
6638
+ return json$1({ status: "success" });
6583
6639
  }
6584
6640
  function useLatest(value) {
6585
6641
  const ref = React.useRef(value);
@@ -7662,19 +7718,23 @@ async function action$7({ request }) {
7662
7718
  results.push(await launchEditor(file.filepath, file.line, file.column));
7663
7719
  }
7664
7720
  if (results.every((r) => r.status === "success")) {
7665
- return json({ status: "success" });
7721
+ return jsonWithPE(formData, { status: "success" });
7666
7722
  } else {
7667
7723
  const messages = results.map(
7668
7724
  (r, index, array) => r.status === "error" ? array.length > 1 ? `${index}. ${r.message}` : r.message : null
7669
7725
  ).filter(Boolean).join("\n");
7670
7726
  console.error("Launch editor error:", messages);
7671
- return json({ status: "error", message: messages }, {
7672
- headers: await createToastHeaders({
7673
- type: "error",
7674
- title: "Launch Editor Error",
7675
- description: messages
7676
- })
7677
- });
7727
+ return jsonWithPE(
7728
+ formData,
7729
+ { status: "error", message: messages },
7730
+ {
7731
+ headers: await createToastHeaders({
7732
+ type: "error",
7733
+ title: "Launch Editor Error",
7734
+ description: messages
7735
+ })
7736
+ }
7737
+ );
7678
7738
  }
7679
7739
  }
7680
7740
  function useLaunchFetcher(onUpdate) {
@@ -7698,6 +7758,7 @@ function LaunchEditorImpl({
7698
7758
  }) {
7699
7759
  var _a2;
7700
7760
  const fetcher = useLaunchFetcher(onUpdate);
7761
+ const peRedirectInput = usePERedirectInput();
7701
7762
  const fileList = typeof appFile === "string" ? [appFile] : appFile;
7702
7763
  const type = file ? "file" : appFile ? "appFile" : "";
7703
7764
  const syncToType = (syncTo == null ? void 0 : syncTo.file) ? "file" : (syncTo == null ? void 0 : syncTo.appFile) ? "appFile" : "";
@@ -7708,6 +7769,7 @@ function LaunchEditorImpl({
7708
7769
  method: "POST",
7709
7770
  className: "flex items-center",
7710
7771
  children: [
7772
+ peRedirectInput,
7711
7773
  showProgressBarField,
7712
7774
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "line", value: line }),
7713
7775
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "column", value: column }),
@@ -8023,18 +8085,12 @@ const meta$4 = ({
8023
8085
  async function loader$n({ request, params }) {
8024
8086
  const timings = makeTimings("exerciseNumberLoader");
8025
8087
  invariantResponse(params.exerciseNumber, "exerciseNumber is required");
8026
- const [exercises, workshopTitle] = await Promise.all([
8027
- time(() => getExercises({ request, timings }), {
8028
- timings,
8029
- type: "getExercises",
8030
- desc: "getExercises in $exerciseNumber.tsx"
8031
- }),
8032
- time(() => getWorkshopTitle(), {
8033
- timings,
8034
- type: "getWorkshopTitle",
8035
- desc: "getWorkshopTitle in $exerciseNumber.tsx"
8036
- })
8037
- ]);
8088
+ const { title: workshopTitle } = getWorkshopConfig();
8089
+ const exercises = await time(() => getExercises({ request, timings }), {
8090
+ timings,
8091
+ type: "getExercises",
8092
+ desc: "getExercises in $exerciseNumber.tsx"
8093
+ });
8038
8094
  const exercise = exercises.find(
8039
8095
  (e) => e.exerciseNumber === Number(params.exerciseNumber)
8040
8096
  );
@@ -8172,17 +8228,15 @@ const route12 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
8172
8228
  async function loader$m({ request, params }) {
8173
8229
  const timings = makeTimings("stepLoader");
8174
8230
  invariantResponse(params.exerciseNumber, "exerciseNumber is required");
8175
- const [exercises, workshopTitle] = await Promise.all([
8176
- getExercises({ request, timings }),
8177
- getWorkshopTitle()
8178
- ]);
8231
+ const exercises = await getExercises({ request, timings });
8232
+ const { title: workshopTitle } = getWorkshopConfig();
8179
8233
  const exercise = exercises.find(
8180
8234
  (e) => e.exerciseNumber === Number(params.exerciseNumber)
8181
8235
  );
8182
8236
  if (!exercise) {
8183
8237
  throw new Response("Not found", { status: 404 });
8184
8238
  }
8185
- const result = json(
8239
+ const result = json$1(
8186
8240
  {
8187
8241
  exerciseNumber: exercise.exerciseNumber,
8188
8242
  exerciseTitle: exercise.title,
@@ -8415,7 +8469,7 @@ async function copyUnignoredFiles(srcDir, destDir, ignoreList) {
8415
8469
  await cachified({
8416
8470
  key,
8417
8471
  cache: diffCodeCache,
8418
- forceFresh: getForceFreshForDir(srcDir, diffCodeCache.get(key)),
8472
+ forceFresh: getForceFreshForDir(diffCodeCache.get(key), srcDir),
8419
8473
  async getFreshValue() {
8420
8474
  const ig = ignore().add(ignoreList);
8421
8475
  await fsExtra.remove(destDir);
@@ -8673,18 +8727,22 @@ async function action$6({ request }) {
8673
8727
  ensureUndeployed();
8674
8728
  const formData = await request.formData();
8675
8729
  const rawData = {
8676
- appName: formData.get("appName")
8730
+ appName: formData.get("appName"),
8731
+ redirectTo: formData.get("redirectTo")
8677
8732
  };
8678
8733
  const result = SetPlaygroundSchema.safeParse(rawData);
8679
8734
  if (!result.success) {
8680
- return json({ status: "error", error: result.error.message }, {
8681
- status: 400
8682
- });
8735
+ return jsonWithPE(
8736
+ formData,
8737
+ { status: "error", error: result.error.message },
8738
+ { status: 400 }
8739
+ );
8683
8740
  }
8684
8741
  const form = result.data;
8685
8742
  const app = await getAppByName(form.appName);
8686
8743
  if (!app) {
8687
- return json(
8744
+ return jsonWithPE(
8745
+ formData,
8688
8746
  { status: "error", error: `App ${form.appName} not found` },
8689
8747
  { status: 404 }
8690
8748
  );
@@ -8695,7 +8753,7 @@ async function action$6({ request }) {
8695
8753
  } catch (error) {
8696
8754
  const message = getErrorMessage(error);
8697
8755
  console.error("Error setting playground", message);
8698
- return json({ status: "error", error: message }, {
8756
+ return json$1({ status: "error", error: message }, {
8699
8757
  status: 500,
8700
8758
  headers: await createToastHeaders({
8701
8759
  type: "error",
@@ -8709,7 +8767,7 @@ async function action$6({ request }) {
8709
8767
  if (playground && converseApp) {
8710
8768
  await getDiffCode(playground, converseApp, { forceFresh: true });
8711
8769
  }
8712
- return json({ status: "success" });
8770
+ return jsonWithPE(formData, { status: "success" });
8713
8771
  }
8714
8772
  function SetPlayground({
8715
8773
  appName,
@@ -8719,6 +8777,7 @@ function SetPlayground({
8719
8777
  }) {
8720
8778
  var _a2;
8721
8779
  const fetcher = useFetcher();
8780
+ const peRedirectInput = usePERedirectInput();
8722
8781
  const submitButton = /* @__PURE__ */ jsx(
8723
8782
  "button",
8724
8783
  {
@@ -8738,6 +8797,7 @@ function SetPlayground({
8738
8797
  method: "POST",
8739
8798
  className: "inline-flex items-center justify-center",
8740
8799
  children: [
8800
+ peRedirectInput,
8741
8801
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "appName", value: appName }),
8742
8802
  reset ? /* @__PURE__ */ jsx("input", { type: "hidden", name: "reset", value: "true" }) : null,
8743
8803
  showProgressBarField,
@@ -8868,7 +8928,7 @@ async function action$5({ request }) {
8868
8928
  const { cacheLocation, embeddedKey, appFullPath } = cacheSchema.parse(rawData);
8869
8929
  if (!await checkFileExists(cacheLocation)) {
8870
8930
  console.log(`file ${cacheLocation} not found`);
8871
- return json({ success: true });
8931
+ return json$1({ success: true });
8872
8932
  }
8873
8933
  const cached = JSON.parse(
8874
8934
  await fs.promises.readFile(cacheLocation, "utf-8")
@@ -8890,7 +8950,7 @@ async function action$5({ request }) {
8890
8950
  );
8891
8951
  }
8892
8952
  setModifiedTimesForDir(appFullPath);
8893
- return json({ success: true });
8953
+ return jsonWithPE(formData, { success: true });
8894
8954
  }
8895
8955
  function UpdateMdxCache({
8896
8956
  handleClick,
@@ -8899,7 +8959,9 @@ function UpdateMdxCache({
8899
8959
  appFullPath
8900
8960
  }) {
8901
8961
  const fetcher = useFetcher();
8962
+ const peRedirectInput = usePERedirectInput();
8902
8963
  return /* @__PURE__ */ jsxs(fetcher.Form, { action: "/update-mdx-cache", method: "POST", children: [
8964
+ peRedirectInput,
8903
8965
  showProgressBarField,
8904
8966
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "cacheLocation", value: cacheLocation }),
8905
8967
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "embeddedKey", value: embeddedKey }),
@@ -9335,7 +9397,7 @@ async function loader$l({ request, params }) {
9335
9397
  var _a2, _b;
9336
9398
  const timings = makeTimings("exerciseStepTypeLoader");
9337
9399
  const url = new URL(request.url);
9338
- const workshopTitle = await getWorkshopTitle();
9400
+ const { title: workshopTitle } = getWorkshopConfig();
9339
9401
  const cacheOptions = { request, timings };
9340
9402
  const exerciseStepApp = await requireExerciseApp(params, cacheOptions);
9341
9403
  const exercise = await requireExercise(
@@ -9617,10 +9679,10 @@ async function action$4({ request }) {
9617
9679
  const appRunningResult = await waitOnApp(app);
9618
9680
  if ((appRunningResult == null ? void 0 : appRunningResult.status) === "success") {
9619
9681
  await new Promise((resolve) => setTimeout(resolve, 200));
9620
- return json({ status: "app-started" });
9682
+ return jsonWithPE(formData, { status: "app-started" });
9621
9683
  } else if (app.dev.type === "script") {
9622
9684
  const errorMessage = appRunningResult ? appRunningResult.error : "Unknown error";
9623
- return json(
9685
+ return json$1(
9624
9686
  {
9625
9687
  status: "app-not-started",
9626
9688
  error: errorMessage,
@@ -9638,7 +9700,7 @@ async function action$4({ request }) {
9638
9700
  );
9639
9701
  }
9640
9702
  } else if (result.portNumber) {
9641
- return json({
9703
+ return jsonWithPE(formData, {
9642
9704
  status: "app-not-started",
9643
9705
  error: result.status,
9644
9706
  port: result.portNumber
@@ -9653,7 +9715,7 @@ async function action$4({ request }) {
9653
9715
  async function stopApp() {
9654
9716
  invariant(app, "app must be defined");
9655
9717
  await closeProcess(app.name);
9656
- return json({ status: "app-stopped" });
9718
+ return jsonWithPE(formData, { status: "app-stopped" });
9657
9719
  }
9658
9720
  switch (intent) {
9659
9721
  case "start": {
@@ -9672,17 +9734,19 @@ async function action$4({ request }) {
9672
9734
  const port2 = formData.get("port");
9673
9735
  invariantResponse(typeof port2 === "string", "port is required");
9674
9736
  await stopPort(port2);
9675
- return json({ status: "port-stopped" });
9737
+ return jsonWithPE(formData, { status: "port-stopped" });
9676
9738
  }
9677
9739
  throw new Error(`Unknown intent: ${intent}`);
9678
9740
  }
9679
9741
  function AppStopper({ name }) {
9680
9742
  var _a2;
9681
9743
  const fetcher = useFetcher();
9744
+ const peRedirectInput = usePERedirectInput();
9682
9745
  const inFlightIntent = (_a2 = fetcher.formData) == null ? void 0 : _a2.get("intent");
9683
9746
  const inFlightState = inFlightIntent === "stop" ? "Stopping App" : inFlightIntent === "restart" ? "Restarting App" : null;
9684
9747
  const altDown = useAltDown();
9685
9748
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/start", children: [
9749
+ peRedirectInput,
9686
9750
  showProgressBarField,
9687
9751
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "name", value: name }),
9688
9752
  /* @__PURE__ */ jsx(
@@ -9699,7 +9763,10 @@ function AppStopper({ name }) {
9699
9763
  }
9700
9764
  function PortStopper({ port: port2 }) {
9701
9765
  const fetcher = useFetcher();
9766
+ const peRedirectInput = usePERedirectInput();
9702
9767
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/start", children: [
9768
+ peRedirectInput,
9769
+ showProgressBarField,
9703
9770
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "port", value: port2 }),
9704
9771
  /* @__PURE__ */ jsx(Button, { varient: "mono", type: "submit", name: "intent", value: "stop-port", children: fetcher.state === "idle" ? "Stop Port" : "Stopping Port" })
9705
9772
  ] });
@@ -9707,6 +9774,7 @@ function PortStopper({ port: port2 }) {
9707
9774
  function AppStarter({ name }) {
9708
9775
  var _a2;
9709
9776
  const fetcher = useFetcher();
9777
+ const peRedirectInput = usePERedirectInput();
9710
9778
  if (((_a2 = fetcher.data) == null ? void 0 : _a2.status) === "app-not-started") {
9711
9779
  if (fetcher.data.error === "port-unavailable") {
9712
9780
  return /* @__PURE__ */ jsxs("div", { children: [
@@ -9718,6 +9786,8 @@ function AppStarter({ name }) {
9718
9786
  }
9719
9787
  }
9720
9788
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/start", children: [
9789
+ peRedirectInput,
9790
+ showProgressBarField,
9721
9791
  /* @__PURE__ */ jsx("input", { type: "hidden", name: "name", value: name }),
9722
9792
  fetcher.state === "idle" ? /* @__PURE__ */ jsx(Button, { type: "submit", name: "intent", value: "start", varient: "mono", children: "Start App" }) : /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Loading, { children: "Starting App" }) })
9723
9793
  ] });
@@ -10188,7 +10258,7 @@ async function loader$k({ request, params }) {
10188
10258
  const timings = makeTimings("exercise-step-test");
10189
10259
  const exerciseStepApp = await requireExerciseApp(params, { request, timings });
10190
10260
  const { isRunning, portIsAvailable } = await getAppRunningState(exerciseStepApp);
10191
- return json({
10261
+ return json$1({
10192
10262
  appInfo: {
10193
10263
  isRunning,
10194
10264
  name: exerciseStepApp.name,
@@ -11052,15 +11122,15 @@ async function loader$j({ request }) {
11052
11122
  const url = new URL(request.url);
11053
11123
  const name = url.searchParams.get("name");
11054
11124
  if (!name) {
11055
- return json({ error: "Missing name" }, { status: 400 });
11125
+ return json$1({ error: "Missing name" }, { status: 400 });
11056
11126
  }
11057
11127
  const app = await getAppByName(name);
11058
11128
  if (!app) {
11059
- return json({ error: "App not found" }, { status: 404 });
11129
+ return json$1({ error: "App not found" }, { status: 404 });
11060
11130
  }
11061
11131
  const processEntry = getTestProcessEntry(app);
11062
11132
  if (!processEntry) {
11063
- return json({ error: "App is not running tests" }, { status: 404 });
11133
+ return json$1({ error: "App is not running tests" }, { status: 404 });
11064
11134
  }
11065
11135
  return eventStream(request.signal, function setup(send) {
11066
11136
  var _a2, _b;
@@ -11132,30 +11202,30 @@ async function action$3({ request }) {
11132
11202
  name: formData.get("name")
11133
11203
  });
11134
11204
  if (!result.success) {
11135
- return json(
11205
+ return json$1(
11136
11206
  { success: false, error: result.error.flatten() },
11137
11207
  { status: 400 }
11138
11208
  );
11139
11209
  }
11140
11210
  const app = await getAppByName(result.data.name);
11141
11211
  if (!app) {
11142
- return json({ success: false, error: "App not found" }, { status: 404 });
11212
+ return json$1({ success: false, error: "App not found" }, { status: 404 });
11143
11213
  }
11144
11214
  switch (result.data.intent) {
11145
11215
  case "run": {
11146
11216
  void runAppTests(app);
11147
- return json({ success: true });
11217
+ return json$1({ success: true });
11148
11218
  }
11149
11219
  case "stop": {
11150
11220
  const processEntry = getTestProcessEntry(app);
11151
11221
  if (processEntry) {
11152
11222
  (_a2 = processEntry.process) == null ? void 0 : _a2.kill();
11153
11223
  }
11154
- return json({ success: true });
11224
+ return json$1({ success: true });
11155
11225
  }
11156
11226
  case "clear": {
11157
11227
  clearTestProcessEntry(app);
11158
- return json({ success: true });
11228
+ return json$1({ success: true });
11159
11229
  }
11160
11230
  }
11161
11231
  }
@@ -11741,7 +11811,7 @@ const route16 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
11741
11811
  async function loader$h({ request, params }) {
11742
11812
  const timings = makeTimings("exercise-step-test");
11743
11813
  const exerciseStepApp = await requireExerciseApp(params, { request, timings });
11744
- return json({
11814
+ return json$1({
11745
11815
  appInfo: {
11746
11816
  name: exerciseStepApp == null ? void 0 : exerciseStepApp.name,
11747
11817
  test: exerciseStepApp == null ? void 0 : exerciseStepApp.test
@@ -11816,13 +11886,9 @@ async function loader$f({ request, params }) {
11816
11886
  if (!exercise) {
11817
11887
  throw new Response("Not found", { status: 404 });
11818
11888
  }
11819
- const workshopTitle = await getWorkshopTitle();
11820
- const exerciseFormTemplate = await getPkgProp(
11821
- workshopRoot,
11822
- "epicshop.forms.exercise",
11823
- `https://docs.google.com/forms/d/e/1FAIpQLSf3o9xyjQepTlOTH5Z7ZwkeSTdXh6YWI_RGc9KiyD3oUN0p6w/viewform?hl=en&embedded=true&entry.1836176234={workshopTitle}&entry.428900931={exerciseTitle}`
11824
- );
11825
- const exerciseFormEmbedUrl = exerciseFormTemplate.replace("{workshopTitle}", encodeURIComponent(workshopTitle)).replace("{exerciseTitle}", encodeURIComponent(exercise.title));
11889
+ const workshopConfig = getWorkshopConfig();
11890
+ const exerciseFormTemplate = workshopConfig.forms.exercise;
11891
+ const exerciseFormEmbedUrl = exerciseFormTemplate.replace("{workshopTitle}", encodeURIComponent(workshopConfig.title)).replace("{exerciseTitle}", encodeURIComponent(exercise.title));
11826
11892
  const nextExercise = await getExercise(exercise.exerciseNumber + 1, {
11827
11893
  timings,
11828
11894
  request
@@ -11836,11 +11902,11 @@ async function loader$f({ request, params }) {
11836
11902
  const apps = await getApps({ request, timings });
11837
11903
  const exerciseApps = apps.filter(isExerciseStepApp).filter((app) => app.exerciseNumber === exercise.exerciseNumber);
11838
11904
  const prevApp = exerciseApps[exerciseApps.length - 1];
11839
- const articleId = `workshop-${slugify(workshopTitle)}-${exercise.exerciseNumber}-finished`;
11905
+ const articleId = `workshop-${slugify(workshopConfig.title)}-${exercise.exerciseNumber}-finished`;
11840
11906
  return defer(
11841
11907
  {
11842
11908
  articleId,
11843
- workshopTitle,
11909
+ workshopTitle: workshopConfig.title,
11844
11910
  exercise,
11845
11911
  exerciseFormEmbedUrl,
11846
11912
  epicVideoInfosPromise: getEpicVideoInfos(
@@ -12001,12 +12067,9 @@ async function loader$e({ request }) {
12001
12067
  desc: "compileMdx in finished"
12002
12068
  });
12003
12069
  const lastExercises = exercises[exercises.length - 1];
12004
- const workshopTitle = await getWorkshopTitle();
12005
- const workshopFormTemplate = await getPkgProp(
12006
- workshopRoot,
12007
- "epicshop.forms.workshop",
12008
- "https://docs.google.com/forms/d/e/1FAIpQLSdRmj9p8-5zyoqRzxp3UpqSbC3aFkweXvvJIKes0a5s894gzg/viewform?hl=en&embedded=true&entry.2123647600={workshopTitle}"
12009
- );
12070
+ const workshopConfig = getWorkshopConfig();
12071
+ const workshopTitle = workshopConfig.title;
12072
+ const workshopFormTemplate = workshopConfig.forms.workshop;
12010
12073
  const workshopFormEmbedUrl = workshopFormTemplate.replace(
12011
12074
  "{workshopTitle}",
12012
12075
  encodeURIComponent(workshopTitle)
@@ -12135,12 +12198,8 @@ const route20 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12135
12198
  }, Symbol.toStringTag, { value: "Module" }));
12136
12199
  async function loader$d({ request }) {
12137
12200
  const timings = makeTimings("indexLoader");
12138
- const [title, exercises, workshopReadme] = await Promise.all([
12139
- time(() => getWorkshopTitle(), {
12140
- timings,
12141
- type: "getWorkshopTitle",
12142
- desc: "getWorkshopTitle in index"
12143
- }),
12201
+ const { title } = getWorkshopConfig();
12202
+ const [exercises, workshopReadme] = await Promise.all([
12144
12203
  time(() => getExercises({ request, timings }), {
12145
12204
  timings,
12146
12205
  type: "getExercises",
@@ -12273,7 +12332,7 @@ const EVENTS = {
12273
12332
  const authEmitter = remember("authEmitter", () => new EventEmitter());
12274
12333
  authEmitter.removeAllListeners();
12275
12334
  async function registerDevice() {
12276
- const epicWorkshopHost = await getEpicWorkshopHost();
12335
+ const { epicWorkshopHost } = getWorkshopConfig();
12277
12336
  const { ISSUER = `https://${epicWorkshopHost}/oauth` } = process.env;
12278
12337
  const GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
12279
12338
  try {
@@ -12372,12 +12431,12 @@ async function loader$b() {
12372
12431
  ensureUndeployed();
12373
12432
  const isAuthenticated = Boolean(await getAuthInfo());
12374
12433
  if (isAuthenticated) throw redirect("/account");
12375
- return json({});
12434
+ return json$1({});
12376
12435
  }
12377
12436
  async function action$2() {
12378
12437
  ensureUndeployed();
12379
12438
  void registerDevice();
12380
- return json({ status: "pending" });
12439
+ return json$1({ status: "pending" });
12381
12440
  }
12382
12441
  function Login() {
12383
12442
  var _a2;
@@ -12591,7 +12650,7 @@ const meta = ({
12591
12650
  async function loader$a({ request }) {
12592
12651
  ensureUndeployed();
12593
12652
  const timings = makeTimings("adminLoader");
12594
- const workshopSlug = await getEpicWorkshopSlug() ?? "Unkown";
12653
+ const workshopSlug = getWorkshopConfig().epicWorkshopSlug ?? "Unkown";
12595
12654
  const apps = (await getApps({ request, timings })).filter(
12596
12655
  (a, i, ar) => ar.findIndex((b) => a.name === b.name) === i
12597
12656
  );
@@ -12609,7 +12668,7 @@ async function loader$a({ request }) {
12609
12668
  ] of getProcesses().testProcesses.entries()) {
12610
12669
  testProcesses2[name] = { pid: process2 == null ? void 0 : process2.pid, exitCode };
12611
12670
  }
12612
- return json(
12671
+ return json$1(
12613
12672
  {
12614
12673
  apps,
12615
12674
  processes,
@@ -12631,19 +12690,19 @@ async function action$1({ request }) {
12631
12690
  switch (intent) {
12632
12691
  case "clear-data": {
12633
12692
  await clearData();
12634
- return json({ success: true });
12693
+ return json$1({ success: true });
12635
12694
  }
12636
12695
  case "clear-caches": {
12637
12696
  await clearCaches();
12638
- return json({ success: true });
12697
+ return json$1({ success: true });
12639
12698
  }
12640
12699
  case "inspect": {
12641
12700
  await startInspector();
12642
- return json({ success: true });
12701
+ return json$1({ success: true });
12643
12702
  }
12644
12703
  case "stop-inspect": {
12645
12704
  await stopInspector();
12646
- return json({ success: true });
12705
+ return json$1({ success: true });
12647
12706
  }
12648
12707
  default: {
12649
12708
  throw new Error(`Unknown intent: ${intent}`);
@@ -12806,7 +12865,7 @@ const route24 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12806
12865
  async function loader$9() {
12807
12866
  ensureUndeployed();
12808
12867
  const apps = await getApps();
12809
- return json({ apps });
12868
+ return json$1({ apps });
12810
12869
  }
12811
12870
  const route25 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12812
12871
  __proto__: null,
@@ -12815,7 +12874,7 @@ const route25 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12815
12874
  async function loader$8() {
12816
12875
  ensureUndeployed();
12817
12876
  const entries = await getAllFileCacheEntries();
12818
- return json({ entries });
12877
+ return json$1({ entries });
12819
12878
  }
12820
12879
  const route26 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12821
12880
  __proto__: null,
@@ -12824,7 +12883,7 @@ const route26 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12824
12883
  async function loader$7({ request }) {
12825
12884
  const timings = makeTimings("appsLoader");
12826
12885
  const apps = await getApps({ request, timings });
12827
- return json(
12886
+ return json$1(
12828
12887
  { apps },
12829
12888
  { headers: { "Server-Timing": getServerTimeHeader(timings) } }
12830
12889
  );
@@ -12926,7 +12985,7 @@ const route28 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12926
12985
  async function loader$5({ request }) {
12927
12986
  const timings = makeTimings("appsLoader");
12928
12987
  const exercises = await getExercises({ request, timings });
12929
- return json(
12988
+ return json$1(
12930
12989
  { exercises },
12931
12990
  { headers: { "Server-Timing": getServerTimeHeader(timings) } }
12932
12991
  );
@@ -12940,17 +12999,17 @@ const HEIGHT = 630;
12940
12999
  async function loader$4({ request }) {
12941
13000
  const timings = makeTimings("og", "og image loader");
12942
13001
  const url = new URL(request.url);
12943
- const workshopTitle = await getWorkshopTitle();
12944
- const title = url.searchParams.get("title") || workshopTitle;
12945
- const subtitle = url.searchParams.get("subtitle") || await getWorkshopSubtitle();
13002
+ const workshopConfig = getWorkshopConfig();
13003
+ const title = url.searchParams.get("title") || workshopConfig.title;
13004
+ const subtitle = url.searchParams.get("subtitle") || workshopConfig.subtitle;
12946
13005
  const urlPathname = url.searchParams.get("urlPathname") || "";
12947
13006
  const element = /* @__PURE__ */ jsx(
12948
13007
  OgLayout,
12949
13008
  {
12950
13009
  request,
12951
- instructor: await getWorkshopInstructor(),
13010
+ instructor: workshopConfig.instructor,
12952
13011
  urlPathname,
12953
- workshopTitle: workshopTitle === title ? null : workshopTitle,
13012
+ workshopTitle: workshopConfig.title === title ? null : workshopConfig.title,
12954
13013
  children: /* @__PURE__ */ jsxs(
12955
13014
  "div",
12956
13015
  {
@@ -13339,10 +13398,10 @@ const handle = {
13339
13398
  };
13340
13399
  async function loader$3({ request }) {
13341
13400
  const timings = makeTimings("onboarding");
13342
- const tourUrl = "https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app";
13343
- const videoInfos = getEpicVideoInfos([tourUrl], { request, timings });
13401
+ const { onboardingVideo } = getWorkshopConfig();
13402
+ const videoInfos = getEpicVideoInfos([onboardingVideo], { request, timings });
13344
13403
  return defer(
13345
- { tourUrl, videoInfos },
13404
+ { onboardingVideo, videoInfos },
13346
13405
  { headers: { "Server-Timing": timings.toString() } }
13347
13406
  );
13348
13407
  }
@@ -13356,7 +13415,8 @@ async function action({ request }) {
13356
13415
  const data = await request.formData();
13357
13416
  const intent = data.get("intent");
13358
13417
  invariantResponse(intent === "complete", "Invalid intent");
13359
- await updateOnboardingData({ finishedTourVideo: true });
13418
+ const { onboardingVideo } = getWorkshopConfig();
13419
+ await markOnboardingVideoWatched(onboardingVideo);
13360
13420
  throw redirect("/account");
13361
13421
  }
13362
13422
  function Onboarding() {
@@ -13370,7 +13430,7 @@ function Onboarding() {
13370
13430
  /* @__PURE__ */ jsx("strong", { children: "you must watch the tour video" }),
13371
13431
  "! You're going to be spending a lot of time in here, so it's important you understand how to work effectively in this workshop"
13372
13432
  ] }),
13373
- /* @__PURE__ */ jsx("div", { className: "w-[780px] max-w-full", children: /* @__PURE__ */ jsx(EpicVideoInfoProvider, { epicVideoInfosPromise: data.videoInfos, children: /* @__PURE__ */ jsx(DeferredEpicVideo, { url: data.tourUrl }) }) })
13433
+ /* @__PURE__ */ jsx("div", { className: "w-[780px] max-w-full", children: /* @__PURE__ */ jsx(EpicVideoInfoProvider, { epicVideoInfosPromise: data.videoInfos, children: /* @__PURE__ */ jsx(DeferredEpicVideo, { url: data.onboardingVideo }) }) })
13374
13434
  ] }),
13375
13435
  /* @__PURE__ */ jsx(Form, { method: "post", className: "pb-4", children: /* @__PURE__ */ jsx(Button, { name: "intent", value: "complete", varient: "primary", children: "I've watched it. Let's go!" }) })
13376
13436
  ] });
@@ -13399,7 +13459,7 @@ async function loader$2() {
13399
13459
  ] of getProcesses().testProcesses.entries()) {
13400
13460
  testProcesses2[name] = { pid: process2 == null ? void 0 : process2.pid, exitCode, output };
13401
13461
  }
13402
- return json({ processes, testProcesses: testProcesses2 });
13462
+ return json$1({ processes, testProcesses: testProcesses2 });
13403
13463
  }
13404
13464
  const route35 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
13405
13465
  __proto__: null,
@@ -13427,7 +13487,7 @@ const route39 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
13427
13487
  __proto__: null,
13428
13488
  loader
13429
13489
  }, Symbol.toStringTag, { value: "Module" }));
13430
- const serverManifest = { "entry": { "module": "/assets/entry.client-3M2p-8I3.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.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-Tcnfz99O.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/use-hydrated-Citou692.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-B-hHvmeV.js", "/assets/index-BATSX33w.js", "/assets/presence-Cr--lRCr.js", "/assets/seo-pBpFCWsy.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/_-DbrWZazo.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/error-boundary-DDTvdkB4.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-AgYGL72C.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/use-hydrated-Citou692.js", "/assets/index-BXWoOGxB.js", "/assets/user-D6tTg1yS.js", "/assets/presence-Cr--lRCr.js", "/assets/progress-BsY6hGPp.js", "/assets/index-BATSX33w.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-7o6O4ruO.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/button-BklqyTPS.js", "/assets/tooltip-BiHTe_7F.js", "/assets/user-D6tTg1yS.js", "/assets/presence-Cr--lRCr.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-K6Dvbx-E.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+/discord": { "id": "routes/_app+/discord", "parentId": "routes/_app+/_layout", "path": "discord", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord-CHP4_JLJ.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/user-D6tTg1yS.js", "/assets/discord-Dq4e30wV.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-BUs3av-e.js", "imports": ["/assets/index-1cKOJFpX.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-Cq0nozYz.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/mdx-M0kcP-mP.js", "/assets/progress-BsY6hGPp.js", "/assets/seo-pBpFCWsy.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-B5pmJ29o.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.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-BocZ2xWF.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/index-Dx5GmdYq.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-M0kcP-mP.js", "/assets/progress-BsY6hGPp.js", "/assets/set-playground-Cdy8VlVD.js", "/assets/seo-pBpFCWsy.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-BiWJY58g.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/button-BklqyTPS.js", "/assets/loading-C5uX0jJw.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-BATSX33w.js", "/assets/preview-CqrRe2BV.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-Dm1ll1kT.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/mdx-M0kcP-mP.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/set-playground-Cdy8VlVD.js", "/assets/button-BklqyTPS.js", "/assets/diff-CpFxuKA2.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/discord-Dq4e30wV.js", "/assets/index-B-hHvmeV.js", "/assets/tests-CmNNBElK.js", "/assets/preview-CqrRe2BV.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-D9HsTvpe.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/index-BATSX33w.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/set-playground-Cdy8VlVD.js", "/assets/tests-CmNNBElK.js"], "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-Q9qnPuZX.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-M0kcP-mP.js", "/assets/progress-BsY6hGPp.js", "/assets/seo-pBpFCWsy.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-Bm-bCUU2.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-M0kcP-mP.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-BsY6hGPp.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-Tfdnz1AB.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/mdx-M0kcP-mP.js", "/assets/progress-BsY6hGPp.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-4cvVFzF8.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/button-BklqyTPS.js", "/assets/loading-C5uX0jJw.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-hcqGIpir.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js"], "css": [] }, "routes/admin+/_layout": { "id": "routes/admin+/_layout", "parentId": "root", "path": "admin", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-C8uU4Hwj.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/progress-BsY6hGPp.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-DP2rzg_V.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/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-l0sNRNKZ.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-Cw8ktQ1-.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-iluUs1-s.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/mdx-M0kcP-mP.js", "/assets/diff-CpFxuKA2.js", "/assets/nav-chevrons-B82nbdyj.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/discord.callback": { "id": "routes/discord.callback", "parentId": "root", "path": "discord/callback", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord.callback-l0sNRNKZ.js", "imports": [], "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-Bib-wh6d.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BATSX33w.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/button-BklqyTPS.js", "/assets/epic-video-iluUs1-s.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-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/update-mdx-cache": { "id": "routes/update-mdx-cache", "parentId": "root", "path": "update-mdx-cache", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/update-mdx-cache-l0sNRNKZ.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-DP2rzg_V.js", "imports": [], "css": [] } }, "url": "/assets/manifest-4bde8719.js", "version": "4bde8719" };
13490
+ const serverManifest = { "entry": { "module": "/assets/entry.client-3M2p-8I3.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.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-D_FY953d.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-B-hHvmeV.js", "/assets/index-6GyYWV9G.js", "/assets/presence-Cr--lRCr.js", "/assets/seo-pBpFCWsy.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/_-DbrWZazo.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/error-boundary-DDTvdkB4.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-BLsKV5zF.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/request-info-CEhUGODY.js", "/assets/pe-CUZaIcdt.js", "/assets/client-hints-DNUUFGmB.js", "/assets/index-BXWoOGxB.js", "/assets/user-D6tTg1yS.js", "/assets/presence-Cr--lRCr.js", "/assets/progress-Biq-ngNF.js", "/assets/index-6GyYWV9G.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-7o6O4ruO.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/button-BklqyTPS.js", "/assets/tooltip-BiHTe_7F.js", "/assets/user-D6tTg1yS.js", "/assets/presence-Cr--lRCr.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-K6Dvbx-E.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+/discord": { "id": "routes/_app+/discord", "parentId": "routes/_app+/_layout", "path": "discord", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord-CHP4_JLJ.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/user-D6tTg1yS.js", "/assets/discord-Dq4e30wV.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-BUs3av-e.js", "imports": ["/assets/index-1cKOJFpX.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-CpcJsfoV.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/mdx-D7ttfz2V.js", "/assets/progress-Biq-ngNF.js", "/assets/seo-pBpFCWsy.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-B5pmJ29o.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.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-DVR5FOsb.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/index-Dx5GmdYq.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-D7ttfz2V.js", "/assets/progress-Biq-ngNF.js", "/assets/set-playground-BXHckvUG.js", "/assets/seo-pBpFCWsy.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-BhrXo-z3.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/pe-CUZaIcdt.js", "/assets/button-BklqyTPS.js", "/assets/loading-C5uX0jJw.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-6GyYWV9G.js", "/assets/preview-CdiZ0d0L.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-CWVcudJR.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/mdx-D7ttfz2V.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/set-playground-BXHckvUG.js", "/assets/button-BklqyTPS.js", "/assets/diff-Cr4LLBag.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/discord-Dq4e30wV.js", "/assets/index-B-hHvmeV.js", "/assets/tests-t3Udab79.js", "/assets/preview-CdiZ0d0L.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-_8PxC8xF.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/index-6GyYWV9G.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/set-playground-BXHckvUG.js", "/assets/tests-t3Udab79.js"], "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-Ddb3J5ls.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-D7ttfz2V.js", "/assets/progress-Biq-ngNF.js", "/assets/seo-pBpFCWsy.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-CU8QBU41.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/nav-chevrons-B82nbdyj.js", "/assets/mdx-D7ttfz2V.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-Biq-ngNF.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-BotyhhDm.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/index-Dx5GmdYq.js", "/assets/error-boundary-DDTvdkB4.js", "/assets/mdx-D7ttfz2V.js", "/assets/progress-Biq-ngNF.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-4cvVFzF8.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/use-event-source-A_0lEOPX.js", "/assets/button-BklqyTPS.js", "/assets/loading-C5uX0jJw.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-hcqGIpir.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/components-CME-nGId.js"], "css": [] }, "routes/admin+/_layout": { "id": "routes/admin+/_layout", "parentId": "root", "path": "admin", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-CGLKLU3y.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/pe-CUZaIcdt.js", "/assets/tooltip-BiHTe_7F.js", "/assets/progress-Biq-ngNF.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-DP2rzg_V.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/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-l0sNRNKZ.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-BNr0PhFR.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/tooltip-BiHTe_7F.js", "/assets/index-BXWoOGxB.js", "/assets/index-hogig2HK.js", "/assets/request-info-CEhUGODY.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/epic-video-CxZy3iqK.js", "/assets/progress-bar-CSvo1ZXP.js", "/assets/accordion-BroSqIct.js", "/assets/mdx-D7ttfz2V.js", "/assets/diff-Cr4LLBag.js", "/assets/nav-chevrons-B82nbdyj.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/discord.callback": { "id": "routes/discord.callback", "parentId": "root", "path": "discord/callback", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/discord.callback-l0sNRNKZ.js", "imports": [], "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-Co4cldZ0.js", "imports": ["/assets/index-1cKOJFpX.js", "/assets/clsx-B-dksMZM.js", "/assets/components-CME-nGId.js", "/assets/misc-ENVX3CWf.js", "/assets/request-info-CEhUGODY.js", "/assets/tooltip-BiHTe_7F.js", "/assets/client-hints-DNUUFGmB.js", "/assets/pe-CUZaIcdt.js", "/assets/index-6GyYWV9G.js", "/assets/loading-C5uX0jJw.js", "/assets/user-D6tTg1yS.js", "/assets/button-BklqyTPS.js", "/assets/epic-video-CxZy3iqK.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/update-mdx-cache": { "id": "routes/update-mdx-cache", "parentId": "root", "path": "update-mdx-cache", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/update-mdx-cache-l0sNRNKZ.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-l0sNRNKZ.js", "imports": [], "css": [] } }, "url": "/assets/manifest-42dc4b5a.js", "version": "42dc4b5a" };
13431
13491
  const mode = "production";
13432
13492
  const assetsBuildDirectory = "build/client";
13433
13493
  const basename = "/";