@epic-web/workshop-app 4.14.1 → 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/build/client/assets/{_exerciseNumber-Ccwr4LZw.js → _exerciseNumber-BLQUCLPr.js} +2 -2
  2. package/build/client/assets/{_exerciseNumber-Ccwr4LZw.js.map → _exerciseNumber-BLQUCLPr.js.map} +1 -1
  3. package/build/client/assets/{_exerciseNumber_.finished-gEWcHsfL.js → _exerciseNumber_.finished-C9TSc48F.js} +2 -2
  4. package/build/client/assets/{_exerciseNumber_.finished-gEWcHsfL.js.map → _exerciseNumber_.finished-C9TSc48F.js.map} +1 -1
  5. package/build/client/assets/_layout-B789acDk.js +6 -0
  6. package/build/client/assets/_layout-B789acDk.js.map +1 -0
  7. package/build/client/assets/_layout-CZ3fjUvs.js +2 -0
  8. package/build/client/assets/_layout-CZ3fjUvs.js.map +1 -0
  9. package/build/client/assets/_layout-DLIzcS5v.js +2 -0
  10. package/build/client/assets/_layout-DLIzcS5v.js.map +1 -0
  11. package/build/client/assets/{_layout-Bn9QhWq9.js → _layout-DMuKczTu.js} +2 -2
  12. package/build/client/assets/{_layout-Bn9QhWq9.js.map → _layout-DMuKczTu.js.map} +1 -1
  13. package/build/client/assets/account-LnI_Eq0t.js +2 -0
  14. package/build/client/assets/account-LnI_Eq0t.js.map +1 -0
  15. package/build/client/assets/{diff-CpG96hGZ.js → diff-4FQLirNf.js} +2 -2
  16. package/build/client/assets/{diff-CpG96hGZ.js.map → diff-4FQLirNf.js.map} +1 -1
  17. package/build/client/assets/{diff-Bd9WnUnR.js → diff-CPrD1rHd.js} +2 -2
  18. package/build/client/assets/{diff-Bd9WnUnR.js.map → diff-CPrD1rHd.js.map} +1 -1
  19. package/build/client/assets/discord-BgaWmFRC.js +2 -0
  20. package/build/client/assets/discord-BgaWmFRC.js.map +1 -0
  21. package/build/client/assets/discord-Dk1wbXOn.js +2 -0
  22. package/build/client/assets/discord-Dk1wbXOn.js.map +1 -0
  23. package/build/client/assets/{epic-video-DJN9_SUj.js → epic-video-CfelX9-n.js} +2 -2
  24. package/build/client/assets/{epic-video-DJN9_SUj.js.map → epic-video-CfelX9-n.js.map} +1 -1
  25. package/build/client/assets/finished-C0mN8TiU.js +2 -0
  26. package/build/client/assets/finished-C0mN8TiU.js.map +1 -0
  27. package/build/client/assets/{index-3HxjMIAS.js → index-De7yI02n.js} +2 -2
  28. package/build/client/assets/{index-3HxjMIAS.js.map → index-De7yI02n.js.map} +1 -1
  29. package/build/client/assets/login-SJlLMYmL.js +2 -0
  30. package/build/client/assets/login-SJlLMYmL.js.map +1 -0
  31. package/build/client/assets/{manifest-1375f1dd.js → manifest-38193299.js} +1 -1
  32. package/build/client/assets/mdx-CQW0I4So.js +2 -0
  33. package/build/client/assets/mdx-CQW0I4So.js.map +1 -0
  34. package/build/client/assets/onboarding-BXGyEPXy.js +2 -0
  35. package/build/client/assets/onboarding-BXGyEPXy.js.map +1 -0
  36. package/build/client/assets/robots_._txt-l0sNRNKZ.js +2 -0
  37. package/build/client/assets/robots_._txt-l0sNRNKZ.js.map +1 -0
  38. package/build/client/assets/root-BUqeXmeK.js.map +1 -1
  39. package/build/client/assets/sitemap_._xml-l0sNRNKZ.js +2 -0
  40. package/build/client/assets/sitemap_._xml-l0sNRNKZ.js.map +1 -0
  41. package/build/client/assets/support-DilzTPHE.js +2 -0
  42. package/build/client/assets/support-DilzTPHE.js.map +1 -0
  43. package/build/server/index.js +982 -925
  44. package/build/server/index.js.map +1 -1
  45. package/dist/server/index.js +18 -5
  46. package/package.json +5 -3
  47. package/start.js +1 -23
  48. package/build/client/assets/_layout-B9Y-8OU6.js +0 -2
  49. package/build/client/assets/_layout-B9Y-8OU6.js.map +0 -1
  50. package/build/client/assets/_layout-DaZNLfOL.js +0 -2
  51. package/build/client/assets/_layout-DaZNLfOL.js.map +0 -1
  52. package/build/client/assets/_layout-dHX2Hcew.js +0 -6
  53. package/build/client/assets/_layout-dHX2Hcew.js.map +0 -1
  54. package/build/client/assets/account-uvU1jIL2.js +0 -2
  55. package/build/client/assets/account-uvU1jIL2.js.map +0 -1
  56. package/build/client/assets/discord-CgwNGD2p.js +0 -2
  57. package/build/client/assets/discord-CgwNGD2p.js.map +0 -1
  58. package/build/client/assets/discord-yZor-3t1.js +0 -2
  59. package/build/client/assets/discord-yZor-3t1.js.map +0 -1
  60. package/build/client/assets/finished-B9qjUR4B.js +0 -2
  61. package/build/client/assets/finished-B9qjUR4B.js.map +0 -1
  62. package/build/client/assets/login-CHE9W6po.js +0 -2
  63. package/build/client/assets/login-CHE9W6po.js.map +0 -1
  64. package/build/client/assets/mdx-BENF-kTQ.js +0 -2
  65. package/build/client/assets/mdx-BENF-kTQ.js.map +0 -1
  66. package/build/client/assets/onboarding-FD1FkQuu.js +0 -2
  67. package/build/client/assets/onboarding-FD1FkQuu.js.map +0 -1
  68. package/build/client/assets/support-D1ydJNdm.js +0 -2
  69. package/build/client/assets/support-D1ydJNdm.js.map +0 -1
@@ -1,23 +1,27 @@
1
- var _a, _b;
1
+ var _a;
2
2
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
3
  import { PassThrough } from "stream";
4
- import fs from "node:fs";
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, Await, useSubmit, useSearchParams, Form, useNavigate } from "@remix-run/react";
6
+ import { isbot } from "isbot";
7
+ import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
5
8
  import path$1 from "node:path";
6
- import { remember } from "@epic-web/remember";
7
- import "@total-typescript/ts-reset";
8
- import { execaCommand, execa } from "execa";
9
- import fsExtra from "fs-extra";
10
- import { glob } from "glob";
11
- import { isGitIgnored, globby } from "globby";
12
- import * as z from "zod";
13
- import { z as z$1 } from "zod";
14
9
  import os from "os";
15
10
  import path from "path";
16
11
  import * as C from "@epic-web/cachified";
17
12
  import { verboseReporter, cachified as cachified$1 } from "@epic-web/cachified";
13
+ import { remember } from "@epic-web/remember";
14
+ import fsExtra from "fs-extra";
18
15
  import { LRUCache } from "lru-cache";
19
16
  import md5 from "md5-hex";
20
- import chokidar from "chokidar";
17
+ import * as z from "zod";
18
+ import { z as z$1 } from "zod";
19
+ import fs from "node:fs";
20
+ import "@total-typescript/ts-reset";
21
+ import { execaCommand, execa } from "execa";
22
+ import { glob } from "glob";
23
+ import { isGitIgnored, globby } from "globby";
24
+ import "chokidar";
21
25
  import closeWithGrace from "close-with-grace";
22
26
  import fs$1 from "fs";
23
27
  import { remarkCodeBlocksShiki } from "@kentcdodds/md-temp";
@@ -33,10 +37,6 @@ import child_process, { spawn } from "child_process";
33
37
  import net from "node:net";
34
38
  import chalk from "chalk";
35
39
  import fkill from "fkill";
36
- import { createReadableStreamFromReadable, redirect, json, createCookieSessionStorage, defer } from "@remix-run/node";
37
- import { RemixServer, Link, useRouteError, useParams, isRouteErrorResponse, useNavigation, useFetchers, useRouteLoaderData, useRevalidator, useFetcher, useLoaderData, Outlet, Meta, Links, ScrollRestoration, Scripts, useLocation, NavLink, Await, useSubmit, useSearchParams, Form, useNavigate } from "@remix-run/react";
38
- import { isbot } from "isbot";
39
- import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
40
40
  import { cssBundleHref } from "@remix-run/css-bundle";
41
41
  import * as React from "react";
42
42
  import React__default, { useRef, useEffect, useMemo, useState, createContext, useContext, useSyncExternalStore, Suspense, forwardRef, useImperativeHandle, useReducer } from "react";
@@ -88,6 +88,42 @@ import { Issuer } from "openid-client";
88
88
  import inspector from "node:inspector";
89
89
  import { Resvg } from "@resvg/resvg-js";
90
90
  import satori from "satori";
91
+ import { generateRobotsTxt, generateSitemap } from "@nasa-gcn/remix-seo";
92
+ const ABORT_DELAY = 15e3;
93
+ function handleRequest(request, responseStatusCode, responseHeaders, remixContext) {
94
+ const callbackName = isbot(request.headers.get("user-agent")) ? "onAllReady" : "onShellReady";
95
+ return new Promise((resolve, reject) => {
96
+ let didError = false;
97
+ const { pipe, abort } = renderToPipeableStream(
98
+ /* @__PURE__ */ jsx(RemixServer, { context: remixContext, url: request.url }),
99
+ {
100
+ [callbackName]() {
101
+ const body = new PassThrough();
102
+ responseHeaders.set("Content-Type", "text/html");
103
+ resolve(
104
+ new Response(createReadableStreamFromReadable(body), {
105
+ status: didError ? 500 : responseStatusCode,
106
+ headers: responseHeaders
107
+ })
108
+ );
109
+ pipe(body);
110
+ },
111
+ onShellError(err) {
112
+ reject(err);
113
+ },
114
+ onError(error) {
115
+ didError = true;
116
+ console.error(error);
117
+ }
118
+ }
119
+ );
120
+ setTimeout(abort, ABORT_DELAY);
121
+ });
122
+ }
123
+ const entryServer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
124
+ __proto__: null,
125
+ default: handleRequest
126
+ }, Symbol.toStringTag, { value: "Module" }));
91
127
  function makeTimings(type, desc) {
92
128
  const timings = {
93
129
  [type]: [{ desc, start: performance.now() }]
@@ -292,153 +328,388 @@ async function shouldForceFresh({
292
328
  if (!key) return false;
293
329
  return fresh.split(",").includes(key);
294
330
  }
295
- let watcher = global.__change_tracker_watcher__;
296
- function getWatcher() {
297
- if (process.env.EPICSHOP_DEPLOYED ?? process.env.EPICSHOP_DISABLE_WATCHER === "true") {
298
- return null;
299
- }
300
- if (watcher) return watcher;
301
- const workshopRoot2 = process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
302
- watcher = chokidar.watch(workshopRoot2, {
303
- ignoreInitial: true,
304
- ignored: [
305
- "**/.git/**",
306
- "**/node_modules/**",
307
- "**/build/**",
308
- "**/public/build/**",
309
- "**/playwright-report/**",
310
- "**/dist/**",
311
- "**/.cache/**"
312
- ]
313
- });
314
- global.__change_tracker_watcher__ = watcher;
315
- return watcher;
316
- }
317
- function getOptionalWatcher() {
318
- return watcher;
319
- }
320
- (_a = global.__change_tracker_close_with_grace_return__) == null ? void 0 : _a.uninstall();
321
- global.__change_tracker_close_with_grace_return__ = closeWithGrace(
322
- () => watcher == null ? void 0 : watcher.close()
323
- );
324
- const APP_TYPES = ["problem", "solution", "playground"];
325
- const safePath$1 = (s) => s.replace(/\\/g, "/");
326
- const REG_EXP = /^(?:\d+(?:-\d+)?,)*\d+(?:-\d+)?$/;
327
- const isValidRangeFormat = (value) => value ? REG_EXP.test(value) : true;
328
- const transformRange = (value) => value == null ? void 0 : value.split(",").map((range) => {
329
- const [start, end] = range.split("-").map(Number);
330
- return [start, end ?? start];
331
+ const DiscordMemberSchema = z$1.object({
332
+ avatarURL: z$1.string().nullable().optional(),
333
+ displayName: z$1.string(),
334
+ id: z$1.string()
331
335
  });
332
- const isRangeBounded = (range, ctx, lines) => {
333
- if (!lines || !Array.isArray(range)) return;
334
- if (range.flat().some((r) => r < 1 || r > lines)) {
335
- ctx.addIssue({
336
- code: z.ZodIssueCode.custom,
337
- message: `Range must be between 1 and ${lines}`
338
- });
339
- }
340
- };
341
- const isRangeInOrder = (range) => Array.isArray(range) ? range.every(([a, b]) => !isNaN(Number(a)) && !isNaN(Number(b)) && b >= a) : true;
342
- const isRangesNonOverlapping = (range) => {
343
- if (!Array.isArray(range)) return true;
344
- return range.every(([a], i) => {
345
- var _a2;
346
- return i === 0 || (((_a2 = range[i - 1]) == null ? void 0 : _a2[1]) ?? 0) < a;
347
- });
348
- };
349
- let fileContentCache = /* @__PURE__ */ new Map();
350
- async function getFileContent(filePath) {
351
- if (fileContentCache.has(filePath)) {
352
- return fileContentCache.get(filePath);
336
+ const TokenSetSchema = z$1.object({
337
+ access_token: z$1.string(),
338
+ token_type: z$1.string(),
339
+ scope: z$1.string()
340
+ });
341
+ const PlayerPreferencesSchema = z$1.object({
342
+ volumeRate: z$1.number().optional(),
343
+ playbackRate: z$1.number().optional(),
344
+ autoplay: z$1.boolean().optional(),
345
+ subtitle: z$1.object({
346
+ id: z$1.string().nullable().default(null),
347
+ mode: z$1.literal("disabled").or(z$1.literal("hidden")).or(z$1.literal("showing")).nullable().default("disabled")
348
+ }).optional().default({}),
349
+ muted: z$1.boolean().optional(),
350
+ theater: z$1.boolean().optional(),
351
+ defaultView: z$1.string().optional(),
352
+ activeSidebarTab: z$1.number().optional()
353
+ }).optional().default({});
354
+ const PresencePreferencesSchema = z$1.object({
355
+ optOut: z$1.boolean()
356
+ }).optional().default({ optOut: false });
357
+ const AuthInfoSchema = z$1.object({
358
+ tokenSet: TokenSetSchema,
359
+ email: z$1.string(),
360
+ name: z$1.string().nullable().optional()
361
+ }).transform((d) => ({ ...d, id: md5(d.email) }));
362
+ const DataSchema = z$1.object({
363
+ onboarding: z$1.object({ finishedTourVideo: z$1.boolean() }).optional().default({ finishedTourVideo: false }),
364
+ preferences: z$1.object({
365
+ player: PlayerPreferencesSchema,
366
+ presence: PresencePreferencesSchema
367
+ }).optional().default({}),
368
+ authInfo: AuthInfoSchema.optional(),
369
+ discordMember: DiscordMemberSchema.optional()
370
+ });
371
+ const appDir = path.join(os.homedir(), ".epicshop");
372
+ const dbPath = path.join(appDir, "data.json");
373
+ async function deleteDb() {
374
+ if (process.env.EPICSHOP_DEPLOYED) return null;
375
+ try {
376
+ if (await fsExtra.exists(dbPath)) {
377
+ await fsExtra.remove(dbPath);
378
+ }
379
+ } catch (error) {
380
+ console.error(`Error deleting the database in ${dbPath}`, error);
353
381
  }
382
+ }
383
+ async function readDb() {
384
+ if (process.env.EPICSHOP_DEPLOYED) return null;
354
385
  try {
355
- const content = await fs.promises.readFile(filePath, "utf-8");
356
- const fileContent = content.split("\n");
357
- fileContentCache.set(filePath, fileContent);
358
- return fileContent;
359
- } catch {
360
- console.warn(
361
- `@epic-web/workshop-app - invalid CodeFile.
362
- Could not read file: ${filePath}
363
- `
386
+ if (await fsExtra.exists(dbPath)) {
387
+ const db = DataSchema.parse(await fsExtra.readJSON(dbPath));
388
+ return db;
389
+ }
390
+ } catch (error) {
391
+ console.error(
392
+ `Error reading the database in ${dbPath}, moving it to a .bkp file to avoid parsing errors in the future`,
393
+ error
364
394
  );
395
+ void fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {
396
+ });
365
397
  }
398
+ return null;
366
399
  }
367
- async function validateProps(props, appDir2) {
368
- let validRange;
369
- let linesCount = 0;
370
- const BooleanSchema = z.nullable(z.string()).optional().refine(
371
- (v) => ["true", "false", null, void 0].includes(v),
372
- 'optional boolean key can be "true", "false", null or undefined'
373
- ).transform((v) => v === null || Boolean(v));
374
- const RangeSchema = z.string().optional().refine(isValidRangeFormat, "Invalid range format").transform(transformRange).superRefine((val, ctx) => isRangeBounded(val, ctx, linesCount)).refine(isRangeInOrder, "Range must be in order low-high");
375
- const inputSchema = z.object({
376
- file: z.string().nonempty().transform(async (file, ctx) => {
377
- const fullPath = path$1.join(appDir2, file);
378
- const content = await getFileContent(fullPath);
379
- if (!content) {
380
- ctx.addIssue({
381
- code: z.ZodIssueCode.custom,
382
- message: `Could not read file`,
383
- fatal: true
384
- });
385
- return z.NEVER;
386
- }
387
- linesCount = content.length;
388
- return {
389
- fullPath: safePath$1(fullPath),
390
- filePath: safePath$1(file),
391
- content
392
- };
393
- }),
394
- range: RangeSchema.refine((range) => {
395
- const isValid = isRangesNonOverlapping(range);
396
- validRange = isValid ? range : void 0;
397
- return isValid;
398
- }, "Ranges must not overlap"),
399
- highlight: RangeSchema.refine((highlight) => {
400
- if (!Array.isArray(highlight) || !Array.isArray(validRange)) {
401
- return z.NEVER;
402
- }
403
- return highlight.every(
404
- ([hStart, hEnd]) => validRange == null ? void 0 : validRange.some(
405
- ([rStart, rEnd]) => hStart >= rStart && hEnd <= rEnd
406
- )
407
- );
408
- }, "Highlight range must be within defined range").transform(() => props.highlight).optional(),
409
- nonumber: BooleanSchema,
410
- nocopy: BooleanSchema,
411
- buttons: z.string().optional().transform(
412
- (str) => str ? str.split(",") : []
413
- ).refine((arr) => arr.every((item) => APP_TYPES.includes(item)), {
414
- message: `Buttons can only be any of ${APP_TYPES.join(",")}`
415
- })
416
- }).strict();
417
- return inputSchema.safeParseAsync(props);
400
+ async function getAuthInfo() {
401
+ const data = await readDb();
402
+ return (data == null ? void 0 : data.authInfo) ?? null;
418
403
  }
419
- async function createErrorNotification(node, errors, mdxFile, appType) {
420
- var _a2, _b2;
421
- const filename = path$1.basename(mdxFile);
422
- const startLine = (_a2 = node.position) == null ? void 0 : _a2.start.line;
423
- const endLine = (_b2 = node.position) == null ? void 0 : _b2.end.line;
424
- const codeFence = async () => {
425
- if (startLine && endLine) {
426
- const contentStr = await getFileContent(mdxFile);
427
- const content = contentStr == null ? void 0 : contentStr.slice(startLine - 1, endLine).join("\n");
428
- if (content) {
429
- return `
430
- \`\`\`tsx filename=${filename} start=${startLine} nocopy
431
- ${content}
432
- \`\`\``.trim();
433
- }
434
- }
435
- return "";
404
+ async function getUserInfo() {
405
+ var _a2, _b;
406
+ const db = await readDb();
407
+ if (!(db == null ? void 0 : db.authInfo)) return null;
408
+ return {
409
+ id: db.authInfo.id,
410
+ name: ((_a2 = db.discordMember) == null ? void 0 : _a2.displayName) ?? db.authInfo.name ?? null,
411
+ email: db.authInfo.email,
412
+ avatarUrl: ((_b = db.discordMember) == null ? void 0 : _b.avatarURL) ?? getGravatar({ email: db.authInfo.email, size: 288 })
436
413
  };
437
- const mdxSource = `
438
- <CodeFileNotification variant="error" file="${filename}" line="${startLine}" type="${appType}">
439
- <callout-danger class="notification">
440
- <div className="title">CodeFile Error: invalid input</div>
441
- ${errors.map((error) => `<div>${error}</div>`).join("")}
414
+ }
415
+ function getGravatar({ email, size }) {
416
+ const gravatarOptions = new URLSearchParams({
417
+ size: size.toString(),
418
+ default: "identicon"
419
+ });
420
+ const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
421
+ email
422
+ )}?${gravatarOptions.toString()}`;
423
+ return gravatarUrl;
424
+ }
425
+ async function requireAuthInfo({
426
+ request,
427
+ redirectTo
428
+ }) {
429
+ const authInfo = await getAuthInfo();
430
+ if (!authInfo) {
431
+ const requestUrl = new URL(request.url);
432
+ redirectTo = redirectTo === null ? null : redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`;
433
+ const loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null;
434
+ const loginRedirect = ["/login", loginParams == null ? void 0 : loginParams.toString()].filter(Boolean).join("?");
435
+ throw redirect(loginRedirect);
436
+ }
437
+ return authInfo;
438
+ }
439
+ async function setAuthInfo({
440
+ tokenSet,
441
+ email = "unknown@example.com",
442
+ name
443
+ }) {
444
+ const data = await readDb();
445
+ const authInfo = AuthInfoSchema.parse({ tokenSet, email, name });
446
+ await fsExtra.ensureDir(appDir);
447
+ await fsExtra.writeJSON(dbPath, { ...data, authInfo });
448
+ return authInfo;
449
+ }
450
+ async function getPreferences() {
451
+ const data = await readDb();
452
+ return (data == null ? void 0 : data.preferences) ?? null;
453
+ }
454
+ async function setPlayerPreferences(playerPreferences) {
455
+ const data = await readDb();
456
+ const updatedData = {
457
+ ...data,
458
+ preferences: { ...data == null ? void 0 : data.preferences, player: playerPreferences }
459
+ };
460
+ await fsExtra.ensureDir(appDir);
461
+ await fsExtra.writeJSON(dbPath, updatedData);
462
+ return updatedData.preferences.player;
463
+ }
464
+ async function setPresencePreferences(presnecePreferences) {
465
+ const data = await readDb();
466
+ const updatedData = {
467
+ ...data,
468
+ preferences: { ...data == null ? void 0 : data.preferences, presence: presnecePreferences }
469
+ };
470
+ await fsExtra.ensureDir(appDir);
471
+ await fsExtra.writeJSON(dbPath, updatedData);
472
+ return updatedData.preferences.presence;
473
+ }
474
+ async function getDiscordMember() {
475
+ const data = await readDb();
476
+ return (data == null ? void 0 : data.discordMember) ?? null;
477
+ }
478
+ async function setDiscordMember(discordMember) {
479
+ const data = await readDb();
480
+ const updatedData = {
481
+ ...data,
482
+ discordMember
483
+ };
484
+ await fsExtra.ensureDir(appDir);
485
+ await fsExtra.writeJSON(dbPath, updatedData);
486
+ return updatedData.discordMember;
487
+ }
488
+ async function deleteDiscordInfo() {
489
+ const data = await readDb();
490
+ data == null ? true : delete data.discordMember;
491
+ await fsExtra.ensureDir(appDir);
492
+ await fsExtra.writeJSON(dbPath, data);
493
+ }
494
+ async function readOnboardingData() {
495
+ const data = await readDb();
496
+ return (data == null ? void 0 : data.onboarding) ?? null;
497
+ }
498
+ async function updateOnboardingData(onboardingData) {
499
+ const data = await readDb();
500
+ const updatedData = {
501
+ ...data,
502
+ onboarding: { ...data == null ? void 0 : data.onboarding, ...onboardingData }
503
+ };
504
+ await fsExtra.ensureDir(appDir);
505
+ await fsExtra.writeJSON(dbPath, updatedData);
506
+ return updatedData.onboarding;
507
+ }
508
+ const partykitRoom = "epic-web-presence";
509
+ const partykitBaseUrl = `https://epic-web-presence.kentcdodds.partykit.dev/parties/main/${partykitRoom}`;
510
+ const UserSchema = z$1.object({
511
+ id: z$1.string(),
512
+ avatarUrl: z$1.string().nullable().optional(),
513
+ name: z$1.string().nullable().optional(),
514
+ location: z$1.object({
515
+ workshopTitle: z$1.string().nullable().optional(),
516
+ origin: z$1.string().nullable().optional(),
517
+ exercise: z$1.object({
518
+ type: z$1.union([z$1.literal("problem"), z$1.literal("solution")]).nullable().optional(),
519
+ exerciseNumber: z$1.number().nullable().optional(),
520
+ stepNumber: z$1.number().nullable().optional()
521
+ }).nullable().optional()
522
+ }).nullable().optional()
523
+ });
524
+ const MessageSchema = z$1.object({
525
+ type: z$1.literal("remove-user"),
526
+ payload: z$1.object({ id: z$1.string() })
527
+ }).or(z$1.object({ type: z$1.literal("add-user"), payload: UserSchema })).or(
528
+ z$1.object({
529
+ type: z$1.literal("presence"),
530
+ payload: z$1.object({ users: z$1.array(UserSchema) })
531
+ })
532
+ );
533
+ const PresenceSchema = z$1.object({ users: z$1.array(UserSchema) });
534
+ const presenceCache = makeSingletonCache("PresenceCache");
535
+ async function getPresentUsers(user, { timings, request } = {}) {
536
+ return cachified({
537
+ key: "presence",
538
+ cache: presenceCache,
539
+ timings,
540
+ request,
541
+ ttl: 1e3 * 60 * 5,
542
+ swr: 1e3 * 60 * 60 * 24,
543
+ checkValue: z$1.array(UserSchema),
544
+ async getFreshValue(context) {
545
+ try {
546
+ const response = await Promise.race([
547
+ fetch(`${partykitBaseUrl}/presence`),
548
+ new Promise(
549
+ (resolve) => setTimeout(() => {
550
+ resolve(new Response("Timeout", { status: 500 }));
551
+ }, 500)
552
+ )
553
+ ]);
554
+ if (response.statusText === "Timeout") {
555
+ throw new Error(`Timeout fetching partykit presence`);
556
+ }
557
+ if (!response.ok) {
558
+ throw new Error(
559
+ `Unexpected response from partykit: ${response.status} ${response.statusText}`
560
+ );
561
+ }
562
+ const presence = PresenceSchema.parse(await response.json());
563
+ const preferences = await getPreferences();
564
+ const users = presence.users;
565
+ if ((preferences == null ? void 0 : preferences.presence.optOut) ?? !user) {
566
+ return uniqueUsers(users.filter((u) => u.id !== (user == null ? void 0 : user.id)));
567
+ } else {
568
+ return uniqueUsers([...users, user]);
569
+ }
570
+ } catch {
571
+ context.metadata.ttl = 300;
572
+ return [];
573
+ }
574
+ }
575
+ });
576
+ }
577
+ function uniqueUsers(users) {
578
+ const seen = /* @__PURE__ */ new Set();
579
+ return users.filter(Boolean).filter((user) => {
580
+ if (seen.has(user.id)) {
581
+ return false;
582
+ }
583
+ seen.add(user.id);
584
+ return true;
585
+ });
586
+ }
587
+ let watcher = global.__change_tracker_watcher__;
588
+ function getOptionalWatcher() {
589
+ return watcher;
590
+ }
591
+ (_a = global.__change_tracker_close_with_grace_return__) == null ? void 0 : _a.uninstall();
592
+ global.__change_tracker_close_with_grace_return__ = closeWithGrace(
593
+ () => watcher == null ? void 0 : watcher.close()
594
+ );
595
+ const APP_TYPES = ["problem", "solution", "playground"];
596
+ const safePath$1 = (s) => s.replace(/\\/g, "/");
597
+ const REG_EXP = /^(?:\d+(?:-\d+)?,)*\d+(?:-\d+)?$/;
598
+ const isValidRangeFormat = (value) => value ? REG_EXP.test(value) : true;
599
+ const transformRange = (value) => value == null ? void 0 : value.split(",").map((range) => {
600
+ const [start, end] = range.split("-").map(Number);
601
+ return [start, end ?? start];
602
+ });
603
+ const isRangeBounded = (range, ctx, lines) => {
604
+ if (!lines || !Array.isArray(range)) return;
605
+ if (range.flat().some((r) => r < 1 || r > lines)) {
606
+ ctx.addIssue({
607
+ code: z.ZodIssueCode.custom,
608
+ message: `Range must be between 1 and ${lines}`
609
+ });
610
+ }
611
+ };
612
+ const isRangeInOrder = (range) => Array.isArray(range) ? range.every(([a, b]) => !isNaN(Number(a)) && !isNaN(Number(b)) && b >= a) : true;
613
+ const isRangesNonOverlapping = (range) => {
614
+ if (!Array.isArray(range)) return true;
615
+ return range.every(([a], i) => {
616
+ var _a2;
617
+ return i === 0 || (((_a2 = range[i - 1]) == null ? void 0 : _a2[1]) ?? 0) < a;
618
+ });
619
+ };
620
+ let fileContentCache = /* @__PURE__ */ new Map();
621
+ async function getFileContent(filePath) {
622
+ if (fileContentCache.has(filePath)) {
623
+ return fileContentCache.get(filePath);
624
+ }
625
+ try {
626
+ const content = await fs.promises.readFile(filePath, "utf-8");
627
+ const fileContent = content.split("\n");
628
+ fileContentCache.set(filePath, fileContent);
629
+ return fileContent;
630
+ } catch {
631
+ console.warn(
632
+ `@epic-web/workshop-app - invalid CodeFile.
633
+ Could not read file: ${filePath}
634
+ `
635
+ );
636
+ }
637
+ }
638
+ async function validateProps(props, appDir2) {
639
+ let validRange;
640
+ let linesCount = 0;
641
+ const BooleanSchema = z.nullable(z.string()).optional().refine(
642
+ (v) => ["true", "false", null, void 0].includes(v),
643
+ 'optional boolean key can be "true", "false", null or undefined'
644
+ ).transform((v) => v === null || Boolean(v));
645
+ const RangeSchema = z.string().optional().refine(isValidRangeFormat, "Invalid range format").transform(transformRange).superRefine((val, ctx) => isRangeBounded(val, ctx, linesCount)).refine(isRangeInOrder, "Range must be in order low-high");
646
+ const inputSchema = z.object({
647
+ file: z.string().nonempty().transform(async (file, ctx) => {
648
+ const fullPath = path$1.join(appDir2, file);
649
+ const content = await getFileContent(fullPath);
650
+ if (!content) {
651
+ ctx.addIssue({
652
+ code: z.ZodIssueCode.custom,
653
+ message: `Could not read file`,
654
+ fatal: true
655
+ });
656
+ return z.NEVER;
657
+ }
658
+ linesCount = content.length;
659
+ return {
660
+ fullPath: safePath$1(fullPath),
661
+ filePath: safePath$1(file),
662
+ content
663
+ };
664
+ }),
665
+ range: RangeSchema.refine((range) => {
666
+ const isValid = isRangesNonOverlapping(range);
667
+ validRange = isValid ? range : void 0;
668
+ return isValid;
669
+ }, "Ranges must not overlap"),
670
+ highlight: RangeSchema.refine((highlight) => {
671
+ if (!Array.isArray(highlight) || !Array.isArray(validRange)) {
672
+ return z.NEVER;
673
+ }
674
+ return highlight.every(
675
+ ([hStart, hEnd]) => validRange == null ? void 0 : validRange.some(
676
+ ([rStart, rEnd]) => hStart >= rStart && hEnd <= rEnd
677
+ )
678
+ );
679
+ }, "Highlight range must be within defined range").transform(() => props.highlight).optional(),
680
+ nonumber: BooleanSchema,
681
+ nocopy: BooleanSchema,
682
+ buttons: z.string().optional().transform(
683
+ (str) => str ? str.split(",") : []
684
+ ).refine((arr) => arr.every((item) => APP_TYPES.includes(item)), {
685
+ message: `Buttons can only be any of ${APP_TYPES.join(",")}`
686
+ })
687
+ }).strict();
688
+ return inputSchema.safeParseAsync(props);
689
+ }
690
+ async function createErrorNotification(node, errors, mdxFile, appType) {
691
+ var _a2, _b;
692
+ const filename = path$1.basename(mdxFile);
693
+ const startLine = (_a2 = node.position) == null ? void 0 : _a2.start.line;
694
+ const endLine = (_b = node.position) == null ? void 0 : _b.end.line;
695
+ const codeFence = async () => {
696
+ if (startLine && endLine) {
697
+ const contentStr = await getFileContent(mdxFile);
698
+ const content = contentStr == null ? void 0 : contentStr.slice(startLine - 1, endLine).join("\n");
699
+ if (content) {
700
+ return `
701
+ \`\`\`tsx filename=${filename} start=${startLine} nocopy
702
+ ${content}
703
+ \`\`\``.trim();
704
+ }
705
+ }
706
+ return "";
707
+ };
708
+ const mdxSource = `
709
+ <CodeFileNotification variant="error" file="${filename}" line="${startLine}" type="${appType}">
710
+ <callout-danger class="notification">
711
+ <div className="title">CodeFile Error: invalid input</div>
712
+ ${errors.map((error) => `<div>${error}</div>`).join("")}
442
713
  ${await codeFence()}
443
714
  </callout-danger>
444
715
  </CodeFileNotification>`;
@@ -468,7 +739,7 @@ function remarkCodeFile(data) {
468
739
  node,
469
740
  parent
470
741
  }) {
471
- var _a2, _b2;
742
+ var _a2, _b;
472
743
  if (!parent) {
473
744
  console.warn(
474
745
  "Unexpected error: replaceCodeFileNode called without a Parent"
@@ -551,7 +822,7 @@ ${rangeContent}
551
822
  // then the changes were reverted and the warning will be remove
552
823
  ((cachedData.warning && cachedData.warning !== contentHash) ?? (!cachedData.warning && cachedData.hash !== contentHash))) {
553
824
  newData.warning = cachedData.warning ?? cachedData.hash;
554
- const startLine = ((_b2 = node.position) == null ? void 0 : _b2.start.line) ?? 1;
825
+ const startLine = ((_b = node.position) == null ? void 0 : _b.start.line) ?? 1;
555
826
  newData.line = startLine;
556
827
  const mdxFilename = path$1.basename(mdxFile);
557
828
  const filename = path$1.basename(filePath);
@@ -881,6 +1152,21 @@ async function queuedBundleMDX(...args) {
881
1152
  const result = await queue.add(() => bundleMDX(...args));
882
1153
  return result;
883
1154
  }
1155
+ z$1.object({
1156
+ NODE_ENV: z$1.enum(["production", "development", "test"]).default("development"),
1157
+ EPICSHOP_GITHUB_REPO: z$1.string(),
1158
+ EPICSHOP_GITHUB_ROOT: z$1.string(),
1159
+ EPICSHOP_CONTEXT_CWD: z$1.string()
1160
+ });
1161
+ function getEnv() {
1162
+ return {
1163
+ MODE: process.env.NODE_ENV,
1164
+ EPICSHOP_CONTEXT_CWD: process.env.EPICSHOP_CONTEXT_CWD,
1165
+ EPICSHOP_GITHUB_REPO: process.env.EPICSHOP_GITHUB_REPO,
1166
+ EPICSHOP_GITHUB_ROOT: process.env.EPICSHOP_GITHUB_ROOT,
1167
+ EPICSHOP_DEPLOYED: process.env.EPICSHOP_DEPLOYED === "true" || process.env.EPICSHOP_DEPLOYED === "1"
1168
+ };
1169
+ }
884
1170
  function getErrorMessage$1(error) {
885
1171
  if (typeof error === "string") return error;
886
1172
  if (error && typeof error === "object" && "message" in error && typeof error.message === "string") {
@@ -987,7 +1273,7 @@ async function runAppDev(app) {
987
1273
  return { status: "process-started", running: true };
988
1274
  }
989
1275
  async function runAppTests(app) {
990
- var _a2, _b2;
1276
+ var _a2, _b;
991
1277
  if (isDeployed$1) throw new Error("cannot run tests in deployed mode");
992
1278
  const key = app.name;
993
1279
  if (app.test.type !== "script") {
@@ -1025,11 +1311,11 @@ async function runAppTests(app) {
1025
1311
  timestamp: Date.now()
1026
1312
  });
1027
1313
  }
1028
- (_b2 = testProcess.stderr) == null ? void 0 : _b2.on("data", handleStdErrData);
1314
+ (_b = testProcess.stderr) == null ? void 0 : _b.on("data", handleStdErrData);
1029
1315
  void testProcess.on("exit", (code) => {
1030
- var _a3, _b3;
1316
+ var _a3, _b2;
1031
1317
  (_a3 = testProcess.stdout) == null ? void 0 : _a3.off("data", handleStdOutData);
1032
- (_b3 = testProcess.stderr) == null ? void 0 : _b3.off("data", handleStdErrData);
1318
+ (_b2 = testProcess.stderr) == null ? void 0 : _b2.off("data", handleStdErrData);
1033
1319
  entry2.process = null;
1034
1320
  entry2.exitCode = code;
1035
1321
  });
@@ -1165,8 +1451,15 @@ async function getPkgProp(fullPath, prop, defaultValue) {
1165
1451
  }
1166
1452
  return value ?? defaultValue;
1167
1453
  }
1168
- (_b = process.env).NODE_ENV ?? (_b.NODE_ENV = "development");
1169
1454
  const workshopRoot = getWorkshopRoot();
1455
+ let packageJson;
1456
+ try {
1457
+ packageJson = JSON.parse(
1458
+ fs.readFileSync(path$1.join(workshopRoot, "package.json"), "utf8")
1459
+ );
1460
+ } catch {
1461
+ throw new Error(`Could not find and parse package.json at ${workshopRoot}`);
1462
+ }
1170
1463
  const playgroundAppNameInfoPath = path$1.join(
1171
1464
  getWorkshopRoot(),
1172
1465
  "node_modules",
@@ -1304,23 +1597,10 @@ function firstToExist(...files) {
1304
1597
  return index === -1 ? null : files[index];
1305
1598
  });
1306
1599
  }
1307
- const modifiedTimes = remember(
1308
- "modified_times",
1309
- () => /* @__PURE__ */ new Map()
1310
- );
1311
- function init() {
1312
- var _a2;
1313
- async function handleFileChanges(event, filePath) {
1314
- const apps = await getApps();
1315
- for (const app of apps) {
1316
- if (filePath.startsWith(app.fullPath)) {
1317
- modifiedTimes.set(app.fullPath, Date.now());
1318
- break;
1319
- }
1320
- }
1321
- }
1322
- (_a2 = getWatcher()) == null ? void 0 : _a2.on("all", handleFileChanges);
1323
- }
1600
+ const modifiedTimes = remember(
1601
+ "modified_times",
1602
+ () => /* @__PURE__ */ new Map()
1603
+ );
1324
1604
  function getForceFresh$1(cacheEntry) {
1325
1605
  if (!cacheEntry) return true;
1326
1606
  const latestModifiedTime = Math.max(...Array.from(modifiedTimes.values()));
@@ -1377,9 +1657,9 @@ function getAppDirInfo(appDir2) {
1377
1657
  return { stepNumber, type, subtitle };
1378
1658
  }
1379
1659
  function extractExerciseNumber(dir) {
1380
- var _a2, _b2;
1660
+ var _a2, _b;
1381
1661
  const regex = /^(?<number>\d+)\./;
1382
- const number = (_b2 = (_a2 = regex.exec(dir)) == null ? void 0 : _a2.groups) == null ? void 0 : _b2.number;
1662
+ const number = (_b = (_a2 = regex.exec(dir)) == null ? void 0 : _a2.groups) == null ? void 0 : _b.number;
1383
1663
  if (!number) {
1384
1664
  return null;
1385
1665
  }
@@ -1699,12 +1979,11 @@ async function getStackBlitzUrl({
1699
1979
  await getPkgProp(workshopRoot, "epicshop.stackBlitzConfig", {})
1700
1980
  );
1701
1981
  if (workshopStackBlitzConfig === null) return null;
1702
- const githubRootUrlString = await getPkgProp(
1703
- workshopRoot,
1704
- "epicshop.githubRoot",
1705
- ""
1706
- );
1982
+ let githubRootUrlString = ENV.EPICSHOP_GITHUB_REPO;
1707
1983
  if (!githubRootUrlString) return null;
1984
+ if (!githubRootUrlString.includes("/blob/") || !githubRootUrlString.includes("/tree/")) {
1985
+ githubRootUrlString = `${githubRootUrlString.replace(/\/$/, "")}/blob/main`;
1986
+ }
1708
1987
  const githubRootUrl = new URL(
1709
1988
  githubRootUrlString.replace(/\/blob\//, "/tree/")
1710
1989
  );
@@ -1968,642 +2247,336 @@ async function getProblemApps({
1968
2247
  timings,
1969
2248
  timingKey: problemDir.replace(`${exercisesDir}${path$1.sep}`, ""),
1970
2249
  request,
1971
- ttl: 1e3 * 60 * 60 * 24,
1972
- forceFresh: getForceFreshForDir(
1973
- problemDir,
1974
- problemAppCache.get(problemDir)
1975
- ),
1976
- getFreshValue: () => getProblemAppFromPath(problemDir).catch((error) => {
1977
- console.error(error);
1978
- return null;
1979
- })
1980
- });
1981
- if (problemApp) problemApps.push(problemApp);
1982
- }
1983
- return problemApps;
1984
- }
1985
- async function getExercise(exerciseNumber, { request, timings } = {}) {
1986
- const exercises = await getExercises({ request, timings });
1987
- return exercises.find((s) => s.exerciseNumber === Number(exerciseNumber));
1988
- }
1989
- async function requireExercise(exerciseNumber, { request, timings } = {}) {
1990
- const exercise = await getExercise(exerciseNumber, { request, timings });
1991
- if (!exercise) {
1992
- throw new Response("Not found", {
1993
- status: 404,
1994
- headers: { "Server-Timing": getServerTimeHeader(timings) }
1995
- });
1996
- }
1997
- return exercise;
1998
- }
1999
- async function requireExerciseApp(params, { request, timings } = {}) {
2000
- const app = await getExerciseApp(params, { request, timings });
2001
- if (!app) {
2002
- throw new Response("Not found", { status: 404 });
2003
- }
2004
- return app;
2005
- }
2006
- const ExerciseAppParamsSchema$1 = z$1.object({
2007
- type: z$1.union([z$1.literal("problem"), z$1.literal("solution")]),
2008
- exerciseNumber: z$1.coerce.number().finite(),
2009
- stepNumber: z$1.coerce.number().finite()
2010
- });
2011
- async function getExerciseApp(params, { request, timings } = {}) {
2012
- const result = ExerciseAppParamsSchema$1.safeParse(params);
2013
- if (!result.success) {
2014
- return null;
2015
- }
2016
- const { type, exerciseNumber, stepNumber } = result.data;
2017
- const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2018
- const exerciseApp = apps.find((app) => {
2019
- if (isExampleApp(app)) return false;
2020
- return app.exerciseNumber === exerciseNumber && app.stepNumber === stepNumber && app.type === type;
2021
- });
2022
- if (!exerciseApp) {
2023
- return null;
2024
- }
2025
- return exerciseApp;
2026
- }
2027
- async function getAppByName(name, { request, timings } = {}) {
2028
- const apps = await getApps({ request, timings });
2029
- return apps.find((a) => a.name === name);
2030
- }
2031
- async function getNextExerciseApp(app, { request, timings } = {}) {
2032
- const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2033
- const index = apps.findIndex((a) => a.name === app.name);
2034
- if (index === -1) {
2035
- throw new Error(`Could not find app ${app.name}`);
2036
- }
2037
- const nextApp = apps[index + 1];
2038
- return nextApp ? nextApp : null;
2039
- }
2040
- async function getPrevExerciseApp(app, { request, timings } = {}) {
2041
- const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2042
- const index = apps.findIndex((a) => a.name === app.name);
2043
- if (index === -1) {
2044
- throw new Error(`Could not find app ${app.name}`);
2045
- }
2046
- const prevApp = apps[index - 1];
2047
- return prevApp ? prevApp : null;
2048
- }
2049
- function getAppPageRoute(app) {
2050
- const exerciseNumber = app.exerciseNumber.toString().padStart(2, "0");
2051
- const stepNumber = app.stepNumber.toString().padStart(2, "0");
2052
- return `/${exerciseNumber}/${stepNumber}/${app.type}`;
2053
- }
2054
- async function getAppFromFile(filePath) {
2055
- const apps = await getApps();
2056
- return apps.find((app) => filePath.startsWith(app.fullPath));
2057
- }
2058
- async function setPlayground(srcDir, { reset } = {}) {
2059
- var _a2, _b2;
2060
- const isIgnored = await isGitIgnored({ cwd: srcDir });
2061
- const destDir = path$1.join(workshopRoot, "playground");
2062
- const playgroundFiles = path$1.join(destDir, "**");
2063
- (_a2 = getOptionalWatcher()) == null ? void 0 : _a2.unwatch(playgroundFiles);
2064
- const playgroundApp = await getAppByName("playground");
2065
- const playgroundWasRunning = playgroundApp ? isAppRunning(playgroundApp) : false;
2066
- if (playgroundApp && reset) {
2067
- await closeProcess(playgroundApp.name);
2068
- await fsExtra.remove(destDir);
2069
- }
2070
- const setPlaygroundTimestamp = Date.now();
2071
- const preSetPlaygroundPath = await firstToExist(
2072
- path$1.join(srcDir, "epicshop", "pre-set-playground.js"),
2073
- path$1.join(workshopRoot, "epicshop", "pre-set-playground.js")
2074
- );
2075
- if (preSetPlaygroundPath) {
2076
- await execa("node", [preSetPlaygroundPath], {
2077
- cwd: workshopRoot,
2078
- stdio: "inherit",
2079
- env: {
2080
- EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
2081
- EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
2082
- EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
2083
- EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString()
2084
- }
2085
- });
2086
- }
2087
- const basename2 = path$1.basename(srcDir);
2088
- await fsExtra.remove(path$1.join(destDir, "node_modules"));
2089
- await fsExtra.copy(srcDir, destDir, {
2090
- filter: async (srcFile, destFile) => {
2091
- if (srcFile.includes(`${basename2}${path$1.sep}build`) || srcFile.includes(`${basename2}${path$1.sep}public${path$1.sep}build`)) {
2092
- return false;
2093
- }
2094
- if (srcFile === srcDir) return true;
2095
- if (srcFile.includes("node_modules")) return true;
2096
- if (srcFile.endsWith(".env")) return true;
2097
- if (isIgnored(srcFile)) return false;
2098
- try {
2099
- const isDir = (await fsExtra.stat(srcFile)).isDirectory();
2100
- if (isDir) return true;
2101
- const destIsDir = (await fsExtra.stat(destFile)).isDirectory();
2102
- if (destIsDir) return true;
2103
- const currentContents = await fsExtra.readFile(destFile);
2104
- const newContents = await fsExtra.readFile(srcFile);
2105
- if (currentContents.equals(newContents)) return false;
2106
- return true;
2107
- } catch {
2108
- return true;
2109
- }
2110
- }
2111
- });
2112
- async function getFiles(dir) {
2113
- const dirPath = dir.replace(/\\/g, "/");
2114
- const files = await globby([`${dirPath}/**/*`, "!**/build/**/*"], {
2115
- onlyFiles: false,
2116
- dot: true
2117
- });
2118
- return files.map((f) => f.replace(dirPath, ""));
2119
- }
2120
- const srcFiles = await getFiles(srcDir);
2121
- const destFiles = await getFiles(destDir);
2122
- const filesToDelete = destFiles.filter(
2123
- (fileName) => !srcFiles.includes(fileName)
2124
- );
2125
- for (const fileToDelete of filesToDelete) {
2126
- await fsExtra.remove(path$1.join(destDir, fileToDelete));
2127
- }
2128
- const appName = getAppName(srcDir);
2129
- await fsExtra.ensureDir(path$1.dirname(playgroundAppNameInfoPath));
2130
- await fsExtra.writeJSON(playgroundAppNameInfoPath, { appName });
2131
- const playgroundIsStillRunning = playgroundApp ? isAppRunning(playgroundApp) : false;
2132
- const restartPlayground = playgroundWasRunning && !playgroundIsStillRunning;
2133
- const postSetPlaygroundPath = await firstToExist(
2134
- path$1.join(srcDir, "epicshop", "post-set-playground.js"),
2135
- path$1.join(workshopRoot, "epicshop", "post-set-playground.js")
2136
- );
2137
- if (postSetPlaygroundPath) {
2138
- await execa("node", [postSetPlaygroundPath], {
2139
- cwd: workshopRoot,
2140
- stdio: "inherit",
2141
- env: {
2142
- EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
2143
- EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
2144
- EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
2145
- EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
2146
- EPICSHOP_PLAYGROUND_IS_STILL_RUNNING: playgroundIsStillRunning.toString(),
2147
- EPICSHOP_PLAYGROUND_RESTART_PLAYGROUND: restartPlayground.toString()
2148
- }
2149
- });
2150
- }
2151
- if (playgroundApp && restartPlayground) {
2152
- await runAppDev(playgroundApp);
2153
- await waitOnApp(playgroundApp);
2154
- }
2155
- (_b2 = getOptionalWatcher()) == null ? void 0 : _b2.add(playgroundFiles);
2156
- modifiedTimes.set(destDir, Date.now());
2157
- }
2158
- async function getPlaygroundAppName() {
2159
- if (!await exists(playgroundAppNameInfoPath)) {
2160
- return null;
2161
- }
2162
- try {
2163
- const jsonString = await fs.promises.readFile(
2164
- playgroundAppNameInfoPath,
2165
- "utf8"
2166
- );
2167
- const { appName } = JSON.parse(jsonString);
2168
- if (typeof appName !== "string") return null;
2169
- return appName;
2170
- } catch {
2171
- return null;
2172
- }
2173
- }
2174
- async function getDirModifiedTime(dir) {
2175
- const isIgnored = await isGitIgnored({ cwd: dir });
2176
- const files = await fs.promises.readdir(dir, { withFileTypes: true });
2177
- const modifiedTimes2 = await Promise.all(
2178
- files.map(async (file) => {
2179
- if (isIgnored(file.name)) return 0;
2180
- const filePath = path$1.join(dir, file.name);
2181
- if (file.isDirectory()) {
2182
- return getDirModifiedTime(filePath);
2183
- } else {
2184
- try {
2185
- const { mtimeMs } = await fs.promises.stat(filePath);
2186
- return mtimeMs;
2187
- } catch {
2188
- return 0;
2189
- }
2190
- }
2191
- })
2192
- );
2193
- return Math.max(0, ...modifiedTimes2);
2194
- }
2195
- function getAppDisplayName(a, allApps) {
2196
- let displayName = `${a.title} (${a.type})`;
2197
- if (isExerciseStepApp(a)) {
2198
- const typeLabel = { problem: "💪", solution: "🏁" }[a.type];
2199
- displayName = `${a.exerciseNumber}.${a.stepNumber} ${a.title} (${typeLabel} ${a.type})`;
2200
- } else if (isPlaygroundApp(a)) {
2201
- const playgroundAppBasis = allApps.find(
2202
- (otherApp) => a.appName === otherApp.name
2203
- );
2204
- if (playgroundAppBasis) {
2205
- const basisDisplayName = getAppDisplayName(playgroundAppBasis, allApps);
2206
- displayName = `🛝 ${basisDisplayName}`;
2207
- } else {
2208
- displayName = `🛝 ${a.appName}`;
2209
- }
2210
- } else if (isExampleApp(a)) {
2211
- displayName = `📚 ${a.title} (example)`;
2212
- }
2213
- return displayName;
2214
- }
2215
- async function getWorkshopTitle() {
2216
- const title = await getPkgProp(workshopRoot, "epicshop.title");
2217
- if (!title) {
2218
- throw new Error(
2219
- `Workshop title not found. Make sure the root of the workshop has "epicshop" with a "title" property in the package.json. ${workshopRoot}`
2220
- );
2250
+ ttl: 1e3 * 60 * 60 * 24,
2251
+ forceFresh: getForceFreshForDir(
2252
+ problemDir,
2253
+ problemAppCache.get(problemDir)
2254
+ ),
2255
+ getFreshValue: () => getProblemAppFromPath(problemDir).catch((error) => {
2256
+ console.error(error);
2257
+ return null;
2258
+ })
2259
+ });
2260
+ if (problemApp) problemApps.push(problemApp);
2221
2261
  }
2222
- return title;
2223
- }
2224
- async function getWorkshopSubtitle() {
2225
- return await getPkgProp(workshopRoot, "epicshop.subtitle", "");
2262
+ return problemApps;
2226
2263
  }
2227
- async function getWorkshopInstructor() {
2228
- const InstructorSchema = z$1.object({
2229
- name: z$1.string().optional(),
2230
- avatar: z$1.string().optional(),
2231
- "𝕏": z$1.string().optional(),
2232
- // alias because 𝕏 is hard to type 😅
2233
- xHandle: z$1.string().optional()
2234
- }).optional();
2235
- const instructor = InstructorSchema.parse(
2236
- await getPkgProp(workshopRoot, "epicshop.instructor")
2237
- );
2238
- return instructor ? { ...instructor, "𝕏": instructor.xHandle } : void 0;
2264
+ async function getExercise(exerciseNumber, { request, timings } = {}) {
2265
+ const exercises = await getExercises({ request, timings });
2266
+ return exercises.find((s) => s.exerciseNumber === Number(exerciseNumber));
2239
2267
  }
2240
- async function getEpicWorkshopHost() {
2241
- const epicWorkshopHost = await getPkgProp(
2242
- workshopRoot,
2243
- "epicshop.epicWorkshopHost",
2244
- "www.epicweb.dev"
2245
- );
2246
- return epicWorkshopHost;
2268
+ async function requireExercise(exerciseNumber, { request, timings } = {}) {
2269
+ const exercise = await getExercise(exerciseNumber, { request, timings });
2270
+ if (!exercise) {
2271
+ throw new Response("Not found", {
2272
+ status: 404,
2273
+ headers: { "Server-Timing": getServerTimeHeader(timings) }
2274
+ });
2275
+ }
2276
+ return exercise;
2247
2277
  }
2248
- async function getEpicWorkshopSlug() {
2249
- const epicWorkshopSlug = await getPkgProp(
2250
- workshopRoot,
2251
- "epicshop.epicWorkshopSlug",
2252
- ""
2253
- );
2254
- return epicWorkshopSlug || null;
2278
+ async function requireExerciseApp(params, { request, timings } = {}) {
2279
+ const app = await getExerciseApp(params, { request, timings });
2280
+ if (!app) {
2281
+ throw new Response("Not found", { status: 404 });
2282
+ }
2283
+ return app;
2255
2284
  }
2256
- function getWorkshopRoot() {
2257
- return process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
2285
+ const ExerciseAppParamsSchema$1 = z$1.object({
2286
+ type: z$1.union([z$1.literal("problem"), z$1.literal("solution")]),
2287
+ exerciseNumber: z$1.coerce.number().finite(),
2288
+ stepNumber: z$1.coerce.number().finite()
2289
+ });
2290
+ async function getExerciseApp(params, { request, timings } = {}) {
2291
+ const result = ExerciseAppParamsSchema$1.safeParse(params);
2292
+ if (!result.success) {
2293
+ return null;
2294
+ }
2295
+ const { type, exerciseNumber, stepNumber } = result.data;
2296
+ const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2297
+ const exerciseApp = apps.find((app) => {
2298
+ if (isExampleApp(app)) return false;
2299
+ return app.exerciseNumber === exerciseNumber && app.stepNumber === stepNumber && app.type === type;
2300
+ });
2301
+ if (!exerciseApp) {
2302
+ return null;
2303
+ }
2304
+ return exerciseApp;
2258
2305
  }
2259
- async function getWorkshopInstructions({
2260
- request
2261
- } = {}) {
2262
- const readmeFilepath = path$1.join(workshopRoot, "exercises", "README.mdx");
2263
- const compiled = await compileMdx(readmeFilepath, { request }).then(
2264
- (r) => ({ ...r, status: "success" }),
2265
- (e) => {
2266
- console.error(
2267
- `There was an error compiling the workshop readme`,
2268
- readmeFilepath,
2269
- e
2270
- );
2271
- return { status: "error", error: getErrorMessage$1(e) };
2272
- }
2273
- );
2274
- return { compiled, file: readmeFilepath, relativePath: "exercises" };
2306
+ async function getAppByName(name, { request, timings } = {}) {
2307
+ const apps = await getApps({ request, timings });
2308
+ return apps.find((a) => a.name === name);
2275
2309
  }
2276
- async function getWorkshopFinished({
2277
- request
2278
- } = {}) {
2279
- const finishedFilepath = path$1.join(workshopRoot, "exercises", "FINISHED.mdx");
2280
- const compiled = await compileMdx(finishedFilepath, { request }).then(
2281
- (r) => ({ ...r, status: "success" }),
2282
- (e) => {
2283
- console.error(
2284
- `There was an error compiling the workshop finished.mdx`,
2285
- finishedFilepath,
2286
- e
2287
- );
2288
- return { status: "error", error: getErrorMessage$1(e) };
2289
- }
2290
- );
2291
- return {
2292
- compiled,
2293
- file: finishedFilepath,
2294
- relativePath: "exercises/finished.mdx"
2295
- };
2310
+ async function getNextExerciseApp(app, { request, timings } = {}) {
2311
+ const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2312
+ const index = apps.findIndex((a) => a.name === app.name);
2313
+ if (index === -1) {
2314
+ throw new Error(`Could not find app ${app.name}`);
2315
+ }
2316
+ const nextApp = apps[index + 1];
2317
+ return nextApp ? nextApp : null;
2296
2318
  }
2297
- const exercisesPath = path$1.join(workshopRoot, "exercises/");
2298
- const playgroundPath = path$1.join(workshopRoot, "playground/");
2299
- function getRelativePath$1(filePath) {
2300
- return path$1.normalize(filePath).replace(playgroundPath, `playground${path$1.sep}`).replace(exercisesPath, "");
2319
+ async function getPrevExerciseApp(app, { request, timings } = {}) {
2320
+ const apps = (await getApps({ request, timings })).filter(isExerciseStepApp);
2321
+ const index = apps.findIndex((a) => a.name === app.name);
2322
+ if (index === -1) {
2323
+ throw new Error(`Could not find app ${app.name}`);
2324
+ }
2325
+ const prevApp = apps[index - 1];
2326
+ return prevApp ? prevApp : null;
2301
2327
  }
2302
- z$1.object({
2303
- NODE_ENV: z$1.enum(["production", "development", "test"]).default("development"),
2304
- EPICSHOP_GITHUB_ROOT: z$1.string(),
2305
- EPICSHOP_CONTEXT_CWD: z$1.string()
2306
- });
2307
- function getEnv() {
2308
- return {
2309
- MODE: process.env.NODE_ENV,
2310
- EPICSHOP_CONTEXT_CWD: process.env.EPICSHOP_CONTEXT_CWD,
2311
- EPICSHOP_GITHUB_ROOT: process.env.EPICSHOP_GITHUB_ROOT,
2312
- EPICSHOP_DEPLOYED: process.env.EPICSHOP_DEPLOYED === "true" || process.env.EPICSHOP_DEPLOYED === "1"
2313
- };
2328
+ function getAppPageRoute(app) {
2329
+ const exerciseNumber = app.exerciseNumber.toString().padStart(2, "0");
2330
+ const stepNumber = app.stepNumber.toString().padStart(2, "0");
2331
+ return `/${exerciseNumber}/${stepNumber}/${app.type}`;
2314
2332
  }
2315
- global.ENV = getEnv();
2316
- const ABORT_DELAY = 15e3;
2317
- init();
2318
- function handleRequest(request, responseStatusCode, responseHeaders, remixContext) {
2319
- const callbackName = isbot(request.headers.get("user-agent")) ? "onAllReady" : "onShellReady";
2320
- return new Promise((resolve, reject) => {
2321
- let didError = false;
2322
- const { pipe, abort } = renderToPipeableStream(
2323
- /* @__PURE__ */ jsx(RemixServer, { context: remixContext, url: request.url }),
2324
- {
2325
- [callbackName]() {
2326
- const body = new PassThrough();
2327
- responseHeaders.set("Content-Type", "text/html");
2328
- resolve(
2329
- new Response(createReadableStreamFromReadable(body), {
2330
- status: didError ? 500 : responseStatusCode,
2331
- headers: responseHeaders
2332
- })
2333
- );
2334
- pipe(body);
2335
- },
2336
- onShellError(err) {
2337
- reject(err);
2338
- },
2339
- onError(error) {
2340
- didError = true;
2341
- console.error(error);
2342
- }
2343
- }
2344
- );
2345
- setTimeout(abort, ABORT_DELAY);
2346
- });
2333
+ async function getAppFromFile(filePath) {
2334
+ const apps = await getApps();
2335
+ return apps.find((app) => filePath.startsWith(app.fullPath));
2347
2336
  }
2348
- const entryServer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2349
- __proto__: null,
2350
- default: handleRequest
2351
- }, Symbol.toStringTag, { value: "Module" }));
2352
- const DiscordMemberSchema = z$1.object({
2353
- avatarURL: z$1.string().nullable().optional(),
2354
- displayName: z$1.string(),
2355
- id: z$1.string()
2356
- });
2357
- const TokenSetSchema = z$1.object({
2358
- access_token: z$1.string(),
2359
- token_type: z$1.string(),
2360
- scope: z$1.string()
2361
- });
2362
- const PlayerPreferencesSchema = z$1.object({
2363
- volumeRate: z$1.number().optional(),
2364
- playbackRate: z$1.number().optional(),
2365
- autoplay: z$1.boolean().optional(),
2366
- subtitle: z$1.object({
2367
- id: z$1.string().nullable().default(null),
2368
- mode: z$1.literal("disabled").or(z$1.literal("hidden")).or(z$1.literal("showing")).nullable().default("disabled")
2369
- }).optional().default({}),
2370
- muted: z$1.boolean().optional(),
2371
- theater: z$1.boolean().optional(),
2372
- defaultView: z$1.string().optional(),
2373
- activeSidebarTab: z$1.number().optional()
2374
- }).optional().default({});
2375
- const PresencePreferencesSchema = z$1.object({
2376
- optOut: z$1.boolean()
2377
- }).optional().default({ optOut: false });
2378
- const AuthInfoSchema = z$1.object({
2379
- tokenSet: TokenSetSchema,
2380
- email: z$1.string(),
2381
- name: z$1.string().nullable().optional()
2382
- }).transform((d) => ({ ...d, id: md5(d.email) }));
2383
- const DataSchema = z$1.object({
2384
- onboarding: z$1.object({ finishedTourVideo: z$1.boolean() }).optional().default({ finishedTourVideo: false }),
2385
- preferences: z$1.object({
2386
- player: PlayerPreferencesSchema,
2387
- presence: PresencePreferencesSchema
2388
- }).optional().default({}),
2389
- authInfo: AuthInfoSchema.optional(),
2390
- discordMember: DiscordMemberSchema.optional()
2391
- });
2392
- const appDir = path.join(os.homedir(), ".epicshop");
2393
- const dbPath = path.join(appDir, "data.json");
2394
- async function deleteDb() {
2395
- if (process.env.EPICSHOP_DEPLOYED) return null;
2396
- try {
2397
- if (await fsExtra.exists(dbPath)) {
2398
- await fsExtra.remove(dbPath);
2337
+ async function setPlayground(srcDir, { reset } = {}) {
2338
+ var _a2, _b;
2339
+ const isIgnored = await isGitIgnored({ cwd: srcDir });
2340
+ const destDir = path$1.join(workshopRoot, "playground");
2341
+ const playgroundFiles = path$1.join(destDir, "**");
2342
+ (_a2 = getOptionalWatcher()) == null ? void 0 : _a2.unwatch(playgroundFiles);
2343
+ const playgroundApp = await getAppByName("playground");
2344
+ const playgroundWasRunning = playgroundApp ? isAppRunning(playgroundApp) : false;
2345
+ if (playgroundApp && reset) {
2346
+ await closeProcess(playgroundApp.name);
2347
+ await fsExtra.remove(destDir);
2348
+ }
2349
+ const setPlaygroundTimestamp = Date.now();
2350
+ const preSetPlaygroundPath = await firstToExist(
2351
+ path$1.join(srcDir, "epicshop", "pre-set-playground.js"),
2352
+ path$1.join(workshopRoot, "epicshop", "pre-set-playground.js")
2353
+ );
2354
+ if (preSetPlaygroundPath) {
2355
+ await execa("node", [preSetPlaygroundPath], {
2356
+ cwd: workshopRoot,
2357
+ stdio: "inherit",
2358
+ env: {
2359
+ EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
2360
+ EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
2361
+ EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
2362
+ EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString()
2363
+ }
2364
+ });
2365
+ }
2366
+ const basename2 = path$1.basename(srcDir);
2367
+ await fsExtra.remove(path$1.join(destDir, "node_modules"));
2368
+ await fsExtra.copy(srcDir, destDir, {
2369
+ filter: async (srcFile, destFile) => {
2370
+ if (srcFile.includes(`${basename2}${path$1.sep}build`) || srcFile.includes(`${basename2}${path$1.sep}public${path$1.sep}build`)) {
2371
+ return false;
2372
+ }
2373
+ if (srcFile === srcDir) return true;
2374
+ if (srcFile.includes("node_modules")) return true;
2375
+ if (srcFile.endsWith(".env")) return true;
2376
+ if (isIgnored(srcFile)) return false;
2377
+ try {
2378
+ const isDir = (await fsExtra.stat(srcFile)).isDirectory();
2379
+ if (isDir) return true;
2380
+ const destIsDir = (await fsExtra.stat(destFile)).isDirectory();
2381
+ if (destIsDir) return true;
2382
+ const currentContents = await fsExtra.readFile(destFile);
2383
+ const newContents = await fsExtra.readFile(srcFile);
2384
+ if (currentContents.equals(newContents)) return false;
2385
+ return true;
2386
+ } catch {
2387
+ return true;
2388
+ }
2399
2389
  }
2400
- } catch (error) {
2401
- console.error(`Error deleting the database in ${dbPath}`, error);
2390
+ });
2391
+ async function getFiles(dir) {
2392
+ const dirPath = dir.replace(/\\/g, "/");
2393
+ const files = await globby([`${dirPath}/**/*`, "!**/build/**/*"], {
2394
+ onlyFiles: false,
2395
+ dot: true
2396
+ });
2397
+ return files.map((f) => f.replace(dirPath, ""));
2398
+ }
2399
+ const srcFiles = await getFiles(srcDir);
2400
+ const destFiles = await getFiles(destDir);
2401
+ const filesToDelete = destFiles.filter(
2402
+ (fileName) => !srcFiles.includes(fileName)
2403
+ );
2404
+ for (const fileToDelete of filesToDelete) {
2405
+ await fsExtra.remove(path$1.join(destDir, fileToDelete));
2406
+ }
2407
+ const appName = getAppName(srcDir);
2408
+ await fsExtra.ensureDir(path$1.dirname(playgroundAppNameInfoPath));
2409
+ await fsExtra.writeJSON(playgroundAppNameInfoPath, { appName });
2410
+ const playgroundIsStillRunning = playgroundApp ? isAppRunning(playgroundApp) : false;
2411
+ const restartPlayground = playgroundWasRunning && !playgroundIsStillRunning;
2412
+ const postSetPlaygroundPath = await firstToExist(
2413
+ path$1.join(srcDir, "epicshop", "post-set-playground.js"),
2414
+ path$1.join(workshopRoot, "epicshop", "post-set-playground.js")
2415
+ );
2416
+ if (postSetPlaygroundPath) {
2417
+ await execa("node", [postSetPlaygroundPath], {
2418
+ cwd: workshopRoot,
2419
+ stdio: "inherit",
2420
+ env: {
2421
+ EPICSHOP_PLAYGROUND_TIMESTAMP: setPlaygroundTimestamp.toString(),
2422
+ EPICSHOP_PLAYGROUND_SRC_DIR: srcDir,
2423
+ EPICSHOP_PLAYGROUND_DEST_DIR: destDir,
2424
+ EPICSHOP_PLAYGROUND_WAS_RUNNING: playgroundWasRunning.toString(),
2425
+ EPICSHOP_PLAYGROUND_IS_STILL_RUNNING: playgroundIsStillRunning.toString(),
2426
+ EPICSHOP_PLAYGROUND_RESTART_PLAYGROUND: restartPlayground.toString()
2427
+ }
2428
+ });
2429
+ }
2430
+ if (playgroundApp && restartPlayground) {
2431
+ await runAppDev(playgroundApp);
2432
+ await waitOnApp(playgroundApp);
2402
2433
  }
2434
+ (_b = getOptionalWatcher()) == null ? void 0 : _b.add(playgroundFiles);
2435
+ modifiedTimes.set(destDir, Date.now());
2403
2436
  }
2404
- async function readDb() {
2405
- if (process.env.EPICSHOP_DEPLOYED) return null;
2437
+ async function getPlaygroundAppName() {
2438
+ if (!await exists(playgroundAppNameInfoPath)) {
2439
+ return null;
2440
+ }
2406
2441
  try {
2407
- if (await fsExtra.exists(dbPath)) {
2408
- const db = DataSchema.parse(await fsExtra.readJSON(dbPath));
2409
- return db;
2410
- }
2411
- } catch (error) {
2412
- console.error(
2413
- `Error reading the database in ${dbPath}, moving it to a .bkp file to avoid parsing errors in the future`,
2414
- error
2442
+ const jsonString = await fs.promises.readFile(
2443
+ playgroundAppNameInfoPath,
2444
+ "utf8"
2415
2445
  );
2416
- void fsExtra.move(dbPath, `${dbPath}.bkp`).catch(() => {
2417
- });
2446
+ const { appName } = JSON.parse(jsonString);
2447
+ if (typeof appName !== "string") return null;
2448
+ return appName;
2449
+ } catch {
2450
+ return null;
2418
2451
  }
2419
- return null;
2420
- }
2421
- async function getAuthInfo() {
2422
- const data = await readDb();
2423
- return (data == null ? void 0 : data.authInfo) ?? null;
2424
- }
2425
- async function getUserInfo() {
2426
- var _a2, _b2;
2427
- const db = await readDb();
2428
- if (!(db == null ? void 0 : db.authInfo)) return null;
2429
- return {
2430
- id: db.authInfo.id,
2431
- name: ((_a2 = db.discordMember) == null ? void 0 : _a2.displayName) ?? db.authInfo.name ?? null,
2432
- email: db.authInfo.email,
2433
- avatarUrl: ((_b2 = db.discordMember) == null ? void 0 : _b2.avatarURL) ?? getGravatar({ email: db.authInfo.email, size: 288 })
2434
- };
2435
2452
  }
2436
- function getGravatar({ email, size }) {
2437
- const gravatarOptions = new URLSearchParams({
2438
- size: size.toString(),
2439
- default: "identicon"
2440
- });
2441
- const gravatarUrl = `https://www.gravatar.com/avatar/${md5(
2442
- email
2443
- )}?${gravatarOptions.toString()}`;
2444
- return gravatarUrl;
2453
+ async function getDirModifiedTime(dir) {
2454
+ const isIgnored = await isGitIgnored({ cwd: dir });
2455
+ const files = await fs.promises.readdir(dir, { withFileTypes: true });
2456
+ const modifiedTimes2 = await Promise.all(
2457
+ files.map(async (file) => {
2458
+ if (isIgnored(file.name)) return 0;
2459
+ const filePath = path$1.join(dir, file.name);
2460
+ if (file.isDirectory()) {
2461
+ return getDirModifiedTime(filePath);
2462
+ } else {
2463
+ try {
2464
+ const { mtimeMs } = await fs.promises.stat(filePath);
2465
+ return mtimeMs;
2466
+ } catch {
2467
+ return 0;
2468
+ }
2469
+ }
2470
+ })
2471
+ );
2472
+ return Math.max(0, ...modifiedTimes2);
2445
2473
  }
2446
- async function requireAuthInfo({
2447
- request,
2448
- redirectTo
2449
- }) {
2450
- const authInfo = await getAuthInfo();
2451
- if (!authInfo) {
2452
- const requestUrl = new URL(request.url);
2453
- redirectTo = redirectTo === null ? null : redirectTo ?? `${requestUrl.pathname}${requestUrl.search}`;
2454
- const loginParams = redirectTo ? new URLSearchParams({ redirectTo }) : null;
2455
- const loginRedirect = ["/login", loginParams == null ? void 0 : loginParams.toString()].filter(Boolean).join("?");
2456
- throw redirect(loginRedirect);
2474
+ function getAppDisplayName(a, allApps) {
2475
+ let displayName = `${a.title} (${a.type})`;
2476
+ if (isExerciseStepApp(a)) {
2477
+ const typeLabel = { problem: "💪", solution: "🏁" }[a.type];
2478
+ displayName = `${a.exerciseNumber}.${a.stepNumber} ${a.title} (${typeLabel} ${a.type})`;
2479
+ } else if (isPlaygroundApp(a)) {
2480
+ const playgroundAppBasis = allApps.find(
2481
+ (otherApp) => a.appName === otherApp.name
2482
+ );
2483
+ if (playgroundAppBasis) {
2484
+ const basisDisplayName = getAppDisplayName(playgroundAppBasis, allApps);
2485
+ displayName = `🛝 ${basisDisplayName}`;
2486
+ } else {
2487
+ displayName = `🛝 ${a.appName}`;
2488
+ }
2489
+ } else if (isExampleApp(a)) {
2490
+ displayName = `📚 ${a.title} (example)`;
2457
2491
  }
2458
- return authInfo;
2459
- }
2460
- async function setAuthInfo({
2461
- tokenSet,
2462
- email = "unknown@example.com",
2463
- name
2464
- }) {
2465
- const data = await readDb();
2466
- const authInfo = AuthInfoSchema.parse({ tokenSet, email, name });
2467
- await fsExtra.ensureDir(appDir);
2468
- await fsExtra.writeJSON(dbPath, { ...data, authInfo });
2469
- return authInfo;
2470
- }
2471
- async function getPreferences() {
2472
- const data = await readDb();
2473
- return (data == null ? void 0 : data.preferences) ?? null;
2474
- }
2475
- async function setPlayerPreferences(playerPreferences) {
2476
- const data = await readDb();
2477
- const updatedData = {
2478
- ...data,
2479
- preferences: { ...data == null ? void 0 : data.preferences, player: playerPreferences }
2480
- };
2481
- await fsExtra.ensureDir(appDir);
2482
- await fsExtra.writeJSON(dbPath, updatedData);
2483
- return updatedData.preferences.player;
2492
+ return displayName;
2484
2493
  }
2485
- async function setPresencePreferences(presnecePreferences) {
2486
- const data = await readDb();
2487
- const updatedData = {
2488
- ...data,
2489
- preferences: { ...data == null ? void 0 : data.preferences, presence: presnecePreferences }
2490
- };
2491
- await fsExtra.ensureDir(appDir);
2492
- await fsExtra.writeJSON(dbPath, updatedData);
2493
- return updatedData.preferences.presence;
2494
+ async function getWorkshopTitle() {
2495
+ const title = await getPkgProp(workshopRoot, "epicshop.title");
2496
+ if (!title) {
2497
+ throw new Error(
2498
+ `Workshop title not found. Make sure the root of the workshop has "epicshop" with a "title" property in the package.json. ${workshopRoot}`
2499
+ );
2500
+ }
2501
+ return title;
2494
2502
  }
2495
- async function getDiscordMember() {
2496
- const data = await readDb();
2497
- return (data == null ? void 0 : data.discordMember) ?? null;
2503
+ async function getWorkshopSubtitle() {
2504
+ return await getPkgProp(workshopRoot, "epicshop.subtitle", "");
2498
2505
  }
2499
- async function setDiscordMember(discordMember) {
2500
- const data = await readDb();
2501
- const updatedData = {
2502
- ...data,
2503
- discordMember
2504
- };
2505
- await fsExtra.ensureDir(appDir);
2506
- await fsExtra.writeJSON(dbPath, updatedData);
2507
- return updatedData.discordMember;
2506
+ async function getWorkshopInstructor() {
2507
+ const InstructorSchema = z$1.object({
2508
+ name: z$1.string().optional(),
2509
+ avatar: z$1.string().optional(),
2510
+ "𝕏": z$1.string().optional(),
2511
+ // alias because 𝕏 is hard to type 😅
2512
+ xHandle: z$1.string().optional()
2513
+ }).optional();
2514
+ const instructor = InstructorSchema.parse(
2515
+ await getPkgProp(workshopRoot, "epicshop.instructor")
2516
+ );
2517
+ return instructor ? { ...instructor, "𝕏": instructor.xHandle } : void 0;
2508
2518
  }
2509
- async function deleteDiscordInfo() {
2510
- const data = await readDb();
2511
- data == null ? true : delete data.discordMember;
2512
- await fsExtra.ensureDir(appDir);
2513
- await fsExtra.writeJSON(dbPath, data);
2519
+ async function getEpicWorkshopHost() {
2520
+ const epicWorkshopHost = await getPkgProp(
2521
+ workshopRoot,
2522
+ "epicshop.epicWorkshopHost",
2523
+ "www.epicweb.dev"
2524
+ );
2525
+ return epicWorkshopHost;
2514
2526
  }
2515
- async function readOnboardingData() {
2516
- const data = await readDb();
2517
- return (data == null ? void 0 : data.onboarding) ?? null;
2527
+ async function getEpicWorkshopSlug() {
2528
+ const epicWorkshopSlug = await getPkgProp(
2529
+ workshopRoot,
2530
+ "epicshop.epicWorkshopSlug",
2531
+ ""
2532
+ );
2533
+ return epicWorkshopSlug || null;
2518
2534
  }
2519
- async function updateOnboardingData(onboardingData) {
2520
- const data = await readDb();
2521
- const updatedData = {
2522
- ...data,
2523
- onboarding: { ...data == null ? void 0 : data.onboarding, ...onboardingData }
2524
- };
2525
- await fsExtra.ensureDir(appDir);
2526
- await fsExtra.writeJSON(dbPath, updatedData);
2527
- return updatedData.onboarding;
2535
+ function getWorkshopRoot() {
2536
+ return process.env.EPICSHOP_CONTEXT_CWD ?? process.cwd();
2528
2537
  }
2529
- const partykitRoom = "epic-web-presence";
2530
- const partykitBaseUrl = `https://epic-web-presence.kentcdodds.partykit.dev/parties/main/${partykitRoom}`;
2531
- const UserSchema = z$1.object({
2532
- id: z$1.string(),
2533
- avatarUrl: z$1.string().nullable().optional(),
2534
- name: z$1.string().nullable().optional(),
2535
- location: z$1.object({
2536
- workshopTitle: z$1.string().nullable().optional(),
2537
- origin: z$1.string().nullable().optional(),
2538
- exercise: z$1.object({
2539
- type: z$1.union([z$1.literal("problem"), z$1.literal("solution")]).nullable().optional(),
2540
- exerciseNumber: z$1.number().nullable().optional(),
2541
- stepNumber: z$1.number().nullable().optional()
2542
- }).nullable().optional()
2543
- }).nullable().optional()
2544
- });
2545
- const MessageSchema = z$1.object({
2546
- type: z$1.literal("remove-user"),
2547
- payload: z$1.object({ id: z$1.string() })
2548
- }).or(z$1.object({ type: z$1.literal("add-user"), payload: UserSchema })).or(
2549
- z$1.object({
2550
- type: z$1.literal("presence"),
2551
- payload: z$1.object({ users: z$1.array(UserSchema) })
2552
- })
2553
- );
2554
- const PresenceSchema = z$1.object({ users: z$1.array(UserSchema) });
2555
- const presenceCache = makeSingletonCache("PresenceCache");
2556
- async function getPresentUsers(user, { timings, request } = {}) {
2557
- return cachified({
2558
- key: "presence",
2559
- cache: presenceCache,
2560
- timings,
2561
- request,
2562
- ttl: 1e3 * 60 * 5,
2563
- swr: 1e3 * 60 * 60 * 24,
2564
- checkValue: z$1.array(UserSchema),
2565
- async getFreshValue(context) {
2566
- try {
2567
- const response = await Promise.race([
2568
- fetch(`${partykitBaseUrl}/presence`),
2569
- new Promise(
2570
- (resolve) => setTimeout(() => {
2571
- resolve(new Response("Timeout", { status: 500 }));
2572
- }, 500)
2573
- )
2574
- ]);
2575
- if (response.statusText === "Timeout") {
2576
- throw new Error(`Timeout fetching partykit presence`);
2577
- }
2578
- if (!response.ok) {
2579
- throw new Error(
2580
- `Unexpected response from partykit: ${response.status} ${response.statusText}`
2581
- );
2582
- }
2583
- const presence = PresenceSchema.parse(await response.json());
2584
- const preferences = await getPreferences();
2585
- const users = presence.users;
2586
- if ((preferences == null ? void 0 : preferences.presence.optOut) ?? !user) {
2587
- return uniqueUsers(users.filter((u) => u.id !== (user == null ? void 0 : user.id)));
2588
- } else {
2589
- return uniqueUsers([...users, user]);
2590
- }
2591
- } catch {
2592
- context.metadata.ttl = 300;
2593
- return [];
2594
- }
2538
+ async function getWorkshopInstructions({
2539
+ request
2540
+ } = {}) {
2541
+ const readmeFilepath = path$1.join(workshopRoot, "exercises", "README.mdx");
2542
+ const compiled = await compileMdx(readmeFilepath, { request }).then(
2543
+ (r) => ({ ...r, status: "success" }),
2544
+ (e) => {
2545
+ console.error(
2546
+ `There was an error compiling the workshop readme`,
2547
+ readmeFilepath,
2548
+ e
2549
+ );
2550
+ return { status: "error", error: getErrorMessage$1(e) };
2595
2551
  }
2596
- });
2552
+ );
2553
+ return { compiled, file: readmeFilepath, relativePath: "exercises" };
2597
2554
  }
2598
- function uniqueUsers(users) {
2599
- const seen = /* @__PURE__ */ new Set();
2600
- return users.filter(Boolean).filter((user) => {
2601
- if (seen.has(user.id)) {
2602
- return false;
2555
+ async function getWorkshopFinished({
2556
+ request
2557
+ } = {}) {
2558
+ const finishedFilepath = path$1.join(workshopRoot, "exercises", "FINISHED.mdx");
2559
+ const compiled = await compileMdx(finishedFilepath, { request }).then(
2560
+ (r) => ({ ...r, status: "success" }),
2561
+ (e) => {
2562
+ console.error(
2563
+ `There was an error compiling the workshop finished.mdx`,
2564
+ finishedFilepath,
2565
+ e
2566
+ );
2567
+ return { status: "error", error: getErrorMessage$1(e) };
2603
2568
  }
2604
- seen.add(user.id);
2605
- return true;
2606
- });
2569
+ );
2570
+ return {
2571
+ compiled,
2572
+ file: finishedFilepath,
2573
+ relativePath: "exercises/finished.mdx"
2574
+ };
2575
+ }
2576
+ const exercisesPath = path$1.join(workshopRoot, "exercises/");
2577
+ const playgroundPath = path$1.join(workshopRoot, "playground/");
2578
+ function getRelativePath$1(filePath) {
2579
+ return path$1.normalize(filePath).replace(playgroundPath, `playground${path$1.sep}`).replace(exercisesPath, "");
2607
2580
  }
2608
2581
  function Confetti({ id }) {
2609
2582
  if (!id) return null;
@@ -3266,7 +3239,7 @@ function useTheme() {
3266
3239
  }
3267
3240
  return requestInfo.session.theme ?? hints.theme;
3268
3241
  }
3269
- const route36 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3242
+ const route38 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3270
3243
  __proto__: null,
3271
3244
  ThemeSwitch,
3272
3245
  action: action$c,
@@ -3719,12 +3692,12 @@ function usePresenceSocket(user) {
3719
3692
  }
3720
3693
  function scoreUsers(location, users) {
3721
3694
  const scoredUsers = users.map((user) => {
3722
- var _a2, _b2, _c, _d;
3695
+ var _a2, _b, _c, _d;
3723
3696
  let score = 0;
3724
3697
  const available = 4;
3725
3698
  if ((location == null ? void 0 : location.workshopTitle) === ((_a2 = user.location) == null ? void 0 : _a2.workshopTitle)) {
3726
3699
  score += 1;
3727
- if (((_b2 = location == null ? void 0 : location.exercise) == null ? void 0 : _b2.exerciseNumber) && location.exercise.exerciseNumber === ((_d = (_c = user.location) == null ? void 0 : _c.exercise) == null ? void 0 : _d.exerciseNumber)) {
3700
+ if (((_b = location == null ? void 0 : location.exercise) == null ? void 0 : _b.exerciseNumber) && location.exercise.exerciseNumber === ((_d = (_c = user.location) == null ? void 0 : _c.exercise) == null ? void 0 : _d.exerciseNumber)) {
3728
3701
  score += 1;
3729
3702
  if (location.exercise.stepNumber && location.exercise.stepNumber === user.location.exercise.stepNumber) {
3730
3703
  score += 1;
@@ -3806,10 +3779,10 @@ const toastSessionStorage = createCookieSessionStorage({
3806
3779
  secure: process.env.NODE_ENV === "production"
3807
3780
  }
3808
3781
  });
3809
- async function redirectWithToast(url, toast2, init2) {
3782
+ async function redirectWithToast(url, toast2, init) {
3810
3783
  return redirect(url, {
3811
- ...init2,
3812
- headers: combineHeaders(init2 == null ? void 0 : init2.headers, await createToastHeaders(toast2))
3784
+ ...init,
3785
+ headers: combineHeaders(init == null ? void 0 : init.headers, await createToastHeaders(toast2))
3813
3786
  });
3814
3787
  }
3815
3788
  async function createToastHeaders(optionalToast) {
@@ -3860,7 +3833,7 @@ const meta$5 = ({ data }) => {
3860
3833
  requestInfo: data.requestInfo
3861
3834
  });
3862
3835
  };
3863
- async function loader$t({ request }) {
3836
+ async function loader$v({ request }) {
3864
3837
  const timings = makeTimings("rootLoader");
3865
3838
  const onboarding = await readOnboardingData();
3866
3839
  if (!ENV.EPICSHOP_DEPLOYED && !(onboarding == null ? void 0 : onboarding.finishedTourVideo)) {
@@ -4078,10 +4051,10 @@ const route0 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4078
4051
  default: AppWithProviders,
4079
4052
  headers: headers$8,
4080
4053
  links,
4081
- loader: loader$t,
4054
+ loader: loader$v,
4082
4055
  meta: meta$5
4083
4056
  }, Symbol.toStringTag, { value: "Module" }));
4084
- async function loader$s() {
4057
+ async function loader$u() {
4085
4058
  throw new Response("Not found", { status: 404 });
4086
4059
  }
4087
4060
  function NotFound() {
@@ -4108,7 +4081,7 @@ const route1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4108
4081
  __proto__: null,
4109
4082
  ErrorBoundary: ErrorBoundary$4,
4110
4083
  default: NotFound,
4111
- loader: loader$s
4084
+ loader: loader$u
4112
4085
  }, Symbol.toStringTag, { value: "Module" }));
4113
4086
  function makeMediaQueryStore(mediaQuery, serverSnapshot) {
4114
4087
  function getSnapshot() {
@@ -4234,9 +4207,9 @@ function useEpicProgress() {
4234
4207
  );
4235
4208
  if (!progressFetcher || !(data == null ? void 0 : data.progress)) return (data == null ? void 0 : data.progress) ?? null;
4236
4209
  return data.progress.map((p) => {
4237
- var _a2, _b2;
4210
+ var _a2, _b;
4238
4211
  const optimisticCompleted = ((_a2 = progressFetcher.formData) == null ? void 0 : _a2.get("complete")) === "true";
4239
- const optimisticLessonSlug = (_b2 = progressFetcher.formData) == null ? void 0 : _b2.get("lessonSlug");
4212
+ const optimisticLessonSlug = (_b = progressFetcher.formData) == null ? void 0 : _b.get("lessonSlug");
4240
4213
  if (optimisticLessonSlug === p.epicLessonSlug) {
4241
4214
  return {
4242
4215
  ...p,
@@ -4387,7 +4360,7 @@ function ProgressToggle({
4387
4360
  className,
4388
4361
  ...progressItemSearch
4389
4362
  }) {
4390
- var _a2, _b2, _c, _d;
4363
+ var _a2, _b, _c, _d;
4391
4364
  const progressFetcher = useFetcher();
4392
4365
  const progressItem = useProgressItem(progressItemSearch);
4393
4366
  const animationRef = React.useRef(null);
@@ -4396,7 +4369,7 @@ function ProgressToggle({
4396
4369
  const [startAnimation, setStartAnimation] = React.useState(false);
4397
4370
  const location = useLocation();
4398
4371
  const navigation = useNavigation();
4399
- const navigationLocationStateFrom = (_c = (_b2 = navigation.location) == null ? void 0 : _b2.state) == null ? void 0 : _c.from;
4372
+ const navigationLocationStateFrom = (_c = (_b = navigation.location) == null ? void 0 : _b.state) == null ? void 0 : _c.from;
4400
4373
  const navigationLocationPathname = (_d = navigation.location) == null ? void 0 : _d.pathname;
4401
4374
  const locationPathname = location.pathname;
4402
4375
  React.useEffect(() => {
@@ -4502,7 +4475,7 @@ const route32 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
4502
4475
  useProgressItemClassName,
4503
4476
  useRequireEpicProgress
4504
4477
  }, Symbol.toStringTag, { value: "Module" }));
4505
- async function loader$r({ request }) {
4478
+ async function loader$t({ request }) {
4506
4479
  var _a2;
4507
4480
  const timings = makeTimings("stepLoader");
4508
4481
  const [exercises, workshopTitle, playgroundAppName] = await Promise.all([
@@ -4602,7 +4575,7 @@ function FacePile({ isMenuOpened }) {
4602
4575
  const overLimitLabel = `${numberOverLimit}${isMenuOpened ? " more " : " "}Epic Web Dev${numberOverLimit === 1 ? "" : "s"} working now`;
4603
4576
  return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: /* @__PURE__ */ jsxs(TooltipProvider, { children: [
4604
4577
  users.slice(0, limit).map(({ user, score }) => {
4605
- var _a2, _b2;
4578
+ var _a2, _b;
4606
4579
  const scoreClassNames = getScoreClassNames(score);
4607
4580
  const locationLabel = getLocationLabel(user.location);
4608
4581
  return /* @__PURE__ */ jsxs(Tooltip, { children: [
@@ -4633,7 +4606,7 @@ function FacePile({ isMenuOpened }) {
4633
4606
  /* @__PURE__ */ jsxs("span", { children: [
4634
4607
  user.name || "An EPIC Web Dev",
4635
4608
  " ",
4636
- locationLabel ? ` is ${((_b2 = (_a2 = user.location) == null ? void 0 : _a2.origin) == null ? void 0 : _b2.includes("epicweb.dev")) ? "learning" : "working"} ${score === 1 && (loggedInUser == null ? void 0 : loggedInUser.id) !== user.id ? "with you" : ""} on` : null
4609
+ locationLabel ? ` is ${((_b = (_a2 = user.location) == null ? void 0 : _a2.origin) == null ? void 0 : _b.includes("epicweb.dev")) ? "learning" : "working"} ${score === 1 && (loggedInUser == null ? void 0 : loggedInUser.id) !== user.id ? "with you" : ""} on` : null
4637
4610
  ] }),
4638
4611
  (locationLabel == null ? void 0 : locationLabel.line1) ? /* @__PURE__ */ jsx("span", { children: locationLabel.line1 }) : null,
4639
4612
  (locationLabel == null ? void 0 : locationLabel.line2) ? /* @__PURE__ */ jsx("span", { children: locationLabel.line2 }) : null
@@ -4725,19 +4698,19 @@ function EpicWebBanner() {
4725
4698
  const isWide = useIsWide();
4726
4699
  const details = /* @__PURE__ */ jsx("div", { children: ENV.EPICSHOP_DEPLOYED ? /* @__PURE__ */ jsxs("div", { children: [
4727
4700
  `This is the deployed version. `,
4728
- ENV.EPICSHOP_GITHUB_ROOT ? /* @__PURE__ */ jsxs(Fragment, { children: [
4701
+ /* @__PURE__ */ jsxs(Fragment, { children: [
4729
4702
  /* @__PURE__ */ jsx(
4730
4703
  Link,
4731
4704
  {
4732
4705
  className: "underline",
4733
4706
  target: "_blank",
4734
4707
  rel: "noopener noreferrer",
4735
- to: ENV.EPICSHOP_GITHUB_ROOT,
4708
+ to: ENV.EPICSHOP_GITHUB_REPO,
4736
4709
  children: "Run locally"
4737
4710
  }
4738
4711
  ),
4739
4712
  ` for full experience.`
4740
- ] }) : null,
4713
+ ] }),
4741
4714
  " "
4742
4715
  ] }) : /* @__PURE__ */ jsxs("div", { children: [
4743
4716
  /* @__PURE__ */ jsx(Link, { to: "/login", className: "underline", children: "Login" }),
@@ -5537,14 +5510,32 @@ const route2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
5537
5510
  __proto__: null,
5538
5511
  default: App,
5539
5512
  headers: headers$7,
5540
- loader: loader$r
5513
+ loader: loader$t
5541
5514
  }, Symbol.toStringTag, { value: "Module" }));
5515
+ const handle$7 = {
5516
+ getSitemapEntries: async (request) => {
5517
+ const exercises = await getExercises({
5518
+ request
5519
+ });
5520
+ return exercises.flatMap((e) => [{
5521
+ route: `/${e.exerciseNumber.toString().padStart(2, "0")}`
5522
+ }, ...e.steps.flatMap((s) => ["problem", "solution"].map((type) => ({
5523
+ route: `/${e.exerciseNumber.toString().padStart(2, "0")}/${s.stepNumber.toString().padStart(2, "0")}/${type}`
5524
+ }))), {
5525
+ route: `/${e.exerciseNumber.toString().padStart(2, "0")}/finished`
5526
+ }]);
5527
+ }
5528
+ };
5542
5529
  function ExercisesLayout() {
5543
- return /* @__PURE__ */ jsx("div", { className: "flex h-full flex-grow", children: /* @__PURE__ */ jsx(Outlet, {}) });
5530
+ return /* @__PURE__ */ jsx("div", {
5531
+ className: "flex h-full flex-grow",
5532
+ children: /* @__PURE__ */ jsx(Outlet, {})
5533
+ });
5544
5534
  }
5545
5535
  const route3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5546
5536
  __proto__: null,
5547
- default: ExercisesLayout
5537
+ default: ExercisesLayout,
5538
+ handle: handle$7
5548
5539
  }, Symbol.toStringTag, { value: "Module" }));
5549
5540
  const PlaybackTimeSchema = z$1.object({
5550
5541
  time: z$1.number(),
@@ -5653,11 +5644,11 @@ function MuxPlayer({
5653
5644
  });
5654
5645
  }, 300);
5655
5646
  React.useEffect(() => {
5656
- var _a2, _b2;
5647
+ var _a2, _b;
5657
5648
  if (!metadataLoaded) return;
5658
5649
  const textTracks = (_a2 = muxPlayerRef.current) == null ? void 0 : _a2.textTracks;
5659
5650
  if (!textTracks) return;
5660
- const subtitlePref = (_b2 = playerPreferencesRef.current) == null ? void 0 : _b2.subtitle;
5651
+ const subtitlePref = (_b = playerPreferencesRef.current) == null ? void 0 : _b.subtitle;
5661
5652
  if (subtitlePref == null ? void 0 : subtitlePref.id) {
5662
5653
  const preferredTextTrack = textTracks.getTrackById(subtitlePref.id);
5663
5654
  if (preferredTextTrack) {
@@ -5734,7 +5725,7 @@ function isDeepEqual(obj1, obj2) {
5734
5725
  }
5735
5726
  return true;
5736
5727
  }
5737
- const route38 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5728
+ const route40 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5738
5729
  __proto__: null,
5739
5730
  MuxPlayer,
5740
5731
  action: action$a,
@@ -5997,7 +5988,7 @@ function DeferredEpicVideo({
5997
5988
  children: (epicVideoInfos) => {
5998
5989
  const epicVideoInfo = epicVideoInfos == null ? void 0 : epicVideoInfos[url];
5999
5990
  const transcriptUI = ENV.EPICSHOP_DEPLOYED ? /* @__PURE__ */ jsxs("div", { children: [
6000
- ENV.EPICSHOP_GITHUB_ROOT ? /* @__PURE__ */ jsx(Link, { to: ENV.EPICSHOP_GITHUB_ROOT, className: "underline", children: "Run locally" }) : "Run locally",
5991
+ /* @__PURE__ */ jsx(Link, { to: ENV.EPICSHOP_GITHUB_REPO, className: "underline", children: "Run locally" }),
6001
5992
  " for transcripts"
6002
5993
  ] }) : /* @__PURE__ */ jsxs("div", { children: [
6003
5994
  /* @__PURE__ */ jsx(Link, { to: "/login", className: "underline", children: user ? "Upgrade" : "Login" }),
@@ -6805,7 +6796,7 @@ function EditFileOnGitHub({
6805
6796
  }
6806
6797
  );
6807
6798
  }
6808
- const LaunchEditor = ENV.EPICSHOP_DEPLOYED ? ENV.EPICSHOP_GITHUB_ROOT ? LaunchGitHub : ({ children }) => /* @__PURE__ */ jsx(SimpleTooltip, { content: "Cannot open files in deployed app", children: /* @__PURE__ */ jsx("button", { className: "launch_button cursor-not-allowed", children }) }) : LaunchEditorImpl;
6799
+ const LaunchEditor = ENV.EPICSHOP_DEPLOYED ? LaunchGitHub : LaunchEditorImpl;
6809
6800
  const route27 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
6810
6801
  __proto__: null,
6811
6802
  EditFileOnGitHub,
@@ -6882,10 +6873,10 @@ function CopyButton() {
6882
6873
  {
6883
6874
  className: cn(buttonClassName, "w-12 uppercase"),
6884
6875
  onClick: (event) => {
6885
- var _a2, _b2, _c;
6876
+ var _a2, _b, _c;
6886
6877
  setCopied(true);
6887
6878
  const button = event.currentTarget;
6888
- const code = ((_c = (_b2 = (_a2 = button.parentElement) == null ? void 0 : _a2.parentElement) == null ? void 0 : _b2.querySelector("pre")) == null ? void 0 : _c.textContent) || "";
6879
+ const code = ((_c = (_b = (_a2 = button.parentElement) == null ? void 0 : _a2.parentElement) == null ? void 0 : _b.querySelector("pre")) == null ? void 0 : _c.textContent) || "";
6889
6880
  void navigator.clipboard.writeText(code);
6890
6881
  },
6891
6882
  children: copied ? "copied" : "copy"
@@ -7007,7 +6998,7 @@ const meta$4 = ({
7007
6998
  requestInfo: rootData.requestInfo
7008
6999
  });
7009
7000
  };
7010
- async function loader$q({ request, params }) {
7001
+ async function loader$s({ request, params }) {
7011
7002
  const timings = makeTimings("exerciseNumberLoader");
7012
7003
  invariantResponse(params.exerciseNumber, "exerciseNumber is required");
7013
7004
  const [exercises, workshopTitle] = await Promise.all([
@@ -7151,10 +7142,10 @@ const route4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
7151
7142
  ErrorBoundary: ErrorBoundary$3,
7152
7143
  default: ExerciseNumberRoute,
7153
7144
  headers: headers$6,
7154
- loader: loader$q,
7145
+ loader: loader$s,
7155
7146
  meta: meta$4
7156
7147
  }, Symbol.toStringTag, { value: "Module" }));
7157
- async function loader$p({ request, params }) {
7148
+ async function loader$r({ request, params }) {
7158
7149
  const timings = makeTimings("stepLoader");
7159
7150
  invariantResponse(params.exerciseNumber, "exerciseNumber is required");
7160
7151
  const [exercises, workshopTitle] = await Promise.all([
@@ -7211,7 +7202,7 @@ const route5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
7211
7202
  ErrorBoundary: ErrorBoundary$2,
7212
7203
  default: StepRoute,
7213
7204
  headers: headers$5,
7214
- loader: loader$p
7205
+ loader: loader$r
7215
7206
  }, Symbol.toStringTag, { value: "Module" }));
7216
7207
  const AccordionComponent = ({
7217
7208
  title,
@@ -7573,7 +7564,7 @@ function getDiscordAuthURL() {
7573
7564
  discordAuthUrl.searchParams.append("scope", scope);
7574
7565
  return discordAuthUrl.toString();
7575
7566
  }
7576
- async function loader$o({ request }) {
7567
+ async function loader$q({ request }) {
7577
7568
  const authInfo = await requireAuthInfo({ request });
7578
7569
  const discordCode = new URL(request.url).searchParams.get("code");
7579
7570
  invariantResponse(discordCode, "Missing code");
@@ -7629,7 +7620,7 @@ async function loader$o({ request }) {
7629
7620
  const route25 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
7630
7621
  __proto__: null,
7631
7622
  getDiscordAuthURL,
7632
- loader: loader$o
7623
+ loader: loader$q
7633
7624
  }, Symbol.toStringTag, { value: "Module" }));
7634
7625
  const epicshopTempDir = path.join(os.tmpdir(), "epicshop");
7635
7626
  const isDeployed = ENV.EPICSHOP_DEPLOYED;
@@ -8187,7 +8178,7 @@ function SetAppToPlayground({ appName }) {
8187
8178
  }
8188
8179
  );
8189
8180
  }
8190
- const route33 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8181
+ const route34 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8191
8182
  __proto__: null,
8192
8183
  PlaygroundChooser,
8193
8184
  SetAppToPlayground,
@@ -8287,7 +8278,10 @@ async function fetchDiscordPosts({ request }) {
8287
8278
  }
8288
8279
  });
8289
8280
  }
8290
- async function loader$n() {
8281
+ const handle$6 = {
8282
+ getSitemapEntries: () => null
8283
+ };
8284
+ async function loader$p() {
8291
8285
  return json({ discordAuthUrl: getDiscordAuthURL() });
8292
8286
  }
8293
8287
  function useDiscordCTALink({
@@ -8390,7 +8384,8 @@ const route15 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
8390
8384
  __proto__: null,
8391
8385
  DiscordCTA,
8392
8386
  default: DiscordRoute,
8393
- loader: loader$n,
8387
+ handle: handle$6,
8388
+ loader: loader$p,
8394
8389
  useDiscordCTALink
8395
8390
  }, Symbol.toStringTag, { value: "Module" }));
8396
8391
  function DiscordChat() {
@@ -8780,7 +8775,7 @@ function AppStarter({ name }) {
8780
8775
  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" }) })
8781
8776
  ] });
8782
8777
  }
8783
- const route34 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8778
+ const route36 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
8784
8779
  __proto__: null,
8785
8780
  AppStarter,
8786
8781
  AppStopper,
@@ -8990,7 +8985,7 @@ function InBrowserBrowserForRealzImpl({ baseUrl, id, name, initialRoute }, ref)
8990
8985
  }
8991
8986
  }, [iframePathname]);
8992
8987
  const navigateChild = (...params) => {
8993
- var _a2, _b2;
8988
+ var _a2, _b;
8994
8989
  const to = params[0];
8995
8990
  if (typeof to === "number") {
8996
8991
  lastDirectionRef.current = to > 0 ? "forward" : "back";
@@ -9003,7 +8998,7 @@ function InBrowserBrowserForRealzImpl({ baseUrl, id, name, initialRoute }, ref)
9003
8998
  lastDirectionTimeout.current = setTimeout(() => {
9004
8999
  lastDirectionRef.current = "new";
9005
9000
  }, 100);
9006
- (_b2 = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b2.postMessage(
9001
+ (_b = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b.postMessage(
9007
9002
  { type: "epicshop:navigate-call", params },
9008
9003
  "*"
9009
9004
  );
@@ -9295,7 +9290,7 @@ function checkFileExists(file) {
9295
9290
  );
9296
9291
  }
9297
9292
  async function action$6({ request }) {
9298
- var _a2, _b2;
9293
+ var _a2, _b;
9299
9294
  ensureUndeployed();
9300
9295
  const formData = await request.formData();
9301
9296
  const rawData = {
@@ -9315,7 +9310,7 @@ async function action$6({ request }) {
9315
9310
  Object.entries(((_a2 = cached == null ? void 0 : cached.value) == null ? void 0 : _a2.embeddedFiles) ?? {})
9316
9311
  );
9317
9312
  if (cachedEmbeddedFiles.has(embeddedKey)) {
9318
- (_b2 = cachedEmbeddedFiles.get(embeddedKey)) == null ? true : delete _b2.warning;
9313
+ (_b = cachedEmbeddedFiles.get(embeddedKey)) == null ? true : delete _b.warning;
9319
9314
  cached.value.embeddedFiles = Object.fromEntries(cachedEmbeddedFiles);
9320
9315
  }
9321
9316
  try {
@@ -9356,7 +9351,7 @@ function UpdateMdxCache({
9356
9351
  )
9357
9352
  ] });
9358
9353
  }
9359
- const route37 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
9354
+ const route39 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
9360
9355
  __proto__: null,
9361
9356
  UpdateMdxCache,
9362
9357
  action: action$6
@@ -9615,10 +9610,10 @@ function LinkToApp({
9615
9610
  }),
9616
9611
  title: ENV.EPICSHOP_DEPLOYED ? "Cannot link to app in deployed version" : void 0,
9617
9612
  onClick: (event) => {
9618
- var _a3, _b2;
9613
+ var _a3, _b;
9619
9614
  if (ENV.EPICSHOP_DEPLOYED) event.preventDefault();
9620
9615
  (_a3 = props.onClick) == null ? void 0 : _a3.call(props, event);
9621
- (_b2 = inBrowserBrowserRef.current) == null ? void 0 : _b2.handleExtrnalNavigation(appTo.toString());
9616
+ (_b = inBrowserBrowserRef.current) == null ? void 0 : _b.handleExtrnalNavigation(appTo.toString());
9622
9617
  },
9623
9618
  children
9624
9619
  }
@@ -9761,8 +9756,8 @@ function InBrowserTestRunner({
9761
9756
  "button",
9762
9757
  {
9763
9758
  onClick: () => {
9764
- var _a2, _b2;
9765
- return (_b2 = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b2.location.reload();
9759
+ var _a2, _b;
9760
+ return (_b = (_a2 = iframeRef.current) == null ? void 0 : _a2.contentWindow) == null ? void 0 : _b.location.reload();
9766
9761
  },
9767
9762
  className: "border-r p-3",
9768
9763
  children: /* @__PURE__ */ jsx(Icon, { name: "Refresh", "aria-label": "Rerun Tests" })
@@ -9822,7 +9817,7 @@ const testEventSchema = z$1.union([
9822
9817
  })
9823
9818
  ]);
9824
9819
  const testEventQueueSchema = z$1.array(testEventSchema);
9825
- async function loader$m({ request }) {
9820
+ async function loader$o({ request }) {
9826
9821
  ensureUndeployed();
9827
9822
  const url = new URL(request.url);
9828
9823
  const name = url.searchParams.get("name");
@@ -9838,7 +9833,7 @@ async function loader$m({ request }) {
9838
9833
  return json({ error: "App is not running tests" }, { status: 404 });
9839
9834
  }
9840
9835
  return eventStream(request.signal, function setup(send) {
9841
- var _a2, _b2;
9836
+ var _a2, _b;
9842
9837
  const ansi = new AnsiToHTML();
9843
9838
  let queue = [];
9844
9839
  function sendEvent(event) {
@@ -9881,19 +9876,19 @@ async function loader$m({ request }) {
9881
9876
  });
9882
9877
  }
9883
9878
  function handleExit(code) {
9884
- var _a3, _b3;
9879
+ var _a3, _b2;
9885
9880
  (_a3 = testProcess == null ? void 0 : testProcess.stdout) == null ? void 0 : _a3.off("data", handleStdOutData);
9886
- (_b3 = testProcess == null ? void 0 : testProcess.stderr) == null ? void 0 : _b3.off("data", handleStdErrData);
9881
+ (_b2 = testProcess == null ? void 0 : testProcess.stderr) == null ? void 0 : _b2.off("data", handleStdErrData);
9887
9882
  testProcess == null ? void 0 : testProcess.off("exit", handleExit);
9888
9883
  sendEvent({ type: "exit", isRunning: false, code });
9889
9884
  }
9890
9885
  (_a2 = testProcess.stdout) == null ? void 0 : _a2.on("data", handleStdOutData);
9891
- (_b2 = testProcess.stderr) == null ? void 0 : _b2.on("data", handleStdErrData);
9886
+ (_b = testProcess.stderr) == null ? void 0 : _b.on("data", handleStdErrData);
9892
9887
  testProcess.on("exit", handleExit);
9893
9888
  return function cleanup() {
9894
- var _a3, _b3;
9889
+ var _a3, _b2;
9895
9890
  (_a3 = testProcess.stdout) == null ? void 0 : _a3.off("data", handleStdOutData);
9896
- (_b3 = testProcess.stderr) == null ? void 0 : _b3.off("data", handleStdErrData);
9891
+ (_b2 = testProcess.stderr) == null ? void 0 : _b2.off("data", handleStdErrData);
9897
9892
  testProcess.off("exit", handleExit);
9898
9893
  clearInterval(interval);
9899
9894
  };
@@ -10046,9 +10041,9 @@ function TestRunner({
10046
10041
  latestOnRun.current = onRun;
10047
10042
  }, [onRun]);
10048
10043
  useEffect(() => {
10049
- var _a2, _b2;
10044
+ var _a2, _b;
10050
10045
  if ((_a2 = fetcher.data) == null ? void 0 : _a2.success) {
10051
- (_b2 = latestOnRun.current) == null ? void 0 : _b2.call(latestOnRun);
10046
+ (_b = latestOnRun.current) == null ? void 0 : _b.call(latestOnRun);
10052
10047
  }
10053
10048
  }, [fetcher.data]);
10054
10049
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/test", className: "h-full", children: [
@@ -10081,9 +10076,9 @@ function ClearTest({
10081
10076
  latestOnClear.current = onClear;
10082
10077
  }, [onClear]);
10083
10078
  useEffect(() => {
10084
- var _a2, _b2;
10079
+ var _a2, _b;
10085
10080
  if ((_a2 = fetcher.data) == null ? void 0 : _a2.success) {
10086
- (_b2 = latestOnClear.current) == null ? void 0 : _b2.call(latestOnClear);
10081
+ (_b = latestOnClear.current) == null ? void 0 : _b.call(latestOnClear);
10087
10082
  }
10088
10083
  }, [fetcher.data]);
10089
10084
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/test", className: "h-full", children: [
@@ -10116,9 +10111,9 @@ function StopTest({
10116
10111
  latestOnStop.current = onStop;
10117
10112
  }, [onStop]);
10118
10113
  useEffect(() => {
10119
- var _a2, _b2;
10114
+ var _a2, _b;
10120
10115
  if ((_a2 = fetcher.data) == null ? void 0 : _a2.success) {
10121
- (_b2 = latestOnStop.current) == null ? void 0 : _b2.call(latestOnStop);
10116
+ (_b = latestOnStop.current) == null ? void 0 : _b.call(latestOnStop);
10122
10117
  }
10123
10118
  }, [fetcher.data]);
10124
10119
  return /* @__PURE__ */ jsxs(fetcher.Form, { method: "POST", action: "/test", className: "h-full", children: [
@@ -10141,14 +10136,14 @@ function StopTest({
10141
10136
  )
10142
10137
  ] });
10143
10138
  }
10144
- const route35 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
10139
+ const route37 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
10145
10140
  __proto__: null,
10146
10141
  ClearTest,
10147
10142
  StopTest,
10148
10143
  TestOutput,
10149
10144
  TestRunner,
10150
10145
  action: action$5,
10151
- loader: loader$m
10146
+ loader: loader$o
10152
10147
  }, Symbol.toStringTag, { value: "Module" }));
10153
10148
  function Tests({
10154
10149
  appInfo: playgroundAppInfo,
@@ -10199,7 +10194,7 @@ function Tests({
10199
10194
  );
10200
10195
  }
10201
10196
  function TouchedFiles() {
10202
- var _a2, _b2;
10197
+ var _a2, _b;
10203
10198
  const data = useLoaderData();
10204
10199
  const [open, setOpen] = React.useState(false);
10205
10200
  const contentRef = React.useRef(null);
@@ -10228,7 +10223,7 @@ function TouchedFiles() {
10228
10223
  sideOffset: 5,
10229
10224
  children: /* @__PURE__ */ jsxs("div", { className: "launch-editor-wrapper", children: [
10230
10225
  /* @__PURE__ */ jsx("strong", { className: "inline-block px-2 pb-4 font-semibold uppercase", children: "Relevant Files" }),
10231
- data.problem && ((_b2 = data.playground) == null ? void 0 : _b2.appName) !== data.problem.name ? /* @__PURE__ */ jsx("div", { className: "mb-2 rounded p-1 font-mono font-medium", children: /* @__PURE__ */ jsx(SetAppToPlayground, { appName: data.problem.name }) }) : null,
10226
+ data.problem && ((_b = data.playground) == null ? void 0 : _b.appName) !== data.problem.name ? /* @__PURE__ */ jsx("div", { className: "mb-2 rounded p-1 font-mono font-medium", children: /* @__PURE__ */ jsx(SetAppToPlayground, { appName: data.problem.name }) }) : null,
10232
10227
  /* @__PURE__ */ jsx("div", { id: "files", children: /* @__PURE__ */ jsx(
10233
10228
  React.Suspense,
10234
10229
  {
@@ -10324,8 +10319,8 @@ const meta$3 = ({
10324
10319
  requestInfo: rootData.requestInfo
10325
10320
  });
10326
10321
  };
10327
- async function loader$l({ request, params }) {
10328
- var _a2, _b2;
10322
+ async function loader$n({ request, params }) {
10323
+ var _a2, _b;
10329
10324
  const timings = makeTimings("exerciseStepTypeLoader");
10330
10325
  const workshopTitle = await getWorkshopTitle();
10331
10326
  const searchParams = new URL(request.url).searchParams;
@@ -10397,7 +10392,7 @@ async function loader$l({ request, params }) {
10397
10392
  const exerciseIndex = allApps.findIndex((step) => step.stepId === exerciseId);
10398
10393
  const exerciseApps = allAppsFull.filter(isExerciseStepApp).filter((app) => app.exerciseNumber === exerciseStepApp.exerciseNumber);
10399
10394
  const isLastStep = ((_a2 = exerciseApps[exerciseApps.length - 1]) == null ? void 0 : _a2.name) === exerciseStepApp.name;
10400
- const isFirstStep = ((_b2 = exerciseApps[0]) == null ? void 0 : _b2.name) === exerciseStepApp.name;
10395
+ const isFirstStep = ((_b = exerciseApps[0]) == null ? void 0 : _b.name) === exerciseStepApp.name;
10401
10396
  const nextApp = await getNextExerciseApp(exerciseStepApp, cacheOptions);
10402
10397
  const prevApp = await getPrevExerciseApp(exerciseStepApp, cacheOptions);
10403
10398
  async function getDiffProp() {
@@ -10517,7 +10512,7 @@ function withParam(searchParams, key, value) {
10517
10512
  return newSearchParams;
10518
10513
  }
10519
10514
  function ExercisePartRoute() {
10520
- var _a2, _b2, _c, _d, _e, _f, _g;
10515
+ var _a2, _b, _c, _d, _e, _f, _g;
10521
10516
  const data = useLoaderData();
10522
10517
  const [searchParams] = useSearchParams();
10523
10518
  const preview = searchParams.get("preview");
@@ -10526,14 +10521,14 @@ function ExercisePartRoute() {
10526
10521
  const altDown = useAltDown();
10527
10522
  const navigate = useNavigate();
10528
10523
  function shouldHideTab(tab) {
10529
- var _a3, _b3, _c2;
10524
+ var _a3, _b2, _c2;
10530
10525
  if (tab === "tests") {
10531
10526
  return ENV.EPICSHOP_DEPLOYED || !data.playground || data.playground.test.type === "none";
10532
10527
  }
10533
10528
  if (tab === "problem" || tab === "solution") {
10534
10529
  if (((_a3 = data[tab]) == null ? void 0 : _a3.dev.type) === "none") return true;
10535
10530
  if (ENV.EPICSHOP_DEPLOYED) {
10536
- return ((_b3 = data[tab]) == null ? void 0 : _b3.dev.type) !== "browser" && !((_c2 = data[tab]) == null ? void 0 : _c2.stackBlitzUrl);
10531
+ return ((_b2 = data[tab]) == null ? void 0 : _b2.dev.type) !== "browser" && !((_c2 = data[tab]) == null ? void 0 : _c2.stackBlitzUrl);
10537
10532
  }
10538
10533
  }
10539
10534
  if (tab === "playground" && ENV.EPICSHOP_DEPLOYED) return true;
@@ -10542,7 +10537,7 @@ function ExercisePartRoute() {
10542
10537
  const activeTab = isValidPreview(preview) ? preview : tabs.find((t) => !shouldHideTab(t));
10543
10538
  const altDiffUrl = `/diff?${new URLSearchParams({
10544
10539
  app1: ((_a2 = data.problem) == null ? void 0 : _a2.name) ?? "",
10545
- app2: ((_b2 = data.solution) == null ? void 0 : _b2.name) ?? ""
10540
+ app2: ((_b = data.solution) == null ? void 0 : _b.name) ?? ""
10546
10541
  })}`;
10547
10542
  function handleDiffTabClick(event) {
10548
10543
  if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
@@ -10756,10 +10751,10 @@ const route6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
10756
10751
  ErrorBoundary: ErrorBoundary$1,
10757
10752
  default: ExercisePartRoute,
10758
10753
  headers: headers$4,
10759
- loader: loader$l,
10754
+ loader: loader$n,
10760
10755
  meta: meta$3
10761
10756
  }, Symbol.toStringTag, { value: "Module" }));
10762
- async function loader$k({ params }) {
10757
+ async function loader$m({ params }) {
10763
10758
  const problemApp = await getExerciseApp({ ...params, type: "problem" }).then(
10764
10759
  (a) => isProblemApp(a) ? a : null
10765
10760
  );
@@ -10777,7 +10772,7 @@ async function loader$k({ params }) {
10777
10772
  }
10778
10773
  const route7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
10779
10774
  __proto__: null,
10780
- loader: loader$k
10775
+ loader: loader$m
10781
10776
  }, Symbol.toStringTag, { value: "Module" }));
10782
10777
  const meta$2 = ({
10783
10778
  data,
@@ -10796,7 +10791,7 @@ const meta$2 = ({
10796
10791
  requestInfo: rootData.requestInfo
10797
10792
  });
10798
10793
  };
10799
- async function loader$j({ request, params }) {
10794
+ async function loader$l({ request, params }) {
10800
10795
  const timings = makeTimings("exerciseFinishedLoader");
10801
10796
  invariantResponse(params.exerciseNumber, "exerciseNumber is required");
10802
10797
  const exercise = await getExercise(params.exerciseNumber, {
@@ -10962,10 +10957,13 @@ const route8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
10962
10957
  __proto__: null,
10963
10958
  default: ExerciseFinished$1,
10964
10959
  headers: headers$3,
10965
- loader: loader$j,
10960
+ loader: loader$l,
10966
10961
  meta: meta$2
10967
10962
  }, Symbol.toStringTag, { value: "Module" }));
10968
- async function loader$i({ request }) {
10963
+ const handle$5 = {
10964
+ getSitemapEntries: () => null
10965
+ };
10966
+ async function loader$k({ request }) {
10969
10967
  ensureUndeployed();
10970
10968
  await requireAuthInfo({ request });
10971
10969
  return json({ discordAuthUrl: getDiscordAuthURL() });
@@ -11096,7 +11094,8 @@ const route9 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
11096
11094
  __proto__: null,
11097
11095
  action: action$4,
11098
11096
  default: Account,
11099
- loader: loader$i
11097
+ handle: handle$5,
11098
+ loader: loader$k
11100
11099
  }, Symbol.toStringTag, { value: "Module" }));
11101
11100
  async function getForceFresh(filePath, cacheEntry) {
11102
11101
  if (!cacheEntry) return true;
@@ -11164,7 +11163,7 @@ async function resolveApps({
11164
11163
  return { app, fileApp: app };
11165
11164
  }
11166
11165
  }
11167
- async function loader$h({ request, params }) {
11166
+ async function loader$j({ request, params }) {
11168
11167
  const timings = makeTimings("app-file");
11169
11168
  const { fileApp, app } = await resolveApps({ request, params, timings });
11170
11169
  if (!fileApp || !app) {
@@ -11220,9 +11219,9 @@ async function loader$h({ request, params }) {
11220
11219
  }
11221
11220
  const route10 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11222
11221
  __proto__: null,
11223
- loader: loader$h
11222
+ loader: loader$j
11224
11223
  }, Symbol.toStringTag, { value: "Module" }));
11225
- async function loader$g(args) {
11224
+ async function loader$i(args) {
11226
11225
  const api = await getApiModule(args);
11227
11226
  invariantResponse(
11228
11227
  api.mod.loader,
@@ -11325,9 +11324,9 @@ async function getApiModule({ request, params }) {
11325
11324
  const route11 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11326
11325
  __proto__: null,
11327
11326
  action: action$3,
11328
- loader: loader$g
11327
+ loader: loader$i
11329
11328
  }, Symbol.toStringTag, { value: "Module" }));
11330
- async function loader$f({ request, params }) {
11329
+ async function loader$h({ request, params }) {
11331
11330
  const timings = makeTimings("epic_ws script");
11332
11331
  const { fileApp, app } = await resolveApps({ request, params, timings });
11333
11332
  if (!fileApp || !app) {
@@ -11393,9 +11392,9 @@ async function loader$f({ request, params }) {
11393
11392
  }
11394
11393
  const route12 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11395
11394
  __proto__: null,
11396
- loader: loader$f
11395
+ loader: loader$h
11397
11396
  }, Symbol.toStringTag, { value: "Module" }));
11398
- async function loader$e({ request, params }) {
11397
+ async function loader$g({ request, params }) {
11399
11398
  var _a2;
11400
11399
  const timings = makeTimings("app");
11401
11400
  const { fileApp, app } = await resolveApps({ request, params, timings });
@@ -11481,9 +11480,9 @@ async function loader$e({ request, params }) {
11481
11480
  }
11482
11481
  const route13 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11483
11482
  __proto__: null,
11484
- loader: loader$e
11483
+ loader: loader$g
11485
11484
  }, Symbol.toStringTag, { value: "Module" }));
11486
- async function loader$d({ request, params }) {
11485
+ async function loader$f({ request, params }) {
11487
11486
  var _a2;
11488
11487
  const timings = makeTimings("app_test_loader");
11489
11488
  const { testName } = params;
@@ -11623,8 +11622,11 @@ import(${JSON.stringify(testScriptPath)}).then(
11623
11622
  }
11624
11623
  const route14 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11625
11624
  __proto__: null,
11626
- loader: loader$d
11625
+ loader: loader$f
11627
11626
  }, Symbol.toStringTag, { value: "Module" }));
11627
+ const handle$4 = {
11628
+ getSitemapEntries: () => [{ route: "/finished" }]
11629
+ };
11628
11630
  const meta$1 = ({
11629
11631
  matches
11630
11632
  }) => {
@@ -11640,7 +11642,7 @@ const meta$1 = ({
11640
11642
  requestInfo: rootData.requestInfo
11641
11643
  });
11642
11644
  };
11643
- async function loader$c({ request }) {
11645
+ async function loader$e({ request }) {
11644
11646
  const timings = makeTimings("finishedLoader");
11645
11647
  const exercises = await getExercises({ request, timings });
11646
11648
  const compiledFinished = await time(() => getWorkshopFinished({ request }), {
@@ -11777,11 +11779,12 @@ function Survey({
11777
11779
  const route16 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
11778
11780
  __proto__: null,
11779
11781
  default: ExerciseFinished,
11782
+ handle: handle$4,
11780
11783
  headers: headers$2,
11781
- loader: loader$c,
11784
+ loader: loader$e,
11782
11785
  meta: meta$1
11783
11786
  }, Symbol.toStringTag, { value: "Module" }));
11784
- async function loader$b({ request }) {
11787
+ async function loader$d({ request }) {
11785
11788
  const timings = makeTimings("indexLoader");
11786
11789
  const [title, exercises, workshopReadme] = await Promise.all([
11787
11790
  time(() => getWorkshopTitle(), {
@@ -11911,7 +11914,7 @@ const route17 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
11911
11914
  ErrorBoundary,
11912
11915
  default: Index,
11913
11916
  headers: headers$1,
11914
- loader: loader$b
11917
+ loader: loader$d
11915
11918
  }, Symbol.toStringTag, { value: "Module" }));
11916
11919
  const EVENTS = {
11917
11920
  USER_CODE_RECEIVED: "USER_CODE_RECEIVED",
@@ -11933,13 +11936,13 @@ async function registerDevice() {
11933
11936
  token_endpoint_auth_method: "none",
11934
11937
  application_type: "native"
11935
11938
  });
11936
- const handle = await client.deviceAuthorization();
11939
+ const handle2 = await client.deviceAuthorization();
11937
11940
  authEmitter.emit(EVENTS.USER_CODE_RECEIVED, {
11938
- code: handle.user_code,
11939
- url: handle.verification_uri_complete
11941
+ code: handle2.user_code,
11942
+ url: handle2.verification_uri_complete
11940
11943
  });
11941
- const timeout = setTimeout(() => handle.abort(), handle.expires_in * 1e3);
11942
- const tokenSet = await handle.poll().catch(() => {
11944
+ const timeout = setTimeout(() => handle2.abort(), handle2.expires_in * 1e3);
11945
+ const tokenSet = await handle2.poll().catch(() => {
11943
11946
  });
11944
11947
  clearTimeout(timeout);
11945
11948
  if (!tokenSet) {
@@ -11974,7 +11977,7 @@ const EventSchema = z$1.union([
11974
11977
  AuthResolvedEventSchema,
11975
11978
  AuthRejectedEventSchema
11976
11979
  ]);
11977
- async function loader$a({ request }) {
11980
+ async function loader$c({ request }) {
11978
11981
  ensureUndeployed();
11979
11982
  return eventStream(request.signal, function setup(send) {
11980
11983
  function handleCodeReceived(data) {
@@ -12011,9 +12014,12 @@ async function loader$a({ request }) {
12011
12014
  const route28 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12012
12015
  __proto__: null,
12013
12016
  EventSchema,
12014
- loader: loader$a
12017
+ loader: loader$c
12015
12018
  }, Symbol.toStringTag, { value: "Module" }));
12016
- async function loader$9() {
12019
+ const handle$3 = {
12020
+ getSitemapEntries: () => null
12021
+ };
12022
+ async function loader$b() {
12017
12023
  ensureUndeployed();
12018
12024
  const isAuthenticated = Boolean(await getAuthInfo());
12019
12025
  if (isAuthenticated) throw redirect("/account");
@@ -12134,14 +12140,18 @@ const route18 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12134
12140
  __proto__: null,
12135
12141
  action: action$2,
12136
12142
  default: Login,
12137
- loader: loader$9
12143
+ handle: handle$3,
12144
+ loader: loader$b
12138
12145
  }, Symbol.toStringTag, { value: "Module" }));
12146
+ const handle$2 = {
12147
+ getSitemapEntries: () => null
12148
+ };
12139
12149
  function Support() {
12140
12150
  var _a2;
12141
- const repoGroups = (_a2 = ENV.EPICSHOP_GITHUB_ROOT.match(
12151
+ const repoGroups = (_a2 = ENV.EPICSHOP_GITHUB_REPO.match(
12142
12152
  /github\.com\/(?<org>[^/?]+)\/(?<repo>[^/?]+)/
12143
12153
  )) == null ? void 0 : _a2.groups;
12144
- let repoUrl = ENV.EPICSHOP_GITHUB_ROOT;
12154
+ let repoUrl = ENV.EPICSHOP_GITHUB_REPO;
12145
12155
  let repoIssuesUrl = repoUrl;
12146
12156
  if ((repoGroups == null ? void 0 : repoGroups.org) && repoGroups.repo) {
12147
12157
  repoUrl = `https://github.com/${repoGroups.org}/${repoGroups.repo}`;
@@ -12191,7 +12201,8 @@ function Support() {
12191
12201
  }
12192
12202
  const route19 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12193
12203
  __proto__: null,
12194
- default: Support
12204
+ default: Support,
12205
+ handle: handle$2
12195
12206
  }, Symbol.toStringTag, { value: "Module" }));
12196
12207
  async function clearData() {
12197
12208
  await clearCaches();
@@ -12219,6 +12230,9 @@ async function stopInspector() {
12219
12230
  console.info(`Inspector already stopped.`);
12220
12231
  }
12221
12232
  }
12233
+ const handle$1 = {
12234
+ getSitemapEntries: () => null
12235
+ };
12222
12236
  const meta = ({
12223
12237
  matches
12224
12238
  }) => {
@@ -12226,7 +12240,7 @@ const meta = ({
12226
12240
  const rootData = (_a2 = matches.find((m) => m.id === "root")) == null ? void 0 : _a2.data;
12227
12241
  return [{ title: `👷 | ${rootData == null ? void 0 : rootData.workshopTitle}` }];
12228
12242
  };
12229
- async function loader$8({ request }) {
12243
+ async function loader$a({ request }) {
12230
12244
  ensureUndeployed();
12231
12245
  const timings = makeTimings("adminLoader");
12232
12246
  const workshopSlug = await getEpicWorkshopSlug() ?? "Unkown";
@@ -12308,12 +12322,12 @@ function linkProgress(progress) {
12308
12322
  }
12309
12323
  }
12310
12324
  function AdminLayout() {
12311
- var _a2, _b2;
12325
+ var _a2, _b;
12312
12326
  const data = useLoaderData();
12313
12327
  const navigation = useNavigation();
12314
12328
  const epicProgress = useEpicProgress();
12315
12329
  const isStartingInspector = ((_a2 = navigation.formData) == null ? void 0 : _a2.get("intent")) === "inspect";
12316
- const isStoppingInspector = ((_b2 = navigation.formData) == null ? void 0 : _b2.get("intent")) === "stop-inspect";
12330
+ const isStoppingInspector = ((_b = navigation.formData) == null ? void 0 : _b.get("intent")) === "stop-inspect";
12317
12331
  const progressStatus = {
12318
12332
  completed: "bg-blue-500",
12319
12333
  incomplete: "bg-yellow-500"
@@ -12437,28 +12451,29 @@ const route20 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
12437
12451
  __proto__: null,
12438
12452
  action: action$1,
12439
12453
  default: AdminLayout,
12440
- loader: loader$8,
12454
+ handle: handle$1,
12455
+ loader: loader$a,
12441
12456
  meta
12442
12457
  }, Symbol.toStringTag, { value: "Module" }));
12443
- async function loader$7() {
12458
+ async function loader$9() {
12444
12459
  ensureUndeployed();
12445
12460
  const apps = await getApps();
12446
12461
  return json({ apps });
12447
12462
  }
12448
12463
  const route21 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12449
12464
  __proto__: null,
12450
- loader: loader$7
12465
+ loader: loader$9
12451
12466
  }, Symbol.toStringTag, { value: "Module" }));
12452
- async function loader$6() {
12467
+ async function loader$8() {
12453
12468
  ensureUndeployed();
12454
12469
  const entries = await getAllFileCacheEntries();
12455
12470
  return json({ entries });
12456
12471
  }
12457
12472
  const route22 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12458
12473
  __proto__: null,
12459
- loader: loader$6
12474
+ loader: loader$8
12460
12475
  }, Symbol.toStringTag, { value: "Module" }));
12461
- async function loader$5({ request }) {
12476
+ async function loader$7({ request }) {
12462
12477
  const timings = makeTimings("appsLoader");
12463
12478
  const apps = await getApps({ request, timings });
12464
12479
  return json(
@@ -12468,10 +12483,10 @@ async function loader$5({ request }) {
12468
12483
  }
12469
12484
  const route23 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12470
12485
  __proto__: null,
12471
- loader: loader$5
12486
+ loader: loader$7
12472
12487
  }, Symbol.toStringTag, { value: "Module" }));
12473
- async function loader$4({ request }) {
12474
- var _a2, _b2, _c, _d;
12488
+ async function loader$6({ request }) {
12489
+ var _a2, _b, _c, _d;
12475
12490
  const reqUrl = new URL(request.url);
12476
12491
  const searchParams = reqUrl.searchParams;
12477
12492
  const timings = makeTimings("diffLoader");
@@ -12516,7 +12531,7 @@ async function loader$4({ request }) {
12516
12531
  const nextApp1Index = usingDefaultApp1 ? 0 : app1Index + 1 < allApps.length ? app1Index + 1 : -2;
12517
12532
  const nextApp2Index = nextApp1Index + 1;
12518
12533
  const prevApp1 = (_a2 = allAppsFull[prevApp1Index]) == null ? void 0 : _a2.name;
12519
- const prevApp2 = (_b2 = allAppsFull[prevApp2Index]) == null ? void 0 : _b2.name;
12534
+ const prevApp2 = (_b = allAppsFull[prevApp2Index]) == null ? void 0 : _b.name;
12520
12535
  const nextApp1 = (_c = allAppsFull[nextApp1Index]) == null ? void 0 : _c.name;
12521
12536
  const nextApp2 = (_d = allAppsFull[nextApp2Index]) == null ? void 0 : _d.name;
12522
12537
  const prevSearchParams = new URLSearchParams(reqUrl.searchParams);
@@ -12558,9 +12573,9 @@ function DiffViewer() {
12558
12573
  const route24 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12559
12574
  __proto__: null,
12560
12575
  default: DiffViewer,
12561
- loader: loader$4
12576
+ loader: loader$6
12562
12577
  }, Symbol.toStringTag, { value: "Module" }));
12563
- async function loader$3({ request }) {
12578
+ async function loader$5({ request }) {
12564
12579
  const timings = makeTimings("appsLoader");
12565
12580
  const exercises = await getExercises({ request, timings });
12566
12581
  return json(
@@ -12570,11 +12585,11 @@ async function loader$3({ request }) {
12570
12585
  }
12571
12586
  const route26 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12572
12587
  __proto__: null,
12573
- loader: loader$3
12588
+ loader: loader$5
12574
12589
  }, Symbol.toStringTag, { value: "Module" }));
12575
12590
  const WIDTH = 1200;
12576
12591
  const HEIGHT = 630;
12577
- async function loader$2({ request }) {
12592
+ async function loader$4({ request }) {
12578
12593
  const timings = makeTimings("og", "og image loader");
12579
12594
  const url = new URL(request.url);
12580
12595
  const workshopTitle = await getWorkshopTitle();
@@ -12964,9 +12979,12 @@ function OgLayout({
12964
12979
  }
12965
12980
  const route29 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
12966
12981
  __proto__: null,
12967
- loader: loader$2
12982
+ loader: loader$4
12968
12983
  }, Symbol.toStringTag, { value: "Module" }));
12969
- async function loader$1({ request }) {
12984
+ const handle = {
12985
+ getSitemapEntries: () => null
12986
+ };
12987
+ async function loader$3({ request }) {
12970
12988
  const timings = makeTimings("onboarding");
12971
12989
  const tourUrl = "https://www.epicweb.dev/tips/get-started-with-the-epic-workshop-app";
12972
12990
  const videoInfos = getEpicVideoInfos([tourUrl], { request, timings });
@@ -13008,10 +13026,11 @@ const route30 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
13008
13026
  __proto__: null,
13009
13027
  action,
13010
13028
  default: Onboarding,
13029
+ handle,
13011
13030
  headers,
13012
- loader: loader$1
13031
+ loader: loader$3
13013
13032
  }, Symbol.toStringTag, { value: "Module" }));
13014
- async function loader() {
13033
+ async function loader$2() {
13015
13034
  ensureUndeployed();
13016
13035
  const processes = {};
13017
13036
  for (const [
@@ -13030,10 +13049,32 @@ async function loader() {
13030
13049
  return json({ processes, testProcesses: testProcesses2 });
13031
13050
  }
13032
13051
  const route31 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
13052
+ __proto__: null,
13053
+ loader: loader$2
13054
+ }, Symbol.toStringTag, { value: "Module" }));
13055
+ function loader$1({ request }) {
13056
+ return generateRobotsTxt([
13057
+ { type: "sitemap", value: `${getDomainUrl(request)}/sitemap.xml` }
13058
+ ]);
13059
+ }
13060
+ const route33 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
13061
+ __proto__: null,
13062
+ loader: loader$1
13063
+ }, Symbol.toStringTag, { value: "Module" }));
13064
+ async function loader({ request, context }) {
13065
+ const serverBuild = await context.serverBuild;
13066
+ return generateSitemap(request, serverBuild.routes, {
13067
+ siteUrl: getDomainUrl(request),
13068
+ headers: {
13069
+ "Cache-Control": `public, max-age=${60 * 5}`
13070
+ }
13071
+ });
13072
+ }
13073
+ const route35 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
13033
13074
  __proto__: null,
13034
13075
  loader
13035
13076
  }, Symbol.toStringTag, { value: "Module" }));
13036
- const serverManifest = { "entry": { "module": "/assets/entry.client-Cs8lBz8l.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.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-BUqeXmeK.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-DjzedX4O.js", "/assets/index-yEAxvbDV.js", "/assets/presence-CW1_eiIo.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/_-DYArYM0Z.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/error-boundary-BVTbN8PZ.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-dHX2Hcew.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-C6ToujzV.js", "/assets/user-CdUDQ7a8.js", "/assets/presence-CW1_eiIo.js", "/assets/progress-BwanvUpB.js", "/assets/index-yEAxvbDV.js"], "css": [] }, "routes/_app+/_exercises+/_layout": { "id": "routes/_app+/_exercises+/_layout", "parentId": "routes/_app+/_layout", "path": void 0, "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-DaZNLfOL.js", "imports": ["/assets/index-Czg1ruVn.js"], "css": [] }, "routes/_app+/_exercises+/$exerciseNumber": { "id": "routes/_app+/_exercises+/$exerciseNumber", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber-Ccwr4LZw.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/mdx-BENF-kTQ.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber/:stepNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber_._stepNumber-Ct5BCuA1.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js"], "css": [] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.$type+/_layout": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.$type+/_layout", "parentId": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber", "path": ":type", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_layout-Bn9QhWq9.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-C6ToujzV.js", "/assets/mdx-BENF-kTQ.js", "/assets/index-D6ukHE4T.js", "/assets/diff-CpG96hGZ.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js", "/assets/discord-yZor-3t1.js", "/assets/index-DjzedX4O.js", "/assets/button-BA3iiLRs.js", "/assets/use-event-source-ySol3hbz.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.index": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.index", "parentId": "routes/_app+/_exercises+/$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+/_exercises+/$exerciseNumber_.finished": { "id": "routes/_app+/_exercises+/$exerciseNumber_.finished", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber/finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_.finished-gEWcHsfL.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/mdx-BENF-kTQ.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js"], "css": ["/assets/epic-video-DUnRvy1A.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-uvU1jIL2.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/button-BA3iiLRs.js", "/assets/tooltip-BtzSIxlB.js", "/assets/user-CdUDQ7a8.js", "/assets/presence-CW1_eiIo.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-DP2rzg_V.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+/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-CgwNGD2p.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/user-CdUDQ7a8.js", "/assets/discord-yZor-3t1.js"], "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-B9qjUR4B.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/mdx-BENF-kTQ.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-BwanvUpB.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-3HxjMIAS.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/mdx-BENF-kTQ.js", "/assets/progress-BwanvUpB.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-CHE9W6po.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/use-event-source-ySol3hbz.js", "/assets/button-BA3iiLRs.js", "/assets/loading-B0uKxERz.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-D1ydJNdm.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.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-B9Y-8OU6.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/progress-BwanvUpB.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-Bd9WnUnR.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-DJN9_SUj.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-C6ToujzV.js", "/assets/mdx-BENF-kTQ.js", "/assets/diff-CpG96hGZ.js", "/assets/nav-chevrons-DaXg0NPS.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-FD1FkQuu.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/button-BA3iiLRs.js", "/assets/epic-video-DJN9_SUj.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/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/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-K6Dvbx-E.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-1375f1dd.js", "version": "1375f1dd" };
13077
+ const serverManifest = { "entry": { "module": "/assets/entry.client-Cs8lBz8l.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.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-BUqeXmeK.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-DjzedX4O.js", "/assets/index-yEAxvbDV.js", "/assets/presence-CW1_eiIo.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/_-DYArYM0Z.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/error-boundary-BVTbN8PZ.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-B789acDk.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-C6ToujzV.js", "/assets/user-CdUDQ7a8.js", "/assets/presence-CW1_eiIo.js", "/assets/progress-BwanvUpB.js", "/assets/index-yEAxvbDV.js"], "css": [] }, "routes/_app+/_exercises+/_layout": { "id": "routes/_app+/_exercises+/_layout", "parentId": "routes/_app+/_layout", "path": void 0, "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": false, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_layout-DLIzcS5v.js", "imports": ["/assets/index-Czg1ruVn.js"], "css": [] }, "routes/_app+/_exercises+/$exerciseNumber": { "id": "routes/_app+/_exercises+/$exerciseNumber", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber-BLQUCLPr.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/mdx-CQW0I4So.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber/:stepNumber", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_exerciseNumber_._stepNumber-Ct5BCuA1.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js"], "css": [] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.$type+/_layout": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.$type+/_layout", "parentId": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber", "path": ":type", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": true, "module": "/assets/_layout-DMuKczTu.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-C6ToujzV.js", "/assets/mdx-CQW0I4So.js", "/assets/index-D6ukHE4T.js", "/assets/diff-4FQLirNf.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js", "/assets/discord-BgaWmFRC.js", "/assets/index-DjzedX4O.js", "/assets/button-BA3iiLRs.js", "/assets/use-event-source-ySol3hbz.js"], "css": ["/assets/epic-video-DUnRvy1A.css"] }, "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.index": { "id": "routes/_app+/_exercises+/$exerciseNumber_.$stepNumber.index", "parentId": "routes/_app+/_exercises+/$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+/_exercises+/$exerciseNumber_.finished": { "id": "routes/_app+/_exercises+/$exerciseNumber_.finished", "parentId": "routes/_app+/_exercises+/_layout", "path": ":exerciseNumber/finished", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_exerciseNumber_.finished-C9TSc48F.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/mdx-CQW0I4So.js", "/assets/progress-BwanvUpB.js", "/assets/seo-pBpFCWsy.js"], "css": ["/assets/epic-video-DUnRvy1A.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-LnI_Eq0t.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/button-BA3iiLRs.js", "/assets/tooltip-BtzSIxlB.js", "/assets/user-CdUDQ7a8.js", "/assets/presence-CW1_eiIo.js"], "css": [] }, "routes/_app+/app.$appName+/$": { "id": "routes/_app+/app.$appName+/$", "parentId": "routes/_app+/_layout", "path": "app/:appName/*", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/api.$": { "id": "routes/_app+/app.$appName+/api.$", "parentId": "routes/_app+/_layout", "path": "app/:appName/api/*", "index": void 0, "caseSensitive": void 0, "hasAction": true, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/api._-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/epic_ws[.js]": { "id": "routes/_app+/app.$appName+/epic_ws[.js]", "parentId": "routes/_app+/_layout", "path": "app/:appName/epic_ws.js", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/epic_ws_.js_-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/index": { "id": "routes/_app+/app.$appName+/index", "parentId": "routes/_app+/_layout", "path": "app/:appName/", "index": true, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/index-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/app.$appName+/test.$testName": { "id": "routes/_app+/app.$appName+/test.$testName", "parentId": "routes/_app+/_layout", "path": "app/:appName/test/:testName", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/test._testName-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/_app+/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-Dk1wbXOn.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/user-CdUDQ7a8.js", "/assets/discord-BgaWmFRC.js"], "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-C0mN8TiU.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/nav-chevrons-DaXg0NPS.js", "/assets/mdx-CQW0I4So.js", "/assets/seo-pBpFCWsy.js", "/assets/progress-BwanvUpB.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-De7yI02n.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.js", "/assets/clsx-B-dksMZM.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-D6ukHE4T.js", "/assets/error-boundary-BVTbN8PZ.js", "/assets/mdx-CQW0I4So.js", "/assets/progress-BwanvUpB.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-SJlLMYmL.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/use-event-source-ySol3hbz.js", "/assets/button-BA3iiLRs.js", "/assets/loading-B0uKxERz.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-DilzTPHE.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/components-wgHiPsTg.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-CZ3fjUvs.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/progress-BwanvUpB.js"], "css": [] }, "routes/admin+/apps": { "id": "routes/admin+/apps", "parentId": "routes/admin+/_layout", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/admin+/cache": { "id": "routes/admin+/cache", "parentId": "routes/admin+/_layout", "path": "cache", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/cache-l0sNRNKZ.js", "imports": [], "css": [] }, "routes/apps": { "id": "routes/apps", "parentId": "root", "path": "apps", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/apps-DP2rzg_V.js", "imports": [], "css": [] }, "routes/diff": { "id": "routes/diff", "parentId": "root", "path": "diff", "index": void 0, "caseSensitive": void 0, "hasAction": false, "hasLoader": true, "hasClientAction": false, "hasClientLoader": false, "hasErrorBoundary": false, "module": "/assets/diff-CPrD1rHd.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/request-info-DHtGM4FI.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/epic-video-CfelX9-n.js", "/assets/progress-bar-wMXWRGq0.js", "/assets/index-C6ToujzV.js", "/assets/mdx-CQW0I4So.js", "/assets/diff-4FQLirNf.js", "/assets/nav-chevrons-DaXg0NPS.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-BXGyEPXy.js", "imports": ["/assets/index-Czg1ruVn.js", "/assets/clsx-B-dksMZM.js", "/assets/components-wgHiPsTg.js", "/assets/misc-S5ZD98sI.js", "/assets/request-info-DHtGM4FI.js", "/assets/tooltip-BtzSIxlB.js", "/assets/client-hints-DLYDs4RF.js", "/assets/index-yEAxvbDV.js", "/assets/loading-B0uKxERz.js", "/assets/user-CdUDQ7a8.js", "/assets/button-BA3iiLRs.js", "/assets/epic-video-CfelX9-n.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-K6Dvbx-E.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-38193299.js", "version": "38193299" };
13037
13078
  const mode = "production";
13038
13079
  const assetsBuildDirectory = "build/client";
13039
13080
  const basename = "/";
@@ -13306,13 +13347,29 @@ const routes = {
13306
13347
  caseSensitive: void 0,
13307
13348
  module: route32
13308
13349
  },
13350
+ "routes/robots[.]txt": {
13351
+ id: "routes/robots[.]txt",
13352
+ parentId: "root",
13353
+ path: "robots.txt",
13354
+ index: void 0,
13355
+ caseSensitive: void 0,
13356
+ module: route33
13357
+ },
13309
13358
  "routes/set-playground": {
13310
13359
  id: "routes/set-playground",
13311
13360
  parentId: "root",
13312
13361
  path: "set-playground",
13313
13362
  index: void 0,
13314
13363
  caseSensitive: void 0,
13315
- module: route33
13364
+ module: route34
13365
+ },
13366
+ "routes/sitemap[.]xml": {
13367
+ id: "routes/sitemap[.]xml",
13368
+ parentId: "root",
13369
+ path: "sitemap.xml",
13370
+ index: void 0,
13371
+ caseSensitive: void 0,
13372
+ module: route35
13316
13373
  },
13317
13374
  "routes/start": {
13318
13375
  id: "routes/start",
@@ -13320,7 +13377,7 @@ const routes = {
13320
13377
  path: "start",
13321
13378
  index: void 0,
13322
13379
  caseSensitive: void 0,
13323
- module: route34
13380
+ module: route36
13324
13381
  },
13325
13382
  "routes/test": {
13326
13383
  id: "routes/test",
@@ -13328,7 +13385,7 @@ const routes = {
13328
13385
  path: "test",
13329
13386
  index: void 0,
13330
13387
  caseSensitive: void 0,
13331
- module: route35
13388
+ module: route37
13332
13389
  },
13333
13390
  "routes/theme/index": {
13334
13391
  id: "routes/theme/index",
@@ -13336,7 +13393,7 @@ const routes = {
13336
13393
  path: "theme",
13337
13394
  index: void 0,
13338
13395
  caseSensitive: void 0,
13339
- module: route36
13396
+ module: route38
13340
13397
  },
13341
13398
  "routes/update-mdx-cache": {
13342
13399
  id: "routes/update-mdx-cache",
@@ -13344,7 +13401,7 @@ const routes = {
13344
13401
  path: "update-mdx-cache",
13345
13402
  index: void 0,
13346
13403
  caseSensitive: void 0,
13347
- module: route37
13404
+ module: route39
13348
13405
  },
13349
13406
  "routes/video-player/index": {
13350
13407
  id: "routes/video-player/index",
@@ -13352,7 +13409,7 @@ const routes = {
13352
13409
  path: "video-player",
13353
13410
  index: void 0,
13354
13411
  caseSensitive: void 0,
13355
- module: route38
13412
+ module: route40
13356
13413
  }
13357
13414
  };
13358
13415
  export {